Implement the C++26 std::hazard_pointer facility per P2530R3
(working draft [32.11.3]).

Public API in namespace std:
  * hazard_pointer_obj_base<T, D> -- CRTP base providing retire().
  * hazard_pointer -- RAII handle to one slot in the default domain;
    protect(), try_protect(), reset_protection(), swap().
  * make_hazard_pointer() factory; free swap().
  * __cpp_lib_hazard_pointer == 202306L (already gated by the prior
    commit).

Implementation details (all in namespace std::__hazard_pointer):
  * _Domain -- process-global singleton owning the slot pool and the
    per-thread retire-list registry.  The slot pool is a
    std::deque<_Slot> so that _Slot addresses (captured by live
    hazard_pointer handles) remain stable across pool growth.  Each
    _Slot is padded to __GCC_DESTRUCTIVE_SIZE to avoid false sharing
    between threads using adjacent slots.
  * _Slot acquisition scans _M_slot_free (a vector<bool>) with
    std::find under _M_slot_free_mutex; release uses std::find_if
    for symmetry.  Both follow the documented outer-before-inner
    lock ordering (_M_slot_free_mutex outer, _RetireListNode
    ::_M_list_mutex inner).
  * Per-thread retire list is bundled with its _RetireListNode in a
    thread_local _ThreadState struct so member-destruction order
    (reverse of declaration) guarantees the retire list outlives the
    registry node.  ~_RetireListNode() drains via _M_synchronize(),
    unregisters from the global list, then offloads any remaining
    survivors to _M_orphan_list for later reclamation.
  * _M_synchronize() uses collect-then-snapshot ordering: every
    thread's retire list is swapped under _M_list_mutex first, then
    a snapshot of every hazard slot is taken under
    _M_slot_free_mutex, and finally records not in the snapshot are
    reclaimed (no lock held -- deleters may re-enter retire() or
    synchronize() safely).  The opposite ordering trips a TSan
    false-positive because the cross-atomic SC reasoning is beyond
    TSan's vector-clock model.
  * Memory ordering follows P2530R3 [32.11.3.4.3]: reset_protection
    (T*) stores seq_cst, try_protect()'s reload is seq_cst, the
    snapshot's per-slot load is acquire.  release+acquire is
    insufficient on weak-memory targets -- the hazard store may sit
    in the store buffer while the snapshot reads null.
  * _Protectable concept enforces T derives from any
    hazard_pointer_obj_base<T, D>; retire() static_asserts the
    derivation is public and non-virtual, the deleter is invocable
    with T* and default-/move-assignable, and that _M_deleter sits
    at offset 0 of the base so the type-erased deleter can recover
    it from a void* via static_cast.

Preconditions use __glibcxx_assert.  Contracts integration is
deferred until libstdc++ exposes a stable "contracts actually
evaluated" macro distinct from __cpp_contracts; the prototype's
contract-violation death tests are not ported in this series.

libstdc++-v3/ChangeLog:

      * include/bits/hazard_ptr.h: Replace stub with full
      implementation of std::hazard_pointer,
      std::hazard_pointer_obj_base, std::make_hazard_pointer, and
      the supporting __hazard_pointer::_Domain.
---
 libstdc++-v3/include/bits/hazard_ptr.h | 533 +++++++++++++++++++++++++
 1 file changed, 533 insertions(+)

diff --git a/libstdc++-v3/include/bits/hazard_ptr.h 
b/libstdc++-v3/include/bits/hazard_ptr.h
index 3ffccfd2d19..728e38c5bc8 100644
--- a/libstdc++-v3/include/bits/hazard_ptr.h
+++ b/libstdc++-v3/include/bits/hazard_ptr.h
@@ -30,4 +30,537 @@
 #ifndef _GLIBCXX_HAZARD_PTR_H
 #define _GLIBCXX_HAZARD_PTR_H 1
 
+#include <algorithm>         // std::find, std::find_if, std::sort,
+#include <atomic>            // std::atomic, std::atomic_size_t
+#include <bits/move.h>       // std::move, std::swap
+#include <bits/unique_ptr.h> // std::default_delete
+#include <deque>             // std::deque
+#include <mutex>             // std::mutex, std::lock_guard
+#include <vector>            // std::vector
+                             //   std::binary_search
+#include <cstddef>           // std::size_t, offsetof
+#include <type_traits>       // std::is_class_v, std::is_base_of_v, etc.
+
+namespace std _GLIBCXX_VISIBILITY(default) {
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+template <typename _Tp, typename _Dp = default_delete<_Tp>>
+class hazard_pointer_obj_base;
+
+class hazard_pointer;
+
+[[nodiscard]] inline hazard_pointer make_hazard_pointer();
+
+namespace __hazard_pointer {
+// Empty tag privately inherited by hazard_pointer_obj_base<T,D>.
+// Lets _Protectable detect T derives from some instantiation without
+// knowing D; std::is_base_of ignores access specifiers.
+struct _Obj_base_tag {};
+
+template <typename _Tp>
+concept _Protectable = is_class_v<_Tp> && is_base_of_v<_Obj_base_tag, _Tp>;
+
+using _Deleter_fn = void (*)(void *);
+
+struct _RetireRecord {
+  void *_M_ptr = nullptr;
+  _Deleter_fn _M_deleter = nullptr;
+};
+
+struct _RetireListNode;
+
+// One hazard slot padded to a full cache line to prevent false sharing
+// between threads using adjacent slots.
+struct alignas(__GCC_DESTRUCTIVE_SIZE) _Slot {
+  atomic<void *> _M_value{nullptr};
+};
+
+// Owns the hazard slot pool and the per-thread retire lists.
+// Process-global singleton; construction restricted to _S_default_domain().
+//
+// Lock ordering (always acquire outer before inner to prevent deadlock):
+//   outer: _M_slot_free_mutex -- guards slot pool structure
+//   inner: _RetireListNode::_M_list_mutex -- guards one thread's list
+// _M_retire_lists_mutex is never held concurrently with _M_slot_free_mutex.
+class _Domain {
+public:
+  [[nodiscard]] atomic<void *> *_M_acquire_slot();
+  void _M_release_slot(atomic<void *> *__slot);
+  void _M_retire_impl(void *__ptr, _Deleter_fn __deleter);
+  void _M_synchronize();
+  [[nodiscard]] size_t _M_active_slots() const noexcept;
+  [[nodiscard]] size_t _M_retire_list_size() const noexcept;
+
+private:
+  static constexpr size_t _S_initial_slots = 8;
+
+  // _M_slot_free and _M_slots are kept separate so _M_acquire_slot scans
+  // the compact bool vector cheaply without touching padded slot memory.
+  // _M_slots uses deque so that _Slot addresses (held by live
+  // hazard_pointer objects as _M_slot) remain stable across push_back.
+  vector<bool> _M_slot_free; // guarded by _M_slot_free_mutex
+  mutex _M_slot_free_mutex;
+  deque<_Slot> _M_slots;             // structure guarded by _M_slot_free_mutex
+  atomic_size_t _M_active_count = 0; // mirrors slot occupancy; relaxed ok
+
+  vector<_RetireRecord> _M_orphan_list; // guarded by _M_orphan_mutex
+  mutex _M_orphan_mutex;
+
+  _RetireListNode *_M_retire_lists_head =
+      nullptr;                          // guarded by _M_retire_lists_mutex
+  size_t _M_retire_list_node_count = 0; // guarded by _M_retire_lists_mutex
+  mutex _M_retire_lists_mutex;
+
+  void _M_ensure_node_registered();
+  void _M_unregister_node(const _RetireListNode &__node) noexcept;
+
+  friend struct _RetireListNode;
+
+  _Domain();
+  ~_Domain();
+  friend _Domain &_S_default_domain() noexcept;
+};
+
+// Per-thread node in the retire_lists linked list.  Registered lazily on
+// first _M_retire_impl()/_M_synchronize() call; removed on thread exit.
+// _M_list_mutex guards *_M_list against concurrent access between
+// _M_synchronize() (any thread) and _M_retire_impl() (owning thread).
+struct _RetireListNode {
+  vector<_RetireRecord> *_M_list = nullptr;
+  mutex _M_list_mutex;
+  _RetireListNode *_M_next = nullptr;
+
+  ~_RetireListNode();
+};
+
+// Bundles the retire list and its registry node so that member
+// destruction order (reverse of declaration) guarantees _M_retire_list
+// outlives _M_node -- ~_RetireListNode() always sees a live list.
+struct _ThreadState {
+  vector<_RetireRecord> _M_retire_list; // destroyed second
+  _RetireListNode _M_node;              // destroyed first
+};
+
+inline thread_local _ThreadState _S_tl_state;
+
+inline _Domain &_S_default_domain() noexcept {
+  static _Domain __domain;
+  return __domain;
+}
+
+inline void __append_to(vector<_RetireRecord> &__dst,
+                        const vector<_RetireRecord> &__src) {
+  __dst.insert(__dst.end(), __src.begin(), __src.end());
+}
+
+} // namespace __hazard_pointer
+
+// -------------------------------------------------------------------------
+// hazard_pointer_obj_base
+// -------------------------------------------------------------------------
+
+template <typename _Tp, typename _Dp>
+class hazard_pointer_obj_base : private __hazard_pointer::_Obj_base_tag {
+public:
+  void retire(_Dp __d = _Dp()) noexcept;
+
+protected:
+  hazard_pointer_obj_base() = default;
+  hazard_pointer_obj_base(const hazard_pointer_obj_base &) = default;
+  hazard_pointer_obj_base(hazard_pointer_obj_base &&) = default;
+  hazard_pointer_obj_base &operator=(const hazard_pointer_obj_base &) = 
default;
+  hazard_pointer_obj_base &operator=(hazard_pointer_obj_base &&) = default;
+  ~hazard_pointer_obj_base() = default;
+
+private:
+  _Dp _M_deleter;
+};
+
+// -------------------------------------------------------------------------
+// hazard_pointer
+// -------------------------------------------------------------------------
+
+class hazard_pointer {
+public:
+  hazard_pointer() noexcept = default;
+
+  hazard_pointer(hazard_pointer &&__other) noexcept;
+
+  hazard_pointer &operator=(hazard_pointer &&__other) noexcept;
+
+  ~hazard_pointer();
+
+  hazard_pointer(const hazard_pointer &) = delete;
+  hazard_pointer &operator=(const hazard_pointer &) = delete;
+
+  [[nodiscard]] bool empty() const noexcept;
+
+  template <typename _Tp>
+  [[nodiscard]] _Tp *protect(const atomic<_Tp *> &__src) noexcept;
+
+  template <typename _Tp>
+  [[nodiscard]] bool try_protect(_Tp *&__ptr,
+                                 const atomic<_Tp *> &__src) noexcept;
+
+  template <typename _Tp> void reset_protection(const _Tp *__ptr) noexcept;
+
+  void reset_protection(nullptr_t = nullptr) noexcept;
+
+  void swap(hazard_pointer &__other) noexcept;
+
+private:
+  atomic<void *> *_M_slot = nullptr;
+
+  explicit hazard_pointer(atomic<void *> *__slot) noexcept;
+
+  friend hazard_pointer make_hazard_pointer();
+};
+
+// -------------------------------------------------------------------------
+// Free functions
+// -------------------------------------------------------------------------
+
+inline void swap(hazard_pointer &__a, hazard_pointer &__b) noexcept {
+  __a.swap(__b);
+}
+
+[[nodiscard]] inline hazard_pointer make_hazard_pointer() {
+  return hazard_pointer(
+      __hazard_pointer::_S_default_domain()._M_acquire_slot());
+}
+
+// -------------------------------------------------------------------------
+// hazard_pointer_obj_base implementation
+// -------------------------------------------------------------------------
+
+template <typename _Tp, typename _Dp>
+inline void hazard_pointer_obj_base<_Tp, _Dp>::retire(_Dp __d) noexcept {
+  static_assert(is_invocable_v<_Dp, _Tp *>,
+                "D must be invocable with T* -- [32.11.3.3]/1");
+  static_assert(is_default_constructible_v<_Dp>,
+                "D must be default-constructible -- [32.11.3.3]/3");
+  static_assert(is_move_assignable_v<_Dp>,
+                "D must be move-assignable -- [32.11.3.3]/3");
+  static_assert(is_base_of_v<hazard_pointer_obj_base<_Tp, _Dp>, _Tp>,
+                "T must derive from hazard_pointer_obj_base<T,D>"
+                " -- [32.11.3.3]/5");
+  static_assert(is_convertible_v<_Tp *, hazard_pointer_obj_base<_Tp, _Dp> *>,
+                "hazard_pointer_obj_base<T,D> must be a public base of T"
+                " -- [32.11.3.3]/5");
+  static_assert(!is_virtual_base_of_v<hazard_pointer_obj_base<_Tp, _Dp>, _Tp>,
+                "hazard_pointer_obj_base<T,D> must be a non-virtual base"
+                " of T -- [32.11.3.3]/5");
+
+  // _M_deleter sits at offset 0: hazard_pointer_obj_base has one empty
+  // private base (_Obj_base_tag, EBO) contributing zero bytes, so
+  // _M_deleter lands at offset 0 of the base subobject, which itself is
+  // at offset 0 of T (single non-virtual inheritance).  The retire lambda
+  // recovers the deleter via a cast to hazard_pointer_obj_base*.
+  static_assert(offsetof(hazard_pointer_obj_base, _M_deleter) == 0,
+                "_M_deleter must be at offset 0 for the retire cast");
+
+  _M_deleter = std::move(__d);
+
+  _Tp *__self = static_cast<_Tp *>(this);
+  __hazard_pointer::_S_default_domain()._M_retire_impl(
+      static_cast<void *>(__self), [](void *__p) {
+        _Tp *__self_ = static_cast<_Tp *>(__p);
+        auto *__base = static_cast<hazard_pointer_obj_base *>(__self_);
+        _Dp __d2 = std::move(__base->_M_deleter);
+        __d2(__self_);
+      });
+}
+
+// -------------------------------------------------------------------------
+// hazard_pointer implementation
+// -------------------------------------------------------------------------
+
+inline hazard_pointer::hazard_pointer(atomic<void *> *__slot) noexcept
+    : _M_slot(__slot) {}
+
+inline hazard_pointer::~hazard_pointer() {
+  if (_M_slot) {
+    reset_protection();
+    __hazard_pointer::_S_default_domain()._M_release_slot(_M_slot);
+  }
+}
+
+inline hazard_pointer::hazard_pointer(hazard_pointer &&__other) noexcept
+    : _M_slot(__other._M_slot) {
+  __other._M_slot = nullptr;
+}
+
+inline hazard_pointer &
+hazard_pointer::operator=(hazard_pointer &&__other) noexcept {
+  if (this == &__other)
+    return *this;
+  if (!empty()) {
+    reset_protection();
+    __hazard_pointer::_S_default_domain()._M_release_slot(_M_slot);
+  }
+  _M_slot = __other._M_slot;
+  __other._M_slot = nullptr;
+  return *this;
+}
+
+inline bool hazard_pointer::empty() const noexcept {
+  return _M_slot == nullptr;
+}
+
+template <typename _Tp>
+inline bool hazard_pointer::try_protect(_Tp *&__ptr,
+                                        const atomic<_Tp *> &__src) noexcept {
+  static_assert(__hazard_pointer::_Protectable<_Tp>,
+                "T must be a hazard-protectable type -- [32.11.3.4.3]/3");
+  __glibcxx_assert(!empty());
+  const _Tp *const __old = __ptr;
+  reset_protection(__old);
+  // seq_cst pairs with the seq_cst store in reset_protection(T*) to form
+  // an SC pair: either the snapshot sees the hazard or this reload sees
+  // the new pointer.  acquire alone is insufficient on weak-memory arches.
+  __ptr = __src.load(memory_order::seq_cst);
+  if (!(__old == __ptr))
+    reset_protection();
+  return __old == __ptr;
+}
+
+template <typename _Tp>
+inline _Tp *hazard_pointer::protect(const atomic<_Tp *> &__src) noexcept {
+  static_assert(__hazard_pointer::_Protectable<_Tp>,
+                "T must be a hazard-protectable type -- [32.11.3.4.3]/3");
+  _Tp *__ptr = __src.load(memory_order::relaxed);
+  while (!try_protect(__ptr, __src))
+    ;
+  return __ptr;
+}
+
+template <typename _Tp>
+inline void hazard_pointer::reset_protection(const _Tp *__ptr) noexcept {
+  static_assert(__hazard_pointer::_Protectable<_Tp>,
+                "T must be a hazard-protectable type -- [32.11.3.4.3]/7");
+  __glibcxx_assert(!empty());
+  if (__ptr == nullptr)
+    reset_protection();
+  else if (_M_slot)
+    // seq_cst drains the store buffer so synchronize()'s acquire-load
+    // snapshot cannot miss the hazard.  release is insufficient: the
+    // store can sit in the buffer while the scan reads null.
+    _M_slot->store(const_cast<void *>(static_cast<const void *>(__ptr)),
+                   memory_order::seq_cst);
+}
+
+inline void hazard_pointer::reset_protection(nullptr_t) noexcept {
+  __glibcxx_assert(!empty());
+  if (_M_slot)
+    _M_slot->store(nullptr, memory_order::release);
+}
+
+inline void hazard_pointer::swap(hazard_pointer &__other) noexcept {
+  std::swap(_M_slot, __other._M_slot);
+}
+
+// -------------------------------------------------------------------------
+// __hazard_pointer::_Domain implementation
+// -------------------------------------------------------------------------
+
+namespace __hazard_pointer {
+inline _Domain::_Domain()
+    : _M_slot_free(_S_initial_slots, true), _M_slots(_S_initial_slots) {}
+
+inline _Domain::~_Domain() {
+  // Thread-local storage is destroyed before static storage; all threads
+  // have exited, so no hazard pointers are held.  Every orphan record can
+  // be deleted unconditionally without a protected-set check.
+  for (const _RetireRecord &__r : _M_orphan_list)
+    __r._M_deleter(__r._M_ptr);
+}
+
+inline atomic<void *> *_Domain::_M_acquire_slot() {
+  const lock_guard<mutex> __lk(_M_slot_free_mutex);
+  const auto __it = std::find(_M_slot_free.begin(), _M_slot_free.end(), true);
+  if (__it != _M_slot_free.end()) {
+    *__it = false;
+    ++_M_active_count;
+    return &_M_slots[size_t(std::distance(_M_slot_free.begin(), __it))]
+                ._M_value;
+  }
+  // All slots occupied -- grow pool by one.
+  _M_slot_free.push_back(false);
+  _M_slots.emplace_back();
+  ++_M_active_count;
+  return &_M_slots.back()._M_value;
+}
+
+inline void _Domain::_M_release_slot(atomic<void *> *__slot) {
+  __slot->store(nullptr, memory_order::release);
+  const lock_guard<mutex> __lk(_M_slot_free_mutex);
+  const auto __it = std::find_if(
+      _M_slots.begin(), _M_slots.end(),
+      [__slot](const _Slot &__s) { return &__s._M_value == __slot; });
+  if (__it != _M_slots.end()) {
+    _M_slot_free[size_t(std::distance(_M_slots.begin(), __it))] = true;
+    --_M_active_count;
+    return;
+  }
+  __glibcxx_assert(false); // slot not found -- unreachable in correct use
+}
+
+inline size_t _Domain::_M_active_slots() const noexcept {
+  return _M_active_count.load(memory_order::relaxed);
+}
+
+inline size_t _Domain::_M_retire_list_size() const noexcept {
+  return _S_tl_state._M_retire_list.size();
+}
+
+inline void _Domain::_M_retire_impl(void *__ptr, _Deleter_fn __deleter) {
+  _M_ensure_node_registered();
+
+  const size_t __sz = [&] {
+    const lock_guard<mutex> __lk(_S_tl_state._M_node._M_list_mutex);
+    _S_tl_state._M_retire_list.push_back({__ptr, __deleter});
+    return _S_tl_state._M_retire_list.size();
+  }();
+
+  // Heuristic threshold: scan when retire list > 2 * active hazard slots.
+  if (__sz > 2 * _M_active_count.load(memory_order::relaxed))
+    _M_synchronize();
+}
+
+inline void
+_Domain::_M_unregister_node(const _RetireListNode &__node) noexcept {
+  const lock_guard<mutex> __lk(_M_retire_lists_mutex);
+  _RetireListNode *__curr = _M_retire_lists_head;
+  _RetireListNode *__prev = nullptr;
+  while (__curr && __curr != &__node) {
+    __prev = __curr;
+    __curr = __curr->_M_next;
+  }
+  if (!__curr)
+    return; // node was never registered
+  if (__prev)
+    __prev->_M_next = __curr->_M_next;
+  else
+    _M_retire_lists_head = __curr->_M_next;
+  --_M_retire_list_node_count;
+}
+
+inline void _Domain::_M_synchronize() {
+  _M_ensure_node_registered();
+
+  // Step 1: collect -- atomically swap every thread's retire list with an
+  // empty one, then drain the orphan list.  Collect BEFORE snapshot so
+  // that any writer publishing a hazard concurrently either has the hazard
+  // visible in the snapshot, or sees its object still in the source list
+  // and retries.  The collect step's _M_list_mutex acquires happen-before
+  // every writer's retirement push, giving the snapshot's acquire-loads a
+  // consistent view without relying on cross-atomic SC reasoning that is
+  // beyond TSan's vector-clock model.
+  vector<_RetireRecord> __all;
+  {
+    vector<vector<_RetireRecord>> __tmp;
+    {
+      const lock_guard<mutex> __lk(_M_retire_lists_mutex);
+      __tmp.reserve(_M_retire_list_node_count);
+      for (_RetireListNode *__n = _M_retire_lists_head; __n;
+           __n = __n->_M_next) {
+        __tmp.emplace_back();
+        const lock_guard<mutex> __lk2(__n->_M_list_mutex);
+        std::swap(__tmp.back(), *__n->_M_list);
+      }
+    }
+    size_t __sz = 0;
+    for (const auto &__t : __tmp)
+      __sz += __t.size();
+    __all.reserve(__sz);
+    for (const auto &__t : __tmp)
+      __append_to(__all, __t);
+  }
+  {
+    const lock_guard<mutex> __lk(_M_orphan_mutex);
+    __append_to(__all, _M_orphan_list);
+    _M_orphan_list.clear();
+  }
+
+  // Step 2: snapshot slot addresses under the lock for a consistent view
+  // of the deque structure, then do the acquire-loads after releasing it.
+  vector<const atomic<void *> *> __slot_ptrs;
+  {
+    const lock_guard<mutex> __lk(_M_slot_free_mutex);
+    __slot_ptrs.reserve(_M_slots.size());
+    for (const _Slot &__s : _M_slots)
+      __slot_ptrs.push_back(&__s._M_value);
+  }
+  vector<void *> __snapshot;
+  __snapshot.reserve(__slot_ptrs.size());
+  for (const atomic<void *> *__sp : __slot_ptrs)
+    if (void *__p = __sp->load(memory_order::acquire))
+      __snapshot.push_back(__p);
+  std::sort(__snapshot.begin(), __snapshot.end());
+
+  // Step 3: reclaim every record not in the protected set.
+  // No lock held; deleters may safely call retire() or synchronize().
+  vector<_RetireRecord> __survivors;
+  __survivors.reserve(__all.size());
+  for (const _RetireRecord &__r : __all) {
+    if (std::binary_search(__snapshot.begin(), __snapshot.end(), __r._M_ptr))
+      __survivors.push_back(__r);
+    else
+      __r._M_deleter(__r._M_ptr);
+  }
+
+  // Step 4: return survivors to calling thread's own retire list.
+  // Survivors cannot go back to their original threads -- those may have
+  // exited.  The calling thread's list is guaranteed live here.
+  if (!__survivors.empty()) {
+    const lock_guard<mutex> __lk(_S_tl_state._M_node._M_list_mutex);
+    __append_to(_S_tl_state._M_retire_list, __survivors);
+  }
+}
+
+inline void _Domain::_M_ensure_node_registered() {
+  thread_local bool __registered = false;
+  if (__registered)
+    return;
+  _S_tl_state._M_node._M_list = &_S_tl_state._M_retire_list;
+  {
+    const lock_guard<mutex> __lk(_M_retire_lists_mutex);
+    _S_tl_state._M_node._M_next = _M_retire_lists_head;
+    _M_retire_lists_head = &_S_tl_state._M_node;
+    ++_M_retire_list_node_count;
+  }
+  __registered = true;
+}
+
+inline _RetireListNode::~_RetireListNode() {
+  if (!_M_list)
+    return; // _M_ensure_node_registered never ran -- nothing to do
+
+  _Domain &__domain = _S_default_domain();
+
+  // Reclaim as much as possible before this thread's storage goes away.
+  // _M_synchronize puts survivors back into _S_tl_state._M_retire_list,
+  // which is still live here because _ThreadState destroys _M_node (this)
+  // before _M_retire_list.
+  __domain._M_synchronize();
+
+  // Unregister before touching *_M_list without _M_list_mutex.
+  // _M_unregister_node blocks until any concurrent _M_synchronize that
+  // has already seen this node has fully released both mutexes; after it
+  // returns no future _M_synchronize can see this node, so *_M_list is
+  // exclusively owned by this thread.
+  __domain._M_unregister_node(*this);
+
+  if (!_M_list->empty()) {
+    const lock_guard<mutex> __lk(__domain._M_orphan_mutex);
+    __append_to(__domain._M_orphan_list, *_M_list);
+    _M_list->clear();
+  }
+}
+
+} // namespace __hazard_pointer
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std _GLIBCXX_VISIBILITY(default)
+
 #endif // _GLIBCXX_HAZARD_PTR_H
-- 
2.54.0

Reply via email to