Master the fundamental concepts of lock-free & wait-free programming through this focused micro-challenge.
You have read the whole brief, and the concepts above stay free on every task. Writing and running the code needs a plan.
Three hints are available for this task, revealed one at a time inside the code workspace so you can struggle productively before seeing them.
Every task includes starter code, theory, and hidden tests so you can implement and verify locally in the browser.
How it worksThe Michael-Scott queue provides FIFO ordering with separate atomic head and tail. Producers CAS the tail forward; consumers CAS the head. It powers many message-passing runtimes where mutex latency spikes under load.
Allocate a dummy node. Enqueue links new node at tail with CAS retry. Dequeue advances head when the next pointer is non-null.
cLoading…
pthread_mutex + std::deque under many producersKeep the relevant documentation open while you implement. When your output disagrees with the reference, trace one failing case by hand before changing random lines.
You will implement the Michael-Scott lock-free queue in C11 atomics. This exercise requires enqueue/dequeue from multiple threads without losing elements.
The Michael. Scott queue is the standard lock-free FIFO. It is a linked list with a dummy node, a Head and a Tail. An enqueue links the new node after the last one with a CAS and then swings Tail with a second CAS. Between those two CASes, Tail lags one node behind, so every other thread that notices the lag helps by advancing Tail itself. Simulate the algorithm step by step under explicit interleavings.
cLoading…
Node #1 is the initial dummy (Head = Tail = #1). New nodes are #2, #3, … and are allocated on an enqueue's first attempt.
enq V:
t = Tail.next = t.next.t != Tail, retry. Else if next != NULL, the tail is lagging: CAS(Tail, t, next) to help, then retry. Else CAS(t.next, NULL, node): on success go to step 3, otherwise retry.deq:
h = Head, t = Tail.next = h.next.h != Head, retry. If h == t: when next is NULL the queue is empty (done), otherwise help CAS(Tail, t, next) and retry. Else read next.value and CAS(Head, h, next): on success return the value (next becomes the new dummy), otherwise retry.Every retry restarts at step 0 and counts as a retry. A help also counts as a help.
cLoading…
(lagging) is printed when Tail is not the last node. Other messages follow the Treiber-stack tasks: NAME: finished, schedule: unknown thread NAME, thread NAME: bad operation: TEXT, and EMPTY for an empty dequeue.
Input:
cLoading…
Output:
cLoading…
Head.next, and that node becomes the new dummy.Hidden tests cover three enqueuers racing for the same next field, a lagging tail seen by state, a dequeuer that loses the race to another dequeuer, dequeuing until the queue is empty, and schedules that name finished or unknown threads.