A concurrent runtime can have thousands of fast-path operations for every rare state transition that requires global coordination. Placing a full memory barrier on every fast path makes each operation pay for that rare transition. Linux membarrier() supports the opposite arrangement: a coordinating thread enters the kernel and forces a defined ordering point across a target set of threads, moving more cost to the infrequent side of the protocol.

This is not a generic replacement for atomics, mutexes, or language memory models. It is a Linux kernel interface whose guarantees apply to memory accesses and targeted threads under specific commands. Correct use requires a protocol that already defines which accesses occur before and after the coordination point.

The barrier is initiated by one thread and completed across others

A local CPU fence orders accesses performed by the thread executing that fence. membarrier() has a different shape. The caller requests a barrier over a set of threads, and a successful return establishes that the targeted threads have passed through a state with the ordering guarantee defined by the selected command.

For MEMBARRIER_CMD_PRIVATE_EXPEDITED, the target set is the running thread siblings in the caller’s process. Non-running siblings are treated as already being in a suitable state for the guarantee. The caller therefore does not need each peer to execute an application-level callback or poll a shared flag merely to reach the kernel’s barrier point.

The distinction is useful in asymmetric synchronization schemes. Read-heavy runtimes can keep a small fast path while a rare writer, collector, or reclamation coordinator pays for cross-thread synchronization.

Private expedited mode requires registration

The private expedited command is not available merely because the kernel reports the command bit. A process first registers its intent with MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED. A later MEMBARRIER_CMD_PRIVATE_EXPEDITED without the required registration fails with EPERM.

Registration is process state, so initialization code can query support, register once, and then expose the capability to the runtime layer that needs it. MEMBARRIER_CMD_QUERY returns a bit mask of supported commands. Software should test the exact command and registration bits it depends on rather than treating the presence of the system call as proof that every mode exists.

Architecture also matters. Some commands have architecture-specific availability. A runtime that has a fallback synchronization path can select it when the required command is absent instead of turning a kernel capability into an unconditional deployment assumption.

Compiler ordering remains part of the user-space protocol

The kernel cannot repair compiler transformations that violate assumptions in user-space source code. The membarrier() contract is described in terms of program-order memory accesses on targeted threads, but source-level operations still pass through a compiler and, in many languages, a formal memory model.

A compiler barrier can prevent selected compiler reordering without emitting a hardware fence. A hardware memory barrier constrains CPU-visible ordering. membarrier() coordinates ordering across targeted threads through the kernel. These are related layers, not interchangeable spellings of the same operation.

Code around a membarrier() protocol therefore needs compiler-visible constraints appropriate to its language and implementation. In C or C++, data races on non-atomic objects remain subject to the language rules; a Linux system call does not retroactively make undefined concurrent accesses valid. Runtime implementations that operate below ordinary portable abstractions must document both the language-level assumptions and the kernel-level guarantee they rely on.

Expedited shifts latency rather than removing synchronization work

The expedited commands are designed to complete without blocking, but they impose extra overhead on the system to achieve that property. Calling them on every operation would defeat the asymmetry that makes the interface attractive.

A typical design has a frequent path that performs ordinary or relatively cheap synchronization and a rare path that changes global state. The rare path publishes its state with the required ordering, invokes membarrier(), and only then performs an action whose safety depends on peers having crossed the barrier point.

Memory reclamation illustrates the shape without prescribing one implementation. A coordinator may need assurance that peer threads have crossed a point after an earlier publication before reclaiming state that old readers could reference. membarrier() can supply one ordering component, but object lifetime still depends on the surrounding reclamation algorithm. The call alone does not prove that no thread retains a pointer.

Global and private commands express different interference boundaries

MEMBARRIER_CMD_GLOBAL targets threads across processes on the system, while private expedited mode limits the relevant target set to the caller’s process. The scope difference is architectural: a library coordinating only its own threads normally has no semantic need to impose a system-wide barrier.

Global expedited operation has a registration model for processes that intend to receive those barriers. Its guarantees apply to registered participants as specified by the interface. This makes registration part of the coordination contract rather than a performance hint that can be omitted casually.

Choosing the narrowest command that matches the ownership boundary reduces accidental coupling. A process-local garbage collector, JIT runtime, or reclamation subsystem usually reasons about threads sharing one address space. Cross-process shared-memory protocols have a different participant model and need an explicit account of which processes are covered.

Sync-core mode adds an instruction-stream guarantee

MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE extends the private expedited memory-ordering behavior by ensuring that running sibling threads execute a core-serializing instruction before the caller returns. It also requires its own registration command.

That extra property matters for mechanisms that modify executable code and need instruction execution to cross a serialization point. It should not be inferred from ordinary private expedited mode. Memory ordering for data and synchronization of instruction execution are distinct guarantees even when both are exposed through the same system call family.

Support for sync-core mode is architecture dependent. Code generation systems must therefore retain architecture-specific reasoning about instruction-cache and code-modification rules; the command is a kernel primitive within that reasoning, not a portable JIT contract by itself.

A successful call does not create a critical section

membarrier() does not stop peer threads and keep them stopped. Peers continue execution after crossing the relevant point. Shared state can change again immediately, so the caller cannot treat successful return as ownership of all process memory.

It also does not identify which object a protocol protects. The kernel orders memory activity according to the command semantics, while object identity, epochs, generations, hazard state, or publication flags remain user-space concepts. If a peer can reacquire an old object after the barrier because the protocol permits it, the barrier cannot make reclamation safe.

This boundary separates synchronization mechanism from synchronization proof. The system call can establish a costly cross-thread ordering event. The application must connect that event to state transitions that make the desired invariant true.

The useful abstraction is an asymmetric coordination primitive

membarrier() is most precise when treated as a primitive for protocols with a very frequent side and a much rarer coordinating side. Its value comes from relocating synchronization work, not from eliminating it.

A sound integration records the exact command, registration requirement, target scope, compiler constraints, and state transition protected by the call. With those boundaries explicit, the kernel can carry the expensive ordering event while fast paths avoid a barrier that would otherwise be paid on every operation.