Repository: vorner/arc-swap (language: rust)

Issue:
Miri tests
Arc-swap contains non-trivial amount of unsafe code. It would be nice to be able to run it inside miri.

Nevertheless, miri doesn't have threads (yet?). So the current tests aren't really applicable. Also, some of the test dependencies can't be compiled under miri.

Examine how far we can get by writing some lighter-weight single-threaded tests as a subcrate, without all that fancy testing dependencies.


Discussion / hints:
I got an error in CI which seems to be caused by this issue.
https://github.com/Manishearth/triomphe/pull/7/checks?check_run_id=581436165
associated PR: Manishearth/triomphe#7
<details>
  <summary>CI log snippet</summary>
<pre>
2020-04-12T23:55:14.8305578Z error: unsupported operation: can't call foreign function: llvm.x86.sse2.pause
2020-04-12T23:55:14.8306231Z     --> /usr/share/rust/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore/../stdarch/crates/core_arch/src/x86/sse2.rs:25:5
2020-04-12T23:55:14.8306698Z      |
2020-04-12T23:55:14.8307145Z 25   |     pause()
2020-04-12T23:55:14.8307611Z      |     ^^^^^^^ can't call foreign function: llvm.x86.sse2.pause
2020-04-12T23:55:14.8307861Z      |
2020-04-12T23:55:14.8308072Z      = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support
2020-04-12T23:55:14.8312403Z      = note: inside `std::arch::x86_64::_mm_pause` at /usr/share/rust/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore/../stdarch/crates/core_arch/src/x86/sse2.rs:25:5
2020-04-12T23:55:14.8313373Z      = note: inside `std::hint::spin_loop` at /usr/share/rust/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore/hint.rs:76:22
2020-04-12T23:55:14.8314364Z      = note: inside `std::sync::atomic::spin_loop_hint` at /usr/share/rust/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore/sync/atomic.rs:149:5
2020-04-12T23:55:14.8315561Z      = note: inside `arc_swap::ArcSwapAny::<Arc<tests::Canary>>::wait_for_readers` at /home/runner/.cargo/registry/src/github.com-1ecc6299db9ec823/arc-swap-0.4.5/src/lib.rs:1009:17
2020-04-12T23:55:14.8316314Z      = note: inside `<arc_swap::ArcSwapAny<Arc<tests::Canary>> as std::ops::Drop>::drop` at /home/runner/.cargo/registry/src/github.com-1ecc6299db9ec823/arc-swap-0.4.5/src/lib.rs:700:9
2020-04-12T23:55:14.8317226Z      = note: inside `std::intrinsics::drop_in_place::<arc_swap::ArcSwapAny<Arc<tests::Canary>>> - shim(Some(arc_swap::ArcSwapAny<Arc<tests::Canary>>))` at /usr/share/rust/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore/ptr/mod.rs:177:1
2020-04-12T23:55:14.8317566Z note: inside `tests::arc_swap_` at lib.rs:1193:30
2020-04-12T23:55:14.8317941Z     --> lib.rs:1193:30
2020-04-12T23:55:14.8318137Z      |
2020-04-12T23:55:14.8318321Z 1193 |             let _ = x.clone();
2020-04-12T23:55:14.8318512Z      |                              ^
2020-04-12T23:55:14.8318694Z note: inside closure at lib.rs:1185:5
2020-04-12T23:55:14.8319026Z     --> lib.rs:1185:5
2020-04-12T23:55:14.8319225Z      |
2020-04-12T23:55:14.8319406Z 1185 | /     fn arc_swap_() {
2020-04-12T23:55:14.8319643Z 1186 | |         type ArcSwap<T> = arc_swap::ArcSwapAny<Arc<T>>;
2020-04-12T23:55:14.8319843Z 1187 | |
2020-04-12T23:55:14.8320059Z 1188 | |         let mut canary = atomic::AtomicUsize::new(0);
2020-04-12T23:55:14.8320236Z ...    |
2020-04-12T23:55:14.8320421Z 1195 | |         assert_eq!(canary.load(Acquire), 1);
2020-04-12T23:55:14.8320611Z 1196 | |     }
2020-04-12T23:55:14.8320788Z      | |_____^
2020-04-12T23:55:14.8321540Z      = note: inside `<[closure@lib.rs:1185:5: 1196:6] as std::ops::FnOnce<()>>::call_once - shim` at /usr/share/rust/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore/ops/function.rs:232:5
</pre>
</details>
Hello

I suspect some confusion here. I don't see how the wish to be able to run miri in arc-swap's CI might in any way be related to a CI failure in some completely different crate.

Can you explain?
I added support for `arc-swap` in `triomphe` behind a feature flag.
Thanks for that, it's a great news.

Nevertheless, I *still* don't understand what you're trying to report here. This issue is about expanding arc-swap's tests. Which hasn't happened. The existence or non-existence of some tests can't in any reasonable way influence *your* CI.

I *guess* you might be complaining that arc-swap (not only its tests) doesn't work under miri. If that's so, I'd open a separate issue for that. Nevertheless, I'm not sure if it should be fixed on the arc-swap side or miri side or if it should be declared „out of scope“ of each, since arc-swap is inherently a thread synchronization primitive and miri doesn't like threads.

If you're trying to say something different than this, then please explain it more clearly.
> I guess

You guessed correctly. I disabled miri for the `arc-swap+triomphe` test for now, but I keep an eye on this (and related) issues.

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index afa9d25..84144f8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,11 +1,18 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
+[[package]]
+name = "adaptive-barrier"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ccb55436ce9ad9dbd99e726649f8ae4ae3644f5011b822ed805358da37e7e00"
+
 [[package]]
 name = "arc-swap"
 version = "1.1.0"
 dependencies = [
+ "adaptive-barrier",
  "criterion",
- "crossbeam-utils 0.7.2",
+ "crossbeam-utils",
  "itertools",
  "num_cpus",
  "once_cell",
@@ -41,9 +48,9 @@ dependencies = [
 
 [[package]]
 name = "bit-vec"
-version = "0.6.2"
+version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f0dc55f2d8a1a85650ac47858bb001b4c0dd73d79e3c455a842925e68d29cd3"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
 
 [[package]]
 name = "bitflags"
@@ -107,20 +114,11 @@ dependencies = [
  "unicode-width",
 ]
 
-[[package]]
-name = "cloudabi"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467"
-dependencies = [
- "bitflags",
-]
-
 [[package]]
 name = "const_fn"
-version = "0.4.3"
+version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c478836e029dcef17fb47c89023448c64f781a046e0300e257ad8225ae59afab"
+checksum = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826"
 
 [[package]]
 name = "criterion"
@@ -165,7 +163,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
 dependencies = [
  "cfg-if 1.0.0",
- "crossbeam-utils 0.8.0",
+ "crossbeam-utils",
 ]
 
 [[package]]
@@ -176,18 +174,18 @@ checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9"
 dependencies = [
  "cfg-if 1.0.0",
  "crossbeam-epoch",
- "crossbeam-utils 0.8.0",
+ "crossbeam-utils",
 ]
 
 [[package]]
 name = "crossbeam-epoch"
-version = "0.9.0"
+version = "0.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec0f606a85340376eef0d6d8fec399e6d4a544d648386c6645eb6d0653b27d9f"
+checksum = "a1aaa739f95311c2c7887a76863f500026092fb1dce0161dab577e559ef3569d"
 dependencies = [
  "cfg-if 1.0.0",
  "const_fn",
- "crossbeam-utils 0.8.0",
+ "crossbeam-utils",
  "lazy_static",
  "memoffset",
  "scopeguard",
@@ -195,32 +193,20 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-utils"
-version = "0.7.2"
+version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
-dependencies = [
- "autocfg",
- "cfg-if 0.1.10",
- "lazy_static",
-]
-
-[[package]]
-name = "crossbeam-utils"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec91540d98355f690a86367e566ecad2e9e579f230230eb7c21398372be73ea5"
+checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d"
 dependencies = [
  "autocfg",
  "cfg-if 1.0.0",
- "const_fn",
  "lazy_static",
 ]
 
 [[package]]
 name = "csv"
-version = "1.1.4"
+version = "1.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc4666154fd004af3fd6f1da2e81a96fd5a81927fe8ddb6ecc79e2aa6e138b54"
+checksum = "f9d58633299b24b515ac72a3f869f8b91306a3cec616a602843a383acd6f9e97"
 dependencies = [
  "bstr",
  "csv-core",
@@ -302,9 +288,9 @@ checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
 
 [[package]]
 name = "js-sys"
-version = "0.3.45"
+version = "0.3.46"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca059e81d9486668f12d455a4ea6daa600bd408134cd17e3d3fb5a32d1f016f8"
+checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175"
 dependencies = [
  "wasm-bindgen",
 ]
@@ -317,9 +303,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.80"
+version = "0.2.81"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
+checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"
 
 [[package]]
 name = "lock_api"
@@ -347,9 +333,9 @@ checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
 
 [[package]]
 name = "memoffset"
-version = "0.5.6"
+version = "0.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
+checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87"
 dependencies = [
  "autocfg",
 ]
@@ -398,12 +384,11 @@ dependencies = [
 
 [[package]]
 name = "parking_lot_core"
-version = "0.8.0"
+version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b"
+checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272"
 dependencies = [
- "cfg-if 0.1.10",
- "cloudabi",
+ "cfg-if 1.0.0",
  "instant",
  "libc",
  "redox_syscall",
@@ -466,9 +451,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
 
 [[package]]
 name = "quote"
-version = "1.0.7"
+version = "1.0.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
+checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
 dependencies = [
  "proc-macro2",
 ]
@@ -543,7 +528,7 @@ checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a"
 dependencies = [
  "crossbeam-channel",
  "crossbeam-deque",
- "crossbeam-utils 0.8.0",
+ "crossbeam-utils",
  "lazy_static",
  "num_cpus",
 ]
@@ -646,9 +631,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
 
 [[package]]
 name = "serde"
-version = "1.0.117"
+version = "1.0.118"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a"
+checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800"
 
 [[package]]
 name = "serde_cbor"
@@ -662,9 +647,9 @@ dependencies = [
 
 [[package]]
 name = "serde_derive"
-version = "1.0.117"
+version = "1.0.118"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e"
+checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -673,9 +658,9 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.59"
+version = "1.0.60"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95"
+checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779"
 dependencies = [
  "itoa",
  "ryu",
@@ -684,15 +669,15 @@ dependencies = [
 
 [[package]]
 name = "smallvec"
-version = "1.5.0"
+version = "1.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7acad6f34eb9e8a259d3283d1e8c1d34d7415943d4895f65cc73813c7396fc85"
+checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75"
 
 [[package]]
 name = "syn"
-version = "1.0.50"
+version = "1.0.55"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "443b4178719c5a851e1bde36ce12da21d74a0e60b4d982ec3385a933c812f0f6"
+checksum = "a571a711dddd09019ccc628e1b17fe87c59b09d513c06c026877aa708334f37a"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -772,19 +757,19 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.68"
+version = "0.2.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42"
+checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e"
 dependencies = [
- "cfg-if 0.1.10",
+ "cfg-if 1.0.0",
  "wasm-bindgen-macro",
 ]
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.68"
+version = "0.2.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f22b422e2a757c35a73774860af8e112bff612ce6cb604224e8e47641a9e4f68"
+checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62"
 dependencies = [
  "bumpalo",
  "lazy_static",
@@ -797,9 +782,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.68"
+version = "0.2.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b13312a745c08c469f0b292dd2fcd6411dba5f7160f593da6ef69b64e407038"
+checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -807,9 +792,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.68"
+version = "0.2.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f249f06ef7ee334cc3b8ff031bfc11ec99d00f34d86da7498396dc1e3b1498fe"
+checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -820,15 +805,15 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.68"
+version = "0.2.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307"
+checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158"
 
 [[package]]
 name = "web-sys"
-version = "0.3.45"
+version = "0.3.46"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bf6ef87ad7ae8008e15a355ce696bed26012b7caa21605188cfd8214ab51e2d"
+checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
diff --git a/Cargo.toml b/Cargo.toml
index 4664275..7812687 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -27,8 +27,9 @@ weak = []
 [dependencies]
 
 [dev-dependencies]
+adaptive-barrier = "~0.1"
 criterion = "~0.3"
-crossbeam-utils = "~0.7"
+crossbeam-utils = "~0.8"
 itertools = "~0.9"
 num_cpus = "~1"
 once_cell = "~1"
diff --git a/README.md b/README.md
index d1d7762..5667add 100644
--- a/README.md
+++ b/README.md
@@ -4,9 +4,6 @@
 [![codecov](https://codecov.io/gh/vorner/arc-swap/branch/master/graph/badge.svg?token=3KA3R2D9fV)](https://codecov.io/gh/vorner/arc-swap)
 [![docs](https://docs.rs/arc-swap/badge.svg)](https://docs.rs/arc-swap)
 
-[![Travis Build Status](https://api.travis-ci.org/vorner/arc-swap.png?branch=master)](https://travis-ci.org/vorner/arc-swap)
-[![AppVeyor Build status](https://ci.appveyor.com/api/projects/status/d9p4equeuhymfny6/branch/master?svg=true)](https://ci.appveyor.com/project/vorner/arc-swap/branch/master)
-
 This provides something similar to what `RwLock<Arc<T>>` is or what
 `Atomic<Arc<T>>` would be if it existed, optimized for read-mostly write-seldom
 scenarios, with consistent performance characteristics.
diff --git a/benches/background.rs b/benches/background.rs
index 176dc1c..7ad78d1 100644
--- a/benches/background.rs
+++ b/benches/background.rs
@@ -69,51 +69,62 @@ macro_rules! noise {
     };
 }
 
-mod arc_swap_b {
-    use super::{black_box, ArcSwap, Criterion, Lazy};
+macro_rules! strategy {
+    ($name: ident, $type: ty) => {
+        mod $name {
+            use super::*;
 
-    static A: Lazy<ArcSwap<usize>> = Lazy::new(|| ArcSwap::from_pointee(0));
-    const NAME: &str = "arc_swap";
+            static A: Lazy<$type> = Lazy::new(|| <$type>::from_pointee(0));
+            const NAME: &str = stringify!($name);
 
-    fn lease() {
-        for _ in 0..ITERS {
-            black_box(**A.load());
-        }
-    }
-
-    // Leases kind of degrade in performance if there are multiple on the same thread.
-    fn four_leases() {
-        for _ in 0..ITERS {
-            let l1 = A.load();
-            let l2 = A.load();
-            let l3 = A.load();
-            let l4 = A.load();
-            black_box((**l1, **l2, **l3, **l4));
-        }
-    }
+            fn lease() {
+                for _ in 0..ITERS {
+                    black_box(**A.load());
+                }
+            }
+
+            // Leases kind of degrade in performance if there are multiple on the same thread.
+            fn four_leases() {
+                for _ in 0..ITERS {
+                    let l1 = A.load();
+                    let l2 = A.load();
+                    let l3 = A.load();
+                    let l4 = A.load();
+                    black_box((**l1, **l2, **l3, **l4));
+                }
+            }
 
-    fn read() {
-        for _ in 0..ITERS {
-            black_box(A.load_full());
-        }
-    }
+            fn read() {
+                for _ in 0..ITERS {
+                    black_box(A.load_full());
+                }
+            }
 
-    fn write() {
-        for _ in 0..ITERS {
-            black_box(A.store(Arc::new(0)));
-        }
-    }
+            fn write() {
+                for _ in 0..ITERS {
+                    black_box(A.store(Arc::new(0)));
+                }
+            }
 
-    noise!();
+            noise!();
 
-    pub fn run_all(c: &mut Criterion) {
-        method!(c, read);
-        method!(c, write);
-        method!(c, lease);
-        method!(c, four_leases);
-    }
+            pub fn run_all(c: &mut Criterion) {
+                method!(c, read);
+                method!(c, write);
+                method!(c, lease);
+                method!(c, four_leases);
+            }
+        }
+    };
 }
 
+strategy!(arc_swap_b, ArcSwap::<usize>);
+#[cfg(feature = "experimental-strategies")]
+strategy!(
+    arc_swap_helping,
+    arc_swap::ArcSwapAny::<Arc<usize>, arc_swap::strategy::experimental::Helping>
+);
+
 mod arc_swap_option {
     use super::{black_box, ArcSwapOption, Criterion, Lazy};
 
@@ -314,6 +325,19 @@ mod parking_rwlock {
     }
 }
 
+#[cfg(feature = "experimental-strategies")]
+criterion_group!(
+    benches,
+    arc_swap_b::run_all,
+    arc_swap_helping::run_all,
+    arc_swap_option::run_all,
+    arc_swap_cached::run_all,
+    mutex::run_all,
+    parking_mutex::run_all,
+    rwlock::run_all,
+    parking_rwlock::run_all,
+);
+#[cfg(not(feature = "experimental-strategies"))]
 criterion_group!(
     benches,
     arc_swap_b::run_all,
diff --git a/src/debt.rs b/src/debt.rs
index 7aff13a..264571c 100644
--- a/src/debt.rs
+++ b/src/debt.rs
@@ -43,7 +43,11 @@ impl Node {
         traverse(|node| {
             // Try to claim this node. Nothing is synchronized through this atomic, we only
             // track if someone claims ownership of it.
-            if !node.in_use.compare_and_swap(false, true, Ordering::Relaxed) {
+            let claimed = node
+                .in_use
+                .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
+                .is_ok();
+            if claimed {
                 Some(node)
             } else {
                 None
@@ -217,6 +221,7 @@ impl Debt {
     /// Pays all the debts on the given pointer.
     pub(crate) fn pay_all<T: RefCnt>(ptr: *const T::Base) {
         let val = unsafe { T::from_ptr(ptr) };
+        // Pre-pay one ref count that can be safely put into a debt slot to pay it.
         T::inc(&val);
         traverse::<(), _>(|node| {
             for slot in &node.slots.0 {
@@ -225,11 +230,13 @@ impl Debt {
                     .compare_exchange(ptr as usize, NO_DEBT, Ordering::AcqRel, Ordering::Relaxed)
                     .is_ok()
                 {
+                    // Pre-pay one more, for another future slot
                     T::inc(&val);
                 }
             }
             None
         });
+        // Implicit dec by dropping val in here, pair for the above
     }
 }
 
diff --git a/src/gen_lock.rs b/src/gen_lock.rs
index d7295b5..1998d4f 100644
--- a/src/gen_lock.rs
+++ b/src/gen_lock.rs
@@ -230,7 +230,7 @@ pub(crate) fn wait_for_readers<S: LockStorage>(storage: &S) {
         let next_gen = gen.wrapping_add(1);
         if groups[next_gen % GEN_CNT] == 0 {
             // Replace it only if someone else didn't do it in the meantime
-            gen_idk.compare_and_swap(gen, next_gen, Ordering::Relaxed);
+            let _ = gen_idk.compare_exchange(gen, next_gen, Ordering::Relaxed, Ordering::Relaxed);
         }
         for i in 0..GEN_CNT {
             seen_group[i] = seen_group[i] || (groups[i] == 0);
diff --git a/src/lib.rs b/src/lib.rs
index c70aeea..1444add 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -314,7 +314,7 @@ impl<T: RefCnt, S: Strategy<T>> Drop for ArcSwapAny<T, S> {
         let ptr = *self.ptr.get_mut();
         unsafe {
             // To pay any possible debts
-            self.strategy.wait_for_readers(ptr);
+            self.strategy.wait_for_readers(ptr, &self.ptr);
             // We are getting rid of the one stored ref count
             T::dec(ptr);
         }
@@ -374,7 +374,7 @@ impl<T: RefCnt, S: Strategy<T>> ArcSwapAny<T, S> {
     pub fn into_inner(mut self) -> T {
         let ptr = *self.ptr.get_mut();
         // To pay all the debts
-        unsafe { self.strategy.wait_for_readers(ptr) };
+        unsafe { self.strategy.wait_for_readers(ptr, &self.ptr) };
         mem::forget(self);
         unsafe { T::from_ptr(ptr) }
     }
@@ -459,7 +459,7 @@ impl<T: RefCnt, S: Strategy<T>> ArcSwapAny<T, S> {
         // SeqCst to synchronize the time lines with the group counters.
         let old = self.ptr.swap(new, Ordering::SeqCst);
         unsafe {
-            self.strategy.wait_for_readers(old);
+            self.strategy.wait_for_readers(old, &self.ptr);
             T::from_ptr(old)
         }
     }
@@ -486,8 +486,6 @@ impl<T: RefCnt, S: Strategy<T>> ArcSwapAny<T, S> {
     {
         let protected = unsafe { self.strategy.compare_and_swap(&self.ptr, current, new) };
         Guard { inner: protected }
-        /*
-         */
     }
 
     /// Read-Copy-Update of the pointer inside.
@@ -760,452 +758,484 @@ pub type IndependentArcSwap<T> = ArcSwapAny<Arc<T>, IndependentStrategy>;
 #[cfg(feature = "weak")]
 pub type ArcSwapWeak<T> = ArcSwapAny<std::sync::Weak<T>>;
 
-#[cfg(test)]
-mod tests {
-    use std::panic;
-    use std::sync::atomic::{self, AtomicUsize};
-    use std::sync::Barrier;
-
-    use crossbeam_utils::thread;
-
-    use super::*;
-
-    const ITERATIONS: usize = 10;
-
-    /// Similar to the one in doc tests of the lib, but more times and more intensive (we want to
-    /// torture it a bit).
-    #[test]
-    fn publish() {
-        const READERS: usize = 3;
-        for _ in 0..ITERATIONS {
-            let config = ArcSwap::<String>::default();
-            let ended = AtomicUsize::new(0);
-            thread::scope(|scope| {
-                for _ in 0..READERS {
-                    scope.spawn(|_| loop {
-                        let cfg = config.load_full();
-                        if !cfg.is_empty() {
-                            assert_eq!(*cfg, "New configuration");
-                            ended.fetch_add(1, Ordering::Relaxed);
-                            return;
+macro_rules! t {
+    ($name: ident, $strategy: ty) => {
+        #[cfg(test)]
+        mod $name {
+            use std::panic;
+            use std::sync::atomic::{self, AtomicUsize};
+
+            use adaptive_barrier::{Barrier, PanicMode};
+            use crossbeam_utils::thread;
+
+            use super::*;
+
+            const ITERATIONS: usize = 10;
+
+            type AS<T> = ArcSwapAny<Arc<T>, $strategy>;
+            type ASO<T> = ArcSwapAny<Option<Arc<T>>, $strategy>;
+
+            /// Similar to the one in doc tests of the lib, but more times and more intensive (we
+            /// want to torture it a bit).
+            #[test]
+            #[cfg_attr(miri, ignore)] // Takes like 1 or 2 infinities to run under miri
+            fn publish() {
+                const READERS: usize = 2;
+                for _ in 0..ITERATIONS {
+                    let config = AS::<String>::default();
+                    let ended = AtomicUsize::new(0);
+                    thread::scope(|scope| {
+                        for _ in 0..READERS {
+                            scope.spawn(|_| loop {
+                                let cfg = config.load_full();
+                                if !cfg.is_empty() {
+                                    assert_eq!(*cfg, "New configuration");
+                                    ended.fetch_add(1, Ordering::Relaxed);
+                                    return;
+                                }
+                                atomic::spin_loop_hint();
+                            });
                         }
-                        atomic::spin_loop_hint();
-                    });
+                        scope.spawn(|_| {
+                            let new_conf = Arc::new("New configuration".to_owned());
+                            config.store(new_conf);
+                        });
+                    })
+                    .unwrap();
+                    assert_eq!(READERS, ended.load(Ordering::Relaxed));
+                    let arc = config.load_full();
+                    assert_eq!(2, Arc::strong_count(&arc));
+                    assert_eq!(0, Arc::weak_count(&arc));
                 }
-                scope.spawn(|_| {
-                    let new_conf = Arc::new("New configuration".to_owned());
-                    config.store(new_conf);
-                });
-            })
-            .unwrap();
-            assert_eq!(READERS, ended.load(Ordering::Relaxed));
-            let arc = config.load_full();
-            assert_eq!(2, Arc::strong_count(&arc));
-            assert_eq!(0, Arc::weak_count(&arc));
-        }
-    }
-
-    /// Similar to the doc tests of ArcSwap, but happens more times.
-    #[test]
-    fn swap_load() {
-        for _ in 0..100 {
-            let arc = Arc::new(42);
-            let arc_swap = ArcSwap::from(Arc::clone(&arc));
-            assert_eq!(42, **arc_swap.load());
-            // It can be read multiple times
-            assert_eq!(42, **arc_swap.load());
-
-            // Put a new one in there
-            let new_arc = Arc::new(0);
-            assert_eq!(42, *arc_swap.swap(Arc::clone(&new_arc)));
-            assert_eq!(0, **arc_swap.load());
-            // One loaded here, one in the arc_swap, one in new_arc
-            let loaded = arc_swap.load_full();
-            assert_eq!(3, Arc::strong_count(&loaded));
-            assert_eq!(0, Arc::weak_count(&loaded));
-            // The original got released from the arc_swap
-            assert_eq!(1, Arc::strong_count(&arc));
-            assert_eq!(0, Arc::weak_count(&arc));
-        }
-    }
+            }
 
-    /// Two different writers publish two series of values. The readers check that it is always
-    /// increasing in each serie.
-    ///
-    /// For performance, we try to reuse the threads here.
-    #[test]
-    fn multi_writers() {
-        let first_value = Arc::new((0, 0));
-        let shared = ArcSwap::from(Arc::clone(&first_value));
-        const WRITER_CNT: usize = 2;
-        const READER_CNT: usize = 3;
-        const ITERATIONS: usize = 100;
-        const SEQ: usize = 50;
-        let barrier = Barrier::new(READER_CNT + WRITER_CNT);
-        thread::scope(|scope| {
-            for w in 0..WRITER_CNT {
-                // We need to move w into the closure. But we want to just reference the other
-                // things.
-                let barrier = &barrier;
-                let shared = &shared;
-                let first_value = &first_value;
-                scope.spawn(move |_| {
-                    for _ in 0..ITERATIONS {
-                        barrier.wait();
-                        shared.store(Arc::clone(&first_value));
-                        barrier.wait();
-                        for i in 0..SEQ {
-                            shared.store(Arc::new((w, i + 1)));
-                        }
-                    }
-                });
+            /// Similar to the doc tests of ArcSwap, but happens more times.
+            #[test]
+            fn swap_load() {
+                for _ in 0..100 {
+                    let arc = Arc::new(42);
+                    let arc_swap = AS::from(Arc::clone(&arc));
+                    assert_eq!(42, **arc_swap.load());
+                    // It can be read multiple times
+                    assert_eq!(42, **arc_swap.load());
+
+                    // Put a new one in there
+                    let new_arc = Arc::new(0);
+                    assert_eq!(42, *arc_swap.swap(Arc::clone(&new_arc)));
+                    assert_eq!(0, **arc_swap.load());
+                    // One loaded here, one in the arc_swap, one in new_arc
+                    let loaded = arc_swap.load_full();
+                    assert_eq!(3, Arc::strong_count(&loaded));
+                    assert_eq!(0, Arc::weak_count(&loaded));
+                    // The original got released from the arc_swap
+                    assert_eq!(1, Arc::strong_count(&arc));
+                    assert_eq!(0, Arc::weak_count(&arc));
+                }
             }
-            for _ in 0..READER_CNT {
-                scope.spawn(|_| {
-                    for _ in 0..ITERATIONS {
-                        barrier.wait();
-                        barrier.wait();
-                        let mut previous = [0; 2];
-                        let mut last = Arc::clone(&first_value);
-                        loop {
-                            let cur = shared.load();
-                            if Arc::ptr_eq(&last, &cur) {
-                                atomic::spin_loop_hint();
-                                continue;
+
+            /// Two different writers publish two series of values. The readers check that it is
+            /// always increasing in each serie.
+            ///
+            /// For performance, we try to reuse the threads here.
+            #[test]
+            fn multi_writers() {
+                let first_value = Arc::new((0, 0));
+                let shared = AS::from(Arc::clone(&first_value));
+                const WRITER_CNT: usize = 2;
+                const READER_CNT: usize = 3;
+                #[cfg(miri)]
+                const ITERATIONS: usize = 10;
+                #[cfg(not(miri))]
+                const ITERATIONS: usize = 100;
+                const SEQ: usize = 50;
+                let barrier = Barrier::new(PanicMode::Poison);
+                thread::scope(|scope| {
+                    for w in 0..WRITER_CNT {
+                        // We need to move w into the closure. But we want to just reference the
+                        // other things.
+                        let mut barrier = barrier.clone();
+                        let shared = &shared;
+                        let first_value = &first_value;
+                        scope.spawn(move |_| {
+                            for _ in 0..ITERATIONS {
+                                barrier.wait();
+                                shared.store(Arc::clone(&first_value));
+                                barrier.wait();
+                                for i in 0..SEQ {
+                                    shared.store(Arc::new((w, i + 1)));
+                                }
                             }
-                            let (w, s) = **cur;
-                            assert!(previous[w] < s);
-                            previous[w] = s;
-                            last = Guard::into_inner(cur);
-                            if s == SEQ {
-                                break;
+                        });
+                    }
+                    for _ in 0..READER_CNT {
+                        let mut barrier = barrier.clone();
+                        let shared = &shared;
+                        let first_value = &first_value;
+                        scope.spawn(move |_| {
+                            for _ in 0..ITERATIONS {
+                                barrier.wait();
+                                barrier.wait();
+                                let mut previous = [0; WRITER_CNT];
+                                let mut last = Arc::clone(&first_value);
+                                loop {
+                                    let cur = shared.load();
+                                    if Arc::ptr_eq(&last, &cur) {
+                                        atomic::spin_loop_hint();
+                                        continue;
+                                    }
+                                    let (w, s) = **cur;
+                                    assert!(previous[w] < s, "{:?} vs {:?}", previous, cur);
+                                    previous[w] = s;
+                                    last = Guard::into_inner(cur);
+                                    if s == SEQ {
+                                        break;
+                                    }
+                                }
                             }
-                        }
+                        });
                     }
-                });
+
+                    drop(barrier);
+                })
+                .unwrap();
             }
-        })
-        .unwrap();
-    }
 
-    #[test]
-    /// Make sure the reference count and compare_and_swap works as expected.
-    fn cas_ref_cnt() {
-        const ITERATIONS: usize = 50;
-        let shared = ArcSwap::from(Arc::new(0));
-        for i in 0..ITERATIONS {
-            let orig = shared.load_full();
-            assert_eq!(i, *orig);
-            if i % 2 == 1 {
-                // One for orig, one for shared
-                assert_eq!(2, Arc::strong_count(&orig));
+            /// Accessing the value inside ArcSwap with Guards (and checks for the reference
+            /// counts).
+            #[test]
+            fn load_cnt() {
+                let a = Arc::new(0);
+                let shared = AS::from(Arc::clone(&a));
+                // One in shared, one in a
+                assert_eq!(2, Arc::strong_count(&a));
+                let guard = shared.load();
+                assert_eq!(0, **guard);
+                // The guard doesn't have its own ref count now
+                assert_eq!(2, Arc::strong_count(&a));
+                let guard_2 = shared.load();
+                // Unlike with guard, this does not deadlock
+                shared.store(Arc::new(1));
+                // But now, each guard got a full Arc inside it
+                assert_eq!(3, Arc::strong_count(&a));
+                // And when we get rid of them, they disappear
+                drop(guard_2);
+                assert_eq!(2, Arc::strong_count(&a));
+                let _b = Arc::clone(&guard);
+                assert_eq!(3, Arc::strong_count(&a));
+                // We can drop the guard it came from
+                drop(guard);
+                assert_eq!(2, Arc::strong_count(&a));
+                let guard = shared.load();
+                assert_eq!(1, **guard);
+                drop(shared);
+                // We can still use the guard after the shared disappears
+                assert_eq!(1, **guard);
+                let ptr = Arc::clone(&guard);
+                // One in shared, one in guard
+                assert_eq!(2, Arc::strong_count(&ptr));
+                drop(guard);
+                assert_eq!(1, Arc::strong_count(&ptr));
             }
-            let n1 = Arc::new(i + 1);
-            // Fill up the slots sometimes
-            let fillup = || {
-                if i % 2 == 0 {
-                    Some((0..50).map(|_| shared.load()).collect::<Vec<_>>())
-                } else {
-                    None
-                }
-            };
-            let guards = fillup();
-            // Success
-            let prev = shared.compare_and_swap(&orig, Arc::clone(&n1));
-            assert!(ptr_eq(&orig, &prev));
-            drop(guards);
-            // One for orig, one for prev
-            assert_eq!(2, Arc::strong_count(&orig));
-            // One for n1, one for shared
-            assert_eq!(2, Arc::strong_count(&n1));
-            assert_eq!(i + 1, **shared.load());
-            let n2 = Arc::new(i);
-            drop(prev);
-            let guards = fillup();
-            // Failure
-            let prev = Guard::into_inner(shared.compare_and_swap(&orig, Arc::clone(&n2)));
-            drop(guards);
-            assert!(ptr_eq(&n1, &prev));
-            // One for orig
-            assert_eq!(1, Arc::strong_count(&orig));
-            // One for n1, one for shared, one for prev
-            assert_eq!(3, Arc::strong_count(&n1));
-            // n2 didn't get increased
-            assert_eq!(1, Arc::strong_count(&n2));
-            assert_eq!(i + 1, **shared.load());
-        }
 
-        let a = shared.load_full();
-        // One inside shared, one for a
-        assert_eq!(2, Arc::strong_count(&a));
-        drop(shared);
-        // Only a now
-        assert_eq!(1, Arc::strong_count(&a));
-    }
+            /// There can be only limited amount of leases on one thread. Following ones are
+            /// created, but contain full Arcs.
+            #[test]
+            fn lease_overflow() {
+                let a = Arc::new(0);
+                let shared = AS::from(Arc::clone(&a));
+                assert_eq!(2, Arc::strong_count(&a));
+                let mut guards = (0..1000).map(|_| shared.load()).collect::<Vec<_>>();
+                let count = Arc::strong_count(&a);
+                assert!(count > 2);
+                let guard = shared.load();
+                assert_eq!(count + 1, Arc::strong_count(&a));
+                drop(guard);
+                assert_eq!(count, Arc::strong_count(&a));
+                // When we delete the first one, it didn't have an Arc in it, so the ref count
+                // doesn't drop
+                guards.swap_remove(0);
+                assert_eq!(count, Arc::strong_count(&a));
+                // But new one reuses now vacant the slot and doesn't create a new Arc
+                let _guard = shared.load();
+                assert_eq!(count, Arc::strong_count(&a));
+            }
 
-    #[test]
-    /// Multiple RCUs interacting.
-    fn rcu() {
-        const ITERATIONS: usize = 50;
-        const THREADS: usize = 10;
-        let shared = ArcSwap::from(Arc::new(0));
-        thread::scope(|scope| {
-            for _ in 0..THREADS {
-                scope.spawn(|_| {
-                    for _ in 0..ITERATIONS {
-                        shared.rcu(|old| **old + 1);
-                    }
-                });
+            #[test]
+            fn load_null() {
+                let shared = ASO::<usize>::default();
+                let guard = shared.load();
+                assert!(guard.is_none());
+                shared.store(Some(Arc::new(42)));
+                assert_eq!(42, **shared.load().as_ref().unwrap());
             }
-        })
-        .unwrap();
-        assert_eq!(THREADS * ITERATIONS, **shared.load());
-    }
 
-    /// Handling null/none values
-    #[test]
-    fn nulls() {
-        let shared = ArcSwapOption::from(Some(Arc::new(0)));
-        let orig = shared.swap(None);
-        assert_eq!(1, Arc::strong_count(&orig.unwrap()));
-        let null = shared.load();
-        assert!(null.is_none());
-        let a = Arc::new(42);
-        let orig = shared.compare_and_swap(ptr::null(), Some(Arc::clone(&a)));
-        assert!(orig.is_none());
-        assert_eq!(2, Arc::strong_count(&a));
-        let orig = Guard::into_inner(shared.compare_and_swap(&None::<Arc<_>>, None));
-        assert_eq!(3, Arc::strong_count(&a));
-        assert!(ptr_eq(&a, &orig));
-    }
+            #[test]
+            fn from_into() {
+                let a = Arc::new(42);
+                let shared = AS::new(a);
+                let guard = shared.load();
+                let a = shared.into_inner();
+                assert_eq!(42, *a);
+                assert_eq!(2, Arc::strong_count(&a));
+                drop(guard);
+                assert_eq!(1, Arc::strong_count(&a));
+            }
 
-    /// We have a callback in RCU. Check what happens if we access the value from within.
-    #[test]
-    fn recursive() {
-        let shared = ArcSwap::from(Arc::new(0));
+            // Note on the Relaxed order here. This should be enough, because there's that
+            // barrier.wait in between that should do the synchronization of happens-before for us.
+            // And using SeqCst would probably not help either, as there's nothing else with SeqCst
+            // here in this test to relate it to.
+            #[derive(Default)]
+            struct ReportDrop(Arc<AtomicUsize>);
+            impl Drop for ReportDrop {
+                fn drop(&mut self) {
+                    self.0.fetch_add(1, Ordering::Relaxed);
+                }
+            }
 
-        shared.rcu(|i| {
-            if **i < 10 {
-                shared.rcu(|i| **i + 1);
+            /// Interaction of two threads about a guard and dropping it.
+            ///
+            /// We make sure everything works in timely manner (eg. dropping of stuff) even if multiple
+            /// threads interact.
+            ///
+            /// The idea is:
+            /// * Thread 1 loads a value.
+            /// * Thread 2 replaces the shared value. The original value is not destroyed.
+            /// * Thread 1 drops the guard. The value is destroyed and this is observable in both threads.
+            #[test]
+            fn guard_drop_in_thread() {
+                for _ in 0..ITERATIONS {
+                    let cnt = Arc::new(AtomicUsize::new(0));
+
+                    let shared = AS::from_pointee(ReportDrop(cnt.clone()));
+                    assert_eq!(cnt.load(Ordering::Relaxed), 0, "Dropped prematurely");
+                    // We need the threads to wait for each other at places.
+                    let sync = Barrier::new(PanicMode::Poison);
+
+                    thread::scope(|scope| {
+                        scope.spawn({
+                            let sync = sync.clone();
+                            |_| {
+                                let mut sync = sync; // Move into the closure
+                                let guard = shared.load();
+                                sync.wait();
+                                // Thread 2 replaces the shared value. We wait for it to confirm.
+                                sync.wait();
+                                drop(guard);
+                                assert_eq!(cnt.load(Ordering::Relaxed), 1, "Value not dropped");
+                                // Let thread 2 know we already dropped it.
+                                sync.wait();
+                            }
+                        });
+
+                        scope.spawn(|_| {
+                            let mut sync = sync;
+                            // Thread 1 loads, we wait for that
+                            sync.wait();
+                            shared.store(Default::default());
+                            assert_eq!(
+                                cnt.load(Ordering::Relaxed),
+                                0,
+                                "Dropped while still in use"
+                            );
+                            // Let thread 2 know we replaced it
+                            sync.wait();
+                            // Thread 1 drops its guard. We wait for it to confirm.
+                            sync.wait();
+                            assert_eq!(cnt.load(Ordering::Relaxed), 1, "Value not dropped");
+                        });
+                    })
+                    .unwrap();
+                }
             }
-            **i
-        });
-        assert_eq!(10, **shared.load());
-        assert_eq!(2, Arc::strong_count(&shared.load_full()));
-    }
 
-    /// A panic from within the rcu callback should not change anything.
-    #[test]
-    fn rcu_panic() {
-        let shared = ArcSwap::from(Arc::new(0));
-        assert!(panic::catch_unwind(|| shared.rcu(|_| -> usize { panic!() })).is_err());
-        assert_eq!(1, Arc::strong_count(&shared.swap(Arc::new(42))));
-    }
+            /// Check dropping a lease in a different thread than it was created doesn't cause any
+            /// problems.
+            #[test]
+            fn guard_drop_in_another_thread() {
+                for _ in 0..ITERATIONS {
+                    let cnt = Arc::new(AtomicUsize::new(0));
+                    let shared = AS::from_pointee(ReportDrop(cnt.clone()));
+                    assert_eq!(cnt.load(Ordering::Relaxed), 0, "Dropped prematurely");
+                    let guard = shared.load();
 
-    /// Accessing the value inside ArcSwap with Guards (and checks for the reference counts).
-    #[test]
-    fn load_cnt() {
-        let a = Arc::new(0);
-        let shared = ArcSwap::from(Arc::clone(&a));
-        // One in shared, one in a
-        assert_eq!(2, Arc::strong_count(&a));
-        let guard = shared.load();
-        assert_eq!(0, **guard);
-        // The guard doesn't have its own ref count now
-        assert_eq!(2, Arc::strong_count(&a));
-        let guard_2 = shared.load();
-        // Unlike with guard, this does not deadlock
-        shared.store(Arc::new(1));
-        // But now, each guard got a full Arc inside it
-        assert_eq!(3, Arc::strong_count(&a));
-        // And when we get rid of them, they disappear
-        drop(guard_2);
-        assert_eq!(2, Arc::strong_count(&a));
-        let _b = Arc::clone(&guard);
-        assert_eq!(3, Arc::strong_count(&a));
-        // We can drop the guard it came from
-        drop(guard);
-        assert_eq!(2, Arc::strong_count(&a));
-        let guard = shared.load();
-        assert_eq!(1, **guard);
-        drop(shared);
-        // We can still use the guard after the shared disappears
-        assert_eq!(1, **guard);
-        let ptr = Arc::clone(&guard);
-        // One in shared, one in guard
-        assert_eq!(2, Arc::strong_count(&ptr));
-        drop(guard);
-        assert_eq!(1, Arc::strong_count(&ptr));
-    }
+                    drop(shared);
+                    assert_eq!(cnt.load(Ordering::Relaxed), 0, "Dropped prematurely");
 
-    /// There can be only limited amount of leases on one thread. Following ones are created, but
-    /// contain full Arcs.
-    #[test]
-    fn lease_overflow() {
-        let a = Arc::new(0);
-        let shared = ArcSwap::from(Arc::clone(&a));
-        assert_eq!(2, Arc::strong_count(&a));
-        let mut guards = (0..1000).map(|_| shared.load()).collect::<Vec<_>>();
-        let count = Arc::strong_count(&a);
-        assert!(count > 2);
-        let guard = shared.load();
-        assert_eq!(count + 1, Arc::strong_count(&a));
-        drop(guard);
-        assert_eq!(count, Arc::strong_count(&a));
-        // When we delete the first one, it didn't have an Arc in it, so the ref count doesn't drop
-        guards.swap_remove(0);
-        // But new one reuses now vacant the slot and doesn't create a new Arc
-        let _guard = shared.load();
-        assert_eq!(count, Arc::strong_count(&a));
-    }
+                    thread::scope(|scope| {
+                        scope.spawn(|_| {
+                            drop(guard);
+                        });
+                    })
+                    .unwrap();
 
-    #[test]
-    fn load_null() {
-        let shared = ArcSwapOption::<usize>::default();
-        let guard = shared.load();
-        assert!(guard.is_none());
-        shared.store(Some(Arc::new(42)));
-        assert_eq!(42, **shared.load().as_ref().unwrap());
-    }
+                    assert_eq!(cnt.load(Ordering::Relaxed), 1, "Not dropped");
+                }
+            }
 
-    #[test]
-    fn from_into() {
-        let a = Arc::new(42);
-        let shared = ArcSwap::new(a);
-        let guard = shared.load();
-        let a = shared.into_inner();
-        assert_eq!(42, *a);
-        assert_eq!(2, Arc::strong_count(&a));
-        drop(guard);
-        assert_eq!(1, Arc::strong_count(&a));
-    }
+            #[test]
+            fn load_option() {
+                let shared = ASO::from_pointee(42);
+                // The type here is not needed in real code, it's just addition test the type matches.
+                let opt: Option<_> = Guard::into_inner(shared.load());
+                assert_eq!(42, *opt.unwrap());
 
-    // Note on the Relaxed order here. This should be enough, because there's that barrier.wait
-    // in between that should do the synchronization of happens-before for us. And using SeqCst
-    // would probably not help either, as there's nothing else with SeqCst here in this test to
-    // relate it to.
-    #[derive(Default)]
-    struct ReportDrop(Arc<AtomicUsize>);
-    impl Drop for ReportDrop {
-        fn drop(&mut self) {
-            self.0.fetch_add(1, Ordering::Relaxed);
-        }
-    }
+                shared.store(None);
+                assert!(shared.load().is_none());
+            }
 
-    /// Interaction of two threads about a guard and dropping it.
-    ///
-    /// We make sure everything works in timely manner (eg. dropping of stuff) even if multiple
-    /// threads interact.
-    ///
-    /// The idea is:
-    /// * Thread 1 loads a value.
-    /// * Thread 2 replaces the shared value. The original value is not destroyed.
-    /// * Thread 1 drops the guard. The value is destroyed and this is observable in both threads.
-    #[test]
-    fn guard_drop_in_thread() {
-        for _ in 0..ITERATIONS {
-            let cnt = Arc::new(AtomicUsize::new(0));
-
-            let shared = ArcSwap::from_pointee(ReportDrop(cnt.clone()));
-            assert_eq!(cnt.load(Ordering::Relaxed), 0, "Dropped prematurely");
-            // We need the threads to wait for each other at places.
-            let sync = Barrier::new(2);
-
-            thread::scope(|scope| {
-                scope.spawn(|_| {
-                    let guard = shared.load();
-                    sync.wait();
-                    // Thread 2 replaces the shared value. We wait for it to confirm.
-                    sync.wait();
-                    drop(guard);
-                    assert_eq!(cnt.load(Ordering::Relaxed), 1, "Value not dropped");
-                    // Let thread 2 know we already dropped it.
-                    sync.wait();
-                });
+            // Check stuff can get formatted
+            #[test]
+            fn debug_impl() {
+                let shared = AS::from_pointee(42);
+                assert_eq!("ArcSwapAny(42)", &format!("{:?}", shared));
+                assert_eq!("42", &format!("{:?}", shared.load()));
+            }
 
-                scope.spawn(|_| {
-                    // Thread 1 loads, we wait for that
-                    sync.wait();
-                    shared.store(Default::default());
-                    assert_eq!(cnt.load(Ordering::Relaxed), 0, "Dropped while still in use");
-                    // Let thread 2 know we replaced it
-                    sync.wait();
-                    // Thread 1 drops its guard. We wait for it to confirm.
-                    sync.wait();
-                    assert_eq!(cnt.load(Ordering::Relaxed), 1, "Value not dropped");
-                });
-            })
-            .unwrap();
-        }
-    }
+            #[test]
+            fn display_impl() {
+                let shared = AS::from_pointee(42);
+                assert_eq!("42", &format!("{}", shared));
+                assert_eq!("42", &format!("{}", shared.load()));
+            }
 
-    /// Check dropping a lease in a different thread than it was created doesn't cause any
-    /// problems.
-    #[test]
-    fn guard_drop_in_another_thread() {
-        for _ in 0..ITERATIONS {
-            let cnt = Arc::new(AtomicUsize::new(0));
-            let shared = ArcSwap::from_pointee(ReportDrop(cnt.clone()));
-            assert_eq!(cnt.load(Ordering::Relaxed), 0, "Dropped prematurely");
-            let guard = shared.load();
-
-            drop(shared);
-            assert_eq!(cnt.load(Ordering::Relaxed), 0, "Dropped prematurely");
-
-            thread::scope(|scope| {
-                scope.spawn(|_| {
-                    drop(guard);
-                });
-            })
-            .unwrap();
+            // The following "tests" are not run, only compiled. They check that things that should be
+            // Send/Sync actually are.
+            fn _check_stuff_is_send_sync() {
+                let shared = AS::from_pointee(42);
+                let moved = AS::from_pointee(42);
+                let shared_ref = &shared;
+                let lease = shared.load();
+                let lease_ref = &lease;
+                let lease = shared.load();
+                thread::scope(|s| {
+                    s.spawn(move |_| {
+                        let _ = lease;
+                        let _ = lease_ref;
+                        let _ = shared_ref;
+                        let _ = moved;
+                    });
+                })
+                .unwrap();
+            }
 
-            assert_eq!(cnt.load(Ordering::Relaxed), 1, "Not dropped");
-        }
-    }
+            /// We have a callback in RCU. Check what happens if we access the value from within.
+            #[test]
+            fn recursive() {
+                let shared = ArcSwap::from(Arc::new(0));
 
-    #[test]
-    fn load_option() {
-        let shared = ArcSwapOption::from_pointee(42);
-        // The type here is not needed in real code, it's just addition test the type matches.
-        let opt: Option<_> = Guard::into_inner(shared.load());
-        assert_eq!(42, *opt.unwrap());
+                shared.rcu(|i| {
+                    if **i < 10 {
+                        shared.rcu(|i| **i + 1);
+                    }
+                    **i
+                });
+                assert_eq!(10, **shared.load());
+                assert_eq!(2, Arc::strong_count(&shared.load_full()));
+            }
 
-        shared.store(None);
-        assert!(shared.load().is_none());
-    }
+            /// A panic from within the rcu callback should not change anything.
+            #[test]
+            fn rcu_panic() {
+                let shared = ArcSwap::from(Arc::new(0));
+                assert!(panic::catch_unwind(|| shared.rcu(|_| -> usize { panic!() })).is_err());
+                assert_eq!(1, Arc::strong_count(&shared.swap(Arc::new(42))));
+            }
 
-    // Check stuff can get formatted
-    #[test]
-    fn debug_impl() {
-        let shared = ArcSwap::from_pointee(42);
-        assert_eq!("ArcSwapAny(42)", &format!("{:?}", shared));
-        assert_eq!("42", &format!("{:?}", shared.load()));
-    }
+            /// Handling null/none values
+            #[test]
+            fn nulls() {
+                let shared = ArcSwapOption::from(Some(Arc::new(0)));
+                let orig = shared.swap(None);
+                assert_eq!(1, Arc::strong_count(&orig.unwrap()));
+                let null = shared.load();
+                assert!(null.is_none());
+                let a = Arc::new(42);
+                let orig = shared.compare_and_swap(ptr::null(), Some(Arc::clone(&a)));
+                assert!(orig.is_none());
+                assert_eq!(2, Arc::strong_count(&a));
+                let orig = Guard::into_inner(shared.compare_and_swap(&None::<Arc<_>>, None));
+                assert_eq!(3, Arc::strong_count(&a));
+                assert!(ptr_eq(&a, &orig));
+            }
 
-    #[test]
-    fn display_impl() {
-        let shared = ArcSwap::from_pointee(42);
-        assert_eq!("42", &format!("{}", shared));
-        assert_eq!("42", &format!("{}", shared.load()));
-    }
+            #[test]
+            /// Multiple RCUs interacting.
+            fn rcu() {
+                const ITERATIONS: usize = 50;
+                const THREADS: usize = 10;
+                let shared = ArcSwap::from(Arc::new(0));
+                thread::scope(|scope| {
+                    for _ in 0..THREADS {
+                        scope.spawn(|_| {
+                            for _ in 0..ITERATIONS {
+                                shared.rcu(|old| **old + 1);
+                            }
+                        });
+                    }
+                })
+                .unwrap();
+                assert_eq!(THREADS * ITERATIONS, **shared.load());
+            }
 
-    // The following "tests" are not run, only compiled. They check that things that should be
-    // Send/Sync actually are.
-    fn _check_stuff_is_send_sync() {
-        let shared = ArcSwap::from_pointee(42);
-        let moved = ArcSwap::from_pointee(42);
-        let shared_ref = &shared;
-        let lease = shared.load();
-        let lease_ref = &lease;
-        let lease = shared.load();
-        thread::scope(|s| {
-            s.spawn(move |_| {
-                let _ = lease;
-                let _ = lease_ref;
-                let _ = shared_ref;
-                let _ = moved;
-            });
-        })
-        .unwrap();
-    }
+            #[test]
+            /// Make sure the reference count and compare_and_swap works as expected.
+            fn cas_ref_cnt() {
+                const ITERATIONS: usize = 50;
+                let shared = ArcSwap::from(Arc::new(0));
+                for i in 0..ITERATIONS {
+                    let orig = shared.load_full();
+                    assert_eq!(i, *orig);
+                    if i % 2 == 1 {
+                        // One for orig, one for shared
+                        assert_eq!(2, Arc::strong_count(&orig));
+                    }
+                    let n1 = Arc::new(i + 1);
+                    // Fill up the slots sometimes
+                    let fillup = || {
+                        if i % 2 == 0 {
+                            Some((0..50).map(|_| shared.load()).collect::<Vec<_>>())
+                        } else {
+                            None
+                        }
+                    };
+                    let guards = fillup();
+                    // Success
+                    let prev = shared.compare_and_swap(&orig, Arc::clone(&n1));
+                    assert!(ptr_eq(&orig, &prev));
+                    drop(guards);
+                    // One for orig, one for prev
+                    assert_eq!(2, Arc::strong_count(&orig));
+                    // One for n1, one for shared
+                    assert_eq!(2, Arc::strong_count(&n1));
+                    assert_eq!(i + 1, **shared.load());
+                    let n2 = Arc::new(i);
+                    drop(prev);
+                    let guards = fillup();
+                    // Failure
+                    let prev = Guard::into_inner(shared.compare_and_swap(&orig, Arc::clone(&n2)));
+                    drop(guards);
+                    assert!(ptr_eq(&n1, &prev));
+                    // One for orig
+                    assert_eq!(1, Arc::strong_count(&orig));
+                    // One for n1, one for shared, one for prev
+                    assert_eq!(3, Arc::strong_count(&n1));
+                    // n2 didn't get increased
+                    assert_eq!(1, Arc::strong_count(&n2));
+                    assert_eq!(i + 1, **shared.load());
+                }
+
+                let a = shared.load_full();
+                // One inside shared, one for a
+                assert_eq!(2, Arc::strong_count(&a));
+                drop(shared);
+                // Only a now
+                assert_eq!(1, Arc::strong_count(&a));
+            }
+        }
+    };
 }
+
+t!(tests_default, DefaultStrategy);
+#[cfg(feature = "experimental-strategies")]
+t!(tests_helping, crate::strategy::experimental::Helping);
diff --git a/src/strategy/experimental.rs b/src/strategy/experimental.rs
index 4b7189e..0a125f6 100644
--- a/src/strategy/experimental.rs
+++ b/src/strategy/experimental.rs
@@ -15,6 +15,7 @@
 use crate::gen_lock::PrivateUnsharded;
 pub use crate::gen_lock::Shard;
 use crate::strategy::gen_lock::GenLockStrategy;
+pub use crate::strategy::helping::Helping;
 
 /// A generation lock.
 ///
diff --git a/src/strategy/gen_lock.rs b/src/strategy/gen_lock.rs
index 8548643..f07f3ec 100644
--- a/src/strategy/gen_lock.rs
+++ b/src/strategy/gen_lock.rs
@@ -1,4 +1,3 @@
-use std::ptr;
 use std::sync::atomic::{AtomicPtr, Ordering};
 
 use crate::gen_lock::{self, GenLock, LockStorage};
@@ -11,6 +10,7 @@ pub struct GenLockStrategy<L>(pub(crate) L);
 impl<T: RefCnt, L: LockStorage> InnerStrategy<T> for GenLockStrategy<L> {
     type Protected = T;
 
+    #[inline]
     unsafe fn load(&self, storage: &AtomicPtr<T::Base>) -> Self::Protected {
         let lock = GenLock::new(&self.0);
 
@@ -23,7 +23,8 @@ impl<T: RefCnt, L: LockStorage> InnerStrategy<T> for GenLockStrategy<L> {
         result
     }
 
-    unsafe fn wait_for_readers(&self, _: *const T::Base) {
+    #[inline]
+    unsafe fn wait_for_readers(&self, _: *const T::Base, _: &AtomicPtr<T::Base>) {
         gen_lock::wait_for_readers(&self.0);
     }
 }
@@ -42,8 +43,12 @@ impl<T: RefCnt, L: LockStorage> CaS<T> for GenLockStrategy<L> {
         // which ones upfront, so we need to implement safety measures for both.
         let lock = GenLock::new(&self.0);
 
-        let previous_ptr = storage.compare_and_swap(cur_ptr, new, Ordering::SeqCst);
-        let swapped = ptr::eq(cur_ptr, previous_ptr);
+        let swap_result =
+            storage.compare_exchange(cur_ptr, new, Ordering::SeqCst, Ordering::SeqCst);
+        let (previous_ptr, swapped) = match swap_result {
+            Ok(previous) => (previous, true),
+            Err(previous) => (previous, false),
+        };
 
         // Drop it here, because:
         // * We can't drop it before the compare_and_swap ‒ in such case, it could get recycled,
diff --git a/src/strategy/helping.rs b/src/strategy/helping.rs
new file mode 100644
index 0000000..2f23944
--- /dev/null
+++ b/src/strategy/helping.rs
@@ -0,0 +1,831 @@
+//! A strategy where the writer is helping the reader in case of collision.
+
+// # How does this work.
+//
+// This is a bit modified version of hazard pointers. The readers put the currently leased pointers
+// into debt slots. That means the reader owes one reference count. But as long as there's one
+// reference inside the storage, everything is fine.
+//
+// When a writer takes the pointer out, it walks all the old debt slots that got created before the
+// change and pays them all by incrementing the reference count as many times, and erases the debts
+// so the readers knows they need to decrement it after the fact.
+//
+// ## Reader, the fast path
+//
+// * Publish an active address ‒ the address we'll be loading stuff from.
+// * Picks a debt slot (note that we always keep at least one free; if we used the last one, we
+//   immediately pay the debt by bumping the reference count and release it and we never
+//   recursively call load). Mark it with a generation (this is to prevent ABA in writers).
+// * Load from the address.
+// * CaS-replace the generation with the address of the destination, reserving the debt. At this
+//   point, we are good to do our stuff.
+//
+// * Later, we pay it back by CaS-replacing it with the NO_DEPT.
+//
+// ## Writer, the non-colliding path
+//
+// * Replaces the pointer in the storage.
+// * The writer walks over all debts. It pays each debt that it is concerned with by bumping the
+//   reference and replacing the dept with NO_DEPT. The relevant reader will fail in the CaS
+//   (either because it finds NO_DEPT or other pointer in there) and knows the reference was
+//   bumped, so it needs to decrement it. Note that it is possible that someone also reuses the
+//   slot for the _same_ pointer. In that case that reader will set it to NO_DEPT and the newer
+//   reader will have a pre-paid debt, which is fine.
+//
+// ## The collision path
+//
+// The reservation of a slot is not atomic, therefore a writer can observe the reservation in
+// progress. But it doesn't want to wait for it to complete (it wants to be lock-free, which means
+// it needs to be able to resolve the situation on its own).
+//
+// The way it knows it is in progress of the reservation is by seeing a generation in there (it has
+// a distinct tag). In that case it'll try to:
+//
+// * First verify that the reservation is being done for the same address it modified, by reading
+//   and re-confirming the active_addr slot corresponding to the currently handled node. If it is
+//   for some other address, the writer doesn't have to be concerned and proceeds to the next slot.
+// * It does a full load. That is fine, because the writer must be on a different thread than the
+//   reader and therefore there is at least one free slot. Full load means paying the debt right
+//   away by incrementing the reference count.
+// * Then it tries to pass the already fully protected/paid pointer to the reader, by CaS-replacing
+//   the previously observed generation with the pointer. If it fails, it decrements the count
+//   again and retries on the same slot, as it might now contain a debt for the same pointer it is
+//   being concerned.
+// * The reader then finds the generation got replaced by a pointer (tagged with the right tag, so
+//   some other writer doesn't try to "pay" a debt that is actually a negative debt). It can then
+//   proceed to using that one instead of what it loaded (and not ever retry anything).
+//
+// ## ABA protection
+//
+// The generation as pre-reserving the slot allows the writer to make sure it is offering the
+// loaded pointer to the same reader and that the read value is new enough (and of the same type).
+//
+// This solves the general case, but there's also much less frequent but theoretical ABA problem
+// that could lead to UB, if left unsolved:
+//
+// * There is a collision on generation G.
+// * The writer loads a pointer, bumps it.
+// * In the meantime, all the 2^30 or 2^62 generations (depending on the usize width) generations
+//   wrap around.
+// * The writer stores the outdated and possibly different-typed pointer in there and the reader
+//   uses it.
+//
+// To mitigate that, every time the counter overflows we take the current node and un-assign it
+// from our current thread. We mark it as in "cooldown" and let it in there until there are no
+// writers messing with that node any more (if they are not on the node, they can't experience the
+// ABA problem on it). After that, we are allowed to use it again.
+//
+// This doesn't block the reader, it'll simply find *a* node next time ‒ this one, or possibly a
+// different (or new) one.
+//
+// # Orderings
+//
+// The debt linked list is the easy part:
+// * The head is used to synchronize the non-atomic content of it through trivial Acquire /
+//   Release. The non-atomic parts are never written to after publishing the node (publish
+//   pattern).
+// * The in_use field is used as a form of lock, to claim exclusive use / write privilege of some
+//   of the fields, also with Acquire / Release (mutex pattern).
+//
+// The actual debt juggling is a bit more interesting. We need to ensure that:
+//
+// * We don't start using anything before the debt is installed.
+// * We don't release the swapped-out pointer to the caller before all debts acquired from
+//   previously loading the old value are paid (and none are somewhere in process).
+//
+// We use SeqCst both on the change of the pointer and on the initial setting of the generation
+// into the debt slot. That way we can be sure both threads agree on what happened first.
+//
+// Then all things (with the small exception about the current address, see the comments inline)
+// happen on the same debt slot, therefore the time line is well established on that. Everything
+// that synchronizes anything is Acquire/Release as needed there.
+//
+// There's a gotcha in the destructor ‒ that one doesn't have the write with SeqCst on it. But at
+// that time other things must have assured that there's an exclusive access to the ArcSwap
+// (because drop takes &mut self) and that also assures nobody is currently loading anything ‒ all
+// the debts that are there must have existed before that exclusive ownership was acquired by
+// whoever drops it.
+
+use std::borrow::Borrow;
+use std::cell::Cell;
+use std::fmt::{Debug, Formatter, Result as FmtResult};
+use std::mem::{self, ManuallyDrop};
+use std::ops::Deref;
+use std::ptr;
+use std::sync::atomic::Ordering::*;
+use std::sync::atomic::{AtomicPtr, AtomicUsize};
+
+use super::sealed::{CaS, InnerStrategy, Protected};
+use crate::{AsRaw, RefCnt};
+
+const NODE_UNUSED: usize = 0;
+const NODE_USED: usize = 1;
+const NODE_COOLDOWN: usize = 2;
+
+/// A wrapper around a node pointer, to un-claim the node on thread shutdown.
+struct DebtHead {
+    // Node for this thread.
+    node: Cell<Option<&'static Node>>,
+
+    // The generation counter.
+    generation: Cell<usize>,
+
+    // Rotate the slots that are tried first.
+    slot_pos: Cell<usize>,
+}
+
+impl Drop for DebtHead {
+    fn drop(&mut self) {
+        if let Some(node) = self.node.get() {
+            // Release - syncing writes/ownership of this Node
+            // We put it to cooldown, because we implicitly reset the generation count by giving it
+            // to another thread. So just to be on the safe side and make sure no writers sees it
+            // across that reset.
+            assert_eq!(node.in_use.swap(NODE_COOLDOWN, Release), NODE_USED);
+        }
+    }
+}
+
+/// The value of pointer `3` should be pretty safe, for two reasons:
+///
+/// * It's an odd number, but the pointers we have are likely aligned at least to the word size,
+///   because the data at the end of the `Arc` has the counters.
+/// * It's in the very first page where NULL lives, so it's not mapped.
+///
+/// Note that we don't want to use 0 directly, because NULL is a valid pointer.
+const NO_DEBT: usize = 0b11;
+const REPLACEMENT_TAG: usize = 0b01;
+const GEN_TAG: usize = 0b10;
+const TAG_MASK: usize = 0b11;
+const DEBT_SLOT_CNT: usize = 8;
+
+thread_local! {
+    /// A debt node assigned to this thread.
+    static THREAD_HEAD: DebtHead = DebtHead {
+        node: Cell::new(None),
+        generation: Cell::new(0),
+        slot_pos: Cell::new(0),
+    };
+}
+
+/// A mostly copy of the Dept from elsewhere
+///
+/// For now, we keep a separate copy, both for simplicity and because we handle them differently
+/// based on the strategy used. We may either delete one copy eventually or unify them, but for the
+/// experiment, let's just copy-paste..
+struct Debt(AtomicUsize);
+
+impl Debt {
+    #[allow(clippy::new_ret_no_self)]
+    #[inline]
+    fn new(ptr: usize) -> (&'static Debt, bool, usize) {
+        Node::with_thread_head(|head| {
+            let node = head
+                .node
+                .get()
+                .expect("with_thread_head ensures we have one");
+            // Check it is in use by *us*
+            debug_assert_eq!(node.in_use.load(Relaxed), NODE_USED);
+
+            // Incrementing by 4 ensures we always have enough space for 2 bit of tags.
+            let gen = head.generation.get().wrapping_add(4);
+            debug_assert_eq!(gen & GEN_TAG, 0);
+            head.generation.set(gen);
+            let discard = gen == 0;
+            let gen = gen | GEN_TAG;
+            // We will sync by the write to the debt. But we also sync the value of the previous
+            // generation/released slot. That way we may re-confirm in the writer that the reader is
+            // not in between here and the compare_exchange below with a stale gen (eg. if we are in
+            // here, the re-confirm there will load the NO_DEPT and we are fine).
+            node.active_addr.store(ptr, Release);
+            let offset = head.slot_pos.get();
+
+            let len = node.slots.0.len();
+            for raw_i in 0..len {
+                let i = (raw_i + offset) % len;
+                let slot = &node.slots.0[i];
+
+                // We could do load and store separately as we are the only ones allowed to
+                // overwrite a NO_DEPT, but we actually need the SeqCst to be a read-write
+                // operation in here (we need both the release and acquire part of it).
+                if slot
+                    .0
+                    .compare_exchange(NO_DEBT, gen, SeqCst, Relaxed)
+                    .is_ok()
+                {
+                    let mut last = true;
+                    for raw_j in raw_i + 1..len {
+                        let j = (raw_j + offset) % len;
+                        if node.slots.0[j].0.load(Relaxed) == NO_DEBT {
+                            last = false;
+                            // We already discovered this one will be empty, so store the info for
+                            // next time.
+                            head.slot_pos.set(j);
+                            break;
+                        }
+                    }
+                    if last {
+                        // OK, store _this_ one for next time because we are going to free it up in
+                        // a moment.
+                        head.slot_pos.set(i);
+                    }
+                    if discard {
+                        // Cool, we have modified the gen, claimed the slot, that's all we need to
+                        // do while still holding ownership of the node. This is the right time to
+                        // put the node into cooldown and get rid of it. We'll pick a new one (or
+                        // this one, if the cooldown succeeds in between) next time.
+                        node.start_cooldown();
+                    }
+                    return (slot, last, gen);
+                }
+            }
+            unreachable!("Run out of slots in {:#?}", node);
+        })
+    }
+
+    /// Confirms the debt.
+    ///
+    /// Replaces the pre-debt mark (the address we are reading from) with the actual debt and
+    /// confirm it that way.
+    ///
+    /// If it got changed in the meantime, returns the (already protected) replacement value.
+    #[inline]
+    fn confirm(&self, gen: usize, ptr_addr: usize) -> Result<(), usize> {
+        // AcqRel -> Release to publish everything (do we need to publish anything?)/make sure
+        // sutff stays "inside" the lock.
+        // Acquire -> we read a potentially new address, need to bring the pointee in.
+        match self.0.compare_exchange(gen, ptr_addr, AcqRel, Acquire) {
+            Ok(_) => Ok(()),
+            Err(replacement) => {
+                // Nothing synchronized by this, we just clear the slot for future reuse. Release
+                // just to make sure, but should Relaxed be enough?
+                self.0.store(NO_DEBT, Release);
+                Err(replacement)
+            }
+        }
+    }
+
+    /// Tries to pay the given debt.
+    ///
+    /// If the debt is still there, for the given pointer, it is paid and `true` is returned. If it
+    /// is empty or if there's some other pointer, it is not paid and `false` is returned, meaning
+    /// the debt was paid previously by someone else.
+    ///
+    /// # Notes
+    ///
+    /// * It is possible that someone paid the debt and then someone else put a debt for the same
+    ///   pointer in there. This is fine, as we'll just pay the debt for that someone else.
+    /// * This relies on the fact that the same pointer must point to the same object and
+    ///   specifically to the same type ‒ the caller provides the type, it's destructor, etc.
+    /// * It also relies on the fact the same thing is not stuffed both inside an `Arc` and `Rc` or
+    ///   something like that, but that sounds like a reasonable assumption. Someone storing it
+    ///   through `ArcSwap<T>` and someone else with `ArcSwapOption<T>` will work.
+    /// * It is possible the slot got reused and currently contains the same pointer, but tagged as
+    ///   already protected. That is fine, as the raw value will not match and we won't change
+    ///   that.
+    #[inline]
+    fn pay<T: RefCnt>(&self, ptr: *const T::Base) -> bool {
+        self.0
+            // If we don't change anything because there's something else, Relaxed is fine (it's
+            // been already paid)
+            //
+            // The Release works as kind of Mutex. We make sure nothing from the debt-protected
+            // sections leaks below this point.
+            .compare_exchange(ptr as usize, NO_DEBT, Release, Relaxed)
+            .is_ok()
+    }
+
+    /// Pays all the debts on the given pointer.
+    #[inline]
+    fn pay_all<T, R>(ptr: *const T::Base, storage_addr: usize, replacement: R)
+    where
+        T: RefCnt,
+        R: Fn() -> T,
+    {
+        let val = unsafe { T::from_ptr(ptr) };
+        // Pre-pay one ref count that can be safely put into a debt slot to pay it.
+        T::inc(&val);
+
+        Node::traverse::<(), _>(|node| {
+            let _reservation = node.reserve_writer();
+            for slot in &node.slots.0 {
+                loop {
+                    match slot
+                        .0
+                        // We don't need to release anything in here. The writes to the reference
+                        // counts take care of themselves before they drop to 0 (they are all to
+                        // the same atomic, and we are doing the decrements later on). We need to
+                        // acquire any increments done by the other threads though and we need to
+                        // acquire the active_addr
+                        .compare_exchange(ptr as usize, NO_DEBT, Acquire, Acquire)
+                    {
+                        Ok(_) => {
+                            // We just paid the debt by the pre-paid ref count from before.
+                            // Pre-pay one more, for another future slot
+                            T::inc(&val);
+                            break;
+                        }
+                        Err(gen) if gen & TAG_MASK == GEN_TAG => {
+                            // The reader is trying to claim the slot right now. Let's have a look
+                            // at the address where the data should come from and help the reader
+                            // out.
+                            let active_addr = node.active_addr.load(Acquire);
+                            if active_addr != storage_addr {
+                                // Re-confirm the gen matches. That way with the above active_addr
+                                // load and Acquire we make sure the active_addr is not newer than
+                                // the gen and therefore we are not missing a place where we need
+                                // to help (eg. that Acquire makes sure the gen catches up with
+                                // it).
+                                if slot.0.load(Relaxed) == gen {
+                                    // OK, it is really doing something with some other ArcSwap,
+                                    // not interested in that.
+                                    break;
+                                } else {
+                                    // The value changed and we are not sure how useful the
+                                    // active_addr is for us. So retry.
+                                    continue;
+                                }
+                            }
+                            // Get a replacement value and try to donate it.
+                            let replacement = replacement();
+                            let replace_addr = T::as_ptr(&replacement) as usize;
+                            assert_eq!(replace_addr & TAG_MASK, 0, "Not enough space for tags");
+                            let replace_addr = replace_addr | REPLACEMENT_TAG;
+                            if slot
+                                .0
+                                // Release the value we send there. TODO: Do we need the Acquire
+                                // there?
+                                //
+                                // Relaxed on failure: Basically, nothing happened anywhere, all
+                                // data stayed with us and we are going to retry this loop from
+                                // scratch.
+                                .compare_exchange_weak(gen, replace_addr, AcqRel, Relaxed)
+                                .is_ok()
+                            {
+                                // OK, it went it
+                                T::into_ptr(replacement);
+                                break;
+                            }
+                            // else -> replacement is dropped.
+                            // Also, loop once more because the current slot did *not* get
+                            // resolved. Retry and see if the reader already got what it wanted or
+                            // try creating a new replacement.
+                        }
+                        // OK, not interested in this slot. Either no debt or different one.
+                        Err(_) => break,
+                    }
+                }
+            }
+            None
+        });
+        // Implicit dec by dropping val in here, pair for the above T::inc
+    }
+}
+
+impl Default for Debt {
+    fn default() -> Self {
+        Debt(AtomicUsize::new(NO_DEBT))
+    }
+}
+
+#[repr(align(64))]
+#[derive(Default)]
+struct Slots([Debt; DEBT_SLOT_CNT]);
+
+impl Debug for Slots {
+    fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
+        fmt.debug_list()
+            .entries(self.0.iter().map(|s| s.0.load(Relaxed) as *const u8))
+            .finish()
+    }
+}
+
+struct NodeReservation<'a>(&'a Node);
+
+impl Drop for NodeReservation<'_> {
+    fn drop(&mut self) {
+        self.0.active_writers.fetch_sub(1, Release);
+    }
+}
+
+/// One thread-local node for debts.
+#[repr(C)]
+struct Node {
+    slots: Slots,
+    next: Option<&'static Node>,
+    // This would be enough with AtomicU8, but that doesn't exist on 1.31 yet.
+    in_use: AtomicUsize,
+    active_addr: AtomicUsize,
+    /// How many writers are currently interested in this one.
+    ///
+    /// See the ABA protection at the top.
+    active_writers: AtomicUsize,
+}
+
+/// The head of the debt chain.
+static DEBT_HEAD: AtomicPtr<Node> = AtomicPtr::new(ptr::null_mut());
+
+impl Default for Node {
+    fn default() -> Self {
+        Node {
+            next: None,
+            in_use: AtomicUsize::new(NODE_USED),
+            slots: Default::default(),
+            active_addr: AtomicUsize::new(0),
+            active_writers: AtomicUsize::new(0),
+        }
+    }
+}
+
+impl Node {
+    /// Goes through the debt linked list.
+    ///
+    /// This traverses the linked list, calling the closure on each node. If the closure returns
+    /// `Some`, it terminates with that value early, otherwise it runs to the end.
+    fn traverse<R, F: FnMut(&'static Node) -> Option<R>>(mut f: F) -> Option<R> {
+        // Acquire ‒ we want to make sure we read the correct version of data at the end of the
+        // pointer. Any write to the DEBT_HEAD is with Release.
+        //
+        // Note that the other pointers in the chain never change and are *ordinary* pointers. The
+        // whole linked list is synchronized through the head.
+        let mut current = unsafe { DEBT_HEAD.load(Acquire).as_ref() };
+        while let Some(node) = current {
+            let result = f(node);
+            if result.is_some() {
+                return result;
+            }
+            current = node.next;
+        }
+        None
+    }
+
+    /// Put the current thread node into cooldown
+    fn start_cooldown(&self) {
+        assert_eq!(NODE_USED, self.in_use.swap(NODE_COOLDOWN, Release));
+        THREAD_HEAD.with(|head| assert!(ptr::eq(head.node.take().unwrap(), self)));
+    }
+
+    /// Perform a cooldown if the node is ready.
+    ///
+    /// See the ABA protection at the top.
+    fn check_cooldown(&self) {
+        // Cooldown succeeded if it is not being looked at by any writers.
+        // This value may be a little bit outdated. But that is fine, as we get an update at least
+        // every load operation (there's a SeqCst read-write operation on both paths, for readers
+        // this can be only one generation stale which is fine, and for writers, if it doesn't
+        // enter the write, because it is not interested, we don't worry about not seeing that
+        // writer).
+        if self.active_writers.load(Relaxed) == 0 {
+            // Relaxed: we don't synchronize anything by this. It'll get used in real just in a
+            // moment. If it doesn't succeed, then this one wasn't doing a cooldown and that's OK.
+            let _ = self
+                .in_use
+                .compare_exchange(NODE_COOLDOWN, NODE_UNUSED, Relaxed, Relaxed);
+        }
+    }
+
+    /// Mark this node that a writer is currently playing with it.
+    #[inline]
+    fn reserve_writer(&self) -> NodeReservation {
+        self.active_writers.fetch_add(1, Acquire);
+        NodeReservation(self)
+    }
+
+    #[inline(never)]
+    fn get() -> &'static Self {
+        // Try to find an unused one in the chain and reuse it.
+        Self::traverse(|node| {
+            node.check_cooldown();
+            if node
+                .in_use
+                // We claim a unique control over the generation and the right to write to slots if
+                // they are NO_DEPT
+                .compare_exchange(NODE_UNUSED, NODE_USED, Acquire, Relaxed)
+                .is_ok()
+            {
+                Some(node)
+            } else {
+                None
+            }
+        })
+        // If that didn't work, create a new one and prepend to the list.
+        .unwrap_or_else(|| {
+            let node = Box::leak(Box::new(Node::default()));
+            // Not shared between threads yet, so ordinary write would be fine too.
+            node.in_use.store(NODE_USED, Relaxed);
+            // We don't want to read any data in addition to the head, Relaxed is fine
+            // here.
+            //
+            // We do need to release the data to others, but for that, we acquire in the
+            // compare_exchange below.
+            let mut head = DEBT_HEAD.load(Relaxed);
+            loop {
+                node.next = unsafe { head.as_ref() };
+                if let Err(old) = DEBT_HEAD.compare_exchange_weak(
+                    head, node,
+                    // We need to release *the whole chain* here. For that, we need to
+                    // acquire it first.
+                    AcqRel, Relaxed, // Nothing changed, go next round of the loop.
+                ) {
+                    head = old;
+                } else {
+                    return node;
+                }
+            }
+        })
+    }
+
+    #[inline]
+    fn with_thread_head<R, F: FnOnce(&DebtHead) -> R>(f: F) -> R {
+        THREAD_HEAD
+            // FIXME: Can this actually fail sometime? Can people use us during some kind of mutual
+            // thread-local destruction thing?
+            .with(|head| {
+                if head.node.get().is_none() {
+                    head.node.set(Some(Node::get()));
+                }
+                f(head)
+            })
+    }
+}
+
+impl Debug for Node {
+    fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
+        fmt.debug_struct("Node")
+            .field("in_use", &self.in_use.load(Relaxed))
+            .field("next", &self.next)
+            .field(
+                "active_addr",
+                &(self.active_addr.load(Relaxed) as *const u8),
+            )
+            .field("active_writers", &self.active_writers)
+            .field("slots", &self.slots)
+            .finish()
+    }
+}
+
+pub struct Protection<T: RefCnt> {
+    debt: Option<&'static Debt>,
+    ptr: ManuallyDrop<T>,
+}
+
+impl<T: RefCnt> Protection<T> {
+    #[inline]
+    unsafe fn new(ptr: *const T::Base, debt: Option<&'static Debt>) -> Self {
+        Self {
+            debt,
+            ptr: ManuallyDrop::new(T::from_ptr(ptr)),
+        }
+    }
+}
+
+impl<T: RefCnt> Drop for Protection<T> {
+    #[inline]
+    fn drop(&mut self) {
+        match self.debt.take() {
+            // We have our own copy of Arc, so we don't need a protection. Do nothing (but release
+            // the Arc below).
+            None => (),
+            // If we owed something, just return the debt. We don't have a pointer owned, so
+            // nothing to release.
+            Some(debt) => {
+                let ptr = T::as_ptr(&self.ptr);
+                if debt.pay::<T>(ptr) {
+                    return;
+                }
+                // But if the debt was already paid for us, we need to release the pointer, as we
+                // were effectively already in the Unprotected mode.
+            }
+        }
+        // Equivalent to T::dec(ptr)
+        unsafe { ManuallyDrop::drop(&mut self.ptr) };
+    }
+}
+
+impl<T: RefCnt> Protected<T> for Protection<T> {
+    #[inline]
+    fn from_inner(ptr: T) -> Self {
+        Self {
+            debt: None,
+            ptr: ManuallyDrop::new(ptr),
+        }
+    }
+
+    #[inline]
+    fn into_inner(mut self) -> T {
+        // Drop any debt and release any lock held by the given guard and return a
+        // full-featured value that even can outlive the ArcSwap it originated from.
+        match self.debt.take() {
+            None => (), // We have a fully loaded ref-counted pointer.
+            Some(debt) => {
+                let ptr = T::inc(&self.ptr);
+                if !debt.pay::<T>(ptr) {
+                    unsafe { T::dec(ptr) };
+                }
+            }
+        }
+
+        // The ptr::read & forget is something like a cheating move. We can't move it out, because
+        // we have a destructor and Rust doesn't allow us to do that.
+        // (it's basically ManuallyDrop::take, but that one is only since 1.42.0 and that's too new
+        // for us).
+        let inner = unsafe { ptr::read(self.ptr.deref()) };
+        mem::forget(self);
+        inner
+    }
+}
+
+impl<T: RefCnt> Borrow<T> for Protection<T> {
+    #[inline]
+    fn borrow(&self) -> &T {
+        &self.ptr
+    }
+}
+
+/// A strategy where a writer helps the reader in case there's a collision.
+/// Which makes it possible for the writer to move forward. We don't use the generation lock here,
+/// so ever writers are lock-free.
+///
+/// This is inspired (but not an exact copy) of
+/// <https://pvk.ca/Blog/2020/07/07/flatter-wait-free-hazard-pointers/>. The debts are mostly
+/// copies of the ones used by the hybrid strategy, but modified a bit. Just like in the hybrid
+/// strategy, in case the slots run out or when the writer updates the value, the debts are paid by
+/// incrementing the ref count (which is a little slower, but still wait-free/lock-free and still
+/// in order of nanoseconds).
+///
+/// The strategy is:
+///
+/// * Wait-free on readers (except every usize::MAX/4 accesses, when it is only lock-free).
+/// * Lock free on writers.
+/// * Precise (data is dropped as soon as possible).
+#[derive(Copy, Clone, Debug, Default)]
+pub struct Helping;
+
+impl<T: RefCnt> InnerStrategy<T> for Helping {
+    type Protected = Protection<T>;
+
+    #[inline]
+    unsafe fn load(&self, storage: &AtomicPtr<T::Base>) -> Self::Protected {
+        // First, we claim a debt slot and store the address of the atomic pointer there, so the
+        // writer can optionally help us out with loading and protecting something.
+        let (debt, last, gen) = Debt::new(storage as *const _ as usize);
+        // We already synchronized the start of the sequence by SeqCst in the Debt::new vs swap on
+        // the pointer. We just need to make sure to bring the pointee in (this can be newer than
+        // what we got in the Debt)
+        let candidate = storage.load(Acquire);
+        // TODO: We should check this much sooner, possibly when the pointer gets into the storage.
+        assert_eq!(
+            candidate as usize & TAG_MASK,
+            0,
+            "Not enough space for tags"
+        );
+
+        let ptr_addr = candidate as usize;
+        // Try to replace the debt with our candidate.
+        match debt.confirm(gen, ptr_addr) {
+            Ok(()) => {
+                // The fast path -> we got the debt confirmed alright.
+                let result = Protection::new(candidate, Some(debt));
+                if last {
+                    // If we got the last debt slot, we must pay it back before proceeding, so
+                    // others can get a slot too.
+                    Protection::from_inner(Protection::into_inner(result))
+                } else {
+                    result
+                }
+            }
+            Err(replacement) => {
+                // We got a (possibly) different pointer out. But that one is already protected and
+                // the slot is paid back. It's not possible someone could have changed our gen to
+                // something but a replacement.
+                debug_assert_eq!(
+                    replacement & TAG_MASK,
+                    REPLACEMENT_TAG,
+                    "Missing tag on replacement",
+                );
+                debug_assert_eq!(NO_DEBT, debt.0.load(Relaxed));
+                let replacement = replacement & !TAG_MASK; // Remove the tag
+                Protection::new(replacement as *const _, None)
+            }
+        }
+    }
+
+    unsafe fn wait_for_readers(&self, old: *const T::Base, storage: &AtomicPtr<T::Base>) {
+        // The pay_all may need to provide fresh replacement values if someone else is loading from
+        // this particular storage. We do so by the exact same way, by `load` ‒ it's OK, a writer
+        // does not hold a slot and the reader doesn't recurse back into writer, so we won't run
+        // out of slots.
+        let replacement = || Protection::into_inner(self.load(storage));
+        Debt::pay_all::<T, _>(old, storage as *const _ as usize, replacement);
+    }
+}
+
+impl<T: RefCnt> CaS<T> for Helping {
+    unsafe fn compare_and_swap<C: AsRaw<T::Base>>(
+        &self,
+        storage: &AtomicPtr<T::Base>,
+        current: C,
+        new: T,
+    ) -> Self::Protected {
+        loop {
+            let old = <Self as InnerStrategy<T>>::load(self, storage);
+            // Observation of their inequality is enough to make a verdict
+            if old.ptr.deref().as_raw() != current.as_raw() {
+                return old;
+            }
+            // If they are still equal, put the new one in.
+            let new_raw = T::as_ptr(&new);
+            if storage
+                .compare_exchange_weak(current.as_raw(), new_raw, SeqCst, Relaxed)
+                .is_ok()
+            {
+                // We successfully put the new value in. The ref count went in there too.
+                T::into_ptr(new);
+                <Self as InnerStrategy<T>>::wait_for_readers(
+                    self,
+                    old.ptr.deref().as_raw(),
+                    storage,
+                );
+                // We just got one ref count out of the storage and we have one in old. We don't
+                // need two.
+                T::dec(old.ptr.deref().as_raw());
+                return old;
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use std::sync::Arc;
+
+    use super::*;
+
+    impl Node {
+        fn is_empty(&self) -> bool {
+            self.slots.0.iter().all(|d| d.0.load(Relaxed) == NO_DEBT)
+        }
+
+        fn get_thread() -> &'static Self {
+            Self::with_thread_head(|h| h.node.get().unwrap())
+        }
+    }
+
+    /// A freshly acquired thread local node is empty.
+    #[test]
+    fn new_empty() {
+        assert!(Node::get_thread().is_empty());
+    }
+
+    /// Just plain load, no contention or whatever.
+    #[test]
+    fn load_bare() {
+        // Check everything after loading stuff
+        type T = Arc<usize>;
+        let a = T::new(42);
+        let s = AtomicPtr::new(T::as_ptr(&a) as *mut usize);
+        let p = unsafe { <Helping as InnerStrategy<T>>::load(&Helping, &s) };
+        let pa: &Arc<usize> = p.borrow();
+        assert_eq!(42, **pa);
+        assert_eq!(T::as_ptr(&a), p.ptr.deref().deref());
+        assert_eq!(s.load(Relaxed) as usize, p.debt.unwrap().0.load(Relaxed));
+        assert!(!Node::get_thread().is_empty());
+        assert_eq!(1, Arc::strong_count(&a));
+        // Now we drop the protection and everything shall clean up.
+        drop(p);
+        assert!(Node::get_thread().is_empty());
+    }
+
+    /// A smoke test exercising the generation overflow thing.
+    #[test]
+    fn gen_overflow() {
+        type T = Arc<usize>;
+        let a = T::new(42);
+        let s = AtomicPtr::new(T::as_ptr(&a) as *mut usize);
+        let p = unsafe { <Helping as InnerStrategy<T>>::load(&Helping, &s) };
+        // Force it to overflow during the next one.
+        THREAD_HEAD.with(|h| h.generation.set(usize::MAX / 4 * 4));
+        drop(p);
+        // When it overflows, the node is released back to the pool (no good way to check it got
+        // into cooldown, because some other thread might claim it in between).
+        let p = unsafe { <Helping as InnerStrategy<T>>::load(&Helping, &s) };
+        THREAD_HEAD.with(|h| assert!(h.node.get().is_none()));
+        drop(p);
+        // But next round will pick some node again.
+        let p = unsafe { <Helping as InnerStrategy<T>>::load(&Helping, &s) };
+        THREAD_HEAD.with(|h| assert!(h.node.get().is_some()));
+        drop(p);
+    }
+
+    /// Check some alignment assumptions.
+    ///
+    /// Note that we also check them at runtime, in case someone doesn't run the tests.
+    #[test]
+    fn alignments() {
+        // We don't need _exactly_ this, but that will ensure that the pointer to data is also
+        // aligned to that. Or at least always unaligned to that.
+        assert!(mem::align_of::<Arc<u8>>() >= 4);
+        assert_eq!(Arc::as_ptr(&Arc::new(0u8)) as usize % 4, 0);
+        assert!(mem::align_of::<AtomicUsize>() >= 4);
+    }
+}
diff --git a/src/strategy/hybrid.rs b/src/strategy/hybrid.rs
index cb4189c..cacb844 100644
--- a/src/strategy/hybrid.rs
+++ b/src/strategy/hybrid.rs
@@ -127,8 +127,8 @@ where
             }
         })
     }
-    unsafe fn wait_for_readers(&self, old: *const T::Base) {
-        self.fallback.wait_for_readers(old);
+    unsafe fn wait_for_readers(&self, old: *const T::Base, storage: &AtomicPtr<T::Base>) {
+        self.fallback.wait_for_readers(old, storage);
         Debt::pay_all::<T>(old);
     }
 }
@@ -147,8 +147,12 @@ impl<T: RefCnt, L: LockStorage> CaS<T> for HybridStrategy<GenLockStrategy<L>> {
         // which ones upfront, so we need to implement safety measures for both.
         let gen = GenLock::new(&self.fallback.0);
 
-        let previous_ptr = storage.compare_and_swap(cur_ptr, new, Ordering::SeqCst);
-        let swapped = ptr::eq(cur_ptr, previous_ptr);
+        let swap_result =
+            storage.compare_exchange(cur_ptr, new, Ordering::SeqCst, Ordering::SeqCst);
+        let (previous_ptr, swapped) = match swap_result {
+            Ok(previous) => (previous, true),
+            Err(previous) => (previous, false),
+        };
 
         // Drop it here, because:
         // * We can't drop it before the compare_and_swap ‒ in such case, it could get recycled,
@@ -185,7 +189,7 @@ impl<T: RefCnt, L: LockStorage> CaS<T> for HybridStrategy<GenLockStrategy<L>> {
             // for all readers to make sure there are no more untracked copies of it.
             //
             // Why is rustc confused about self.wait_for_readers???
-            InnerStrategy::<T>::wait_for_readers(self, previous_ptr);
+            InnerStrategy::<T>::wait_for_readers(self, previous_ptr, storage);
         } else {
             // We didn't swap, so new is black-holed.
             T::dec(new);
diff --git a/src/strategy/mod.rs b/src/strategy/mod.rs
index e0e9f6a..cb92b07 100644
--- a/src/strategy/mod.rs
+++ b/src/strategy/mod.rs
@@ -53,6 +53,7 @@ use crate::ref_cnt::RefCnt;
 #[cfg(feature = "experimental-strategies")]
 pub mod experimental;
 mod gen_lock;
+mod helping;
 mod hybrid;
 mod rw_lock;
 
@@ -118,7 +119,7 @@ pub(crate) mod sealed {
         // Drop „unlocks“
         type Protected: Protected<T>;
         unsafe fn load(&self, storage: &AtomicPtr<T::Base>) -> Self::Protected;
-        unsafe fn wait_for_readers(&self, old: *const T::Base);
+        unsafe fn wait_for_readers(&self, old: *const T::Base, storage: &AtomicPtr<T::Base>);
     }
 
     pub trait CaS<T: RefCnt>: InnerStrategy<T> {
diff --git a/src/strategy/rw_lock.rs b/src/strategy/rw_lock.rs
index 457a279..5a3fd48 100644
--- a/src/strategy/rw_lock.rs
+++ b/src/strategy/rw_lock.rs
@@ -28,7 +28,7 @@ impl<T: RefCnt> InnerStrategy<T> for RwLock<()> {
         ptr
     }
 
-    unsafe fn wait_for_readers(&self, _: *const T::Base) {
+    unsafe fn wait_for_readers(&self, _: *const T::Base, _: &AtomicPtr<T::Base>) {
         // By acquiring the write lock, we make sure there are no read locks present across it.
         drop(self.write().expect("We don't panic in here"));
     }
diff --git a/src/weak.rs b/src/weak.rs
index 1f6d593..27a8a36 100644
--- a/src/weak.rs
+++ b/src/weak.rs
@@ -49,6 +49,8 @@ mod tests {
 
     // Replace a weak pointer with a NULL one
     #[test]
+    // Miri-bug in std, see https://github.com/rust-lang/rust/issues/80365
+    #[cfg_attr(miri, ignore)]
     fn reset() {
         let data = Arc::new("Hello");
         let shared = ArcSwapWeak::new(Arc::downgrade(&data));


Test patch:
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index 83378b4..680577e 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -102,7 +102,7 @@ jobs:
         uses: Swatinem/rust-cache@v1
 
       - name: Run clippy linter
-        run: cargo clippy --all --tests -- -D clippy::all -D warnings
+        run: cargo clippy --all --all-features --tests -- -D clippy::all -D warnings
 
   bench:
     name: Verify benchmarks compile
@@ -144,4 +144,26 @@ jobs:
       - name: Run clippy linter
         run: rm Cargo.lock && cargo check
 
-# TODO: Some miri checks
+  miri:
+    name: Miri checks
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v2
+
+      - name: Install Rust
+        uses: actions-rs/toolchain@v1
+        with:
+          toolchain: nightly
+          profile: minimal
+          default: true
+          components: "miri"
+
+      - name: Restore cache
+        uses: Swatinem/rust-cache@v1
+
+      - name: Run miri
+        env:
+          PROPTEST_CASES: "10"
+          MIRIFLAGS: "-Zmiri-disable-isolation"
+        run: cargo miri test --all-features
diff --git a/tests/random.rs b/tests/random.rs
index 81c9959..50fa9bc 100644
--- a/tests/random.rs
+++ b/tests/random.rs
@@ -29,6 +29,10 @@ impl OpsInstruction {
 proptest! {}
 
 const LIMIT: usize = 5;
+#[cfg(not(miri))]
+const SIZE: usize = 100;
+#[cfg(miri)]
+const SIZE: usize = 10;
 static ARCS: Lazy<Vec<Arc<usize>>> = Lazy::new(|| (0..LIMIT).map(Arc::new).collect());
 
 #[derive(Copy, Clone, Debug)]
@@ -48,39 +52,47 @@ impl SelInstruction {
 
 // Generate the same tests for bunch of strategies (one module for one strategy)
 macro_rules! t {
-    ($name: ident, $strategy: ty) => {
-        mod $name {
-            use super::*;
-
-            proptest! {
-                #[test]
-                fn selection(
-                    instructions in proptest::collection::vec(SelInstruction::random(), 1..100),
-                ) {
-                    let mut bare = Arc::clone(&ARCS[0]);
-                    let a = ArcSwapAny::<_, $strategy>::from(Arc::clone(&ARCS[0]));
-                    for ins in instructions {
-                        match ins {
-                            SelInstruction::Swap(idx) => {
-                                let expected = mem::replace(&mut bare, Arc::clone(&ARCS[idx]));
-                                let actual = a.swap(Arc::clone(&ARCS[idx]));
-                                assert!(Arc::ptr_eq(&expected, &actual));
-                            }
-                            SelInstruction::Cas(cur, new) => {
-                                let expected = Arc::clone(&bare);
-                                if bare == ARCS[cur] {
-                                    bare = Arc::clone(&ARCS[new]);
-                                }
-                                let actual = a.compare_and_swap(&ARCS[cur], Arc::clone(&ARCS[new]));
-                                assert!(Arc::ptr_eq(&expected, &actual));
+    (@full => $name: ident, $strategy: ty) => {
+        t!(@compose => $name, $strategy,
+            #[test]
+            fn selection(
+                instructions in proptest::collection::vec(SelInstruction::random(), 1..SIZE),
+            ) {
+                let mut bare = Arc::clone(&ARCS[0]);
+                let a = ArcSwapAny::<_, $strategy>::from(Arc::clone(&ARCS[0]));
+                for ins in instructions {
+                    match ins {
+                        SelInstruction::Swap(idx) => {
+                            let expected = mem::replace(&mut bare, Arc::clone(&ARCS[idx]));
+                            let actual = a.swap(Arc::clone(&ARCS[idx]));
+                            assert!(Arc::ptr_eq(&expected, &actual));
+                        }
+                        SelInstruction::Cas(cur, new) => {
+                            let expected = Arc::clone(&bare);
+                            if bare == ARCS[cur] {
+                                bare = Arc::clone(&ARCS[new]);
                             }
+                            let actual = a.compare_and_swap(&ARCS[cur], Arc::clone(&ARCS[new]));
+                            assert!(Arc::ptr_eq(&expected, &actual));
                         }
                     }
                 }
+            }
+        );
+    };
+    (@nocas => $name: ident, $strategy: ty) => {
+        t!(@compose => $name, $strategy, );
+    };
+    (@compose => $name: ident, $strategy: ty, $($extra: tt)*) => {
+        mod $name {
+            use super::*;
+
+            proptest! {
+                $($extra)*
 
                 #[test]
                 fn ops(
-                    instructions in proptest::collection::vec(OpsInstruction::random(), 1..100),
+                    instructions in proptest::collection::vec(OpsInstruction::random(), 1..SIZE),
                 ) {
                     use crate::OpsInstruction::*;
                     let mut m = 0;
@@ -105,10 +117,17 @@ macro_rules! t {
     };
 }
 
-t!(default, DefaultStrategy);
-t!(independent, IndependentStrategy);
+t!(@full => default, DefaultStrategy);
+t!(@full => independent, IndependentStrategy);
 #[cfg(feature = "experimental-strategies")]
 t!(
+    @full =>
     simple_genlock,
     arc_swap::strategy::experimental::SimpleGenLock
 );
+#[cfg(feature = "experimental-strategies")]
+t!(
+    @full =>
+    helping,
+    arc_swap::strategy::experimental::Helping
+);
diff --git a/tests/stress.rs b/tests/stress.rs
index 8eedc59..b0812a9 100644
--- a/tests/stress.rs
+++ b/tests/stress.rs
@@ -4,8 +4,9 @@
 //! discover any possible race condition.
 
 use std::sync::atomic::{AtomicUsize, Ordering};
-use std::sync::{Arc, Barrier, Mutex, MutexGuard, PoisonError};
+use std::sync::{Arc, Mutex, MutexGuard, PoisonError};
 
+use adaptive_barrier::{Barrier, PanicMode};
 use arc_swap::strategy::{CaS, DefaultStrategy, IndependentStrategy, Strategy};
 use arc_swap::ArcSwapAny;
 use crossbeam_utils::thread;
@@ -35,13 +36,15 @@ where
 {
     let _lock = lock();
     let head = ArcSwapAny::<_, S>::from(None::<Arc<LLNode<S>>>);
+    #[cfg(not(miri))]
     let cpus = num_cpus::get();
-    // FIXME: If one thread fails, but others don't, it'll deadlock.
-    let barr = Barrier::new(cpus);
+    #[cfg(miri)]
+    let cpus = 2;
+    let barr = Barrier::new(PanicMode::Poison);
     thread::scope(|scope| {
         for thread in 0..cpus {
             // We want to borrow these, but that kind-of conflicts with the move closure mode
-            let barr = &barr;
+            let mut barr = barr.clone();
             let head = &head;
             scope.spawn(move |_| {
                 let nodes = (0..node_cnt)
@@ -112,6 +115,8 @@ where
                 }
             });
         }
+
+        drop(barr);
     })
     .unwrap();
 }
@@ -137,8 +142,11 @@ where
 {
     let _lock = lock();
 
+    #[cfg(not(miri))]
     let cpus = num_cpus::get();
-    let barr = Barrier::new(cpus);
+    #[cfg(miri)]
+    let cpus = 2;
+    let barr = Barrier::new(PanicMode::Poison);
     let global_cnt = AtomicUsize::new(0);
     // We plan to create this many nodes during the whole test.
     let live_cnt = AtomicUsize::new(cpus * node_cnt * iters);
@@ -147,7 +155,7 @@ where
         for thread in 0..cpus {
             // Borrow these instead of moving.
             let head = &head;
-            let barr = &barr;
+            let mut barr = barr.clone();
             let global_cnt = &global_cnt;
             let live_cnt = &live_cnt;
             scope.spawn(move |_| {
@@ -186,6 +194,8 @@ where
                 }
             });
         }
+
+        drop(barr);
     })
     .unwrap();
     // Everything got destroyed properly.
@@ -197,7 +207,10 @@ where
     S: Default + Strategy<Arc<usize>> + Send + Sync,
 {
     let _lock = lock();
+    #[cfg(not(miri))]
     let cpus = num_cpus::get();
+    #[cfg(miri)]
+    let cpus = 2;
     let shared = ArcSwapAny::<_, S>::from(Arc::new(0));
     thread::scope(|scope| {
         scope.spawn(|_| {
@@ -221,6 +234,16 @@ where
     assert_eq!(2, Arc::strong_count(&v));
 }
 
+#[cfg(not(miri))]
+const ITER_SMALL: usize = 100;
+#[cfg(not(miri))]
+const ITER_MID: usize = 1000;
+
+#[cfg(miri)]
+const ITER_SMALL: usize = 2;
+#[cfg(miri)]
+const ITER_MID: usize = 5;
+
 macro_rules! t {
     ($name: ident, $strategy: ty) => {
         mod $name {
@@ -228,7 +251,7 @@ macro_rules! t {
 
             #[test]
             fn storm_link_list_small() {
-                storm_link_list::<$strategy>(100, 5);
+                storm_link_list::<$strategy>(ITER_SMALL, 5);
             }
 
             #[test]
@@ -239,7 +262,7 @@ macro_rules! t {
 
             #[test]
             fn storm_unroll_small() {
-                storm_unroll::<$strategy>(100, 5);
+                storm_unroll::<$strategy>(ITER_SMALL, 5);
             }
 
             #[test]
@@ -250,7 +273,7 @@ macro_rules! t {
 
             #[test]
             fn load_parallel_small() {
-                load_parallel::<$strategy>(1000);
+                load_parallel::<$strategy>(ITER_MID);
             }
 
             #[test]
@@ -269,3 +292,5 @@ t!(
     simple_genlock,
     arc_swap::strategy::experimental::SimpleGenLock
 );
+#[cfg(feature = "experimental-strategies")]
+t!(helping, arc_swap::strategy::experimental::Helping);