{"id":4866,"date":"2026-11-11T18:45:56","date_gmt":"2026-11-11T16:45:56","guid":{"rendered":"https:\/\/mariusbancila.ro\/blog\/?p=4866"},"modified":"2026-11-11T18:45:57","modified_gmt":"2026-11-11T16:45:57","slug":"lock-free-data-structures-and-the-cpp26-hazard_pointer","status":"publish","type":"post","link":"https:\/\/mariusbancila.ro\/blog\/2026\/11\/11\/lock-free-data-structures-and-the-cpp26-hazard_pointer\/","title":{"rendered":"Lock-free data structures and the C++26 hazard_pointer"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">The C++26 standard features a new header called <code>&lt;hazard_pointer><\/code> and a new class with the same name, which, together with <code>hazard_pointer_obj_base<\/code>, provides safe memory reclamation for lock-free data structures. In this article, we&#8217;ll see how all this works.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The memory reclamation problem<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">To understand why <code>hazard_pointer<\/code> exists, we first need to look at what makes memory management in concurrent code so difficult.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Imagine a container, such as a linked list shared by dozens of threads. Most of them only read it; occasionally one removes a node. Removing the node is easy. Deciding when to free it is not, because another thread may have loaded a pointer to that node a moment earlier and be about to dereference it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The traditional answer is a mutex: hold the lock while reading or modifying the list, and nobody can be inside it when you delete a node. But a lock makes every thread depend on whichever thread currently holds it. If that thread is preempted, page-faults, or stalls for any reason, everyone else waits.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Concurrent algorithms are often classified by how they handle this:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><em>Blocking<\/em> algorithms, like anything built on a mutex, allow one stalled thread to hold up the others, possible indefinitely. Deadlocks can occur when two or more threads each wait for a resource held by another, so none of them can ever proceed.<\/li>\n\n\n\n<li><em>Lock-free<\/em> algorithms guarantee that at least one thread always makes progress, no matter what the others are doing. Even if some threads are paused, slowed down, or de-scheduled at the worst possible moment, at least one other thread can still finish its operation in a finite number of steps.<\/li>\n\n\n\n<li><em>Wait-free<\/em> algorithms go further and guarantee that every thread finishes its operation in a bounded number of steps.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Lock-free data structure are usually built with atomic compare-and-swap (CAS) loops. Each operation reads the current state, computes the new state, and tries to install it atomically, retrying if another thread changed things in the meantime. The following snippet shows a possible implementation for push() on a lock-free stack:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void push(Node* n) {\n    Node* old = head.load();\n    do {\n        n->next = old;\n    } while (!head.compare_exchange_weak(old, n));\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Apart from spurious failures, which <code>compare_exchange_weak<\/code> is allowed to have, a failed CAS means another thread\u2019s operation succeeded, and that&#8217;s the indication that the system as a whole is making progress. On the edge case, in theory, it&#8217;s possible that a thread can keep losing and retrying forever. This is called starvation; lock-free algorithms allow it, wait-free algorithms do not.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Lock-free and wait-free algorithms avoid the stalled-thread problem, but they bring back the original question in a harder form: with no lock, a writer has no way to know whether any reader still holds a pointer to the node it wants to free. To understand this better, let&#8217;s look at another example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Node* pop() {\n    Node* old = head.load();\n    while (old &amp;&amp; !head.compare_exchange_weak(old, old->next)) {}\n    return old;   \/\/ the caller now owns old, but when can it be deleted?\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>pop()<\/code> function loads the current head and tries to swing <code>head<\/code> to the next node. If the CAS succeeds, the popped node belongs to the caller, who will eventually want to delete it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The problem is the expression <code>old->next<\/code>. Between loading <code>head<\/code> and reading <code>old->next<\/code>, another thread may pop the same node and delete it, so the first thread ends up dereferencing freed memory. Even if the read happens to work, a subtler problem remains, known as the ABA problem.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A CAS checks only whether a value is the same as before. It cannot tell whether the value changed in between and then changed back. Suppose the first thread reads node A as the head and A\u2019s <code>next<\/code> as B, and then gets preempted. Meanwhile, another thread pops A and B and deletes A. A third thread pushes a new node, and the allocator happens to place it at A&#8217;s old address, so the head holds the same address A again. When the first thread resumes, its CAS sees the address A it expected and succeeds, setting <code>head<\/code> to B, which has already been popped and may have been freed. The stack is now corrupted.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A mutex would prevent both issues, but it would also bring back everything we wanted to avoid. What we need instead is a way for a thread to announce, cheaply, \u201cI am about to use this node, don&#8217;t free it yet,\u201d and a way for the thread that removed the node to defer deleting it until no one has made that announcement. This is exactly what <code>std::hazard_pointer<\/code> in C++26 provides.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How hazard pointers work<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The idea behind hazard pointers is simple: before a thread uses an object, it tells everyone else; before a thread frees an object, it checks whether anyone has said they&#8217;re using it. This works for any object that threads reach through a shared atomic pointer. It could be a node in a linked list or a stack, an entry in a hash map, or a single configuration object that is replaced from time to time.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The announcement is made through a hazard pointer. A hazard pointer is just a pointer variable that all threads can read. Each one is owned by a single thread at a time. Only the owner writes to it. Everyone else only reads it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You can picture all hazard pointers as a shared table of slots. When a thread wants to use an object, it writes the object&#8217;s address into one of its slots. The slot now &#8220;points to&#8221; that object. As long as the address stays there, the object must not be freed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A reader follows four steps:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Load the pointer to the object.<\/li>\n\n\n\n<li>Write that address into its hazard pointer.<\/li>\n\n\n\n<li>Load the pointer again and check that it hasn&#8217;t changed.<\/li>\n\n\n\n<li>Use the object. When done, clear the hazard pointer.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Step 3 is essential. Between steps 1 and 2, another thread could remove the object and free it. Announcing it afterwards would be too late. Re-reading the pointer catches this. If the value is still the same, the object was still reachable after our announcement became visible. From that moment on, nobody will free it. If the value changed, the thread simply starts over.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A writer follows a different path. It first makes the object unreachable, so that no new reader can find it. For a stack, this means unlinking the node. For a configuration object, it means replacing the shared pointer with a pointer to a new object. But the writer does not delete the old object. Instead, it retires it. Retiring means putting the object on a list of objects waiting to be freed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">From time to time, the library processes this list. It reads every hazard pointer and collects all the addresses it finds. Then it frees every retired object whose address is not in that collection. Objects that are still protected stay on the list until a later pass.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This scan is relatively expensive, so it doesn&#8217;t happen on every retire. It runs only after enough objects have piled up. This keeps the cost low on average.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The number of objects waiting to be freed is always limited. Only objects that are currently protected can stay on the list, and there are only as many of those as there are hazard pointers in use. A stalled thread can keep alive only the few objects it has protected, and nothing more.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The slots are not created per thread in advance. They are allocated when threads ask for them. When a thread no longer needs a slot, the slot is marked free and can be reused by any thread.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">One thing to keep in mind: hazard pointers guarantee only that an object is not freed while you use it. They do not protect the object&#8217;s contents from being modified. In practice, objects are usually not changed after they become visible to other threads.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The C++26 API<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>std::hazard_pointer<\/code> class and everything related lives in the header <code>&lt;hazard_pointer><\/code>. There are two main components.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The first is the class template <code>std::hazard_pointer_obj_base&lt;T, D><\/code>. Objects that you want to protect must derive from it. <code>T<\/code> is your own type. <code>D<\/code> is a deleter and defaults to <code>std::default_delete<\/code>. The base class gives your type a member function <code>retire()<\/code>. Calling it hands the object over to the library. The library will delete it later, when no hazard pointer refers to it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The second is the class <code>std::hazard_pointer<\/code>. Despite its name, it is not a pointer you dereference. It is a handle that owns one hazard pointer slot. You create one with <code>std::make_hazard_pointer()<\/code>. It can be moved but not copied. When it is destroyed, its slot is cleared and released.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Its most important member functions are:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>protect(src)<\/code> takes a <code>std::atomic&lt;T*><\/code>. It loads the pointer, publishes it, and checks it again, retrying until it succeeds. It returns a pointer that is safe to use.<\/li>\n\n\n\n<li><code>try_protect(ptr, src)<\/code> makes a single attempt and returns whether it succeeded.<\/li>\n\n\n\n<li><code>reset_protection(ptr)<\/code> publishes a pointer you already have. Called with no argument or with <code>nullptr<\/code>, it clears the protection.<\/li>\n\n\n\n<li><code>empty()<\/code> tells whether the handle owns a slot. A default-constructed <code>hazard_pointer<\/code> owns none.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s put this together and consider the linked-list from the previous exaple. First, the node must derive from <code>std::hazard_pointer_obj_base<\/code>:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#include &lt;atomic>\n#include &lt;hazard_pointer>\n#include &lt;optional>\n\nstruct Node : std::hazard_pointer_obj_base&lt;Node>\n{\n   int value;\n   Node* next;\n};\n\nstd::atomic&lt;Node*> head{nullptr};<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>push()<\/code> function stays the same as before. It never reads through a pointer that another thread might free, so it needs no protection:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void push(int value)\n{\n   Node* n = new Node;\n   n->value = value;\n   n->next = head.load();\n   while (!head.compare_exchange_weak(n->next, n)) {}\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>pop()<\/code> function changes. It now returns the value instead of the node, because the caller is no longer allowed to delete the node directly:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">std::optional&lt;int> pop()\n{\n   std::hazard_pointer hp = std::make_hazard_pointer();\n   Node* old = hp.protect(head);\n   while (old) {\n       Node* next = old->next;        \/\/ safe: old is protected\n       if (head.compare_exchange_weak(old, next)) {\n           int value = old->value;\n           old->retire();             \/\/ freed later, not now\n           return value;\n       }\n       old = hp.protect(head);        \/\/ CAS failed: protect the new head\n   }\n   return std::nullopt;\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s walk through it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">First, we get a hazard pointer with <code>make_hazard_pointer()<\/code>. Then <code>protect(head)<\/code> loads the head and protects it. From now on, the node <code>old<\/code> cannot be freed, so reading <code>old->next<\/code> is safe.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Next, we try to swing head to next. If the CAS succeeds, the node is ours. No new thread can reach it anymore. We read its value and call <code>retire()<\/code>. We don&#8217;t call <code>delete<\/code>, because other threads may have protected the same node a moment ago. The library will free it once they are done.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If the CAS fails, <code>old<\/code> now holds the current head. But that node is not protected yet. So we call <code>protect(head)<\/code> again before using it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When the function returns, <code>hp<\/code> is destroyed and the protection is cleared.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This version also solves the ABA problem. ABA happened because a node was freed and its address was reused for a new node. Now, as long as we protect <code>old<\/code>, it cannot be freed. Its address cannot be reused. So if the CAS sees the same address, it really is the same node, and next is still correct.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Pitfalls and other considerations<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Hazard pointers make memory reclamation safe, but only if they are used correctly. There are some things that you should keep in mind when you use them.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Protect before every dereference.<\/strong> A pointer is safe only after it has been protected. This includes pointers you get from a failed CAS. In <code>pop()<\/code>, that is why we call <code>protect()<\/code> again after a failure. Reading through an unprotected pointer brings back the use-after-free bug.<\/li>\n\n\n\n<li><strong>Protection does not last forever.<\/strong> A pointer is protected only while its <code>hazard_pointer<\/code> exists and holds that address. Once the <code>hazard_pointer<\/code> is destroyed, reset, or used to protect something else, the old pointer is no longer safe. Don&#8217;t keep it around.<\/li>\n\n\n\n<li><strong>Never delete, always retire.<\/strong> An object that other threads might still see must not be deleted directly. Call <code>retire()<\/code> instead. Retire an object only once, and only after it has been made unreachable. If an object is retired while it is still reachable, new readers can find it after it has been freed.<\/li>\n\n\n\n<li><strong>Freeing is delayed.<\/strong> Retired objects are not freed right away. Memory use is therefore a bit higher than with immediate deletion. The destructor and deleter also run at a later time and possibly on a different thread. They must not depend on running in the thread that called <code>retire()<\/code>.<\/li>\n\n\n\n<li><strong>There is no way to wait for reclamation.<\/strong> C++26 doesn&#8217;t provide a function that waits until retired objects are actually freed. This can matter at shutdown, or when an object holds a resource that must be released at a known time. A follow-up proposal aims to add this in a later standard.<\/li>\n\n\n\n<li><strong>Only the lifetime is protected.<\/strong> A hazard pointer guarantees that an object exists. It does not prevent another thread from changing the object&#8217;s data. Either treat objects as immutable once they are published, or synchronize access to their fields separately.<\/li>\n\n\n\n<li><strong>Protection is not free.<\/strong> <code>make_hazard_pointer()<\/code> has to obtain a slot. Implementations cache slots per thread, so it is usually cheap, but it still has a cost. In a hot loop, create one <code>hazard_pointer<\/code> and reuse it.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">For the example of linked-lists that we discussed, mind that traversals need more than one hazard pointer. Walking a linked list requires protecting the current node and the next one at the same time. You need two (sometimes three) <code>hazard_pointer<\/code> objects and you swap them as you move forward.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>hazard_pointer<\/code> not the only solution lock-free code. C++26 also adds <code>&lt;rcu><\/code> header (short for read-copy-update). RCU protects a whole region of code instead of individual pointers. Reads are even cheaper, but one stalled reader can block all reclamation. Hazard pointers cost a bit more per pointer, but they keep the amount of unreclaimed memory limited.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Unfortunately, at the time of writing, no major compiler has support for <code>&lt;hazard_pointer><\/code>. If you want to experiment today, Facebook\u2019s Folly library has an implementation with an almost identical API in <code>folly\/synchronization\/HazPtr.h<\/code>. The C++26 design is based on it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">See also<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/www.open-std.org\/jtc1\/sc22\/wg21\/docs\/papers\/2023\/p2530r3.pdf\" target=\"_blank\" rel=\"noopener\">Hazard pointers for C++26<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/towardsdev.com\/cpp26-hazard-pointers-fix-aba-problem-lock-free-08b4b1db8a01\" target=\"_blank\" rel=\"noopener\">C++26 Hazard Pointers: Safe Memory Reclamation for Lock-Free Code<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.modernescpp.com\/index.php\/hazard-pointers-in-c26\/\" target=\"_blank\" rel=\"noopener\">Hazard Pointers in C++26<\/a><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The C++26 standard features a new header called &lt;hazard_pointer> and a new class with the same name, which, together with hazard_pointer_obj_base, provides safe memory reclamation for lock-free data structures. In this article, we&#8217;ll see how all this works. The memory reclamation problem To understand why hazard_pointer exists, we first need to look at what makes &#8230; <a title=\"Lock-free data structures and the C++26 hazard_pointer\" class=\"read-more\" href=\"https:\/\/mariusbancila.ro\/blog\/2026\/11\/11\/lock-free-data-structures-and-the-cpp26-hazard_pointer\/\" aria-label=\"Read more about Lock-free data structures and the C++26 hazard_pointer\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_uag_custom_page_level_css":"","advgb_blocks_editor_width":"","advgb_blocks_columns_visual_guide":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[7],"tags":[451,647,194,658],"class_list":["post-4866","post","type-post","status-publish","format-standard","hentry","category-c","tag-c","tag-c26","tag-concurrency","tag-hazard_pointer"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.4 - aioseo.com -->\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Marius Bancila\"\/>\n\t<meta name=\"keywords\" content=\"c++,c++26,concurrency,hazard_pointer\" \/>\n\t<link rel=\"canonical\" href=\"https:\/\/mariusbancila.ro\/blog\/2026\/11\/11\/lock-free-data-structures-and-the-cpp26-hazard_pointer\/\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.4\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"Marius Bancila&#039;s Blog | About code. Mostly on C++\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Lock-free data structures and the C++26 hazard_pointer\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/mariusbancila.ro\/blog\/2026\/11\/11\/lock-free-data-structures-and-the-cpp26-hazard_pointer\/\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2026-11-11T16:45:56+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2026-11-11T16:45:57+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Lock-free data structures and the C++26 hazard_pointer\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2026\\\/11\\\/11\\\/lock-free-data-structures-and-the-cpp26-hazard_pointer\\\/#article\",\"name\":\"Lock-free data structures and the C++26 hazard_pointer\",\"headline\":\"Lock-free data structures and the C++26 hazard_pointer\",\"author\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#organization\"},\"datePublished\":\"2026-11-11T18:45:56+02:00\",\"dateModified\":\"2026-11-11T18:45:57+02:00\",\"inLanguage\":\"en-US\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2026\\\/11\\\/11\\\/lock-free-data-structures-and-the-cpp26-hazard_pointer\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2026\\\/11\\\/11\\\/lock-free-data-structures-and-the-cpp26-hazard_pointer\\\/#webpage\"},\"articleSection\":\"C++, C++, C++26, concurrency, hazard_pointer\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2026\\\/11\\\/11\\\/lock-free-data-structures-and-the-cpp26-hazard_pointer\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/#listItem\",\"name\":\"IT\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/#listItem\",\"position\":2,\"name\":\"IT\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/#listItem\",\"name\":\"Software\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/#listItem\",\"position\":3,\"name\":\"Software\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/#listItem\",\"name\":\"C++\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/#listItem\",\"name\":\"IT\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/#listItem\",\"position\":4,\"name\":\"C++\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2026\\\/11\\\/11\\\/lock-free-data-structures-and-the-cpp26-hazard_pointer\\\/#listItem\",\"name\":\"Lock-free data structures and the C++26 hazard_pointer\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/#listItem\",\"name\":\"Software\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2026\\\/11\\\/11\\\/lock-free-data-structures-and-the-cpp26-hazard_pointer\\\/#listItem\",\"position\":5,\"name\":\"Lock-free data structures and the C++26 hazard_pointer\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/#listItem\",\"name\":\"C++\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#organization\",\"name\":\"Marius Bancila's Blog\",\"description\":\"About code. Mostly on C++\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/\",\"name\":\"Marius Bancila\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2026\\\/11\\\/11\\\/lock-free-data-structures-and-the-cpp26-hazard_pointer\\\/#authorImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/a84dd2831d955c38355ddea55df4df260809b88f36408bc14fd4eab8f7f131c9?s=96&d=mm&r=g\",\"width\":96,\"height\":96,\"caption\":\"Marius Bancila\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2026\\\/11\\\/11\\\/lock-free-data-structures-and-the-cpp26-hazard_pointer\\\/#webpage\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2026\\\/11\\\/11\\\/lock-free-data-structures-and-the-cpp26-hazard_pointer\\\/\",\"name\":\"Lock-free data structures and the C++26 hazard_pointer\",\"inLanguage\":\"en-US\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2026\\\/11\\\/11\\\/lock-free-data-structures-and-the-cpp26-hazard_pointer\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\"},\"datePublished\":\"2026-11-11T18:45:56+02:00\",\"dateModified\":\"2026-11-11T18:45:57+02:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/\",\"name\":\"Marius Bancila's Blog\",\"description\":\"About code. Mostly on C++\",\"inLanguage\":\"en-US\",\"publisher\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"Lock-free data structures and the C++26 hazard_pointer","description":"","canonical_url":"https:\/\/mariusbancila.ro\/blog\/2026\/11\/11\/lock-free-data-structures-and-the-cpp26-hazard_pointer\/","robots":"max-image-preview:large","keywords":"c++,c++26,concurrency,hazard_pointer","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/mariusbancila.ro\/blog\/2026\/11\/11\/lock-free-data-structures-and-the-cpp26-hazard_pointer\/#article","name":"Lock-free data structures and the C++26 hazard_pointer","headline":"Lock-free data structures and the C++26 hazard_pointer","author":{"@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author"},"publisher":{"@id":"https:\/\/mariusbancila.ro\/blog\/#organization"},"datePublished":"2026-11-11T18:45:56+02:00","dateModified":"2026-11-11T18:45:57+02:00","inLanguage":"en-US","mainEntityOfPage":{"@id":"https:\/\/mariusbancila.ro\/blog\/2026\/11\/11\/lock-free-data-structures-and-the-cpp26-hazard_pointer\/#webpage"},"isPartOf":{"@id":"https:\/\/mariusbancila.ro\/blog\/2026\/11\/11\/lock-free-data-structures-and-the-cpp26-hazard_pointer\/#webpage"},"articleSection":"C++, C++, C++26, concurrency, hazard_pointer"},{"@type":"BreadcrumbList","@id":"https:\/\/mariusbancila.ro\/blog\/2026\/11\/11\/lock-free-data-structures-and-the-cpp26-hazard_pointer\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/#listItem","position":1,"name":"Home","item":"https:\/\/mariusbancila.ro\/blog\/","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/#listItem","name":"IT"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/#listItem","position":2,"name":"IT","item":"https:\/\/mariusbancila.ro\/blog\/category\/it\/","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/#listItem","name":"Software"},"previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/#listItem","position":3,"name":"Software","item":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/#listItem","name":"C++"},"previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/#listItem","name":"IT"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/#listItem","position":4,"name":"C++","item":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/2026\/11\/11\/lock-free-data-structures-and-the-cpp26-hazard_pointer\/#listItem","name":"Lock-free data structures and the C++26 hazard_pointer"},"previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/#listItem","name":"Software"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/2026\/11\/11\/lock-free-data-structures-and-the-cpp26-hazard_pointer\/#listItem","position":5,"name":"Lock-free data structures and the C++26 hazard_pointer","previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/#listItem","name":"C++"}}]},{"@type":"Organization","@id":"https:\/\/mariusbancila.ro\/blog\/#organization","name":"Marius Bancila's Blog","description":"About code. Mostly on C++","url":"https:\/\/mariusbancila.ro\/blog\/"},{"@type":"Person","@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author","url":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/","name":"Marius Bancila","image":{"@type":"ImageObject","@id":"https:\/\/mariusbancila.ro\/blog\/2026\/11\/11\/lock-free-data-structures-and-the-cpp26-hazard_pointer\/#authorImage","url":"https:\/\/secure.gravatar.com\/avatar\/a84dd2831d955c38355ddea55df4df260809b88f36408bc14fd4eab8f7f131c9?s=96&d=mm&r=g","width":96,"height":96,"caption":"Marius Bancila"}},{"@type":"WebPage","@id":"https:\/\/mariusbancila.ro\/blog\/2026\/11\/11\/lock-free-data-structures-and-the-cpp26-hazard_pointer\/#webpage","url":"https:\/\/mariusbancila.ro\/blog\/2026\/11\/11\/lock-free-data-structures-and-the-cpp26-hazard_pointer\/","name":"Lock-free data structures and the C++26 hazard_pointer","inLanguage":"en-US","isPartOf":{"@id":"https:\/\/mariusbancila.ro\/blog\/#website"},"breadcrumb":{"@id":"https:\/\/mariusbancila.ro\/blog\/2026\/11\/11\/lock-free-data-structures-and-the-cpp26-hazard_pointer\/#breadcrumblist"},"author":{"@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author"},"creator":{"@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author"},"datePublished":"2026-11-11T18:45:56+02:00","dateModified":"2026-11-11T18:45:57+02:00"},{"@type":"WebSite","@id":"https:\/\/mariusbancila.ro\/blog\/#website","url":"https:\/\/mariusbancila.ro\/blog\/","name":"Marius Bancila's Blog","description":"About code. Mostly on C++","inLanguage":"en-US","publisher":{"@id":"https:\/\/mariusbancila.ro\/blog\/#organization"}}]},"og:locale":"en_US","og:site_name":"Marius Bancila's Blog | About code. Mostly on C++","og:type":"article","og:title":"Lock-free data structures and the C++26 hazard_pointer","og:url":"https:\/\/mariusbancila.ro\/blog\/2026\/11\/11\/lock-free-data-structures-and-the-cpp26-hazard_pointer\/","article:published_time":"2026-11-11T16:45:56+00:00","article:modified_time":"2026-11-11T16:45:57+00:00","twitter:card":"summary","twitter:title":"Lock-free data structures and the C++26 hazard_pointer"},"aioseo_meta_data":{"post_id":"4866","title":null,"description":null,"keywords":null,"keyphrases":{"focus":{"keyphrase":"","score":0,"analysis":{"keyphraseInTitle":{"score":0,"maxScore":9,"error":1}}},"additional":[]},"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":"","og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"Article","isEnabled":true},"graphs":[]},"schema_type":"default","schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":"-1","robots_max_videopreview":"-1","robots_max_imagepreview":"large","priority":null,"frequency":"default","location":null,"local_seo":null,"breadcrumb_settings":null,"limit_modified_date":false,"ai":{"faqs":[],"keyPoints":[],"schemas":[],"titles":[],"descriptions":[],"socialPosts":{"email":{"subject":"","preview":"","content":""},"linkedin":[],"twitter":[],"facebook":[],"instagram":[]}},"created":"2026-11-11 15:21:16","updated":"2026-11-11 16:45:57","seo_analyzer_scan_date":null,"focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"author_meta":{"display_name":"Marius Bancila","author_link":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/"},"featured_img":null,"jetpack_publicize_connections":[],"uagb_featured_image_src":{"full":false,"thumbnail":false,"medium":false,"medium_large":false,"large":false,"1536x1536":false,"2048x2048":false},"uagb_author_info":{"display_name":"Marius Bancila","author_link":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/"},"uagb_comment_info":1,"uagb_excerpt":"The C++26 standard features a new header called &lt;hazard_pointer> and a new class with the same name, which, together with hazard_pointer_obj_base, provides safe memory reclamation for lock-free data structures. In this article, we&#8217;ll see how all this works. The memory reclamation problem To understand why hazard_pointer exists, we first need to look at what makes&hellip;","coauthors":[],"tax_additional":{"categories":{"linked":["<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">C++<\/a>"],"unlinked":["<span class=\"advgb-post-tax-term\">C++<\/span>"]},"tags":{"linked":["<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">C++<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">C++26<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">concurrency<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">hazard_pointer<\/a>"],"unlinked":["<span class=\"advgb-post-tax-term\">C++<\/span>","<span class=\"advgb-post-tax-term\">C++26<\/span>","<span class=\"advgb-post-tax-term\">concurrency<\/span>","<span class=\"advgb-post-tax-term\">hazard_pointer<\/span>"]}},"comment_count":"0","relative_dates":{"created":"Posted 1 hour ago","modified":"Updated 1 hour ago"},"absolute_dates":{"created":"Posted on November 11, 2026","modified":"Updated on November 11, 2026"},"absolute_dates_time":{"created":"Posted on November 11, 2026 6:45 pm","modified":"Updated on November 11, 2026 6:45 pm"},"featured_img_caption":"","series_order":"","jetpack_shortlink":"https:\/\/wp.me\/pYNdv-1gu","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts\/4866","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/comments?post=4866"}],"version-history":[{"count":5,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts\/4866\/revisions"}],"predecessor-version":[{"id":4871,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts\/4866\/revisions\/4871"}],"wp:attachment":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/media?parent=4866"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/categories?post=4866"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/tags?post=4866"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}