Recipe / Concurrency
Memory ordering primer
A practical walkthrough of how the Meridian runtime reasons about atomic loads, stores, and the happens-before relationships your agents rely on when shared state crosses thread boundaries. Understanding the memory model is the difference between a routing layer that survives 250 models and one that randomly drops tokens.
1. Relaxed loads and stores
Relaxed ordering guarantees atomicity for a single location but imposes no synchronization. Use it for monotonic counters, sampled telemetry, and statistics that tolerate stale reads. Never use it to publish pointers, flags, or any value another thread interprets as a signal that some other piece of state is ready to consume.
2. Acquire and release pairing
Acquire on a load pairs with release on the matching store to form a happens-before edge between producer and consumer. Everything the producer wrote before the release becomes visible to the consumer after the acquire. This is the workhorse pattern behind Meridian request queues, model-router caches, and the gateway dispatch ring.
3. Sequential consistency only when you need it
Sequentially consistent ordering enforces a single total order over all such operations across all threads. It is the safest choice and the most expensive. Reach for it when correctness depends on every thread observing the same interleaving, such as Dekker-style mutual-exclusion or multi-flag handshake protocols.
Reference snippet
std::atomic<Payload*> head{nullptr};
// Producer
auto* node = new Payload{...};
head.store(node, std::memory_order_release);
// Consumer
auto* node = head.load(std::memory_order_acquire);
if (node) {
// All writes the producer made
// before the release are visible here.
process(node);
}