Concurrency safety at a glance
Adding external synchronization
For Queue, Stack, OrderedMap, and Set, wrap every access in a mutex.sync.RWMutex if reads (e.g., Len() on a Countable) vastly outnumber writes and you need maximum read throughput. For the common case, a plain sync.Mutex is sufficient and easier to reason about.
Alternatively, confine the collection to a single goroutine and communicate through channels—the standard Go concurrency pattern.
WaitableQueue
WaitableQueue is the purpose-built solution for concurrent producer-consumer pipelines. It is:
- Unbounded:
Enqueuenever blocks, unlike a buffered channel that back-pressures the producer when full. - Signal-based: consumers wait on a channel returned by
WaitChan()rather than spinning or polling. - Flow-aware: watermark states let producers throttle themselves before the queue grows without bound.
When to use WaitableQueue vs channels
Use
WaitableQueue when:
- You cannot afford to block the producer.
- You want explicit, graduated flow control (warn at high watermark, shed load near saturation).
- The queue must remain responsive under bursty load.
- Back-pressure on the producer is acceptable or desirable.
- You want the simplest possible implementation.
Watermark states
WaitableQueue reports one of five states after every Enqueue or Dequeue:
Do not use
QueueIsBelowLowWatermark as the sole signal to resume a fully-stopped producer. If the producer has stopped calling Enqueue, it will never observe the state change and will never restart. Check watermark state on the consumer (Dequeue) side for reliable flow control, or poll periodically.Constructor parameters
highWatermark is computed automatically from initialCapacity and highWatermark. You do not need to configure it directly.
Producer-consumer example
The pattern below shows a single producer and a single consumer. Scale to multiple goroutines by sharing the sameWaitableQueue reference—it is safe to call Enqueue and Dequeue (and WaitChan) concurrently from any number of goroutines.
Closing the queue
Callq.Close() from the producer when no more items will be enqueued. This closes the signal channel returned by WaitChan(), permanently unblocking any consumer waiting on it. Consumers should drain remaining items after observing a closed channel.
Using Len() safely
WaitableQueue implements container.Countable, so you can call Len() to inspect the current size. However, Len() acquires a separate mutex lock and its result is not atomic with respect to concurrent Enqueue or Dequeue calls.