Skip to content

Search is only available in production builds. Try building and previewing the site to test it out locally.

Test MDX File V14 Atomic

Atomics & Thread Safety

Rust Atomics & Thread Safety

🔢

The shared counter problem

Two threads each run counter += 1 once. You expect the final value to be 2. But += 1 isn't one operation — it's read → add → write, and the scheduler can interrupt a thread between those steps. Step through an interleaving below and watch what happens.

Not thread-safe

let counter = 0;
// in each thread:
counter += 1;   // read+add+write
// = three separate steps

Thread-safe (atomic)

let counter = AtomicUsize::new(0);
// in each thread:
counter.fetch_add(1, SeqCst);
// = one indivisible step
Mode
Interleaving

Steps of an increment

hover or tap for detail

Execution order

AR
BR
A+
B+
AW
BW
Memory0
A reg0
B reg
Thread ARead
step 1 / 6
Final counter?

Both threads read 0 before either writes. Both compute 1. Both write 1. One increment vanishes.

Reach for an Atomic when…

You have a single scalar — a counter, a flag, an id. Atomics are lock-free: no thread ever blocks waiting for another, and the hardware guarantees the operation is indivisible.AtomicUsize,AtomicBool.

Reach for a Mutex when…

You have compound state — several fields that must stay consistent together, or a whole Vec/HashMap. The lock makes a critical section temporarily exclusive. Slower, but the only safe choice for multi-step updates.

Takeaway

A data race happens when an operation that looks single is actually several steps the scheduler can split apart. Atomic operations close that gap by making read-modify-write indivisible in hardware. The second argument to fetch_add is the , which controls visibility guarantees — use Ordering::SeqCst until you have a measured reason not to.

When you write new, you take on a debt: somewhere, on every path out of the function, there must be a matching delete. Miss one (e.g., forget it, return early before reaching it, or have an exception jump over it) and you leak. The Stack & Heap lab showed exactly that failure. C++ solves it with an idea so central it underlies the entire standard library: Resource Acquisition Is Initialisation, or RAII. Wrap the resource in an object, acquire it in that object’s constructor, release it in its destructor, and the compiler, which guarantees every destructor runs when its object goes out of scope, now guarantees your clean-up too.

The catch is that none of this is visible in your source. You never write the call to the destructor; the compiler inserts it at the closing brace, and on every other exit path besides. The demonstration below makes that invisible step visible. Step through each scenario and watch when the destructors run — first in a plain block, then with a heap resource that frees itself, and finally when an exception tears through the function and the clean-up still happens anyway.

C++ RAII Lab

Resource Acquisition Is Initialization — watch destructors run so you don't have to call them.

Two objects, one scope. Watch the destructors run in reverse.

Source
void demo() {
Guard a("a"); // constructor runs
Guard b("b"); // constructor runs
} // a and b go out of scope here
step 0 / 6
Stack — demo()Guards live here · destroyed in reverse order
No objects yet. Press Step to begin executing.
Heapfreed by a Guard's destructor — not by you
This stage uses no heap — it is purely about destructor order.
Notice:In every stage, you never wrote a single delete. The destructor — inserted by the compiler at the closing brace, and on every path out of the scope including the thrown exception — did the freeing. That guarantee is RAII. The leak you saw in the Stack & Heap lab simply cannot happen here.

C++ Templates Lab

A template is a recipe for code — watch the compiler stamp out one concrete copy per type.

Call max() with different types. Each new type stamps out its own concrete function.

Template definition — T is a placeholder, not a type
template <typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
Call sites — click to compile a call
Instantiated functionsgenerated by the compiler · one per distinct type
Nothing instantiated yet. The template above generates NO code on its own — fire a call to stamp out a concrete function.
Notice:The template definition at the top compiles to no machine code at all until you use it. Each distinct type you call it with stamps out one concrete copy — and calling the same type twice reuses the copy already made. That is why a heavily-templated header can be fast at runtime but slow to compile: the compiler is writing all those copies for you.

C++ Move Semantics Lab

Copy duplicates the buffer; move steals it. Watch the pointer change hands.

string b = a; runs the copy constructor — b gets its own duplicated buffer.

Source
std::string a = "data";
std::string b = a; // copy constructor
// a and b now own SEPARATE buffers
step 0 / 3
Stack — std::string objectseach holds a pointer into the heap
Nothing declared yet. Press Step to begin.
Heapcopy allocates a second buffer
Heap is empty. A buffer appears when a string is constructed.
The whole idea:A copy duplicates the expensive part — the heap buffer. A move just hands the pointer over and nulls the original, which is why it is cheap. std::move does not move anything itself; it only casts a to an rvalue so the move constructor is chosen instead of the copy constructor. The moved-from object is left valid and empty — safe to destroy or reassign, but never assume it still holds its old value.
C++ arena game

Robot Arena: Control Flow

Mission

Destroy the enemy robot within 20 ticks.

Each Run drops both robots at random positions, and your tank starts facing a random direction, with 50 energy. A hit needs you facing the enemy with distance() <= 3. Because the start changes every time, a program hard-wired to one approach will not work — scan to locate the enemy, turn to face it, close the gap with a loop, then fire.

Instruction palette — tap to add
robot::tick() — your program
arena.cpp
Tap instructions above to assemble the robot's per-tick program.
Arena — random start
you enemy
How the pseudo-compiler thinks

Before anything runs, the structural pass checks your braces balance, that every block actually guards a statement, and that no } appears without an open block — exactly the class of errors g++ reports first. Only a clean compile is allowed to run. The interpreter then walks your program as the robot's tick loop: if runs its body once when the guard holds, for repeats a fixed number of times, and whilerepeats until its sensor condition stops holding. Watch the energy and distance readouts as you scrub the frames — wasted actions cost energy you may need for fire().