Cover the full C++26 std::hazard_pointer API surface with DejaGnu
tests under 30_threads/hazard_pointer/.

Functional tests (one file per API / concern):
    * make_hazard_pointer.cc -- factory, slot accounting, pool growth.
    * ctor.cc -- default ctor, move ctor, destructor, slot accounting.
    * empty.cc -- empty() across every state transition.
    * move_assign.cc -- transfers ownership, releases prior slot, self-
      assignment is a no-op.
    * swap.cc -- member swap, free swap, protection follows the slot.
    * protect.cc -- returns current value, null source, slot ownership,
      re-protect updates the slot.
    * try_protect.cc -- success / failure return, ptr output, agreement
      with protect() on a stable pointer.
    * reset_protection.cc -- both overloads, idempotence, typed-pointer
      overload publishes a hazard directly.
    * retire.cc -- single retire, multiple retires, threshold auto-
      synchronize fires when retired count exceeds 2 * active slots.
    * custom_deleter.cc -- functor deleter invoked, default-constructed
      deleter, deleter suppressed while protected.

  Compile-time property tests:
    * noexcept.cc -- static_asserts for every noexcept-annotated API.
    * type_constraints.cc -- _Protectable concept for derived / non-
      derived types, retire() deleter constraints.

  Multi-threaded tests:
    * concurrent.cc -- readers + writer, multi-writer multi-reader,
      high-contention; run under TSan to verify no data races and no
      use-after-free.
    * thread_exit.cc -- ~_RetireListNode drains the retire list on
      thread exit; protected survivors offload to the orphan list.

  Negative compile tests (dg-error + dg-prune-output for cascade
  diagnostics past the static_assert / -Werror=unused-result):
    * protect_neg.cc -- protect<int>() and protect<NonHazard>()
      rejected by the _Protectable concept.
    * retire_neg.cc -- retire() rejected when hazard_pointer_obj_base
      is a private base.
    * retire_virtual_neg.cc -- retire() rejected when
      hazard_pointer_obj_base is a virtual base; gated on
      __cpp_lib_is_virtual_base_of.
    * nodiscard_neg.cc -- discarding the return of
      make_hazard_pointer(), empty(), protect(), or try_protect()
      trips -Werror=unused-result.

All tests pass at -std=gnu++26 on x86_64-pc-linux-gnu.

libstdc++-v3/ChangeLog:

        * testsuite/30_threads/hazard_pointer/concurrent.cc: New test.
        * testsuite/30_threads/hazard_pointer/ctor.cc: New test.
        * testsuite/30_threads/hazard_pointer/custom_deleter.cc: New test.
        * testsuite/30_threads/hazard_pointer/empty.cc: New test.
        * testsuite/30_threads/hazard_pointer/make_hazard_pointer.cc: New test.
        * testsuite/30_threads/hazard_pointer/move_assign.cc: New test.
        * testsuite/30_threads/hazard_pointer/nodiscard_neg.cc: New test.
        * testsuite/30_threads/hazard_pointer/noexcept.cc: New test.
        * testsuite/30_threads/hazard_pointer/protect.cc: New test.
        * testsuite/30_threads/hazard_pointer/protect_neg.cc: New test.
        * testsuite/30_threads/hazard_pointer/reset_protection.cc: New test.
        * testsuite/30_threads/hazard_pointer/retire.cc: New test.
        * testsuite/30_threads/hazard_pointer/retire_neg.cc: New test.
        * testsuite/30_threads/hazard_pointer/retire_virtual_neg.cc: New test.
        * testsuite/30_threads/hazard_pointer/swap.cc: New test.
        * testsuite/30_threads/hazard_pointer/thread_exit.cc: New test.
        * testsuite/30_threads/hazard_pointer/try_protect.cc: New test.
        * testsuite/30_threads/hazard_pointer/type_constraints.cc: New test.
---
 .../30_threads/hazard_pointer/concurrent.cc   | 226 ++++++++++++++++++
 .../30_threads/hazard_pointer/ctor.cc         | 102 ++++++++
 .../hazard_pointer/custom_deleter.cc          | 126 ++++++++++
 .../30_threads/hazard_pointer/empty.cc        | 105 ++++++++
 .../hazard_pointer/make_hazard_pointer.cc     |  84 +++++++
 .../30_threads/hazard_pointer/move_assign.cc  |  91 +++++++
 .../hazard_pointer/nodiscard_neg.cc           |  58 +++++
 .../30_threads/hazard_pointer/noexcept.cc     |  56 +++++
 .../30_threads/hazard_pointer/protect.cc      |  96 ++++++++
 .../30_threads/hazard_pointer/protect_neg.cc  |  43 ++++
 .../hazard_pointer/reset_protection.cc        | 116 +++++++++
 .../30_threads/hazard_pointer/retire.cc       | 119 +++++++++
 .../30_threads/hazard_pointer/retire_neg.cc   |  39 +++
 .../hazard_pointer/retire_virtual_neg.cc      |  40 ++++
 .../30_threads/hazard_pointer/swap.cc         | 133 +++++++++++
 .../30_threads/hazard_pointer/thread_exit.cc  |  98 ++++++++
 .../30_threads/hazard_pointer/try_protect.cc  | 132 ++++++++++
 .../hazard_pointer/type_constraints.cc        |  56 +++++
 18 files changed, 1720 insertions(+)
 create mode 100644 
libstdc++-v3/testsuite/30_threads/hazard_pointer/concurrent.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/hazard_pointer/ctor.cc
 create mode 100644 
libstdc++-v3/testsuite/30_threads/hazard_pointer/custom_deleter.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/hazard_pointer/empty.cc
 create mode 100644 
libstdc++-v3/testsuite/30_threads/hazard_pointer/make_hazard_pointer.cc
 create mode 100644 
libstdc++-v3/testsuite/30_threads/hazard_pointer/move_assign.cc
 create mode 100644 
libstdc++-v3/testsuite/30_threads/hazard_pointer/nodiscard_neg.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/hazard_pointer/noexcept.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/hazard_pointer/protect.cc
 create mode 100644 
libstdc++-v3/testsuite/30_threads/hazard_pointer/protect_neg.cc
 create mode 100644 
libstdc++-v3/testsuite/30_threads/hazard_pointer/reset_protection.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/hazard_pointer/retire.cc
 create mode 100644 
libstdc++-v3/testsuite/30_threads/hazard_pointer/retire_neg.cc
 create mode 100644 
libstdc++-v3/testsuite/30_threads/hazard_pointer/retire_virtual_neg.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/hazard_pointer/swap.cc
 create mode 100644 
libstdc++-v3/testsuite/30_threads/hazard_pointer/thread_exit.cc
 create mode 100644 
libstdc++-v3/testsuite/30_threads/hazard_pointer/try_protect.cc
 create mode 100644 
libstdc++-v3/testsuite/30_threads/hazard_pointer/type_constraints.cc

diff --git a/libstdc++-v3/testsuite/30_threads/hazard_pointer/concurrent.cc 
b/libstdc++-v3/testsuite/30_threads/hazard_pointer/concurrent.cc
new file mode 100644
index 00000000000..097866eea7e
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/hazard_pointer/concurrent.cc
@@ -0,0 +1,226 @@
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-pthread" { target pthread } }
+// { dg-require-gthreads "" }
+
+// Copyright (C) 2026 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <hazard_pointer>
+#include <atomic>
+#include <chrono>
+#include <functional>
+#include <thread>
+#include <vector>
+#include <testsuite_hooks.h>
+
+namespace hpd = std::__hazard_pointer;
+
+namespace
+{
+  struct Node : std::hazard_pointer_obj_base<Node>
+  {
+    int value;
+    explicit Node(int v) : value(v) {}
+  };
+
+  void reader_fn(std::atomic<Node*>& shared, std::atomic<bool>& stop,
+                 std::atomic<int>& errors)
+  {
+    auto hp = std::make_hazard_pointer();
+    while (!stop.load(std::memory_order_acquire))
+    {
+      const Node* const p = hp.protect(shared);
+      if (p && p->value < 0)
+        errors.fetch_add(1, std::memory_order_relaxed);
+      hp.reset_protection();
+    }
+  }
+
+  void writer_fn(std::atomic<Node*>& shared, std::atomic<bool>& stop)
+  {
+    int counter = 0;
+    while (!stop.load(std::memory_order_acquire))
+    {
+      Node* next = new Node(++counter);
+      Node* const old = shared.exchange(next, std::memory_order_seq_cst);
+      if (old)
+        old->retire();
+    }
+    Node* final_ = shared.exchange(nullptr, std::memory_order_seq_cst);
+    if (final_)
+      final_->retire();
+  }
+}
+
+// Readers and one writer: no data race, no error from reading freed memory.
+void test01()
+{
+  std::atomic<Node*> shared{new Node(1)};
+  std::atomic<bool> stop{false};
+  std::atomic<int> errors{0};
+
+  constexpr int kReaders = 4;
+  std::vector<std::thread> threads;
+  threads.reserve(kReaders + 1);
+
+  for (int i = 0; i < kReaders; ++i)
+    threads.emplace_back(reader_fn, std::ref(shared), std::ref(stop),
+                         std::ref(errors));
+  threads.emplace_back(writer_fn, std::ref(shared), std::ref(stop));
+
+  std::this_thread::sleep_for(std::chrono::milliseconds(200));
+  stop.store(true, std::memory_order_release);
+  for (auto& t : threads)
+    t.join();
+
+  hpd::_S_default_domain()._M_synchronize();
+  VERIFY( errors.load() == 0 );
+}
+
+// protect() sees the latest value across stores.
+void test02()
+{
+  auto hp = std::make_hazard_pointer();
+  Node a{10}, b{20};
+  std::atomic<Node*> src{&a};
+
+  const Node* const p1 = hp.protect(src);
+  VERIFY( p1 == &a );
+
+  src.store(&b, std::memory_order_release);
+  const Node* const p2 = hp.protect(src);
+  VERIFY( p2 == &b );
+}
+
+// Multiple writers, multiple readers: no data race.
+void test03()
+{
+  constexpr int kReaders = 4;
+  constexpr int kWriters = 4;
+
+  std::vector<std::atomic<Node*>> srcs(kWriters);
+  for (auto& s : srcs)
+    s.store(new Node(1), std::memory_order_relaxed);
+
+  std::atomic<bool> stop{false};
+  std::atomic<int> errors{0};
+
+  auto multi_reader = [&] {
+    std::vector<std::hazard_pointer> hps;
+    hps.reserve(kWriters);
+    for (int i = 0; i < kWriters; ++i)
+      hps.emplace_back(std::make_hazard_pointer());
+    while (!stop.load(std::memory_order_acquire))
+      for (int i = 0; i < kWriters; ++i)
+      {
+        const Node* const p = hps[i].protect(srcs[i]);
+        if (p && p->value < 0)
+          errors.fetch_add(1, std::memory_order_relaxed);
+        hps[i].reset_protection();
+      }
+  };
+
+  auto single_writer = [&](int idx) {
+    int counter = 1;
+    while (!stop.load(std::memory_order_acquire))
+    {
+      Node* next = new Node(++counter);
+      Node* old = srcs[idx].exchange(next, std::memory_order_seq_cst);
+      if (old)
+        old->retire();
+    }
+    Node* final_ = srcs[idx].exchange(nullptr, std::memory_order_seq_cst);
+    if (final_)
+      final_->retire();
+  };
+
+  std::vector<std::thread> threads;
+  threads.reserve(kReaders + kWriters);
+  for (int i = 0; i < kReaders; ++i)
+    threads.emplace_back(multi_reader);
+  for (int i = 0; i < kWriters; ++i)
+    threads.emplace_back(single_writer, i);
+
+  std::this_thread::sleep_for(std::chrono::milliseconds(200));
+  stop.store(true, std::memory_order_release);
+  for (auto& t : threads)
+    t.join();
+
+  hpd::_S_default_domain()._M_synchronize();
+  VERIFY( errors.load() == 0 );
+}
+
+// Retire from many threads: all reclaimed by thread-exit drain.
+void test04()
+{
+  constexpr int kThreads = 8;
+  constexpr int kObjectsPerThread = 10;
+  std::atomic<int> total_deleted{0};
+
+  struct CountedNode : std::hazard_pointer_obj_base<CountedNode>
+  {
+    std::atomic<int>& counter;
+    explicit CountedNode(std::atomic<int>& c) : counter(c) {}
+    ~CountedNode() { counter.fetch_add(1, std::memory_order_relaxed); }
+  };
+
+  std::vector<std::thread> threads;
+  threads.reserve(kThreads);
+  for (int i = 0; i < kThreads; ++i)
+    threads.emplace_back([&] {
+      for (int j = 0; j < kObjectsPerThread; ++j)
+        (new CountedNode(total_deleted))->retire();
+    });
+  for (auto& t : threads)
+    t.join();
+
+  VERIFY( total_deleted.load() == kThreads * kObjectsPerThread );
+}
+
+// High contention: readers + writer over longer duration.
+void test05()
+{
+  constexpr int kReaders = 6;
+  std::atomic<Node*> shared{new Node(42)};
+  std::atomic<bool> stop{false};
+  std::atomic<int> errors{0};
+
+  std::vector<std::thread> threads;
+  threads.reserve(kReaders + 1);
+
+  for (int i = 0; i < kReaders; ++i)
+    threads.emplace_back(reader_fn, std::ref(shared), std::ref(stop),
+                         std::ref(errors));
+  threads.emplace_back(writer_fn, std::ref(shared), std::ref(stop));
+
+  std::this_thread::sleep_for(std::chrono::milliseconds(300));
+  stop.store(true, std::memory_order_release);
+  for (auto& t : threads)
+    t.join();
+
+  hpd::_S_default_domain()._M_synchronize();
+  VERIFY( errors.load() == 0 );
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  test05();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/hazard_pointer/ctor.cc 
b/libstdc++-v3/testsuite/30_threads/hazard_pointer/ctor.cc
new file mode 100644
index 00000000000..791b3cdbadc
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/hazard_pointer/ctor.cc
@@ -0,0 +1,102 @@
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-pthread" { target pthread } }
+// { dg-require-gthreads "" }
+
+// Copyright (C) 2026 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <hazard_pointer>
+#include <utility>
+#include <testsuite_hooks.h>
+
+namespace hpd = std::__hazard_pointer;
+
+// Default-constructed handle is empty.
+void test01()
+{
+  const std::hazard_pointer hp;
+  VERIFY( hp.empty() );
+}
+
+// Default-constructed handle does not increment active slot count.
+void test02()
+{
+  const auto before = hpd::_S_default_domain()._M_active_slots();
+  const std::hazard_pointer hp;
+  VERIFY( hpd::_S_default_domain()._M_active_slots() == before );
+}
+
+// Move ctor transfers slot ownership: count unchanged.
+void test03()
+{
+  const auto before = hpd::_S_default_domain()._M_active_slots();
+  auto a = std::make_hazard_pointer();
+  VERIFY( hpd::_S_default_domain()._M_active_slots() == before + 1 );
+  const std::hazard_pointer b = std::move(a);
+  VERIFY( hpd::_S_default_domain()._M_active_slots() == before + 1 );
+}
+
+// Move ctor: source becomes empty.
+void test04()
+{
+  auto a = std::make_hazard_pointer();
+  const std::hazard_pointer b = std::move(a);
+  VERIFY( a.empty() );
+  VERIFY( !b.empty() );
+}
+
+// Destructor releases slot.
+void test05()
+{
+  const auto before = hpd::_S_default_domain()._M_active_slots();
+  {
+    auto hp = std::make_hazard_pointer();
+    VERIFY( hpd::_S_default_domain()._M_active_slots() == before + 1 );
+  }
+  VERIFY( hpd::_S_default_domain()._M_active_slots() == before );
+}
+
+// Destructor on default-constructed handle is a no-op.
+void test06()
+{
+  const auto before = hpd::_S_default_domain()._M_active_slots();
+  {
+    const std::hazard_pointer hp;
+  }
+  VERIFY( hpd::_S_default_domain()._M_active_slots() == before );
+}
+
+// Multiple hazard_pointers are distinct slots.
+void test07()
+{
+  const auto before = hpd::_S_default_domain()._M_active_slots();
+  auto a = std::make_hazard_pointer();
+  auto b = std::make_hazard_pointer();
+  auto c = std::make_hazard_pointer();
+  VERIFY( hpd::_S_default_domain()._M_active_slots() == before + 3 );
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  test05();
+  test06();
+  test07();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/hazard_pointer/custom_deleter.cc 
b/libstdc++-v3/testsuite/30_threads/hazard_pointer/custom_deleter.cc
new file mode 100644
index 00000000000..36d143945a6
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/hazard_pointer/custom_deleter.cc
@@ -0,0 +1,126 @@
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-pthread" { target pthread } }
+// { dg-require-gthreads "" }
+
+// Copyright (C) 2026 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <hazard_pointer>
+#include <atomic>
+#include <testsuite_hooks.h>
+
+namespace hpd = std::__hazard_pointer;
+
+namespace
+{
+  struct LoggingNode;
+
+  struct LoggingDeleter
+  {
+    int* delete_count = nullptr;
+    LoggingDeleter() = default;
+    explicit LoggingDeleter(int* c) : delete_count(c) {}
+    void operator()(LoggingNode* p) const;
+  };
+
+  struct LoggingNode : std::hazard_pointer_obj_base<LoggingNode, 
LoggingDeleter> {};
+
+  inline void LoggingDeleter::operator()(LoggingNode* p) const
+  {
+    if (delete_count)
+      ++(*delete_count);
+    delete p;
+  }
+
+  struct TrackedNode : std::hazard_pointer_obj_base<TrackedNode>
+  {
+    explicit TrackedNode(int& c) : counter(c) {}
+    ~TrackedNode() { ++counter; }
+    int& counter;
+  };
+}
+
+// Default deleter calls delete.
+void test01()
+{
+  int dtor_count = 0;
+  auto* node = new TrackedNode(dtor_count);
+  node->retire();
+  hpd::_S_default_domain()._M_synchronize();
+  VERIFY( dtor_count == 1 );
+}
+
+// Functor deleter is invoked.
+void test02()
+{
+  int delete_count = 0;
+  auto* node = new LoggingNode();
+  node->retire(LoggingDeleter{&delete_count});
+  hpd::_S_default_domain()._M_synchronize();
+  VERIFY( delete_count == 1 );
+}
+
+// Functor deleter not called while protected.
+void test03()
+{
+  int delete_count = 0;
+  auto* node = new LoggingNode();
+  const std::atomic<LoggingNode*> src{node};
+
+  auto hp = std::make_hazard_pointer();
+  (void)hp.protect(src);
+  node->retire(LoggingDeleter{&delete_count});
+  hpd::_S_default_domain()._M_synchronize();
+  VERIFY( delete_count == 0 );
+
+  hp.reset_protection();
+  hpd::_S_default_domain()._M_synchronize();
+  VERIFY( delete_count == 1 );
+}
+
+// retire() with no arg uses default-constructed D.
+void test04()
+{
+  int dtor_count = 0;
+  auto* node = new TrackedNode(dtor_count);
+  node->retire();
+  hpd::_S_default_domain()._M_synchronize();
+  VERIFY( dtor_count == 1 );
+}
+
+// Multiple nodes with custom deleter all invoked.
+void test05()
+{
+  constexpr int kCount = 5;
+  int delete_count = 0;
+  for (int i = 0; i < kCount; ++i)
+  {
+    auto* node = new LoggingNode();
+    node->retire(LoggingDeleter{&delete_count});
+  }
+  hpd::_S_default_domain()._M_synchronize();
+  VERIFY( delete_count == kCount );
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  test05();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/hazard_pointer/empty.cc 
b/libstdc++-v3/testsuite/30_threads/hazard_pointer/empty.cc
new file mode 100644
index 00000000000..afd53c8bd8a
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/hazard_pointer/empty.cc
@@ -0,0 +1,105 @@
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-pthread" { target pthread } }
+// { dg-require-gthreads "" }
+
+// Copyright (C) 2026 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <hazard_pointer>
+#include <atomic>
+#include <utility>
+#include <testsuite_hooks.h>
+
+namespace
+{
+  struct Node : std::hazard_pointer_obj_base<Node> {};
+}
+
+void test01()
+{
+  const std::hazard_pointer hp;
+  VERIFY( hp.empty() );
+}
+
+void test02()
+{
+  auto hp = std::make_hazard_pointer();
+  VERIFY( !hp.empty() );
+}
+
+// After move ctor: source empty, dest non-empty.
+void test03()
+{
+  auto a = std::make_hazard_pointer();
+  const std::hazard_pointer b = std::move(a);
+  VERIFY( a.empty() );
+  VERIFY( !b.empty() );
+}
+
+// After move-assign: source empty.
+void test04()
+{
+  auto a = std::make_hazard_pointer();
+  std::hazard_pointer b;
+  b = std::move(a);
+  VERIFY( a.empty() );
+  VERIFY( !b.empty() );
+}
+
+// After protect(): still owns slot.
+void test05()
+{
+  auto hp = std::make_hazard_pointer();
+  Node x;
+  const std::atomic<Node*> src{&x};
+  (void)hp.protect(src);
+  VERIFY( !hp.empty() );
+}
+
+// After reset_protection(): hazard cleared, slot kept.
+void test06()
+{
+  auto hp = std::make_hazard_pointer();
+  Node x;
+  const std::atomic<Node*> src{&x};
+  (void)hp.protect(src);
+  hp.reset_protection();
+  VERIFY( !hp.empty() );
+}
+
+// After swap: ownership exchanged.
+void test07()
+{
+  auto a = std::make_hazard_pointer();
+  std::hazard_pointer b;
+  VERIFY( !a.empty() );
+  VERIFY( b.empty() );
+  a.swap(b);
+  VERIFY( a.empty() );
+  VERIFY( !b.empty() );
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  test05();
+  test06();
+  test07();
+}
diff --git 
a/libstdc++-v3/testsuite/30_threads/hazard_pointer/make_hazard_pointer.cc 
b/libstdc++-v3/testsuite/30_threads/hazard_pointer/make_hazard_pointer.cc
new file mode 100644
index 00000000000..a726480dc63
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/hazard_pointer/make_hazard_pointer.cc
@@ -0,0 +1,84 @@
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-pthread" { target pthread } }
+// { dg-require-gthreads "" }
+
+// Copyright (C) 2026 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <hazard_pointer>
+#include <cstddef>
+#include <vector>
+#include <testsuite_hooks.h>
+
+namespace hpd = std::__hazard_pointer;
+
+// Returns non-empty handle.
+void test01()
+{
+  auto hp = std::make_hazard_pointer();
+  VERIFY( !hp.empty() );
+}
+
+// Increments active slot count by 1.
+void test02()
+{
+  const auto before = hpd::_S_default_domain()._M_active_slots();
+  auto hp = std::make_hazard_pointer();
+  VERIFY( hpd::_S_default_domain()._M_active_slots() == before + 1 );
+}
+
+// Multiple calls return distinct slots.
+void test03()
+{
+  const auto before = hpd::_S_default_domain()._M_active_slots();
+  auto a = std::make_hazard_pointer();
+  auto b = std::make_hazard_pointer();
+  auto c = std::make_hazard_pointer();
+  VERIFY( hpd::_S_default_domain()._M_active_slots() == before + 3 );
+}
+
+// Slot reused after handle destruction.
+void test04()
+{
+  const auto before = hpd::_S_default_domain()._M_active_slots();
+  {
+    auto hp = std::make_hazard_pointer();
+  }
+  auto hp2 = std::make_hazard_pointer();
+  VERIFY( hpd::_S_default_domain()._M_active_slots() == before + 1 );
+}
+
+// Pool grows beyond initial capacity (_S_initial_slots == 8).
+void test05()
+{
+  const auto before = hpd::_S_default_domain()._M_active_slots();
+  constexpr std::size_t kExtra = 8 + 4;
+  std::vector<std::hazard_pointer> hps;
+  hps.reserve(kExtra);
+  for (std::size_t i = 0; i < kExtra; ++i)
+    hps.emplace_back(std::make_hazard_pointer());
+  VERIFY( hpd::_S_default_domain()._M_active_slots() == before + kExtra );
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  test05();
+}
\ No newline at end of file
diff --git a/libstdc++-v3/testsuite/30_threads/hazard_pointer/move_assign.cc 
b/libstdc++-v3/testsuite/30_threads/hazard_pointer/move_assign.cc
new file mode 100644
index 00000000000..7f4e40c1cda
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/hazard_pointer/move_assign.cc
@@ -0,0 +1,91 @@
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-pthread" { target pthread } }
+// { dg-require-gthreads "" }
+
+// Copyright (C) 2026 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <hazard_pointer>
+#include <utility>
+#include <testsuite_hooks.h>
+
+namespace hpd = std::__hazard_pointer;
+
+// Transfers slot ownership.
+void test01()
+{
+  const auto before = hpd::_S_default_domain()._M_active_slots();
+  auto a = std::make_hazard_pointer();
+  std::hazard_pointer b;
+  VERIFY( !a.empty() );
+  VERIFY( b.empty() );
+  b = std::move(a);
+  VERIFY( a.empty() );
+  VERIFY( !b.empty() );
+  VERIFY( hpd::_S_default_domain()._M_active_slots() == before + 1 );
+}
+
+// Source becomes empty after move-assign.
+void test02()
+{
+  auto a = std::make_hazard_pointer();
+  std::hazard_pointer b;
+  b = std::move(a);
+  VERIFY( a.empty() );
+}
+
+// Destination releases prior slot.
+void test03()
+{
+  const auto before = hpd::_S_default_domain()._M_active_slots();
+  auto a = std::make_hazard_pointer();
+  auto b = std::make_hazard_pointer();
+  VERIFY( hpd::_S_default_domain()._M_active_slots() == before + 2 );
+  b = std::move(a);
+  VERIFY( hpd::_S_default_domain()._M_active_slots() == before + 1 );
+}
+
+// Self-assignment is a no-op.
+void test04()
+{
+  auto hp = std::make_hazard_pointer();
+  const auto before = hpd::_S_default_domain()._M_active_slots();
+  auto& alias = hp;
+  hp = std::move(alias);
+  VERIFY( hpd::_S_default_domain()._M_active_slots() == before );
+  VERIFY( !hp.empty() );
+}
+
+// Move-assign empty to empty: both stay empty, no slot change.
+void test05()
+{
+  const auto before = hpd::_S_default_domain()._M_active_slots();
+  std::hazard_pointer a;
+  std::hazard_pointer b;
+  b = std::move(a);
+  VERIFY( b.empty() );
+  VERIFY( hpd::_S_default_domain()._M_active_slots() == before );
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  test05();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/hazard_pointer/nodiscard_neg.cc 
b/libstdc++-v3/testsuite/30_threads/hazard_pointer/nodiscard_neg.cc
new file mode 100644
index 00000000000..eb725da6790
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/hazard_pointer/nodiscard_neg.cc
@@ -0,0 +1,58 @@
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-pthread -Werror=unused-result" { target pthread } 
}
+// { dg-require-gthreads "" }
+
+// Copyright (C) 2026 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// Negative tests: each [[nodiscard]] return discard must trip
+// -Werror=unused-result.
+
+#include <hazard_pointer>
+#include <atomic>
+
+struct Node : std::hazard_pointer_obj_base<Node> {};
+
+void test_make_hazard_pointer()
+{
+  std::make_hazard_pointer(); // { dg-error "ignoring return value" }
+}
+
+void test_empty()
+{
+  auto hp = std::make_hazard_pointer();
+  hp.empty(); // { dg-error "ignoring return value" }
+}
+
+void test_protect()
+{
+  auto hp = std::make_hazard_pointer();
+  Node x;
+  std::atomic<Node*> src{&x};
+  hp.protect(src); // { dg-error "ignoring return value" }
+}
+
+void test_try_protect()
+{
+  auto hp = std::make_hazard_pointer();
+  Node x;
+  std::atomic<Node*> src{&x};
+  Node* ptr = &x;
+  hp.try_protect(ptr, src); // { dg-error "ignoring return value" }
+}
+
+// { dg-prune-output "some warnings being treated as errors" }
diff --git a/libstdc++-v3/testsuite/30_threads/hazard_pointer/noexcept.cc 
b/libstdc++-v3/testsuite/30_threads/hazard_pointer/noexcept.cc
new file mode 100644
index 00000000000..cc19bc309cd
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/hazard_pointer/noexcept.cc
@@ -0,0 +1,56 @@
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-pthread" { target pthread } }
+// { dg-require-gthreads "" }
+
+// Copyright (C) 2026 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <hazard_pointer>
+#include <atomic>
+#include <type_traits>
+#include <utility>
+
+namespace
+{
+  struct Node : std::hazard_pointer_obj_base<Node> {};
+  using HP = std::hazard_pointer;
+
+  static_assert(std::is_nothrow_default_constructible_v<HP>);
+  static_assert(std::is_nothrow_move_constructible_v<HP>);
+  static_assert(std::is_nothrow_move_assignable_v<HP>);
+
+  static_assert(noexcept(std::declval<const HP&>().empty()));
+
+  static_assert(noexcept(std::declval<HP&>().protect(
+      std::declval<std::atomic<Node*>&>())));
+
+  static_assert(noexcept(std::declval<HP&>().try_protect(
+      std::declval<Node*&>(),
+      std::declval<const std::atomic<Node*>&>())));
+
+  static_assert(noexcept(std::declval<HP&>().reset_protection()));
+  static_assert(noexcept(std::declval<HP&>().reset_protection(nullptr)));
+
+  static_assert(noexcept(std::declval<HP&>().reset_protection(
+      std::declval<const Node*>())));
+
+  static_assert(noexcept(std::declval<HP&>().swap(std::declval<HP&>())));
+
+  static_assert(noexcept(std::swap(std::declval<HP&>(), std::declval<HP&>())));
+
+  static_assert(noexcept(std::declval<Node&>().retire()));
+}
diff --git a/libstdc++-v3/testsuite/30_threads/hazard_pointer/protect.cc 
b/libstdc++-v3/testsuite/30_threads/hazard_pointer/protect.cc
new file mode 100644
index 00000000000..19f38292263
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/hazard_pointer/protect.cc
@@ -0,0 +1,96 @@
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-pthread" { target pthread } }
+// { dg-require-gthreads "" }
+
+// Copyright (C) 2026 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <hazard_pointer>
+#include <atomic>
+#include <testsuite_hooks.h>
+
+namespace
+{
+  struct Node : std::hazard_pointer_obj_base<Node>
+  {
+    int value = 0;
+  };
+}
+
+// Returns current value of the atomic.
+void test01()
+{
+  auto hp = std::make_hazard_pointer();
+  Node x;
+  x.value = 42;
+  const std::atomic<Node*> src{&x};
+  const Node* const p = hp.protect(src);
+  VERIFY( p == &x );
+  VERIFY( p->value == 42 );
+}
+
+// Returns nullptr for null atomic.
+void test02()
+{
+  auto hp = std::make_hazard_pointer();
+  const std::atomic<Node*> src{nullptr};
+  VERIFY( hp.protect(src) == nullptr );
+}
+
+// Slot not empty after protect.
+void test03()
+{
+  auto hp = std::make_hazard_pointer();
+  Node x;
+  const std::atomic<Node*> src{&x};
+  (void)hp.protect(src);
+  VERIFY( !hp.empty() );
+}
+
+// protect() matches atomic load.
+void test04()
+{
+  auto hp = std::make_hazard_pointer();
+  Node vals[3];
+  vals[0].value = 1;
+  vals[1].value = 2;
+  vals[2].value = 3;
+  const std::atomic<Node*> src{&vals[0]};
+  const Node* const p = hp.protect(src);
+  VERIFY( p == src.load() );
+}
+
+// Re-protect on the same handle updates the slot.
+void test05()
+{
+  auto hp = std::make_hazard_pointer();
+  Node a, b;
+  std::atomic<Node*> src{&a};
+  (void)hp.protect(src);
+  src.store(&b, std::memory_order_relaxed);
+  const Node* const p = hp.protect(src);
+  VERIFY( p == &b );
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  test05();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/hazard_pointer/protect_neg.cc 
b/libstdc++-v3/testsuite/30_threads/hazard_pointer/protect_neg.cc
new file mode 100644
index 00000000000..07cd3f66d6b
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/hazard_pointer/protect_neg.cc
@@ -0,0 +1,43 @@
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-pthread" { target pthread } }
+// { dg-require-gthreads "" }
+
+// Copyright (C) 2026 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// Negative tests: protect<T>() must be rejected when T is not _Protectable.
+
+#include <hazard_pointer>
+#include <atomic>
+
+struct Plain {};
+
+void test_int()
+{
+  auto hp = std::make_hazard_pointer();
+  std::atomic<int*> src{nullptr};
+  (void)hp.protect(src); // int is not _Protectable
+}
+
+void test_nonhazard_class()
+{
+  auto hp = std::make_hazard_pointer();
+  std::atomic<Plain*> src{nullptr};
+  (void)hp.protect(src); // Plain has no _Obj_base_tag base
+}
+
+// { dg-error "hazard-protectable type" "" { target *-*-* } 0 }
diff --git 
a/libstdc++-v3/testsuite/30_threads/hazard_pointer/reset_protection.cc 
b/libstdc++-v3/testsuite/30_threads/hazard_pointer/reset_protection.cc
new file mode 100644
index 00000000000..5dddef71611
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/hazard_pointer/reset_protection.cc
@@ -0,0 +1,116 @@
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-pthread" { target pthread } }
+// { dg-require-gthreads "" }
+
+// Copyright (C) 2026 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <hazard_pointer>
+#include <atomic>
+#include <testsuite_hooks.h>
+
+namespace hpd = std::__hazard_pointer;
+
+namespace
+{
+  struct Node : std::hazard_pointer_obj_base<Node> {};
+
+  struct Tracked : std::hazard_pointer_obj_base<Tracked>
+  {
+    explicit Tracked(int& c) : counter(c) {}
+    ~Tracked() { ++counter; }
+    int& counter;
+  };
+}
+
+// reset_protection clears hazard, slot still owned.
+void test01()
+{
+  auto hp = std::make_hazard_pointer();
+  Node x;
+  const std::atomic<Node*> src{&x};
+  (void)hp.protect(src);
+  hp.reset_protection();
+  VERIFY( !hp.empty() );
+}
+
+// nullptr overload clears hazard.
+void test02()
+{
+  auto hp = std::make_hazard_pointer();
+  Node x;
+  const std::atomic<Node*> src{&x};
+  (void)hp.protect(src);
+  hp.reset_protection(nullptr);
+  VERIFY( !hp.empty() );
+}
+
+// After reset, object can be reclaimed.
+void test03()
+{
+  auto hp = std::make_hazard_pointer();
+  int dtor_count = 0;
+  auto* obj = new Tracked(dtor_count);
+  const std::atomic<Tracked*> src{obj};
+  (void)hp.protect(src);
+
+  obj->retire();
+  hpd::_S_default_domain()._M_synchronize();
+  VERIFY( dtor_count == 0 );
+
+  hp.reset_protection();
+  hpd::_S_default_domain()._M_synchronize();
+  VERIFY( dtor_count == 1 );
+}
+
+// Typed-pointer overload publishes a hazard.
+void test04()
+{
+  auto hp = std::make_hazard_pointer();
+  int dtor = 0;
+  auto* obj = new Tracked(dtor);
+  hp.reset_protection(obj);
+
+  obj->retire();
+  hpd::_S_default_domain()._M_synchronize();
+  VERIFY( dtor == 0 );
+
+  hp.reset_protection();
+  hpd::_S_default_domain()._M_synchronize();
+  VERIFY( dtor == 1 );
+}
+
+// Double reset is safe (idempotent).
+void test05()
+{
+  auto hp = std::make_hazard_pointer();
+  Node x;
+  const std::atomic<Node*> src{&x};
+  (void)hp.protect(src);
+  hp.reset_protection();
+  hp.reset_protection();
+  VERIFY( !hp.empty() );
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  test05();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/hazard_pointer/retire.cc 
b/libstdc++-v3/testsuite/30_threads/hazard_pointer/retire.cc
new file mode 100644
index 00000000000..afcc97f2ca2
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/hazard_pointer/retire.cc
@@ -0,0 +1,119 @@
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-pthread" { target pthread } }
+// { dg-require-gthreads "" }
+
+// Copyright (C) 2026 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <hazard_pointer>
+#include <atomic>
+#include <testsuite_hooks.h>
+
+namespace hpd = std::__hazard_pointer;
+
+namespace
+{
+  struct Tracked : std::hazard_pointer_obj_base<Tracked>
+  {
+    explicit Tracked(int& counter) : counter_(counter) {}
+    ~Tracked() { ++counter_; }
+    int& counter_;
+  };
+}
+
+// Retired object deleted after synchronize.
+void test01()
+{
+  // Hold one active slot so the auto-sync threshold (2 * active_count) >= 2,
+  // ensuring the single retire below does NOT auto-sync.
+  auto hp = std::make_hazard_pointer();
+  int dtor_count = 0;
+  (new Tracked(dtor_count))->retire();
+  VERIFY( dtor_count == 0 );
+  hpd::_S_default_domain()._M_synchronize();
+  VERIFY( dtor_count == 1 );
+}
+
+// Multiple retired objects all deleted on synchronize.
+void test02()
+{
+  int dtor_count = 0;
+  for (int i = 0; i < 5; ++i)
+    (new Tracked(dtor_count))->retire();
+  hpd::_S_default_domain()._M_synchronize();
+  VERIFY( dtor_count == 5 );
+}
+
+// Protected object survives synchronize.
+void test03()
+{
+  int dtor_count = 0;
+  auto* obj = new Tracked(dtor_count);
+  const std::atomic<Tracked*> src{obj};
+
+  auto hp = std::make_hazard_pointer();
+  const Tracked* const protected_obj = hp.protect(src);
+  VERIFY( protected_obj == obj );
+
+  obj->retire();
+  hpd::_S_default_domain()._M_synchronize();
+  VERIFY( dtor_count == 0 );
+
+  hp.reset_protection();
+  hpd::_S_default_domain()._M_synchronize();
+  VERIFY( dtor_count == 1 );
+}
+
+// Retire list grows before synchronize.
+void test04()
+{
+  auto hp = std::make_hazard_pointer();
+  const auto before = hpd::_S_default_domain()._M_retire_list_size();
+  int dtor_count = 0;
+  (new Tracked(dtor_count))->retire();
+  (new Tracked(dtor_count))->retire();
+  VERIFY( hpd::_S_default_domain()._M_retire_list_size() == before + 2 );
+  hpd::_S_default_domain()._M_synchronize();
+  VERIFY( hpd::_S_default_domain()._M_retire_list_size() == 0u );
+  VERIFY( dtor_count == 2 );
+}
+
+// Threshold auto-triggers synchronize: 1 active slot -> threshold 2 -> 3 
retires trip it.
+void test05()
+{
+  auto hp = std::make_hazard_pointer();
+  int dummy = 0;
+  Tracked x{dummy};
+  const std::atomic<Tracked*> src{&x};
+  const Tracked* const protected_x = hp.protect(src);
+  VERIFY( protected_x == &x );
+
+  int dtor_count = 0;
+  for (int i = 0; i < 3; ++i)
+    (new Tracked(dtor_count))->retire();
+
+  VERIFY( dtor_count > 0 );
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  test05();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/hazard_pointer/retire_neg.cc 
b/libstdc++-v3/testsuite/30_threads/hazard_pointer/retire_neg.cc
new file mode 100644
index 00000000000..9b1c952f3af
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/hazard_pointer/retire_neg.cc
@@ -0,0 +1,39 @@
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-pthread" { target pthread } }
+// { dg-require-gthreads "" }
+
+// Copyright (C) 2026 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// Negative test: retire() must be rejected when hazard_pointer_obj_base
+// is a private base of T (is_convertible_v<T*, base*> fails).
+
+#include <hazard_pointer>
+
+struct Node : private std::hazard_pointer_obj_base<Node>
+{
+  using hazard_pointer_obj_base::retire;
+};
+
+void test()
+{
+  auto* p = new Node();
+  p->retire();
+}
+
+// { dg-error "must be a public base of T" "" { target *-*-* } 0 }
+// { dg-prune-output "inaccessible base of" }
diff --git 
a/libstdc++-v3/testsuite/30_threads/hazard_pointer/retire_virtual_neg.cc 
b/libstdc++-v3/testsuite/30_threads/hazard_pointer/retire_virtual_neg.cc
new file mode 100644
index 00000000000..7275a7b3f10
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/hazard_pointer/retire_virtual_neg.cc
@@ -0,0 +1,40 @@
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-pthread" { target pthread } }
+// { dg-require-gthreads "" }
+
+// Copyright (C) 2026 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// Negative test: retire() must be rejected when hazard_pointer_obj_base
+// is a virtual base of T.  Requires C++26 std::is_virtual_base_of.
+
+#include <hazard_pointer>
+
+#ifndef __cpp_lib_is_virtual_base_of
+# error "Requires __cpp_lib_is_virtual_base_of for this negative check"
+#endif
+
+struct Node : virtual std::hazard_pointer_obj_base<Node> {};
+
+void test()
+{
+  auto* p = new Node();
+  p->retire();
+}
+
+// { dg-error "must be a non-virtual base" "" { target *-*-* } 0 }
+// { dg-prune-output "cannot convert from pointer to base class" }
diff --git a/libstdc++-v3/testsuite/30_threads/hazard_pointer/swap.cc 
b/libstdc++-v3/testsuite/30_threads/hazard_pointer/swap.cc
new file mode 100644
index 00000000000..2d516d0bfc7
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/hazard_pointer/swap.cc
@@ -0,0 +1,133 @@
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-pthread" { target pthread } }
+// { dg-require-gthreads "" }
+
+// Copyright (C) 2026 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <hazard_pointer>
+#include <atomic>
+#include <testsuite_hooks.h>
+
+namespace hpd = std::__hazard_pointer;
+
+namespace
+{
+  struct Tracked : std::hazard_pointer_obj_base<Tracked>
+  {
+    explicit Tracked(int& c) : counter(c) {}
+    ~Tracked() { ++counter; }
+    int& counter;
+  };
+}
+
+// Member swap exchanges non-empty and empty.
+void test01()
+{
+  auto a = std::make_hazard_pointer();
+  std::hazard_pointer b;
+  VERIFY( !a.empty() );
+  VERIFY( b.empty() );
+  a.swap(b);
+  VERIFY( a.empty() );
+  VERIFY( !b.empty() );
+}
+
+// Member swap both non-empty: slot count unchanged.
+void test02()
+{
+  const auto before = hpd::_S_default_domain()._M_active_slots();
+  auto a = std::make_hazard_pointer();
+  auto b = std::make_hazard_pointer();
+  VERIFY( hpd::_S_default_domain()._M_active_slots() == before + 2 );
+  a.swap(b);
+  VERIFY( hpd::_S_default_domain()._M_active_slots() == before + 2 );
+}
+
+// After swap, the protecting slot moves to the other handle.
+void test03()
+{
+  auto hp1 = std::make_hazard_pointer();
+  auto hp2 = std::make_hazard_pointer();
+
+  int dtor = 0;
+  auto* obj = new Tracked(dtor);
+  const std::atomic<Tracked*> src{obj};
+  (void)hp1.protect(src);
+
+  hp1.swap(hp2);
+
+  obj->retire();
+  hpd::_S_default_domain()._M_synchronize();
+  VERIFY( dtor == 0 );
+
+  hp2.reset_protection();
+  hpd::_S_default_domain()._M_synchronize();
+  VERIFY( dtor == 1 );
+}
+
+// Free swap exchanges slots.
+void test04()
+{
+  auto a = std::make_hazard_pointer();
+  std::hazard_pointer b;
+  VERIFY( !a.empty() );
+  VERIFY( b.empty() );
+  std::swap(a, b);
+  VERIFY( a.empty() );
+  VERIFY( !b.empty() );
+}
+
+// Free swap both non-empty: count unchanged.
+void test05()
+{
+  const auto before = hpd::_S_default_domain()._M_active_slots();
+  auto a = std::make_hazard_pointer();
+  auto b = std::make_hazard_pointer();
+  std::swap(a, b);
+  VERIFY( hpd::_S_default_domain()._M_active_slots() == before + 2 );
+}
+
+// Member self-swap is a no-op.
+void test06()
+{
+  auto hp = std::make_hazard_pointer();
+  VERIFY( !hp.empty() );
+  hp.swap(hp);
+  VERIFY( !hp.empty() );
+}
+
+// Free swap both empty: remains empty.
+void test07()
+{
+  std::hazard_pointer a;
+  std::hazard_pointer b;
+  std::swap(a, b);
+  VERIFY( a.empty() );
+  VERIFY( b.empty() );
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  test05();
+  test06();
+  test07();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/hazard_pointer/thread_exit.cc 
b/libstdc++-v3/testsuite/30_threads/hazard_pointer/thread_exit.cc
new file mode 100644
index 00000000000..cabf95eaeef
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/hazard_pointer/thread_exit.cc
@@ -0,0 +1,98 @@
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-pthread" { target pthread } }
+// { dg-require-gthreads "" }
+
+// Copyright (C) 2026 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <hazard_pointer>
+#include <atomic>
+#include <thread>
+#include <testsuite_hooks.h>
+
+namespace hpd = std::__hazard_pointer;
+
+namespace
+{
+  struct Tracked : std::hazard_pointer_obj_base<Tracked>
+  {
+    explicit Tracked(int& c) : counter(c) {}
+    ~Tracked() { ++counter; }
+    int& counter;
+  };
+}
+
+// Retired objects in a worker thread reclaimed on thread exit.
+void test01()
+{
+  int dtor_count = 0;
+  std::thread t([&] {
+    for (int i = 0; i < 3; ++i)
+      (new Tracked(dtor_count))->retire();
+  });
+  t.join();
+  VERIFY( dtor_count == 3 );
+}
+
+// Protected survivors offloaded to orphan list, reclaimed by later 
synchronize.
+void test02()
+{
+  int dtor_count = 0;
+  auto* obj = new Tracked(dtor_count);
+  const std::atomic<Tracked*> src{obj};
+
+  auto hp = std::make_hazard_pointer();
+  (void)hp.protect(src);
+
+  std::thread t([&] {
+    obj->retire();
+  });
+  t.join();
+
+  VERIFY( dtor_count == 0 );
+
+  hp.reset_protection();
+  hpd::_S_default_domain()._M_synchronize();
+  VERIFY( dtor_count == 1 );
+}
+
+// Many threads each drain own retire list on exit.
+void test03()
+{
+  constexpr int kThreads = 4;
+  constexpr int kObjectsPerThread = 5;
+  int dtor_counts[kThreads] = {};
+
+  std::thread threads[kThreads];
+  for (int i = 0; i < kThreads; ++i)
+    threads[i] = std::thread([&dtor_counts, i] {
+      for (int j = 0; j < kObjectsPerThread; ++j)
+        (new Tracked(dtor_counts[i]))->retire();
+    });
+  for (auto& t : threads)
+    t.join();
+
+  for (int i = 0; i < kThreads; ++i)
+    VERIFY( dtor_counts[i] == kObjectsPerThread );
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/hazard_pointer/try_protect.cc 
b/libstdc++-v3/testsuite/30_threads/hazard_pointer/try_protect.cc
new file mode 100644
index 00000000000..4af3192a2ad
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/hazard_pointer/try_protect.cc
@@ -0,0 +1,132 @@
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-pthread" { target pthread } }
+// { dg-require-gthreads "" }
+
+// Copyright (C) 2026 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <hazard_pointer>
+#include <atomic>
+#include <testsuite_hooks.h>
+
+namespace hpd = std::__hazard_pointer;
+
+namespace
+{
+  struct Node : std::hazard_pointer_obj_base<Node>
+  {
+    int value = 0;
+  };
+
+  struct Tracked : std::hazard_pointer_obj_base<Tracked>
+  {
+    explicit Tracked(int& c) : counter(c) {}
+    ~Tracked() { ++counter; }
+    int& counter;
+  };
+}
+
+// Returns true when pointer is stable.
+void test01()
+{
+  auto hp = std::make_hazard_pointer();
+  Node x;
+  const std::atomic<Node*> src{&x};
+  Node* ptr = src.load(std::memory_order::relaxed);
+  VERIFY( hp.try_protect(ptr, src) );
+}
+
+// Sets ptr on success.
+void test02()
+{
+  auto hp = std::make_hazard_pointer();
+  Node x;
+  x.value = 7;
+  const std::atomic<Node*> src{&x};
+  Node* ptr = src.load(std::memory_order::relaxed);
+  const bool ok = hp.try_protect(ptr, src);
+  VERIFY( ok );
+  VERIFY( ptr == &x );
+}
+
+// Returns true for null source.
+void test03()
+{
+  auto hp = std::make_hazard_pointer();
+  const std::atomic<Node*> src{nullptr};
+  Node* ptr = nullptr;
+  VERIFY( hp.try_protect(ptr, src) );
+  VERIFY( ptr == nullptr );
+}
+
+// Slot still owned after success.
+void test04()
+{
+  auto hp = std::make_hazard_pointer();
+  Node x;
+  const std::atomic<Node*> src{&x};
+  Node* ptr = &x;
+  (void)hp.try_protect(ptr, src);
+  VERIFY( !hp.empty() );
+}
+
+// Protected object not reclaimed after success.
+void test05()
+{
+  auto hp = std::make_hazard_pointer();
+  int dtor_count = 0;
+  auto* obj = new Tracked(dtor_count);
+  const std::atomic<Tracked*> src{obj};
+  Tracked* ptr = src.load(std::memory_order::relaxed);
+  VERIFY( hp.try_protect(ptr, src) );
+
+  obj->retire();
+  hpd::_S_default_domain()._M_synchronize();
+  VERIFY( dtor_count == 0 );
+
+  hp.reset_protection();
+  hpd::_S_default_domain()._M_synchronize();
+  VERIFY( dtor_count == 1 );
+}
+
+// try_protect and protect agree on a stable pointer.
+void test06()
+{
+  Node x;
+  const std::atomic<Node*> src{&x};
+
+  auto hp1 = std::make_hazard_pointer();
+  auto hp2 = std::make_hazard_pointer();
+
+  Node* ptr = src.load(std::memory_order::relaxed);
+  const bool ok = hp1.try_protect(ptr, src);
+  const Node* const via_protect = hp2.protect(src);
+
+  VERIFY( ok );
+  VERIFY( ptr == via_protect );
+  VERIFY( ptr == &x );
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  test05();
+  test06();
+}
diff --git 
a/libstdc++-v3/testsuite/30_threads/hazard_pointer/type_constraints.cc 
b/libstdc++-v3/testsuite/30_threads/hazard_pointer/type_constraints.cc
new file mode 100644
index 00000000000..60040bc3c71
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/hazard_pointer/type_constraints.cc
@@ -0,0 +1,56 @@
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-pthread" { target pthread } }
+// { dg-require-gthreads "" }
+
+// Copyright (C) 2026 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <hazard_pointer>
+#include <memory>
+#include <string>
+#include <type_traits>
+
+namespace
+{
+  struct SimpleNode : std::hazard_pointer_obj_base<SimpleNode> {};
+  struct CustomDelNode
+      : std::hazard_pointer_obj_base<CustomDelNode,
+                                     std::default_delete<CustomDelNode>> {};
+
+  // Indirect derivation must still satisfy _Protectable.
+  struct FurtherDerived : SimpleNode {};
+
+  // Not hazard-protectable.
+  struct PlainClass {};
+}
+
+// _Protectable concept (libstdc++ internal name for HazardProtectable).
+static_assert(std::__hazard_pointer::_Protectable<SimpleNode>);
+static_assert(std::__hazard_pointer::_Protectable<CustomDelNode>);
+static_assert(std::__hazard_pointer::_Protectable<FurtherDerived>);
+
+static_assert(!std::__hazard_pointer::_Protectable<int>);
+static_assert(!std::__hazard_pointer::_Protectable<void>);
+static_assert(!std::__hazard_pointer::_Protectable<int*>);
+
+static_assert(!std::__hazard_pointer::_Protectable<PlainClass>);
+static_assert(!std::__hazard_pointer::_Protectable<std::string>);
+
+// retire() deleter constraints.
+static_assert(std::is_invocable_v<std::default_delete<SimpleNode>, 
SimpleNode*>);
+static_assert(std::is_default_constructible_v<std::default_delete<SimpleNode>>);
+static_assert(std::is_move_assignable_v<std::default_delete<SimpleNode>>);
-- 
2.54.0

Reply via email to