CloudtoidCloudtoid / interprocess
Interprocess/Docs/Queue concepts

Queue lifetime, delivery, and interoperability

One queue, multiple publishers, competing subscribers. These rules apply across every language binding.

Queue identity

All participants must use the same name, capacity, and Unix backing directory. Windows ignores the path; its mapping and lease objects are session-local, so participants must share a Windows session and compatible permissions.

Use an explicit absolute path for cross-language Unix applications: runtime temporary directories can differ. Names must also be unique across directories because the notification semaphore uses the name, not the backing path.

Transient lifetime

A queue remains usable while at least one publisher or subscriber is attached. Closing a reader does not destroy a queue retained by other participants. After all endpoints close or their processes exit, unread messages are lost. Opening the same name again creates a fresh, empty queue.

On Unix, stale files may remain after a crash until the next opener cleans them up. Their presence does not mean that messages can be resumed.

Delivery and ordering

Subscribers compete for messages: under ordinary operation, each message is consumed by one subscriber. This is a work-sharing queue, not broadcast. To deliver a copy to every consumer, use separate queues.

Delivery follows reservation order. An unfinished earlier reservation can delay later messages. Successful sequential sends preserve order; concurrent calls have no defined relative order. Batch sends commit a prefix and may interleave with other publishers; a batch is not a transaction.

The queue transports bytes, without a serializer. Agree on encoding, schema, and byte order in your application. Empty messages are valid. Distinguish an empty payload from the API's “no message” result.

Capacity and backpressure

Capacity is the circular message buffer size in bytes. It must exceed 16 and be divisible by 8. Total mapped storage is 262400 + capacity bytes, including the header and publisher table. Each queue supports up to 2,048 connected publisher objects.

Records include metadata and alignment, so the entire buffer is not available for payload bytes. A nonblocking send can report unavailable because space is exhausted or recovery temporarily blocks admission. Choose a retry deadline, bounded backoff, or an application-level overflow policy. Retrying cannot make an inherently oversized message fit.

Receive-into APIs copy at most the supplied buffer length. An undersized buffer truncates and consumes the message; the remainder cannot be read later. Size reusable buffers for the largest payload you accept.

Waiting and cancellation

“Try” methods do not wait for a message or free space. An unavailable receive may also mean a reader owns consumption or the next record is not ready. Open, close, recovery checks, and wrapper synchronization can still involve operating-system work.

Rust and C offer blocking receives with timeouts. Python waits with the GIL released and checks Python signals between bounded waits. .NET accepts a cancellation token on blocking Dequeue. Node.js and Go check immediately, then use adaptive timer backoff from 1 to 10 ms while idle. Their idle-to-active latency can include that interval plus scheduling delay. Prefer one receive loop per subscriber.

Crashes and recovery

Recovery reclaims abandoned work only after checking participant liveness. Paused live owners are not expired just because time passes. Messages can be lost during recovery, including completed messages behind an abandoned reservation. The queue does not provide durable acknowledgements or exactly-once effects.

Publisher reservations use native 64-bit atomics. The complete queue is not formally lock-free: readers serialize consumption, live paused owners can block progress, and creation and destruction use OS locks. Open native endpoints after fork(); do not use inherited endpoints in the child.

Upgrades and long-lived queues

v3 is incompatible with the v1/v2 shared-memory layout. Drain the old queue, stop all participants, and upgrade them together using a fresh queue. The physical buffer wraps; logical counters do not. Counter exhaustion is an explicit error and requires a fresh queue after participants finish.

For layout offsets, atomic ordering, and recovery details, read the full protocol v3 specification.