<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Lock-Free-Programming on Sofía Belén López Vicens</title><link>https://sofiabelen.github.io/tags/lock-free-programming/</link><description>Recent content in Lock-Free-Programming on Sofía Belén López Vicens</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Fri, 18 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://sofiabelen.github.io/tags/lock-free-programming/index.xml" rel="self" type="application/rss+xml"/><item><title>Visualizing The ABA Problem: What crossbeam-epoch Solves</title><link>https://sofiabelen.github.io/projects/visualizing-the-aba-problem/</link><pubDate>Fri, 18 Sep 2026 00:00:00 +0000</pubDate><guid>https://sofiabelen.github.io/projects/visualizing-the-aba-problem/</guid><description>&lt;img src="https://sofiabelen.github.io/projects/visualizing-the-aba-problem/Janus-statue-and-his-two-faces-past-and-future.webp" alt="Featured image of post Visualizing The ABA Problem: What crossbeam-epoch Solves" />&lt;p>As a new Rust convert (Rustacean?) coming from C++, it can be all too tempting to believe Rust&amp;rsquo;s ownership model is the panacea, the cure for all our problems. While it is truly groundbreaking, I&amp;rsquo;m beginning to learn about the cases where it can&amp;rsquo;t really save us. But there&amp;rsquo;s hope, let&amp;rsquo;s not panic!&lt;/p>
&lt;p>Ever since I watched &lt;a class="link" href="https://www.youtube.com/watch?v=ZQFzMfHIxng" target="_blank" rel="noopener"
>Fedor Pikus talk on atomics&lt;/a> (highly recommend!), I&amp;rsquo;ve had an interest in lock-free programming. I played around with implementing my own SPMC queue in C++, and have since started (and not finished) some books, trying specially hard to wrap my head around memory ordering. But I&amp;rsquo;ve come to realize I&amp;rsquo;ve been a bit stuck in tutorial hell, so this post is the beginning of me getting unstuck.&lt;/p>
&lt;p>My goal is to poke at the &lt;a class="link" href="https://github.com/crossbeam-rs/crossbeam" target="_blank" rel="noopener"
>crossbeam crate&lt;/a>, with the hopes of gaining a better understanding of a real-world use case and hopefully gain some insight which might help me contribute something back.&lt;/p>
&lt;p>Let&amp;rsquo;s start from the beginning, exploring what is one of the problems that crossbeam-epoch tackles.&lt;/p>
&lt;p>See all the code and experiments over at my &lt;a class="link" href="https://github.com/sofiabelen/visualizing-crossbeam-epoch" target="_blank" rel="noopener"
>GitHub&lt;/a>.&lt;/p>
&lt;blockquote>
&lt;p>Side note: the two-faced statue in the thumbnail is the Roman god Janus, who looks toward the past and future simultaneously. In a moment you&amp;rsquo;ll see what this has to do with our pointers in the ABA problem.&lt;/p>&lt;/blockquote>
&lt;h2 id="the-question-of-when-to-drop-in-lock-free-code">The Question of When to drop() in Lock-Free Code
&lt;/h2>&lt;p>It turns out, one of the cases where Rust&amp;rsquo;s ownership model isn&amp;rsquo;t enough is lock-free code.&lt;/p>
&lt;p>Normally, every value has a single owner, and when that owner goes out of scope, Rust calls &lt;code>drop()&lt;/code> to first execute its destructor and then free its memory. If we&amp;rsquo;re using a &lt;code>Mutex&lt;/code>, we can rest assured that while one thread is mutating or destroying a node in our data structure, no other thread can hold a reference to it.&lt;/p>
&lt;p>If we want to do this the lock-free way, we&amp;rsquo;d use a CAS (Compare-And-Swap) loop instead of a Mutex. This way, multiple threads could read and modify different nodes in the data structure simultaneously, without waiting for one another.&lt;/p>
&lt;h2 id="what-cas-guarantees">What CAS Guarantees
&lt;/h2>&lt;p>So what does CAS actually promise?&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="k">fn&lt;/span> &lt;span class="nf">compare_exchange&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">current&lt;/span>: &lt;span class="nc">T&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">new&lt;/span>: &lt;span class="nc">T&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">success&lt;/span>: &lt;span class="nc">Ordering&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">failure&lt;/span>: &lt;span class="nc">Ordering&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="nb">Result&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In simple terms, what this says is: &amp;ldquo;if the current value equals &lt;code>current&lt;/code>, swap it for the new one, atomically.&amp;rdquo; The intuition is that we usually want to modify a variable based on what it currently holds. If, in between reading it and attempting to change it, another thread has modified it from under our feet, we&amp;rsquo;d need to update our notion of &lt;em>what&amp;rsquo;s the current value&lt;/em> before we can change it. This is the usual usecase for the CAS loop.&lt;/p>
&lt;p>Here&amp;rsquo;s the simplest example, of incrementing a counter using a CAS loop:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="k">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">std&lt;/span>::&lt;span class="n">sync&lt;/span>::&lt;span class="n">atomic&lt;/span>::&lt;span class="p">{&lt;/span>&lt;span class="n">AtomicUsize&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Ordering&lt;/span>&lt;span class="p">};&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">increment&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">counter&lt;/span>: &lt;span class="kp">&amp;amp;&lt;/span>&lt;span class="nc">AtomicUsize&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">loop&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">current&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">counter&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">load&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Ordering&lt;/span>::&lt;span class="n">Relaxed&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">current&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">counter&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">compare_exchange_weak&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">current&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">new&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Ordering&lt;/span>::&lt;span class="n">Release&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Ordering&lt;/span>::&lt;span class="n">Relaxed&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">is_ok&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// else: someone else changed it first, retry
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>What if I told you there&amp;rsquo;s a scenario when this promise isn&amp;rsquo;t enough and our intuition can betray us into a false sense of correctness? When the A we saw first is not the same as the current A, even though the value is exactly the same? When A ≠ A?&lt;/p>
&lt;p>My first time hearing about this, I was in utter disbelief. I had a hard time imagining, in pure mathematical/abstract terms, why in the world would it matter that the value has changed, if it&amp;rsquo;s ultimately been &amp;ldquo;restored&amp;rdquo; to the same value? Surely our previous assumptions still hold and we&amp;rsquo;re free to continue with our operation&amp;hellip;&lt;/p>
&lt;!--
## The Use-After-Free Race Condition
Maybe you've guessed that the case where our normal intuition starts falling apart is when we are working not with values directly but with pointers. How does the old saying go? All problems in computer science are caused by adding a level of indirection? The important thing to understand is that a pointer can point to the same memory but that memory may not be the same.
To build a bit of suspense, let's first look at a similar bug, which helped me to build the intuition for understanding the ABA problem.
Imagine we want to use a lock-free stack. Consider how we'd implement the `pop()` operation. The naive way would look something like this:
```rust
fn pop(&amp;self) -> Option&lt;T> {
let mut current_head = self.head.load(Ordering::Relaxed);
loop {
if current_head.is_null() { return None; }
let new_head = unsafe { (*current_head).next };
match self.head.compare_exchange_weak(
current_head,
new_head,
Ordering::AcqRel,
Ordering::Relaxed) {
Ok(_) => {
let node = unsafe { Box::from_raw(current_head) };
// If we drop here, any other thread paused at step 2
// will crash when it wakes up and tries to read (*current_head).next
return Some(node.value); // our ptr gets dropped as the Box goes out of scope
},
Err(actual_head) => {
current_head = actual_head;
}
}
}
}
```
This creates a conflict though. Let's imagine the following scenario:
1. Thread 1 starts `pop()`: it reads the shared `head` pointer and sees it points to Node `A` (address `0x1000`).
2. Thread 1 get's context-switched before it can execute its CAS step (just before it can read `Node A.next`.
3. Thread 2 runs `pop()`:
1. Reads `head` (`0x1000`, Node A).
2. Reads `Node A.next` (`0x2000`, Node B).
3. Succesfully executes CAS: `head` now points to Node B.
4. Thread 2 runs `drop()` for Node A. Since thread 2 "owns" the popped Node A, it immediately calls `drops()`. Node A's destructor runs, and its memory at `0x1000` is returned to the allocator.
5. Thread 1 resumes and attempts to now read `Node A.next`, so it tries to dereference pointer `0x1000`.
&lt;style>
#mermaid-slider-0.mermaid-slider {
display: flex;
flex-direction: column;
align-items: center;
background: transparent;
padding: 10px;
width: 100%;
}
#mermaid-slider-0 .m-canvas {
width: 100%;
height: 520px;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
}
#mermaid-slider-0 .m-canvas svg {
width: auto !important;
height: 100% !important;
max-height: 480px !important;
min-height: 380px !important;
transform: scale(1.15);
transform-origin: center center;
}
#mermaid-slider-0 .m-controls {
display: flex;
gap: 12px;
margin-top: 16px;
}
#mermaid-slider-0 .m-controls button {
padding: 6px 18px;
background: #f8c1cc;
color: #333333;
border: 1px solid #e8a7b5;
border-radius: 4px;
cursor: pointer;
font-weight: 600;
font-size: 14px;
transition: background 0.2s ease;
}
#mermaid-slider-0 .m-controls button:hover:not(:disabled) {
background: #e8a7b5;
}
#mermaid-slider-0 .m-controls button:disabled {
opacity: 0.4;
cursor: not-allowed;
}
#mermaid-slider-0 .mermaid .node rect,
#mermaid-slider-0 .mermaid .node circle,
#mermaid-slider-0 .mermaid .node polygon {
fill: #fcdbe2 !important;
stroke: #d97d90 !important;
stroke-width: 2px !important;
}
#mermaid-slider-0 .mermaid .cluster rect {
fill: #fff0f3 !important;
stroke: #d97d90 !important;
stroke-width: 2px !important;
}
#mermaid-slider-0 .mermaid .edgePath .path {
stroke: #d97d90 !important;
stroke-width: 3px !important;
}
#mermaid-slider-0 .mermaid marker,
#mermaid-slider-0 .mermaid marker path,
#mermaid-slider-0 .mermaid [id*="flowchart-pointEnd"] {
fill: #d97d90 !important;
stroke: #d97d90 !important;
}
#mermaid-slider-0 .mermaid text,
#mermaid-slider-0 .mermaid .cluster-label {
fill: #333333 !important;
font-size: 14px !important;
font-weight: 600 !important;
font-family: inherit !important;
}
#mermaid-slider-0 .mermaid .flowchartTitleText {
fill: #d97d90 !important;
font-size: 18px !important;
font-weight: bold !important;
}
&lt;/style>
&lt;div id="mermaid-slider-0" class="mermaid-slider">
&lt;div id="mermaidTarget-0" class="m-canvas">&lt;/div>
&lt;div class="m-controls">
&lt;button class="prevBtn" disabled>Prev&lt;/button>
&lt;button class="nextBtn">Next&lt;/button>
&lt;/div>
&lt;/div>
&lt;script>
(function() {
function loadMermaid(callback) {
if (window.mermaid) {
callback();
return;
}
if (document.getElementById('mermaid-js-script')) {
document.getElementById('mermaid-js-script').addEventListener('load', callback);
return;
}
const script = document.createElement('script');
script.id = 'mermaid-js-script';
script.src = 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js';
script.onload = callback;
document.head.appendChild(script);
}
loadMermaid(function() {
if (!window.mermaidInitialized) {
mermaid.initialize({
startOnLoad: false,
theme: 'base',
themeVariables: {
primaryColor: '#fcdbe2',
primaryBorderColor: '#d97d90',
primaryTextColor: '#333333',
lineColor: '#d97d90',
tertiaryColor: '#fff0f3'
}
});
window.mermaidInitialized = true;
}
const container = document.getElementById("mermaid-slider-0");
const target = document.getElementById("mermaidTarget-0");
const prevBtn = container.querySelector(".prevBtn");
const nextBtn = container.querySelector(".nextBtn");
const diagrams = ["---\ntitle: \"Initial state\"\n---\nflowchart TB\n head((\"head\"))\n\n subgraph nodea[\"node A\"]\n direction TB\n na_value[\"value: 1\"]\n na_memory[\"memory: 0x1000\"]\n end\n\n subgraph nodeb[\"node B\"]\n direction TB\n nb_value[\"value: 2\"]\n nb_memory[\"memory: 0x2000\"]\n end\n\n head --\u003e nodea\n nodea --\u003e nodeb","---\ntitle: \"Step 1: Thread 1 starts pop() and reads head\"\n---\nflowchart TB\n head((\"head\"))\n\n subgraph nodea[\"node A\"]\n direction TB\n na_value[\"value: 1\"]\n na_memory[\"memory: 0x1000\"]\n end\n\n subgraph nodeb[\"node B\"]\n direction TB\n nb_value[\"value: 2\"]\n nb_memory[\"memory: 0x2000\"]\n end\n\n t1[\"Thread 1\u003cbr/\u003ehead: 0x1000\u003cbr/\u003enext: (not read yet)\"]\n\n head --\u003e nodea\n nodea --\u003e nodeb\n t1 -.-\u003e|reads| nodea","---\ntitle: \"Step 2: Thread 1 context-switched before reading A.next\"\n---\nflowchart TB\n head((\"head\"))\n\n subgraph nodea[\"node A\"]\n direction TB\n na_value[\"value: 1\"]\n na_memory[\"memory: 0x1000\"]\n end\n\n subgraph nodeb[\"node B\"]\n direction TB\n nb_value[\"value: 2\"]\n nb_memory[\"memory: 0x2000\"]\n end\n\n t1[\"Thread 1 (PAUSED)\u003cbr/\u003ehead: 0x1000\u003cbr/\u003enext: ???\"]\n\n head --\u003e nodea\n nodea --\u003e nodeb\n t1 -.-\u003e nodea","---\ntitle: \"Step 3: Thread 2 executes pop() successfully\"\n---\nflowchart TB\n head((\"head\"))\n\n subgraph nodeb[\"node B\"]\n direction TB\n nb_value[\"value: 2\"]\n nb_memory[\"memory: 0x2000\"]\n end\n\n t1[\"Thread 1 (PAUSED)\u003cbr/\u003ehead: 0x1000\u003cbr/\u003enext: ???\"]\n\n subgraph nodea[\"node A (popped by T2)\"]\n direction TB\n na_value[\"value: 1\"]\n na_memory[\"memory: 0x1000\"]\n end\n\n head --\u003e nodeb\n nodeb ~~~ t1\n t1 -.-\u003e nodea","---\ntitle: \"Step 4: Thread 2 drops Node A and frees memory at 0x1000\"\n---\nflowchart TB\n head((\"head\"))\n\n subgraph nodeb[\"node B\"]\n direction TB\n nb_value[\"value: 2\"]\n nb_memory[\"memory: 0x2000\"]\n end\n\n t1[\"Thread 1 (PAUSED)\u003cbr/\u003ehead: 0x1000\u003cbr/\u003enext: ?\"]\n\n subgraph nodea[\"freed memory (0x1000)\"]\n direction TB\n na_status[\"[deallocated]\"]\n end\n\n head --\u003e nodeb\n nodeb ~~~ t1\n t1 -.-\u003e|dangling reference| nodea","---\ntitle: \"Step 5: Thread 1 resumes and attempts use-after-free read\"\n---\nflowchart TB\n head((\"head\"))\n\n subgraph nodeb[\"node B\"]\n direction TB\n nb_value[\"value: 2\"]\n nb_memory[\"memory: 0x2000\"]\n end\n\n t1[\"Thread 1 resumes\u003cbr/\u003etries to read (0x1000).next\u003cbr/\u003euse-after-free\"]\n\n subgraph nodea[\"freed memory (0x1000)\"]\n direction TB\n na_status[\"[deallocated]\"]\n end\n\n head --\u003e nodeb\n nodeb ~~~ t1\n t1 ==\u003e |fails to dereference!| nodea"];
let currentIdx = 0;
async function render() {
target.innerHTML = `&lt;div class="mermaid">${diagrams[currentIdx].trim()}&lt;/div>`;
const el = target.querySelector('.mermaid');
el.removeAttribute('data-processed');
await mermaid.run({ nodes: [el] });
prevBtn.disabled = currentIdx === 0;
nextBtn.disabled = currentIdx === diagrams.length - 1;
}
function step(dir) {
currentIdx = Math.max(0, Math.min(diagrams.length - 1, currentIdx + dir));
render();
}
prevBtn.addEventListener("click", () => step(-1));
nextBtn.addEventListener("click", () => step(1));
render();
});
})();
&lt;/script>
**Result:** Memory at `0x1000` has already been freed or repurposed, which leads to undefined behaviour or segmentation fault (use-after-free).
&lt;figure>
&lt;img src="william-dmytrow-pS6GsfrQZDk-unsplash.jpg" alt="">
&lt;caption>
Photo by &lt;a href="https://unsplash.com/@williamdmytrow?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">William Dmytrow&lt;/a> on &lt;a href="https://unsplash.com/photos/winged-victory-of-samothrace-marble-statue-on-a-ship-prow-pS6GsfrQZDk?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash&lt;/a>
&lt;/caption>
&lt;/figure>
The fundamental question is:
> How do we defer `drop()` until Thread A is no longer reading it, without adding extra overhead in the form of locks or atomic reference counter updates on every single read?
-->
&lt;h2 id="what-is-the-aba-problem">What is The ABA Problem
&lt;/h2>&lt;p>Maybe you’ve guessed that the case where our normal intuition starts falling apart is when we are working not with values directly but with pointers. How does the old saying go? All problems in computer science are caused by adding a level of indirection? The important thing to understand is that a pointer can point to the same memory but that memory may not be the same.&lt;/p>
&lt;!--Now that we've built an intuition about the strange type of bugs that come up when working with pointers in lock-free code, what is the ABA bug?-->
&lt;p>Let&amp;rsquo;s picture this scenario:&lt;/p>
&lt;ol start="0">
&lt;li>Current shape of our stack &lt;code>[A, B]&lt;/code>.&lt;/li>
&lt;li>Thread 1 reads &lt;code>head = A&lt;/code>, reads that &lt;code>A.next = B&lt;/code>, then gets descheduled &lt;em>right before&lt;/em> the CAS loop.&lt;/li>
&lt;li>Thread 2 pops &lt;code>A&lt;/code> and deallocates the memory.&lt;/li>
&lt;li>Thread 2 pops &lt;code>B&lt;/code>.&lt;/li>
&lt;li>Thread 2 allocates a new node object at the newly freed memory with address &lt;code>A&lt;/code>, and pushes &lt;code>A&lt;/code> to the stack. &lt;code>A.next&lt;/code> is therefore set to null (the stack is now just &lt;code>[A]&lt;/code>).&lt;/li>
&lt;li>Thread 1 resumes. Its &lt;code>compare_exchange(expected = A, new = B)&lt;/code> succeeds, because &lt;code>head&lt;/code> is in fact &lt;code>A&lt;/code> again. However, it should&amp;rsquo;ve failed because it&amp;rsquo;s not the &lt;em>same&lt;/em> A. Thread 1 had no way of knowing this though. The CAS sets &lt;code>head = B&lt;/code>, but &lt;code>B&lt;/code> was already popped and deallocated in step 3. Now the stack&amp;rsquo;s head is a dangling pointer to freed memory.&lt;/li>
&lt;/ol>
&lt;p>The worst thing about this bug is that it&amp;rsquo;s very easy to miss. It needs precise conditions to be met: the interleaving of the two threads as well as the allocator to actually reuse the recently freed address. (stick with me, &lt;strong>promised diagram&lt;/strong> coming up).&lt;/p>
&lt;h2 id="reproducing-the-aba-problem">Reproducing The ABA Problem
&lt;/h2>&lt;figure>
&lt;img src="william-dmytrow-pS6GsfrQZDk-unsplash.jpg" alt="">
&lt;caption>
Photo by &lt;a href="https://unsplash.com/@williamdmytrow?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">William Dmytrow&lt;/a> on &lt;a href="https://unsplash.com/photos/winged-victory-of-samothrace-marble-statue-on-a-ship-prow-pS6GsfrQZDk?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash&lt;/a>
&lt;/caption>
&lt;/figure>
&lt;!--&lt;figure style="text-align: center;">
&lt;img src="Shipwreck_of_the_Minotaur_William_Turner.jpg" alt="J. M. W. Turner: The Wreck of a Transport Ship" style="display: block; margin: 0 auto;">
&lt;caption>
J. M. W. Turner: The Wreck of a Transport Ship
&lt;/caption>
&lt;/figure>
-->
&lt;p>I had opened a can of worms. I still didn&amp;rsquo;t understand why it was the first time in my career hearing about this strange kind of bug. Needless to say, I was intrigued, I felt the urge to try to reproduce it myself, hoping that maybe that&amp;rsquo;d help build my intuition for detecting this kind of bugs that weren&amp;rsquo;t under my radar before. It turned out to be harder than I expected.&lt;/p>
&lt;p>These next few sections are me hitting my head against a wall until I got the basics down. Feel free to skip if you&amp;rsquo;re familiar with these concepts already :)&lt;/p>
&lt;h3 id="rusts-mut-t">Rust&amp;rsquo;s *mut T
&lt;/h3>&lt;p>The first roadblock I came across when trying to build my lock-free stack, was understanding raw pointers (&lt;code>*mut&lt;/code>), since using something like &lt;code>Arc&lt;/code> would defeat the lock-free effort. This is another rabbit hole, but what&amp;rsquo;s important to understand for us, is that it requires an &lt;code>unsafe&lt;/code> block for dereferencing. You can pass around raw pointers, that&amp;rsquo;s fine, but when dereferencing it to read or write to the memory behind &lt;code>*mut T&lt;/code>, the compiler can&amp;rsquo;t guarantee memory safety, hence the need for &lt;code>unsafe&lt;/code>.&lt;/p>
&lt;p>So, our nodes would look like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="k">struct&lt;/span> &lt;span class="nc">Node&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">value&lt;/span>: &lt;span class="nc">T&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">next&lt;/span>: &lt;span class="o">*&lt;/span>&lt;span class="k">mut&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Node&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We&amp;rsquo;d dereference it like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="k">unsafe&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">node&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">next&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">another_node&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Also important to remember, dropping a &lt;code>*mut T&lt;/code> does NOT run &lt;code>T&lt;/code>&amp;rsquo;s &lt;code>Drop&lt;/code> or free its heap memory. For that, we can manually convert it back to an owned type and let it handle the cleanup automatically when it goes out of scope.&lt;/p>
&lt;p>??? do we need to assign it?&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">node&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">unsafe&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">Box&lt;/span>::&lt;span class="n">from_raw&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">current_head&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">};&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Similarly, we use &lt;code>Box::new&lt;/code> for allocating memory, and then &lt;code>Box::into_raw&lt;/code> for taking ownership of the underlying &lt;code>*mut T&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">node&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">Box&lt;/span>::&lt;span class="n">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Node&lt;/span>::&lt;span class="n">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">new_head&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">Box&lt;/span>::&lt;span class="n">into_raw&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">node&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="why-passing-self-is-enough">Why Passing &amp;amp;self is Enough
&lt;/h3>&lt;figure style="text-align: center;">
&lt;img src="Echo_and_Narcissus_-_John_William_Waterhouse.jpg" alt="John William Waterhouse: Echo and Narcissus" style="display: block; margin: 0 auto;">
&lt;caption>
John William Waterhouse: Echo and Narcissus
&lt;/caption>
&lt;/figure>
&lt;p>My first instinct when typing out the &lt;code>pop&lt;/code> and &lt;code>push&lt;/code> methods was to use &lt;code>&amp;amp;mut self&lt;/code>. This made sense at first, since in both cases we&amp;rsquo;d be modifying the stack. However, I soon realized that if I wanted to pass a reference of the same stack to multiple threads, it&amp;rsquo;d have to be a shared reference. It sounds obvious in retrospect, I mean, that&amp;rsquo;s the whole point of what we&amp;rsquo;re trying to achieve!&lt;/p>
&lt;p>The reason why &lt;code>&amp;amp;self&lt;/code> is enough is because atomics (&lt;code>AtomicPtr&lt;/code>) handle thread synchronization internally at the hardware level, essentially bypassing the compiler&amp;rsquo;s borrow rules. They can be mutated behind a shared (&lt;code>&amp;amp;self&lt;/code>) reference using atomic instructions, like CAS.&lt;/p>
&lt;h3 id="sharing-across-threads">Sharing Across Threads
&lt;/h3>
&lt;style>
#mermaid-diagram-1.mermaid-diagram {
display: flex;
justify-content: center;
background: transparent;
padding: 10px;
width: 100%;
}
#mermaid-diagram-1 .m-canvas {
width: 100%;
height: 480px;
display: flex;
justify-content: center;
align-items: center;
overflow-x: auto;
cursor: zoom-in;
position: relative;
}
#mermaid-diagram-1 .m-canvas::after {
content: "🔍 Expand";
position: absolute;
bottom: 8px;
right: 12px;
font-size: 11px;
background: rgba(255, 255, 255, 0.85);
color: #333333;
padding: 3px 8px;
border-radius: 4px;
border: 1px solid #d97d90;
pointer-events: none;
font-family: inherit;
font-weight: 500;
}
#mermaid-diagram-1 .m-canvas svg {
width: 100% !important;
height: 100% !important;
max-height: 480px !important;
min-height: 340px !important;
min-width: 450px !important;
transform: scale(1.20);
transform-origin: center center;
}
@media (max-width: 768px) {
#mermaid-diagram-1 .m-canvas {
height: auto !important;
}
#mermaid-diagram-1 .m-canvas svg {
height: auto !important;
min-height: unset !important;
min-width: unset !important;
transform: none !important;
}
}
#mermaid-diagram-1 .mermaid .node rect,
#mermaid-diagram-1 .mermaid .node circle,
#mermaid-diagram-1 .mermaid .node polygon,
#mermaidModal-1 .mermaid .node rect,
#mermaidModal-1 .mermaid .node circle,
#mermaidModal-1 .mermaid .node polygon {
fill: #fcdbe2 !important;
stroke: #d97d90 !important;
stroke-width: 2px !important;
}
#mermaid-diagram-1 .mermaid .cluster rect,
#mermaidModal-1 .mermaid .cluster rect {
fill: #fff0f3 !important;
stroke: #d97d90 !important;
stroke-width: 2px !important;
}
#mermaid-diagram-1 .mermaid .edgePath .path,
#mermaidModal-1 .mermaid .edgePath .path {
stroke: #d97d90 !important;
stroke-width: 3px !important;
}
#mermaid-diagram-1 .mermaid marker,
#mermaid-diagram-1 .mermaid marker path,
#mermaid-diagram-1 .mermaid [id*="flowchart-pointEnd"],
#mermaidModal-1 .mermaid marker,
#mermaidModal-1 .mermaid marker path,
#mermaidModal-1 .mermaid [id*="flowchart-pointEnd"] {
fill: #d97d90 !important;
stroke: #d97d90 !important;
}
#mermaid-diagram-1 .mermaid text,
#mermaid-diagram-1 .mermaid .cluster-label,
#mermaidModal-1 .mermaid text,
#mermaidModal-1 .mermaid .cluster-label {
fill: #333333 !important;
font-size: 14px !important;
font-weight: 600 !important;
font-family: inherit !important;
}
#mermaid-diagram-1 .mermaid .flowchartTitleText,
#mermaidModal-1 .mermaid .flowchartTitleText {
fill: #d97d90 !important;
font-size: 18px !important;
font-weight: bold !important;
}
#mermaidModal-1.mermaid-modal {
display: none;
position: fixed;
z-index: 99999;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.85);
backdrop-filter: blur(4px);
justify-content: center;
align-items: center;
opacity: 0;
transition: opacity 0.25s ease-in-out;
}
#mermaidModal-1.mermaid-modal.active {
display: flex;
opacity: 1;
}
#mermaidModal-1 .mermaid-modal-content {
background: #ffffff;
padding: 30px;
border-radius: 12px;
width: 90vw;
height: 85vh;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
position: relative;
display: flex;
justify-content: center;
align-items: center;
overflow: auto;
}
#mermaidModal-1 .mermaid-modal-content svg {
width: 100% !important;
height: 100% !important;
max-width: 100% !important;
max-height: 100% !important;
}
#mermaidModal-1 .mermaid-modal-close {
position: absolute;
top: 12px;
right: 16px;
font-size: 28px;
font-weight: bold;
color: #555;
cursor: pointer;
background: none;
border: none;
line-height: 1;
z-index: 100;
}
#mermaidModal-1 .mermaid-modal-close:hover {
color: #000;
}
&lt;/style>
&lt;div id="mermaid-diagram-1" class="mermaid-diagram">
&lt;div id="mermaidTarget-1" class="m-canvas">&lt;/div>
&lt;/div>
&lt;div id="mermaidModal-1" class="mermaid-modal">
&lt;div class="mermaid-modal-content">
&lt;button class="mermaid-modal-close" aria-label="Close modal">&amp;times;&lt;/button>
&lt;div class="modal-svg-container" style="width: 100%; height: 100%; display: flex; justify-content: center; align-items: center;">&lt;/div>
&lt;/div>
&lt;/div>
&lt;script>
(function() {
function loadMermaid(callback) {
if (window.mermaid) {
callback();
return;
}
if (document.getElementById('mermaid-js-script')) {
document.getElementById('mermaid-js-script').addEventListener('load', callback);
return;
}
const script = document.createElement('script');
script.id = 'mermaid-js-script';
script.src = 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js';
script.onload = callback;
document.head.appendChild(script);
}
loadMermaid(function() {
if (!window.mermaidInitialized) {
mermaid.initialize({
startOnLoad: false,
theme: 'base',
themeVariables: {
primaryColor: '#fcdbe2',
primaryBorderColor: '#d97d90',
primaryTextColor: '#333333',
lineColor: '#d97d90',
tertiaryColor: '#fff0f3'
}
});
window.mermaidInitialized = true;
}
const target = document.getElementById("mermaidTarget-1");
const diagram = "---\ntitle: \"Arc shared across threads\"\n---\nflowchart TB\n subgraph heap[\"heap\"]\n stack[\"Stack＜T＞\"]\n end\n\n subgraph t1[\"thread 1\"]\n arc1[\"Arc＜Stack＜T＞＞\"]\n end\n\n subgraph t2[\"thread 2\"]\n arc2[\"Arc＜Stack＜T＞＞\"]\n end\n\n arc1 --\u003e|points to| stack\n arc2 --\u003e|points to| stack\n\n t1 -. \"pop\" .-\u003e stack\n t2 -. \"push\" .-\u003e stack";
target.innerHTML = `&lt;div class="mermaid">${diagram}&lt;/div>`;
const el = target.querySelector('.mermaid');
el.removeAttribute('data-processed');
mermaid.run({ nodes: [el] }).then(() => {
const modal = document.getElementById("mermaidModal-1");
const modalContainer = modal.querySelector(".modal-svg-container");
const closeBtn = modal.querySelector(".mermaid-modal-close");
target.addEventListener("click", function() {
const svg = target.querySelector("svg");
if (svg) {
modalContainer.innerHTML = svg.outerHTML;
modal.classList.add("active");
document.body.style.overflow = "hidden";
}
});
function closeModal() {
modal.classList.remove("active");
document.body.style.overflow = "";
}
closeBtn.addEventListener("click", closeModal);
modal.addEventListener("click", function(e) {
if (e.target === modal) {
closeModal();
}
});
document.addEventListener("keydown", function(e) {
if (e.key === "Escape" &amp;&amp; modal.classList.contains("active")) {
closeModal();
}
});
});
});
})();
&lt;/script>
&lt;p>By wrapping our stack in an &lt;code>Arc&lt;/code>, each thread gets a cloned shared handle pointing to the same stack memory address, allowing concurrent calls to &lt;code>pop(&amp;amp;self)&lt;/code> and &lt;code>push(&amp;amp;self, value: T)&lt;/code>.&lt;/p>
&lt;p>However, one thing is still missing. It turns out that Rust automatically &lt;strong>disables&lt;/strong> &lt;code>Send&lt;/code> and &lt;code>Sync&lt;/code> for any type containing raw pointers.&lt;/p>
&lt;ul>
&lt;li>&lt;code>Send&lt;/code>: Safe to transfer ownership to another thread.&lt;/li>
&lt;li>&lt;code>Sync&lt;/code>: Safe to share references (&lt;code>&amp;amp;Stack&amp;lt;T&amp;gt;&lt;/code>) across multiple threads simultaneously.&lt;/li>
&lt;/ul>
&lt;p>So we need to explicitly implement those traits (though technically we only need &lt;code>Sync&lt;/code> for our example):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="k">unsafe&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">impl&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>: &lt;span class="nb">Send&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">Send&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Stack&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">unsafe&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">impl&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>: &lt;span class="nb">Send&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">Sync&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Stack&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We can conclude our final picture of what it looks like in memory:&lt;/p>
&lt;style>
#mermaid-diagram-2.mermaid-diagram {
display: flex;
justify-content: center;
background: transparent;
padding: 10px;
width: 100%;
}
#mermaid-diagram-2 .m-canvas {
width: 100%;
height: 900px;
display: flex;
justify-content: center;
align-items: center;
overflow-x: auto;
cursor: zoom-in;
position: relative;
}
#mermaid-diagram-2 .m-canvas::after {
content: "🔍 Expand";
position: absolute;
bottom: 8px;
right: 12px;
font-size: 11px;
background: rgba(255, 255, 255, 0.85);
color: #333333;
padding: 3px 8px;
border-radius: 4px;
border: 1px solid #d97d90;
pointer-events: none;
font-family: inherit;
font-weight: 500;
}
#mermaid-diagram-2 .m-canvas svg {
width: 100% !important;
height: 100% !important;
max-height: 900px !important;
min-height: 340px !important;
min-width: 450px !important;
transform: scale(1.20);
transform-origin: center center;
}
@media (max-width: 768px) {
#mermaid-diagram-2 .m-canvas {
height: auto !important;
}
#mermaid-diagram-2 .m-canvas svg {
height: auto !important;
min-height: unset !important;
min-width: unset !important;
transform: none !important;
}
}
#mermaid-diagram-2 .mermaid .node rect,
#mermaid-diagram-2 .mermaid .node circle,
#mermaid-diagram-2 .mermaid .node polygon,
#mermaidModal-2 .mermaid .node rect,
#mermaidModal-2 .mermaid .node circle,
#mermaidModal-2 .mermaid .node polygon {
fill: #fcdbe2 !important;
stroke: #d97d90 !important;
stroke-width: 2px !important;
}
#mermaid-diagram-2 .mermaid .cluster rect,
#mermaidModal-2 .mermaid .cluster rect {
fill: #fff0f3 !important;
stroke: #d97d90 !important;
stroke-width: 2px !important;
}
#mermaid-diagram-2 .mermaid .edgePath .path,
#mermaidModal-2 .mermaid .edgePath .path {
stroke: #d97d90 !important;
stroke-width: 3px !important;
}
#mermaid-diagram-2 .mermaid marker,
#mermaid-diagram-2 .mermaid marker path,
#mermaid-diagram-2 .mermaid [id*="flowchart-pointEnd"],
#mermaidModal-2 .mermaid marker,
#mermaidModal-2 .mermaid marker path,
#mermaidModal-2 .mermaid [id*="flowchart-pointEnd"] {
fill: #d97d90 !important;
stroke: #d97d90 !important;
}
#mermaid-diagram-2 .mermaid text,
#mermaid-diagram-2 .mermaid .cluster-label,
#mermaidModal-2 .mermaid text,
#mermaidModal-2 .mermaid .cluster-label {
fill: #333333 !important;
font-size: 14px !important;
font-weight: 600 !important;
font-family: inherit !important;
}
#mermaid-diagram-2 .mermaid .flowchartTitleText,
#mermaidModal-2 .mermaid .flowchartTitleText {
fill: #d97d90 !important;
font-size: 18px !important;
font-weight: bold !important;
}
#mermaidModal-2.mermaid-modal {
display: none;
position: fixed;
z-index: 99999;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.85);
backdrop-filter: blur(4px);
justify-content: center;
align-items: center;
opacity: 0;
transition: opacity 0.25s ease-in-out;
}
#mermaidModal-2.mermaid-modal.active {
display: flex;
opacity: 1;
}
#mermaidModal-2 .mermaid-modal-content {
background: #ffffff;
padding: 30px;
border-radius: 12px;
width: 90vw;
height: 85vh;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
position: relative;
display: flex;
justify-content: center;
align-items: center;
overflow: auto;
}
#mermaidModal-2 .mermaid-modal-content svg {
width: 100% !important;
height: 100% !important;
max-width: 100% !important;
max-height: 100% !important;
}
#mermaidModal-2 .mermaid-modal-close {
position: absolute;
top: 12px;
right: 16px;
font-size: 28px;
font-weight: bold;
color: #555;
cursor: pointer;
background: none;
border: none;
line-height: 1;
z-index: 100;
}
#mermaidModal-2 .mermaid-modal-close:hover {
color: #000;
}
&lt;/style>
&lt;div id="mermaid-diagram-2" class="mermaid-diagram">
&lt;div id="mermaidTarget-2" class="m-canvas">&lt;/div>
&lt;/div>
&lt;div id="mermaidModal-2" class="mermaid-modal">
&lt;div class="mermaid-modal-content">
&lt;button class="mermaid-modal-close" aria-label="Close modal">&amp;times;&lt;/button>
&lt;div class="modal-svg-container" style="width: 100%; height: 100%; display: flex; justify-content: center; align-items: center;">&lt;/div>
&lt;/div>
&lt;/div>
&lt;script>
(function() {
function loadMermaid(callback) {
if (window.mermaid) {
callback();
return;
}
if (document.getElementById('mermaid-js-script')) {
document.getElementById('mermaid-js-script').addEventListener('load', callback);
return;
}
const script = document.createElement('script');
script.id = 'mermaid-js-script';
script.src = 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js';
script.onload = callback;
document.head.appendChild(script);
}
loadMermaid(function() {
if (!window.mermaidInitialized) {
mermaid.initialize({
startOnLoad: false,
theme: 'base',
themeVariables: {
primaryColor: '#fcdbe2',
primaryBorderColor: '#d97d90',
primaryTextColor: '#333333',
lineColor: '#d97d90',
tertiaryColor: '#fff0f3'
}
});
window.mermaidInitialized = true;
}
const target = document.getElementById("mermaidTarget-2");
const diagram = "---\ntitle: \"Memory Layout\"\n---\nflowchart TB\n subgraph threads[\"What each thread views\"]\n subgraph t1[\"thread 1 call stack\"]\n arc1[\"Arc＜Stack＜T＞＞\"]\n end\n\n subgraph t2[\"thread 2 call stack\"]\n arc2[\"Arc＜Stack＜T＞＞\"]\n end\n end\n\n subgraph heap[\"heap\"]\n subgraph arc_inner[\"ArcInner＜Stack＜T＞＞\"]\n metadata[\"strong_count: 2\u003cbr/\u003eweak_count: 0\"]\n\n subgraph stack_obj[\"Stack＜T＞\"]\n head[\"head: AtomicPtr＜Node＜T＞＞\"]\n end\n end\n\n subgraph nodes[\"linked nodes\"]\n n1[\"Node A (head)\u003cbr/\u003evalue: T\u003cbr/\u003enext: *mut Node＜T＞\"]\n n2[\"Node B\u003cbr/\u003evalue: T\u003cbr/\u003enext: *mut Node＜T＞\"]\n n3[\"Node C\u003cbr/\u003evalue: T\u003cbr/\u003enext: null\"]\n end\n end\n\n arc1 --\u003e|\"\u0026self shared ref\"| arc_inner\n arc2 --\u003e|\"\u0026self shared ref\"| arc_inner\n\n head --\u003e|\"raw ptr (*mut)\"| n1\n\n n1 --\u003e|next| n2\n n2 --\u003e|next| n3\n\n t1 -. \"pop\" .-\u003e head\n t2 -. \"push\" .-\u003e head";
target.innerHTML = `&lt;div class="mermaid">${diagram}&lt;/div>`;
const el = target.querySelector('.mermaid');
el.removeAttribute('data-processed');
mermaid.run({ nodes: [el] }).then(() => {
const modal = document.getElementById("mermaidModal-2");
const modalContainer = modal.querySelector(".modal-svg-container");
const closeBtn = modal.querySelector(".mermaid-modal-close");
target.addEventListener("click", function() {
const svg = target.querySelector("svg");
if (svg) {
modalContainer.innerHTML = svg.outerHTML;
modal.classList.add("active");
document.body.style.overflow = "hidden";
}
});
function closeModal() {
modal.classList.remove("active");
document.body.style.overflow = "";
}
closeBtn.addEventListener("click", closeModal);
modal.addEventListener("click", function(e) {
if (e.target === modal) {
closeModal();
}
});
document.addEventListener("keydown", function(e) {
if (e.key === "Escape" &amp;&amp; modal.classList.contains("active")) {
closeModal();
}
});
});
});
})();
&lt;/script>
&lt;h3 id="buggy-lock-free-stack">Buggy Lock-Free Stack
&lt;/h3>&lt;p>So, for my naive implementation of a lock-free stack, this is what &lt;code>push&lt;/code> turned out like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="k">fn&lt;/span> &lt;span class="nf">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">value&lt;/span>: &lt;span class="nc">T&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">mut&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">current_head&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">head&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">load&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Ordering&lt;/span>::&lt;span class="n">Relaxed&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">node&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">Box&lt;/span>::&lt;span class="n">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Node&lt;/span>::&lt;span class="n">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">new_head&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">Box&lt;/span>::&lt;span class="n">into_raw&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">node&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">loop&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">unsafe&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">new_head&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">next&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">current_head&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">};&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">match&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">head&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">compare_exchange_weak&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">current_head&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">new_head&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Ordering&lt;/span>::&lt;span class="n">AcqRel&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Ordering&lt;/span>::&lt;span class="n">Relaxed&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nb">Ok&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">_&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">break&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nb">Err&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">actual_head&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">current_head&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">actual_head&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">},&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And &lt;code>pop&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="k">fn&lt;/span> &lt;span class="nf">pop&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="nb">Option&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">mut&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">current_head&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">head&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">load&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Ordering&lt;/span>::&lt;span class="n">Relaxed&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">loop&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">current_head&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">is_null&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">None&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Safety: how do we know no other read is modifying this?
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">new_head&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">unsafe&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">current_head&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">next&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">};&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">match&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">head&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">compare_exchange_weak&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">current_head&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">new_head&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Ordering&lt;/span>::&lt;span class="n">AcqRel&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Ordering&lt;/span>::&lt;span class="n">Relaxed&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nb">Ok&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">_&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Safety: as I&amp;#39;m writing this, rust forces me to think about the safety of the unsafe operations,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// and the fact that I can&amp;#39;t write a safety statement should be a red flag
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">node&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">unsafe&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">Box&lt;/span>::&lt;span class="n">from_raw&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">current_head&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">};&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">Some&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">node&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// our ptr gets dropped as the Box goes out of scope
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">},&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nb">Err&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">actual_head&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">current_head&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">actual_head&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="reproducing-it-why-no-segfault">Reproducing It (Why No SegFault?)
&lt;/h3>&lt;figure style="text-align: center;">
&lt;img src="Boxer_at_rest.jpg" alt="Boxer at Rest" style="display: block; margin: 0 auto;">
&lt;/figure>
&lt;p>Now for the moment we&amp;rsquo;ve all been waiting for. I&amp;rsquo;ve made use of some &lt;code>thread::sleep&lt;/code>s to trigger the (un)desired order of operations.&lt;/p>
&lt;details>
&lt;summary>Expand to view the aba test&lt;/summary>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#[test]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">aba&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">stack&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Arc&lt;/span>::&lt;span class="n">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Stack&lt;/span>::&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">i32&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>::&lt;span class="n">new&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">stack_clone&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">stack&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">clone&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">stack&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">stack&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// stack at this point: head -&amp;gt; [1] -&amp;gt; [2] -&amp;gt; nullptr
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">thread&lt;/span>::&lt;span class="n">scope&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// This thread pops, but with a delay between reading head and the CAS loop
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// By the time it enters the CAS loop, the second thread has essentially
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// replaced the head, [1], with [3] that shares the same memory address as [1]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// original: head -&amp;gt; [1] -&amp;gt; [2]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// now : head -&amp;gt; [3] -&amp;gt; nullptr
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">spawn&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">||&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;thread 1 starts pop operation&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">node&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">stack&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">pop_with_delay&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// If this pop shows up as [3] this means the CAS succeeded,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// and we were able to reproduce the bug, yay!
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;thread 1 pop: &lt;/span>&lt;span class="si">{:?}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">node&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">});&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// During the first thread&amp;#39;s delay window:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 1. This thread pops [1]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 2. Then pops [2]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 3. Pushes a new node [3], with the same address as [1]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">spawn&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">||&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// We wait a little to make sure the first thread gets a head-start (pun intended)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">thread&lt;/span>::&lt;span class="n">sleep&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Duration&lt;/span>::&lt;span class="n">from_millis&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">20&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;thread 2 pops: [&lt;/span>&lt;span class="si">{}&lt;/span>&lt;span class="s">]&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">stack_clone&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">pop&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">unwrap&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;thread 2 pops: [&lt;/span>&lt;span class="si">{}&lt;/span>&lt;span class="s">]&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">stack_clone&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">pop&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">unwrap&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Let&amp;#39;s hope the system heap allocator reuses the memory that was just freed
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// Stack now: head -&amp;gt; [3] -&amp;gt; nullptr
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">stack_clone&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;thread 2 pushes [3]&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">});&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">thread&lt;/span>::&lt;span class="n">sleep&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Duration&lt;/span>::&lt;span class="n">from_millis&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Current stack: head -&amp;gt; [2 (freed)]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Final pop: [&lt;/span>&lt;span class="si">{:?}&lt;/span>&lt;span class="s">]&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">stack&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">pop&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">});&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;/details>
&lt;p>The output, surprisingly:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">thread 1 starts pop operation
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">thread 2 pops: [1]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">thread 2 pops: [2]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">thread 2 pushes [3]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">thread 1 pop: Some(3)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Final pop: [None]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Essentially, we&amp;rsquo;ve shown that this is what happens:&lt;/p>
&lt;style>
#mermaid-slider-4.mermaid-slider {
display: flex;
flex-direction: column;
align-items: center;
background: transparent;
padding: 10px;
width: 100%;
}
#mermaid-slider-4 .m-canvas {
width: 100%;
height: 520px;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
}
#mermaid-slider-4 .m-canvas svg {
width: auto !important;
height: 100% !important;
max-height: 480px !important;
min-height: 380px !important;
transform: scale(1.15);
transform-origin: center center;
}
#mermaid-slider-4 .m-controls {
display: flex;
gap: 12px;
margin-top: 16px;
}
#mermaid-slider-4 .m-controls button {
padding: 6px 18px;
background: #f8c1cc;
color: #333333;
border: 1px solid #e8a7b5;
border-radius: 4px;
cursor: pointer;
font-weight: 600;
font-size: 14px;
transition: background 0.2s ease;
}
#mermaid-slider-4 .m-controls button:hover:not(:disabled) {
background: #e8a7b5;
}
#mermaid-slider-4 .m-controls button:disabled {
opacity: 0.4;
cursor: not-allowed;
}
#mermaid-slider-4 .mermaid .node rect,
#mermaid-slider-4 .mermaid .node circle,
#mermaid-slider-4 .mermaid .node polygon {
fill: #fcdbe2 !important;
stroke: #d97d90 !important;
stroke-width: 2px !important;
}
#mermaid-slider-4 .mermaid .cluster rect {
fill: #fff0f3 !important;
stroke: #d97d90 !important;
stroke-width: 2px !important;
}
#mermaid-slider-4 .mermaid .edgePath .path {
stroke: #d97d90 !important;
stroke-width: 3px !important;
}
#mermaid-slider-4 .mermaid marker,
#mermaid-slider-4 .mermaid marker path,
#mermaid-slider-4 .mermaid [id*="flowchart-pointEnd"] {
fill: #d97d90 !important;
stroke: #d97d90 !important;
}
#mermaid-slider-4 .mermaid text,
#mermaid-slider-4 .mermaid .cluster-label {
fill: #333333 !important;
font-size: 14px !important;
font-weight: 600 !important;
font-family: inherit !important;
}
#mermaid-slider-4 .mermaid .flowchartTitleText {
fill: #d97d90 !important;
font-size: 18px !important;
font-weight: bold !important;
}
&lt;/style>
&lt;div id="mermaid-slider-4" class="mermaid-slider">
&lt;div id="mermaidTarget-4" class="m-canvas">&lt;/div>
&lt;div class="m-controls">
&lt;button class="prevBtn" disabled>Prev&lt;/button>
&lt;button class="nextBtn">Next&lt;/button>
&lt;/div>
&lt;/div>
&lt;script>
(function() {
function loadMermaid(callback) {
if (window.mermaid) {
callback();
return;
}
if (document.getElementById('mermaid-js-script')) {
document.getElementById('mermaid-js-script').addEventListener('load', callback);
return;
}
const script = document.createElement('script');
script.id = 'mermaid-js-script';
script.src = 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js';
script.onload = callback;
document.head.appendChild(script);
}
loadMermaid(function() {
if (!window.mermaidInitialized) {
mermaid.initialize({
startOnLoad: false,
theme: 'base',
themeVariables: {
primaryColor: '#fcdbe2',
primaryBorderColor: '#d97d90',
primaryTextColor: '#333333',
lineColor: '#d97d90',
tertiaryColor: '#fff0f3'
}
});
window.mermaidInitialized = true;
}
const container = document.getElementById("mermaid-slider-4");
const target = document.getElementById("mermaidTarget-4");
const prevBtn = container.querySelector(".prevBtn");
const nextBtn = container.querySelector(".nextBtn");
const diagrams = ["---\ntitle: \"Initial state\"\n---\nflowchart TB\n head((\"head\"))\n\n subgraph nodea[\"node 'A'\"]\n direction TB\n na_value[\"value: 1\"]\n na_memory[\"memory: A\"]\n end\n\n subgraph nodeb[\"node 'B'\"]\n direction TB\n nb_value[\"value: 2\"]\n nb_memory[\"memory: B\"]\n end\n\n head --\u003e nodea\n nodea --\u003e nodeb","---\ntitle: \"Thread 1 reads head\"\n---\nflowchart TB\n head((\"head\"))\n\n subgraph nodea[\"node 'A'\"]\n direction TB\n na_value[\"value: 1\"]\n na_memory[\"memory: A\"]\n end\n\n subgraph nodeb[\"node 'B'\"]\n direction TB\n nb_value[\"value: 2\"]\n nb_memory[\"memory: B\"]\n end\n\n t1[\"Thread 1\u003cbr/\u003ehead: A\u003cbr/\u003enext: B\"]\n\n head --\u003e nodea\n nodea --\u003e nodeb\n t1 --\u003e nodea","---\ntitle: \"Thread 2 pops node 'A'\"\n---\nflowchart TB\n head((\"head\"))\n\n subgraph nodeb[\"node 'B'\"]\n direction TB\n nb_value[\"value: 2\"]\n nb_memory[\"memory: B\"]\n end\n\n t1[\"Thread 1\u003cbr/\u003ehead: A\u003cbr/\u003enext: B\"]\n\n subgraph nodea[\"node 'A' (popped)\"]\n direction TB\n na_value[\"value: 1\"]\n na_memory[\"memory: A\"]\n end\n\n head --\u003e nodeb\n nodeb ~~~ t1\n t1 --\u003e nodea","---\ntitle: \"Thread 2 pops node 'B'\"\n---\nflowchart TB\n head((\"head (null)\"))\n\n t1[\"Thread 1\u003cbr/\u003ehead: A\u003cbr/\u003enext: B\"]\n\n subgraph nodea[\"node 'A' (freed)\"]\n direction TB\n na_value[\"value: 1\"]\n na_memory[\"memory: A\"]\n end\n\n head ~~~ t1\n t1 --\u003e nodea","---\ntitle: \"Thread 2 pushes node 'C' (address A)\"\n---\nflowchart TB\n head((\"head\"))\n\n subgraph nodec[\"node 'C'\"]\n direction TB\n nc_value[\"value: 3\"]\n nc_memory[\"memory: A\"]\n end\n\n t1[\"Thread 1\u003cbr/\u003ehead: A\u003cbr/\u003enext: B\"]\n\n head --\u003e nodec\n nodec ~~~ t1\n t1 --\u003e nodec","---\ntitle: \"Thread 1 CAS succeeds: ABA bug triggered *happy noises*\"\n---\nflowchart TB\n head((\"head\"))\n\n subgraph nodec[\"node 'C'\"]\n direction TB\n nc_value[\"value: 3\"]\n nc_memory[\"memory: A\"]\n end\n\n t1[\"Thread 1 CAS\u003cbr/\u003ehead == A? true\u003cbr/\u003eset head = B\"]\n\n subgraph nodeb[\"node 'B' (freed)\"]\n direction TB\n nb_value[\"value: 2\"]\n nb_memory[\"memory: B\"]\n end\n\n head --\u003e nodec\n nodec ~~~ t1\n t1 --\u003e nodec\n t1 ~~~ nodeb","---\ntitle: \"Final state (dangling head)\"\n---\nflowchart TB\n head((\"head (dangling)\"))\n\n subgraph nodeb[\"node 'B' (freed)\"]\n direction TB\n nb_value[\"value: 2\"]\n nb_memory[\"memory: B\"]\n end\n\n subgraph nodec[\"node 'C' (popped)\"]\n direction TB\n nc_value[\"value: 3\"]\n nc_memory[\"memory: A\"]\n end\n\n head --\u003e nodeb\n nodeb ~~~ nodec"];
let currentIdx = 0;
async function render() {
target.innerHTML = `&lt;div class="mermaid">${diagrams[currentIdx].trim()}&lt;/div>`;
const el = target.querySelector('.mermaid');
el.removeAttribute('data-processed');
await mermaid.run({ nodes: [el] });
prevBtn.disabled = currentIdx === 0;
nextBtn.disabled = currentIdx === diagrams.length - 1;
}
function step(dir) {
currentIdx = Math.max(0, Math.min(diagrams.length - 1, currentIdx + dir));
render();
}
prevBtn.addEventListener("click", () => step(-1));
nextBtn.addEventListener("click", () => step(1));
render();
});
})();
&lt;/script>
&lt;p>But&amp;hellip;&lt;/p>
&lt;p>The output is a bit anticlimactic, isn&amp;rsquo;t it? I was expecting explosions or a segfault at least.&lt;/p>
&lt;p>If we didn&amp;rsquo;t know what we were looking for, we might see the output and believe it&amp;rsquo;s completely normal and all is well. What actually happens is undefined behaviour (UB). Why not segfault necessarily? Well, because in this scenario the memory behind the freed object is most likely still mapped, meaning that because it still belongs to the process, the OS doesn&amp;rsquo;t raise a segmentation fault.&lt;/p>
&lt;p>However, if you want to see chaos, there&amp;rsquo;s still hope!&lt;/p>
&lt;h3 id="miri">Miri
&lt;/h3>&lt;p>What the crossbeam devs use for testing is &lt;a class="link" href="https://github.com/rust-lang/miri/" target="_blank" rel="noopener"
>Miri&lt;/a>. Quoting Miri&amp;rsquo;s official repo README:&lt;/p>
&lt;blockquote>
&lt;p>Miri is an Undefined Behavior detection tool for Rust. It can run binaries and test suites of cargo projects and detect unsafe code that fails to uphold its safety requirements. For instance:&lt;/p>
&lt;ul>
&lt;li>Out-of-bounds memory accesses and use-after-free&lt;/li>
&lt;li>Invalid use of uninitialized data&lt;/li>
&lt;li>Violation of intrinsic preconditions (an unreachable_unchecked being reached, calling copy_nonoverlapping with overlapping ranges, &amp;hellip;)&lt;/li>
&lt;li>Not sufficiently aligned memory accesses and references&lt;/li>
&lt;li>Violation of basic type invariants (a bool that is not 0 or 1, for example, or an invalid enum discriminant)&lt;/li>
&lt;li>Data races and emulation of some weak memory effects, i.e., atomic reads can return outdated values&lt;/li>
&lt;/ul>&lt;/blockquote>
&lt;p>Wonderful, exactly what we need!!&lt;/p>
&lt;p>Let&amp;rsquo;s try it:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">cargo +nightly miri &lt;span class="nb">test&lt;/span> aba_problem::tests::aba
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>It caught our bug! Notice how it explicitly mentions &amp;ldquo;Undefined Behaviour&amp;rdquo; due to a data race.&lt;/p>
&lt;style>
.miri-output {
--bg-color: #181825;
--border-color: #313244;
--text-color: #cdd6f4;
--err-color: #f38ba8;
--err-bg: rgba(243, 139, 168, 0.18);
--help-color: #89b4fa;
--location-color: #e5c890;
--code-color: #a6e3a1;
--dim-color: #6c7086;
--badge-bg: #313244;
--t1-color: #89b4fa;
--t2-color: #f38ba8;
--details-border: #45475a;
--details-hover: #ffffff;
background-color: var(--bg-color);
color: var(--text-color);
font-family: 'JetBrains Mono', 'Fira Code', Consolas, monospace;
line-height: 1.5;
padding: 16px;
border-radius: 8px;
border: 1px solid var(--border-color);
overflow-x: auto;
}
@media (prefers-color-scheme: light) {
.miri-output {
--bg-color: #f8f9fa;
--border-color: #dcdfe6;
--text-color: #24292e;
--err-color: #d73a49;
--err-bg: rgba(215, 58, 73, 0.12);
--help-color: #0366d6;
--location-color: #b05a00;
--code-color: #1b7c2b;
--dim-color: #6a737d;
--badge-bg: #e1e4e8;
--t1-color: #0366d6;
--t2-color: #d73a49;
--details-border: #d1d5da;
--details-hover: #000000;
}
}
.miri-err-title { color: var(--err-color); font-weight: bold; }
.miri-highlight { background-color: var(--err-bg); padding: 1px 4px; border-radius: 3px; }
.miri-help { color: var(--help-color); }
.miri-location { color: var(--location-color); font-weight: 500; }
.miri-code { color: var(--code-color); }
.miri-noise {
opacity: 0.45;
transition: opacity 0.2s ease;
}
.miri-noise:hover {
opacity: 0.9;
}
.badge {
background-color: var(--badge-bg);
padding: 1px 6px;
border-radius: 4px;
font-weight: bold;
text-transform: uppercase;
}
.badge-thread1 { color: var(--t1-color); }
.badge-thread2 { color: var(--t2-color); }
.miri-backtrace {
margin: 8px 0;
color: var(--dim-color);
}
.miri-backtrace summary {
cursor: pointer;
color: var(--dim-color);
user-select: none;
font-weight: 500;
}
.miri-backtrace summary:hover {
color: var(--details-hover);
}
.miri-backtrace-content {
opacity: 0.55;
padding-left: 12px;
border-left: 2px solid var(--details-border);
margin-top: 4px;
}
&lt;/style>
&lt;pre class="miri-output">&lt;code>&lt;span class="miri-noise">test aba_problem::tests::aba ... &lt;/span>&lt;span class="miri-err-title">error: Undefined Behavior: Data race detected&lt;/span> between &lt;span class="badge badge-thread1">(1) thread `unnamed-2`&lt;/span> and &lt;span class="badge badge-thread2">(2) thread `unnamed-3`&lt;/span> at alloc44257
--> /home/sofia/.../alloc/src/boxed.rs:1578:9
|
1578 | &lt;span class="miri-highlight">Box(unsafe { Unique::new_unchecked(raw) }, alloc)&lt;/span>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ &lt;span class="badge badge-thread2">(2) retag write happened here&lt;/span>
|
&lt;span class="miri-help">help: and (1) occurred earlier here&lt;/span>
--> &lt;span class="miri-location">src/aba_problem.rs:67:37&lt;/span>
|
67 | &lt;span class="miri-code">let new_head = unsafe { (*current_head).next };&lt;/span>
| ^^^^^^^^^^^^^^^^^^^^ &lt;span class="badge badge-thread1">(1) non-atomic read&lt;/span>
|
&lt;span class="miri-noise"> = help: retags occur on all (re)borrows and as well as when references are copied or moved&lt;/span>
&lt;span class="miri-noise"> = help: retags permit optimizations that insert speculative reads or writes&lt;/span>
&lt;span class="miri-noise"> = help: therefore from the perspective of data races, a retag has the same implications as a read or write&lt;/span>
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= note: this is on thread `unnamed-3`
&lt;details class="miri-backtrace">
&lt;summary>[ Click to expand full backtrace ]&lt;/summary>
&lt;div class="miri-backtrace-content">
= note: stack backtrace:
0: std::boxed::Box::&amp;lt;aba_problem::Node&amp;lt;i32&amp;gt;&amp;gt;::from_raw_in
at /home/sofia/.../alloc/src/boxed.rs:1578:9
1: std::boxed::Box::&amp;lt;aba_problem::Node&amp;lt;i32&amp;gt;&amp;gt;::from_raw
at /home/sofia/.../alloc/src/boxed.rs:1345:18
2: aba_problem::Stack::&amp;lt;i32&amp;gt;::pop_internal
at src/aba_problem.rs:83:41
3: aba_problem::Stack::&amp;lt;i32&amp;gt;::pop
at src/aba_problem.rs:53:9
4: aba_problem::tests::aba::{closure#0}::{closure#1}
at src/aba_problem.rs:138:49
&lt;/div>
&lt;/details>
note: the last function in that backtrace got called indirectly due to this code
--> &lt;span class="miri-location">src/aba_problem.rs:134:13&lt;/span>
|
134 | / &lt;span class="miri-code">s.spawn(|| {&lt;/span>
135 | | // We wait a little to make sure the first thread gets a head start
136 | | thread::sleep(Duration::from_millis(20));
... |
144 | | println!("thread 2 pushes [3]");
145 | | &lt;span class="miri-code">});&lt;/span>
| |______________^
&lt;/code>&lt;/pre>
&lt;p>There is still much to learn about Miri, like what exactly is a &lt;strong>&amp;ldquo;retag&amp;rdquo;&lt;/strong> operation, but for now, we can be happy that it helped us detect our bug.&lt;/p>
&lt;h2 id="the-fix">The fix
&lt;/h2>&lt;p>The big question now is, okay, what do we do about it? I won&amp;rsquo;t go into detail in this post, but some of the techniques include:&lt;/p>
&lt;ul>
&lt;li>Tagged pointers: adding a tag to the pointer, that is incremented every time the pointer changes.&lt;/li>
&lt;li>Hazard pointers: threads use hazard pointers to mark the objects they are working on so they don&amp;rsquo;t get dropped.&lt;/li>
&lt;li>Deferred reclamation
&lt;ul>
&lt;li>garbage collection&lt;/li>
&lt;li>epoch-based reclamation (EBR) &amp;ndash;&amp;gt; what &lt;strong>crossbeam-epoch&lt;/strong> provides :) stay tuned for the next post exploring this!&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>Check out this &lt;a class="link" href="https://aturon.github.io/blog/2015/08/27/epoch/" target="_blank" rel="noopener"
>post&lt;/a> by Aaron Turon, the creator of crossbeam, for a very detailed and easy to follow deep dive into how epoch-based reclamation works.&lt;/p>
&lt;h2 id="conclusion">Conclusion
&lt;/h2>&lt;p>It was a fun experiment trying to reproduce this and seeing first hand how non-trivial it actually is to catch.&lt;/p>
&lt;p>My takeaway: If it was so tricky to reproduce knowing from the start what we&amp;rsquo;re looking for, imagine how hard it&amp;rsquo;d be to detect in production code, if we&amp;rsquo;re not vigilant?&lt;/p>
&lt;p>My plan for the future is to continue learning how crossbeam works and hopefully create a second post dissecting &lt;code>crossbeam-epoch&lt;/code>. Thank you so much for reading, hope you enjoyed it as much as I enjoyed writing it!&lt;/p></description></item></channel></rss>