Hazard Pointers in C++26

Hazard pointers provide garbage collection in C++ and solve the ABA problem.

First of all. What is a hazard pointer? Proposal P2530R3 gives a nice explanation:

A hazard pointer is a single-writer multi-reader pointer that can be owned by at most one thread at any time. Only the owner of the hazard pointer can set its value, while any number of threads may read its value. A thread that is about to access dynamic objects acquires ownership of hazard pointer(s) to protect such objects from being reclaimed. The owner thread sets the value of a hazard pointer to point to an object in order to indicate to concurrent threads — that may remove such object — that the object is not yet safe to reclaim.
Hazard pointers are owned and written by threads that act as accessors/protectors (i.e., protect removable objects from unsafe reclamation in order to access such objects) and are read by threads that act as removers/reclaimers (i.e., may remove and try to reclaim objects). Removers retire removed objects to the hazard pointer library (i.e., pass the responsibility for reclaiming the objects to the library code rather than normally by user code). The set of protector and remover threads may overlap.

To make it short, Hazard Pointers guarantee that the referenced objects are deleted if they are no longer needed.

Hazard pointers perform and interact with protection and deferred reclamation.

Protection

They guarantee that objects are only destroyed if they are no longer needed.

Deferred Reclamation

Collect the retired objects and extract their values into a set. Read the values of all hazard pointers and compare them with the addresses of the extracted values in the set. If a value from the set is not found in all values of the hazard pointers, it can be destroyed. If found, it will go back in the set of retired objects.

So, what are the advantages of hazard pointers? My answer consists of two points: correctness and performance.

Advantage

I would like to start with correctness.

Correctness

The advantage of hazard pointers can be illustrated with a simple example.

 

Rainer D 6 P2 500x500Modernes C++ Mentoring

  • "Fundamentals for C++ Professionals" (open)
  • "Design Patterns and Architectural Patterns with C++" (open)
  • "C++20: Get the Details" (open)
  • "Concurrency with Modern C++" (open)
  • "Embedded Programming with Modern C++": January 2025
  • "Generic Programming (Templates) with C++": February 2025
  • "Clean Code: Best Practices for Modern C++": May 2025
  • Do you want to stay informed: Subscribe.

     

    Node* currentNode = this->head;
    Node* nextNode = currentNode->next;
    

    The critical question regarding this small code snippet is: how can we guarantee that currentNode is still valid? While one thread is executing this code, another thread may already be executing currentNode.

    Atomic compare-and-swap (CAS) operations exacerbate this problem. In C++, the compare_exchange_strong operation is typically used for this purpose. However, CAS operations suffer from the ABA problem. I discuss the ABA problem in more detail in my article Deferred Reclamation in C++26: Read-Copy Update and Hazard Pointers. The solution to the problem is relatively obvious: automatic garbage collection. This is exactly what hazard pointers offer.

    Performance


    With C++14, reader-writer locks were introduced. With these special locks, reading threads are treated differently than writing threads. This means that any number of reading threads can be executed simultaneously, but only one writing thread. As a result, reader-writer locks promise a performance improvement over exclusive locks. More details on reader-writer locks can be found in my earlier article Reader-Writer Locks.


    Proposal P2530R3 provides a nice example of a data structure that is predominantly read. A classic reader-writer lock and hazard pointer are used.



    Typical latency of hazard_pointer construction/destruction in the Folly implementation on contemporary commodity server is approximately 4 ns. Using a pre-constructed hazard_pointer typically takes under one nanosecond to stop for protection.

    Facebook’s open library Folly provides the reference implementation of hazard pointers.

    Interface



    Hazard Pointers consisting of the two classes hazard_pointer_obj_base and hazard_pointer and the two functions make_hazard_pointer and swap.

    • hazard_pointer_obj_base is the base class of the protected class, and provides the function retire.
    • hazard_pointer provides the functions empty, protect, try_protect, reset_protection,and swap.
    • make_hazard_pointer creates a hazard pointer.

    What’s next?

    RCU stands for Read Copy Update, a synchronization technique for almost read-only data structures created by Paul McKenney and used in the Linux kernel since 2002. RCU is currently mentioned in connection with hazard pointers.







    Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Stephen Kelley, Kyle Dean, Tusar Palauri, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, Philipp Lenk, Charles-Jianye Chen, Keith Jeffery, Matt Godbolt, Honey Sukesan, bruce_lee_wayne, Silviu Ardelean, schnapper79, and Seeker.

    Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.

    My special thanks to Embarcadero
    My special thanks to PVS-Studio
    My special thanks to Tipi.build 
    My special thanks to Take Up Code
    My special thanks to SHAVEDYAKS

    Modernes C++ GmbH

    Modernes C++ Mentoring (English)

    Do you want to stay informed about my mentoring programs? Subscribe Here

    Rainer Grimm
    Yalovastraße 20
    72108 Rottenburg

    Mobil: +49 176 5506 5086
    Mail: schulung@ModernesCpp.de
    Mentoring: www.ModernesCpp.org