Atomic read-modify-write 2026-10-14
An atomic operation that reads a shared object's value and changes it as one indivisible action. For an increment from , the two operations under mutual exclusion produce then in either software thread order. A test-and-set is another example. The operation's ordering guarantees for other data still depend on the memory model and its selected acquire-release synchronization semantics.
Circular buffer operation costs 2026-10-18
With storage allocated in advance, each successful insertion, removal,
peek, size, and full/empty check uses worst-case time complexity in Big O notation. This assumes constant-cost indexed array access, bounded machine word index arithmetic and fixed-cost scalar item reads and writes. Each operation touches a bounded number of slots and fields regardless of and ; no shifting, scanning or resizing occurs. Rejected operations also take constant work. Producing a copy of all live items takes work under the same scalar-cost assumptions.The preallocated array occupies scalar slots plus constant bookkeeping. Allocation and any initial slot construction are separate setup costs. Copying a large item, running an expensive destructor or callback, allocating an item, or growing a dynamic array changes the operation cost and must be charged separately. The bound is worst-case structural work, with no need for amortized analysis, and says nothing about a measured wall-clock deadline.
These formulas describe sequential calls. Wrapping array indices implies neither atomic operations nor safe access from multiple software threads. Publication of an item's contents and reuse of its slot require a protocol justified under the relevant memory model, including any necessary acquire-release synchronization. The kernel documentation discusses barriers under explicit producer/consumer restrictions; Boost's documentation separately requires mutual exclusion for shared access involving writes. Neither property follows merely from choosing a circular buffer.[ref]
Circular Buffers by David Howells and Paul E. McKenney. David Howells and Paul E. McKenney, Circular Buffers, Using memory barriers with circular buffers; The producer; The Consumer: explicit synchronization and ordering requirements
[ref]Implementation. Jan Gaspar, Boost.Circular Buffer, Implementation, Thread-Safety: shared access involving a writer requires mutual exclusion
Critical section Created 2026-10-14 Updated 2026-10-25
A region whose accesses must obey a shared synchronization protocol. For the lost update in mutual exclusion, the critical section includes the read and the local addition as well as the write. Choosing its boundary means identifying the complete operation whose intermediate state must be protected. Every conflicting access must participate in a compatible protocol; merely declaring a mutex does not protect accesses that bypass it.
Deadlock 2026-10-14
A group of participants cannot proceed because each waits for a resource whose release depends on another participant in the group. Consider two initially free mutex objects A and B. T1 acquires A then tries B; T2 acquires B then tries A. If each obtains its first mutex, both wait permanently for the other, with release reachable only after the second acquisition. Each mutex still enforces mutual exclusion.[ref]
Common Concurrency Problems. pp. 5–7, figures 32.6–32.7: opposing acquisition orders and circular wait
Reachable states for opposite mutex acquisition orders and for one shared lock ordering
. Details. Each software thread performs four indivisible operations: acquire its first mutex, acquire its second, release the second, release the first. An unavailable acquisition blocks; neither participant releases early. A state records each participant's completed-operation count and both owners. Exhaustive exploration finds 19 reachable states with opposite orders, including exactly one nonterminal state with no enabled operation: both counts are , T1 owns A, and T2 owns B. Giving both participants the order A then B leaves 16 reachable states and no such blocked state. Unshown grid positions are unreachable. These are tiny sequential consistency models, without timeouts, cancellation, recursive acquisition or other resources.
Liveness property 2026-10-14
A requirement of eventual progress, with no finite prefix ruling out its eventual satisfaction. For starvation freedom, a persistent requester must eventually enter its critical section. Establishing such a liveness property requires explicit scheduling and protocol assumptions in addition to mutual exclusion. Exhaustive finite thread interleaving checks alone do not establish eventual service for repeatedly competing software threads.[ref]
Defining Liveness by Bowen Alpern and others. p. 182: every finite prefix can still be extended to satisfy a liveness property
Lost update 2026-10-14
A change that is overwritten by another update based on an older value. In mutual exclusion, both software threads read and later write , so one intended increment is lost. This race condition can occur with individually atomic reads and writes and therefore need not involve a data race. Its repair must cover the complete update, using a critical section or an appropriate atomic read-modify-write.
Memory barrier 2026-10-31
An operation constraining specified memory-access orderings. The strength and scope of a memory barrier matter; it does not by itself provide mutual exclusion or safe memory reclamation.
Memory-mapped file synchronization 2026-10-14
There are two distinct questions: whether another operating-system process can observe shared contents, and whether those contents survive a crash. In the timeline, persistent storage deliberately remains while the shared page cache contains . No background page writeback occurs in this toy interval. Real page writeback may persist some contents earlier; shared visibility alone cannot tell the reader which contents are durable.
For the covered shared range,
msync(base, length, MS_SYNC) requests page writeback and waits for completion; check its return value. MS_ASYNC is not a durable completion acknowledgment, and on modern Linux kernel versions it is a no-op because dirty contents are already tracked. The address must be aligned, so synchronizing our changed second page uses base + 4096, length 4096, rather than base + 4133. Unmapping alone is not a guarantee of completed writeback.[ref]msync(2) — Linux manual page by Linux man-pages project. DESCRIPTION, RETURN VALUE, ERRORS and VERSIONS: covered range, MS_SYNC versus MS_ASYNC, page-aligned address, and Linux MS_ASYNC behavior since 2.6.19
For persistence of the file and associated file metadata, fsync on an open file descriptor is a broader interface; a new directory entry needs separate synchronization of its file-system directory. Retain a suitable file descriptor if the persistence protocol needs it after mapping. Successful storage completion still relies on the file system and device honoring their persistence contracts.[ref]
fsync(2) — Linux manual page by Linux man-pages project. DESCRIPTION, RETURN VALUE and ERRORS: file data and metadata, directory entry, completion and reported failures
Neither fsync nor msync supplies mutual exclusion, an atomic storage write for several memory pages, or an application-wide file-system transaction. Stop or coordinate writers before deciding which update a completion acknowledges. A crash during page writeback can leave a mixture of old and new contents unless a separate recovery protocol provides file-system crash consistency. The last timeline row assumes successful synchronous completion, honored storage flush semantics and no subsequent writer; it illustrates a persistence boundary, not a promise about arbitrary hardware failures.
Message-passing mutual exclusion server 2026-10-25
Maintain an owner identity, initially empty, and a FIFO queue of accepted client requests. A request appends its sender to the FIFO queue. A release from the owner clears the owner. Whenever there is no owner and the FIFO queue is nonempty, remove its oldest request, record that client as owner, and send a grant. The server processes these state changes sequentially.
Safety proof. At most one grant is outstanding: a new grant requires an empty owner field, and a valid release clears precisely the preceding ownership. Compliant clients access the resource only while holding their grant, so mutual exclusion follows. With reliable eventual message passing, continued server execution and finite resource-use durations, the FIFO queue also gives starvation freedom for accepted requests, since later requests cannot overtake them.
Monitor (synchronization) 2026-10-31
A construct combining mutual exclusion for operations on shared state with condition-based waiting. A waiting caller releases the monitor while asleep and must recheck its predicate after regaining access.
Mutual exclusion Created 2026-10-14 Updated 2026-10-25
Mutual exclusion allows at most one participating software thread at a time to execute a protected critical section. A mutex is an object used to enforce that rule; the critical section is the region of the program that needs the rule. Several different critical sections can use the same mutex to protect the same shared data. The benefit is reasoning about a complete update without another cooperating software thread observing or changing its intermediate state.[ref]
For a small lost update, shared Assume sequential consistency: each step is indivisible, each software thread keeps the displayed order, and a read sees the latest preceding write to
x initially equals . Each of two software threads, T1 and T2, uses its own local r and performs these three steps:r = read(x)
r = r + 1
write(x, r)x. In the thread interleaving T1 read, T2 read, T1 add, T2 add, T1 write, T2 write, both local values become and both writes store . Final instead of : a lost update despite two correctly executed local additions. Separate indivisible reads and writes are insufficient; the complete read, modify and write must be protected.The binomial coefficient counts the thread interleavings preserving the two three-step orders: choose T1's three positions, with the remaining positions belonging to T2. Exhaustive enumeration gives final in 18 orders and in the two serial orders. These counts are not probabilities: no random scheduler has been specified. With the same mutex acquired before reading and released after writing, only the two complete increment orders remain, and both give . Alternatively, an atomic read-modify-write increment updates
x in one indivisible step. Protecting only the write still allows both software threads to read beforehand.Mutual exclusion is a safety property: two participants must never be inside the protected critical section together. Eventual entry for every persistent requester is a liveness property, expressed here as starvation freedom. A protocol that never admits anyone satisfies the first requirement while failing the second. Nor does a mutex alone promise which waiting software thread enters next, or that a producer runs before a consumer. Such application ordering needs an additional synchronization protocol.[ref]
Defining Liveness by Bowen Alpern and others. pp. 181–182: mutual exclusion, finite witnesses to safety violations, and eventual service
The sequential consistency assumptions delimit this calculation. In C++, unsynchronized conflicting accesses to an ordinary shared object can be a data race, which makes behavior undefined; the 20 schedules are not a prediction for that program. An atomic operation with relaxed ordering also supplies no acquire-release synchronization for other data. A real mutex must supply both exclusive entry and the required happens-before ordering between its users.[ref]
O1 supplies mutual exclusion over each complete operation. A reader sees the protected state from a serial order of readers and writers; a writer can safely mutate the existing object while holding the common mutex. If an update reads the old state and modifies it, the entire read-modify-write can be protected against a lost update.
O2 has different semantics. Assume an indivisible aligned access to
p, correct publication ordering, and no later mutation of a published version. A reader chooses one version when it loads p; the subsequent load instructions use the same saved pointer, so an overlapping writer need not block that reader. The reader may continue observing the old complete version after the writer has published the new one. A sufficiently specified snapshot read can still be linearizable, with the load of p as its linearization point: a read overlapping a write is allowed to come before that write in the abstract order. Thus reading an old version during overlap does not by itself prove failure of linearizability.Multiple writers can each construct a valid replacement and race to publish it. If the operation is simply “replace the whole object”, atomic pointer stores give a last-publication order. If each writer instead computes a change from the previously published state, O2 alone does not preserve both changes. Two writers can both read a counter value zero, independently build a version containing one, and publish their respective versions; the final counter is one. A writer mutex or compare-and-swap with retry is needed when the required operation must avoid that lost update. Memory barriers alone do not provide this exclusion.
Releasing the old object's computer memory immediately after publishing a replacement is unsafe. For example:The final operation is a use-after-free: it may crash, see allocator metadata, or read a different object that reuses the same memory address. An atomic update of
reader: q := LOAD [p] // gets the old object
writer: STORE [p], new_object
writer: free(old_object)
reader: LOAD [q + field] // accesses freed storagep does not update the reader's already copied pointer. Freeing the new object immediately would of course also invalidate p itself.Publication and reclamation are separate: publish a fully initialized version, and retain every old version until all readers that might hold it have finished. A read-copy-update grace period or a suitable hazard pointer protocol can provide that lifetime guarantee; naive reference-count acquisition after loading an unprotected pointer can itself race with freeing. Correct memory barriers in part (iii) ensure visibility, not object lifetime.
Safety: only the driver accesses the sensor, and
active identifies at most one conversion. Accepted requests are serialized by the FIFO queue. The reply destination and token identify the owner of the resulting sample; no caller can clear another conversion's ready flag or consume another result. Receiving new requests while busy only changes the queue, not the active measurement. These are mutual exclusion and request/result association properties, not timing guesses.Progress: assume each started conversion completes in finite time, the corresponding notification is eventually delivered, the driver and compliant clients are eventually scheduled, and pending message acceptance is fair. A caller remains ready to receive its reply. Every accepted request has finitely many earlier requests in the queue, and later arrivals cannot overtake it. Each earlier request is completed and removed in finite time, so the caller eventually becomes active and receives its own reading. Fair acceptance extends this argument from accepted requests to callers waiting to submit one.
These assumptions are necessary for starvation freedom. A failed sensor, a client that disappears before accepting a synchronous reply, or an unfair scheduler can prevent progress; serialization alone cannot prove otherwise. Under the stated operating model, simultaneous calls are safe and every call completes. A single shared “latest temperature” variable would not establish either a fresh conversion per call or correct reply ownership.
Use a message-passing mutual exclusion server with an A client sends
owner field and a FIFO queue. A request includes its client's identity. The server handles one received message at a time, and a client has at most one outstanding request. The following pseudocode assumes reliable message passing and compliant clients:owner = NONE
waiting = empty FIFO queue
forever:
(kind, client) = receive_from_any_client()
if kind == REQUEST:
waiting.push_back(client)
else if kind == RELEASE:
assert client == owner
owner = NONE
if owner == NONE and not waiting.empty():
owner = waiting.pop_front()
send(owner, GRANT)REQUEST, waits for GRANT, runs its complete animation, and sends RELEASE before making another request.The invariant proving mutual exclusion is: at most one client has received an unreturned grant, and that client is
owner. Only the final block issues a grant, and it first requires no current owner. A valid release removes that ownership. Since clients use the display only while holding a grant, simultaneous use is impossible. The FIFO queue prevents accepted requests from being overtaken by later ones. With eventual message delivery, continued server execution, and finite animations, each accepted request is eventually served; these progress assumptions are separate from the safety invariant.Use two FIFO queues, one for each priority class, and perform nonpreemptive strict-priority arbitration whenever the display becomes free:The same ownership invariant proves mutual exclusion. Among requests already accepted by the server, every pending urgent request takes precedence over every ordinary one. Each class retains its own arrival order. A newly received urgent request goes ahead of waiting ordinary requests but waits for the current animation's
owner = NONE
ordinary = empty FIFO queue
urgent = empty FIFO queue
forever:
message = receive_from_any_client()
if message.kind == REQUEST:
assert message.priority in {0, 1}
if message.priority == 1:
urgent.push_back(message.client)
else:
ordinary.push_back(message.client)
else if message.kind == RELEASE:
assert message.client == owner
owner = NONE
if owner == NONE:
if not urgent.empty():
owner = urgent.pop_front()
else if not ordinary.empty():
owner = ordinary.pop_front()
if owner != NONE:
send(owner, GRANT)RELEASE; the given protocol has no interruption or revocation operation.This is strict priority at dispatch, based on requests the server has received. Requests still in transit cannot influence a decision. If the transport provides a mailbox-draining operation, the server may also collect all currently delivered requests before dispatching; the two-FIFO queue selection rule remains the same.
Strict priority does not guarantee starvation freedom for ordinary clients under an endless supply of urgent work. If urgent traffic eventually stops, accepted requests have only finitely many earlier requests, and all service times are finite, every pending ordinary request is eventually granted. Guaranteeing ordinary progress under unlimited urgent traffic would require a different fairness policy, such as limiting consecutive urgent grants; that would relax strict priority.
Request-reply sensor conversion 2026-10-24
A device driver protocol giving each accepted client request its own fresh hardware conversion. A single driver owns the device registers, queues requests, starts one conversion, waits for its completion, reads the result and replies to the identified requester before starting the next conversion. Sender identity and a per-call token prevent reply confusion. A FIFO queue prevents later accepted requests from overtaking earlier ones.
Safety follows from at most one active conversion and exclusive register ownership. Starvation freedom additionally needs finite conversion time, eventual interrupt delivery, fair request acceptance and scheduling, and clients ready to receive their replies. These are progress assumptions, not consequences of mutual exclusion alone.
Safety property 2026-10-14
A requirement whose violation has a finite execution prefix that no continuation can repair. Two software threads simultaneously inside a protected critical section already violate mutual exclusion; later leaving does not erase that violation. This contrasts with a liveness property, for which a long finite wait alone does not establish failure.[ref]
Defining Liveness by Bowen Alpern and others. pp. 181–182: finite irremediable prefixes
Sequential consistency Created 2026-10-14 Updated 2026-10-31
Execution behaves as if steps occurred in one total order preserving each participant's program order; reads observe the latest preceding writes in that order. This is the definition given in the 1979 paper How to Make a Multiprocessor Computer That Correctly Executes Multiprocess Programs, printed p. 690. It permits many thread interleavings, including the lost update under mutual exclusion; ordering individual accesses does not combine them into one atomic operation.
Shared file mapping 2026-10-14
Spinlock 2026-10-14
A mechanism that waits for ownership by repeatedly trying while consuming CPU execution time. A spinlock built from test-and-set can enforce mutual exclusion without guaranteeing starvation freedom. Progress also requires the owner to run and release: if a waiting software thread monopolizes the only CPU while the owner is suspended, exclusive entry alone cannot help.[ref]

