<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Loom on Sofía Belén López Vicens</title><link>https://sofiabelen.github.io/tags/loom/</link><description>Recent content in Loom on Sofía Belén López Vicens</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Sun, 20 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://sofiabelen.github.io/tags/loom/index.xml" rel="self" type="application/rss+xml"/><item><title>Using Loom to (Try To) Catch an ABA Bug in Lock-Free Rust</title><link>https://sofiabelen.github.io/projects/using-loom-to-catch-an-aba-bug/</link><pubDate>Sun, 20 Sep 2026 00:00:00 +0000</pubDate><guid>https://sofiabelen.github.io/projects/using-loom-to-catch-an-aba-bug/</guid><description>&lt;img src="https://sofiabelen.github.io/projects/using-loom-to-catch-an-aba-bug/Laurits_Andersen_Ring_Johanne_WIlde_at_Her_Loom-1892.JPG" alt="Featured image of post Using Loom to (Try To) Catch an ABA Bug in Lock-Free Rust" />&lt;p>This is a follow-up to &lt;a class="link" href="https://sofiabelen.github.io/projects/visualizing-the-aba-problem/" target="_blank" rel="noopener"
>my previous post&lt;/a>, where we reproduced the ABA problem in a lock-free stack by manually sleeping the threads to achieve the desired scheduling. This is, however, an undeniably unviable strategy for testing and finding bugs in real life. To quote the docs, &lt;a class="link" href="https://docs.rs/loom/latest/loom/" target="_blank" rel="noopener"
>Loom&lt;/a> offers a way to &amp;ldquo;run tests many times, permuting the possible concurrent executions of each test according to what constitutes valid executions under the C11 memory model.&amp;rdquo;&lt;/p>
&lt;p>My goal for this post is to explore the usage of loom and apply it to our broken lock-free stack, to see if it can detect the aba bug. It&amp;rsquo;s my first time working with this tool, so it should be exciting!&lt;/p>
&lt;p>The code can be found on my &lt;a class="link" href="https://github.com/sofiabelen/visualizing-crossbeam-epoch" target="_blank" rel="noopener"
>github&lt;/a>!&lt;/p>
&lt;h2 id="aba-scenario-recap">ABA Scenario Recap
&lt;/h2>&lt;p>Here&amp;rsquo;s a diagram for a quick recap of the ABA scenario:&lt;/p>
&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: 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\"\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;h2 id="loom-setup">Loom Setup
&lt;/h2>&lt;h3 id="shim-for-using-loom-primitives">Shim For Using Loom Primitives
&lt;/h3>&lt;figure>
&lt;img style='object-fit: contain' src="robert-ruggiero-oY6774he6GQ-unsplash.jpg" atl="">
&lt;figcaption>
Photo by &lt;a href="https://unsplash.com/@robert2301?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Robert Ruggiero&lt;/a> on &lt;a href="https://unsplash.com/photos/a-pile-of-different-colored-washers-sitting-on-top-of-a-table-oY6774he6GQ?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash&lt;/a>
&lt;p>As a non-native speaker, it&amp;rsquo;s my first seeing this word o.O So I looked it up.&lt;/p>
&lt;p>From &lt;a href="https://en.wikipedia.org/wiki/Shim_(computing)">wikipedia&lt;/a>: a shim is a library that transparently intercepts API calls and changes the arguments passed, handles the operation itself or redirects the operation elsewhere. It&amp;rsquo;s also that thing in the picture ^^&lt;/p>
&lt;/figcaption>
&lt;/figure>
&lt;p>The way loom tests our code essentially is by replacing the &lt;code>std&lt;/code> primitives with its own:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: left">&lt;code>std&lt;/code>&lt;/th>
&lt;th style="text-align: left">&lt;code>loom&lt;/code>&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: left">&lt;code>std::thread&lt;/code>&lt;/td>
&lt;td style="text-align: left">&lt;code>loom::thread&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>std::sync::Arc&lt;/code>&lt;/td>
&lt;td style="text-align: left">&lt;code>loom::sync::Arc&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>std::cell::UnsafeCell&lt;/code>&lt;/td>
&lt;td style="text-align: left">&lt;code>loom::cell::UnsafeCell&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>std::sync::atomic::*&lt;/code>&lt;/td>
&lt;td style="text-align: left">&lt;code>loom::sync::atomic::*&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;code>std::thread::scope&lt;/code>&lt;/td>
&lt;td style="text-align: left">&lt;em>Not supported&lt;/em> (use &lt;code>loom::thread::spawn&lt;/code> + &lt;code>Arc&lt;/code>)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>What we want is to be able to use the &lt;code>std&lt;/code> types normally and only replace them when compiling with the &lt;code>loom&lt;/code> flag.&lt;/p>
&lt;p>So, for running the loom tests, we pass &lt;code>RUSTFLAGS=&amp;quot;--cfg loom&amp;quot;&lt;/code>.&lt;/p>
&lt;p>In order to toggle the loom types, we can add a shim to our &lt;code>lib.rs&lt;/code> file:&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="cp">#[cfg(not(loom))]&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">pub&lt;/span>&lt;span class="w"> &lt;/span>&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">AtomicPtr&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="cp">#[cfg(loom)]&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">pub&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">loom&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">AtomicPtr&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;/code>&lt;/pre>&lt;/div>&lt;p>I&amp;rsquo;m just demonstrating the usecase, but we&amp;rsquo;d need to do this for all of types we want to replace (check out the complete &lt;a class="link" href="https://github.com/sofiabelen/visualizing-crossbeam-epoch/blob/main/src/lib.rs" target="_blank" rel="noopener"
>lib.rs&lt;/a> for this demo).&lt;/p>
&lt;h3 id="cargotoml">Cargo.toml
&lt;/h3>&lt;p>Then, we want to add loom to our project, but gated behind a config flag, so that it doesn&amp;rsquo;t bloat our production builds. I added this my &lt;code>Cargo.toml&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="c1">// tells cargo to only include when compiling with --cfg loom
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">target&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">&amp;#39;cfg&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">loom&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="na">&amp;#39;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">dependencies&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">loom&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;0.7&amp;#34;&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">// registers loom as custom configuration flag (gets rid of compiler warnings)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">lints&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">rust&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">unexpected_cfgs&lt;/span>&lt;span class="w"> &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 class="n">level&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;warn&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">check&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">cfg&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="na">&amp;#39;cfg&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">loom&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="na">&amp;#39;&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;h3 id="wrapping-raw-pointers-in-unsafecell">Wrapping Raw Pointers In UnsafeCell
&lt;/h3>&lt;figure>
&lt;img style='object-fit: contain' src="jozsef-hocza-yBtRl173PWA-unsplash.jpg" atl="">
&lt;figcaption>
Photo by &lt;a href="https://unsplash.com/@hocza?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Jozsef Hocza&lt;/a> on &lt;a href="https://unsplash.com/photos/black-cat-wrapped-in-maroon-and-grey-plaid-textile-yBtRl173PWA?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash&lt;/a>
&lt;/figcaption>
&lt;/figure>
&lt;p>This is something I didn&amp;rsquo;t realize until later and didn&amp;rsquo;t understand why the loom tests passed for the broken stack ;)&lt;/p>
&lt;p>We need to wrap the fields that get unsafely read or written to across threads in &lt;code>loom::cell::UnsafeCell&lt;/code>. This gives loom visibility over the memory that it needs to track for conflicting accesses.&lt;/p>
&lt;p>Since the API for &lt;code>UnsafeCell&lt;/code> in &lt;code>std&lt;/code> and &lt;code>loom&lt;/code> are a bit different, the (docs)[https://docs.rs/loom/latest/loom/#handling-loom-api-differences] recommends adding:&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="cp">#[cfg(not(loom))]&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="cp">#[derive(Debug)]&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">pub&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">crate&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="nc">UnsafeCell&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="n">std&lt;/span>::&lt;span class="n">cell&lt;/span>::&lt;span class="n">UnsafeCell&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>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="cp">#[cfg(not(loom))]&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">impl&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="n">UnsafeCell&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">pub&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">crate&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>: &lt;span class="nc">T&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="nc">UnsafeCell&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">UnsafeCell&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">std&lt;/span>::&lt;span class="n">cell&lt;/span>::&lt;span class="n">UnsafeCell&lt;/span>::&lt;span class="n">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&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="k">pub&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">crate&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">with&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="o">&amp;gt;&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">f&lt;/span>: &lt;span class="nc">impl&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">FnOnce&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="k">const&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="nc">R&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="nc">R&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">f&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="mf">0.&lt;/span>&lt;span class="n">get&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="k">pub&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">crate&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">with_mut&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">R&lt;/span>&lt;span class="o">&amp;gt;&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">f&lt;/span>: &lt;span class="nc">impl&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">FnOnce&lt;/span>&lt;span class="p">(&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">T&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="nc">R&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="nc">R&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">f&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="mf">0.&lt;/span>&lt;span class="n">get&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>Tldr; &lt;code>loom::cell::UnsafeCell&lt;/code> uses &lt;code>.with&lt;/code> and &lt;code>.with_mut&lt;/code> to track reads and writes, whereas &lt;code>std::cell::UnsafeCell&lt;/code> just uses &lt;code>.get&lt;/code>, so we need to work around this a bit.&lt;/p>
&lt;p>Node becomes:&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">pub&lt;/span>&lt;span class="w"> &lt;/span>&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="c1">//next: *mut Node&amp;lt;T&amp;gt;,
&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">next&lt;/span>: &lt;span class="nc">UnsafeCell&lt;/span>&lt;span class="o">&amp;lt;*&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;&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>For the &lt;code>push&lt;/code> I&amp;rsquo;ve replaced&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">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;/code>&lt;/pre>&lt;/div>&lt;p>with&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>&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="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="p">.&lt;/span>&lt;span class="n">with_mut&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="n">next_ptr&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="o">*&lt;/span>&lt;span class="n">next_ptr&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="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>Let&amp;rsquo;s break this down:&lt;/p>
&lt;pre>&lt;code>
&lt;mark>(*new_head).next&lt;/mark>
│ │
│ └── retrieves next, which is of type UnsafeCell&lt;*mut Node>
│
└── dereference
navigates through the new_head raw pointer to get the actual Node struct
&lt;mark>.with_mut( |next_ptr| { ... } )&lt;/mark>
│ │
│ └── closure arg: next_ptr
│ the closure receives next_ptr, which is a pointer-to-the-pointer:
│ type: `*mut (*mut Node)`
│ think of this as: "a pointer targeting the inner pointer slot"
│
└── Calls .with_mut() on the UnsafeCell
- cfg(loom): loom sees that a thread is writing
- cfg(not(loom)): standard raw access
&lt;mark>*next_ptr = current_head;&lt;/mark>
│ │
│ └── set the value inside the slot to point to current_head
│
└── dereference and write
*next_ptr dereferences the outer pointer to reach the inner pointer slot
&lt;/code>&lt;/pre>
&lt;p>For the &lt;code>pop&lt;/code>, I need to replace&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">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;/code>&lt;/pre>&lt;/div>&lt;p>with&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">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>&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="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="p">.&lt;/span>&lt;span class="n">with&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="n">next_ptr&lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">next_ptr&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;h2 id="aba-loom-test">ABA Loom Test
&lt;/h2>&lt;p>We are now ready to write our loom test (separte from the normal &lt;code>#[test]&lt;/code>s). We wrap everything in a &lt;code>loom::model&lt;/code>. And&amp;hellip; I had to rewrite this a bit because loom doesn&amp;rsquo;t have &lt;code>std::thread::scope&lt;/code>. The reason for this, from what I could understand, is that &lt;code>std::thread::scope&lt;/code> works with standard OS scheduling that loom cannot intercept.&lt;/p>
&lt;p>With that out of the way, so the way this works is that loom runs the &lt;code>loom::model(|| { ... })&lt;/code> closure many times, each time with a different schedule and thread-interleaving permutation.&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="cp">#[cfg(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="cp">#[cfg(loom)]&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">mod&lt;/span> &lt;span class="nn">loom_tests&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">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">super&lt;/span>::&lt;span class="o">*&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">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">crate&lt;/span>::&lt;span class="n">thread&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">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">crate&lt;/span>::&lt;span class="n">Arc&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">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">loom&lt;/span>::&lt;span class="n">model&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="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_problem&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="n">model&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="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">s2&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">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="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">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>&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">t1&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">thread&lt;/span>::&lt;span class="n">spawn&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">move&lt;/span>&lt;span class="w"> &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="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>&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">t2&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">thread&lt;/span>::&lt;span class="n">spawn&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">move&lt;/span>&lt;span class="w"> &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="n">s2&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="n">s2&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="n">s2&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">4&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">t1&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">join&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="n">t2&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">join&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="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>Running the test:&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">RUSTFLAGS=&amp;#34;--cfg loom&amp;#34; RUST_BACKTRACE=1 cargo test --release aba_problem
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="big-reveal-happy-noises-or-maybe-i-celebrated-too-early">Big Reveal **happy noises** (or maybe I celebrated too early)
&lt;/h2>&lt;figure>
&lt;img style='object-fit: contain' src="viktor-forgacs-WHusHiMMx6s-unsplash.jpg" atl="">
&lt;figcaption>
Photo by &lt;a href="https://unsplash.com/@sonance?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Viktor Forgacs&lt;/a> on &lt;a href="https://unsplash.com/photos/a-red-and-blue-fireworks-display-WHusHiMMx6s?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash&lt;/a>
&lt;/figcaption>
&lt;/figure>
&lt;style>
.loom-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) {
.loom-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;
}
}
.loom-err-title { color: var(--err-color); font-weight: bold; }
.loom-highlight { background-color: var(--err-bg); padding: 1px 4px; border-radius: 3px; }
.loom-help { color: var(--help-color); }
.loom-location { color: var(--location-color); font-weight: 500; }
.loom-code { color: var(--code-color); }
.loom-noise {
opacity: 0.45;
transition: opacity 0.2s ease;
}
.loom-noise:hover {
opacity: 0.9;
}
.loom-badge {
background-color: var(--badge-bg);
padding: 1px 6px;
border-radius: 4px;
font-weight: bold;
text-transform: uppercase;
}
.loom-badge-thread1 { color: var(--t1-color); }
.loom-fail { color: var(--err-color); font-weight: bold; }
.loom-pass-count { color: var(--code-color); }
.loom-backtrace {
margin: 8px 0;
color: var(--dim-color);
}
.loom-backtrace summary {
cursor: pointer;
color: var(--dim-color);
user-select: none;
font-weight: 500;
}
.loom-backtrace summary:hover {
color: var(--details-hover);
}
.loom-backtrace-content {
opacity: 0.55;
padding-left: 12px;
border-left: 2px solid var(--details-border);
margin-top: 4px;
}
&lt;/style>
&lt;pre class="loom-output">&lt;code>&lt;span class="loom-noise">running 1 test&lt;/span>
&lt;span class="loom-noise">test naive_lock_free_stack::loom_tests::aba_problem ... &lt;/span>&lt;span class="loom-fail">FAILED&lt;/span>
[...]
&lt;span class="loom-badge loom-badge-thread1">(25841) thread `naive_lock_free_stack::loom_tests::aba_problem`&lt;/span> panicked at &lt;span class="loom-location">loom-0.7.2/src/rt/object.rs:286:38&lt;/span>:
&lt;span class="loom-err-title loom-highlight">index out of bounds: the len is 7 but the index is 34091991057&lt;/span>
&lt;details class="loom-backtrace">
&lt;summary>[ Click to expand full backtrace ]&lt;/summary>
&lt;div class="loom-backtrace-content">
= note: stack backtrace:
0: __rustc::rust_begin_unwind
1: core::panicking::panic_fmt
2: core::panicking::panic_bounds_check
3: &amp;lt;scoped_tls::ScopedKey&amp;lt;core::cell::RefCell&amp;lt;loom::rt::scheduler::State&amp;gt;&amp;gt;&amp;gt;::with::&amp;lt;&amp;lt;loom::rt::scheduler::Scheduler&amp;gt;::with_state&amp;lt;&amp;lt;loom::rt::scheduler::Scheduler&amp;gt;::with_execution&amp;lt;loom::rt::synchronize&amp;lt;&amp;lt;loom::rt::cell::Cell&amp;gt;::start_write::{closure#0}, loom::rt::cell::Writing&amp;gt;::{closure#0}, loom::rt::cell::Writing&amp;gt;::{closure#0}, loom::rt::cell::Writing&amp;gt;::{closure#0}, loom::rt::cell::Writing&amp;gt;
4: &lt;span class="loom-help">&amp;lt;loom::rt::cell::Cell&amp;gt;::start_write&lt;/span>
5: &lt;span class="loom-highlight loom-code">&amp;lt;[...]::naive_lock_free_stack::Stack&amp;lt;i32&amp;gt;&amp;gt;::pop&lt;/span>
6: &amp;lt;loom::rt::spawn&amp;lt;loom::thread::spawn_internal&amp;lt;visualizing_crossbeam_epoch::naive_lock_free_stack::loom_tests::aba_problem::{closure#0}::{closure#1}, ()&amp;gt;::{closure#0}&amp;gt;::{closure#1} as core::ops::function::FnOnce&amp;lt;()&amp;gt;&amp;gt;::call_once::{shim:vtable#0}
7: &amp;lt;generator::stack::StackBox&amp;lt;&amp;lt;generator::gen_impl::GeneratorImpl&amp;lt;core::option::Option&amp;lt;alloc::boxed::Box&amp;lt;dyn core::ops::function::FnOnce&amp;lt;(), Output = ()&amp;gt;&amp;gt;&amp;gt;, ()&amp;gt;&amp;gt;::init_code&amp;lt;loom::rt::scheduler::spawn_thread::{closure#0}&amp;gt;::{closure#0}&amp;gt;&amp;gt;::call_once
8: generator::detail::gen::gen_init_impl
9: generator::detail::asm::gen_init
&lt;/div>
&lt;/details>
&lt;span class="loom-noise">note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.&lt;/span>
failures:
&lt;span class="loom-help">naive_lock_free_stack::loom_tests::aba_problem&lt;/span>
&lt;span class="loom-noise">test result: &lt;/span>&lt;span class="loom-fail">FAILED&lt;/span>&lt;span class="loom-noise">. &lt;/span>&lt;span class="loom-pass-count">0&lt;/span>&lt;span class="loom-noise"> passed; &lt;/span>&lt;span class="loom-pass-count">1&lt;/span>&lt;span class="loom-noise"> failed; &lt;/span>&lt;span class="loom-pass-count">0&lt;/span>&lt;span class="loom-noise"> ignored; &lt;/span>&lt;span class="loom-pass-count">0&lt;/span>&lt;span class="loom-noise"> measured; &lt;/span>&lt;span class="loom-pass-count">0&lt;/span>&lt;span class="loom-noise"> filtered out; finished in 0.00s&lt;/span>
&lt;/code>&lt;/pre>
&lt;p>Soooo&amp;hellip; the test fails with an &lt;code>index out of bounds&lt;/code>. I looked into what this means, and in loom&amp;rsquo;s runtime engine, every atomic, &lt;code>UnsafeCell&lt;/code>, thread and allocation is assigned a small &lt;em>object id&lt;/em>. In this case, the valid ones are from 0 to 6, so this huge index basically doesn&amp;rsquo;t belong to any of the objects that loom is tracking.&lt;/p>
&lt;p>However, this doesn&amp;rsquo;t yet prove that we&amp;rsquo;ve managed to hit the ABA path yet. After printing printing the addresses, it turns out it&amp;rsquo;s just a use-after-free case. I tried running it a few more times but no luck.&lt;/p>
&lt;p>Essentially, what an use-after-free scenario could look like:&lt;/p>
&lt;style>
#mermaid-slider-1.mermaid-slider {
display: flex;
flex-direction: column;
align-items: center;
background: transparent;
padding: 10px;
width: 100%;
}
#mermaid-slider-1 .m-canvas {
width: 100%;
height: 520px;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
}
#mermaid-slider-1 .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-1 .m-controls {
display: flex;
gap: 12px;
margin-top: 16px;
}
#mermaid-slider-1 .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-1 .m-controls button:hover:not(:disabled) {
background: #e8a7b5;
}
#mermaid-slider-1 .m-controls button:disabled {
opacity: 0.4;
cursor: not-allowed;
}
#mermaid-slider-1 .mermaid .node rect,
#mermaid-slider-1 .mermaid .node circle,
#mermaid-slider-1 .mermaid .node polygon {
fill: #fcdbe2 !important;
stroke: #d97d90 !important;
stroke-width: 2px !important;
}
#mermaid-slider-1 .mermaid .cluster rect {
fill: #fff0f3 !important;
stroke: #d97d90 !important;
stroke-width: 2px !important;
}
#mermaid-slider-1 .mermaid .edgePath .path {
stroke: #d97d90 !important;
stroke-width: 3px !important;
}
#mermaid-slider-1 .mermaid marker,
#mermaid-slider-1 .mermaid marker path,
#mermaid-slider-1 .mermaid [id*="flowchart-pointEnd"] {
fill: #d97d90 !important;
stroke: #d97d90 !important;
}
#mermaid-slider-1 .mermaid text,
#mermaid-slider-1 .mermaid .cluster-label {
fill: #333333 !important;
font-size: 14px !important;
font-weight: 600 !important;
font-family: inherit !important;
}
#mermaid-slider-1 .mermaid .flowchartTitleText {
fill: #d97d90 !important;
font-size: 18px !important;
font-weight: bold !important;
}
&lt;/style>
&lt;div id="mermaid-slider-1" class="mermaid-slider">
&lt;div id="mermaidTarget-1" 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-1");
const target = document.getElementById("mermaidTarget-1");
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>
&lt;p>Soooo.. if we did want to reproduce the exact ABA scenario, we&amp;rsquo;d need to interfere with the timing ourselves, like in the &lt;a class="link" href="https://sofiabelen.github.io/projects/visualizing-the-aba-problem/" target="_blank" rel="noopener"
>previous post&lt;/a>, which I believe doesn&amp;rsquo;t add any more value at this point.&lt;/p>
&lt;h2 id="conclusion">Conclusion
&lt;/h2>&lt;p>While it is a bit dissapointing that we couldn&amp;rsquo;t catch the actual ABA bug, the &lt;em>why&lt;/em> we couldn&amp;rsquo;t helped me better understand what loom actually does. Since it runs different permutations until it panics, the first time it encounters a typical use-after-free, it stops there. It did prove, however, that loom does detect that our stack is indeed broken, which is the most important thing.&lt;/p>
&lt;p>Originally, I had written a section discussing the use-after-free bug on my previous post, which I removed because it added too much complexity to the structure of the article without much extra benefit. So, here, I used that diagram that I had created before, so that&amp;rsquo;s something :)&lt;/p>
&lt;p>Thank you so much for tagging along! Any feedback is appreciated as I&amp;rsquo;m just dipping my toes in these tools.&lt;/p></description></item></channel></rss>