| PCQ(9) | Kernel Developer's Manual | PCQ(9) |
pcq —
producer/consumer queue
#include
<sys/pcq.h>
pcq_t *
pcq_create(size_t
maxlen, km_flags_t
kmflags);
void
pcq_destroy(pcq_t
*pcq);
void *
pcq_get(pcq_t
*pcq);
size_t
pcq_maxitems(pcq_t
*pcq);
void *
pcq_peek(pcq_t
*pcq);
bool
pcq_put(pcq_t
*pcq, void
*item);
The machine-independent pcq interface
provides lockless producer/consumer queues. A queue
(pcq_t) allows multiple writers (producers), but only
a single reader (consumer). The consumer is expected to be protected by a
lock that covers the structure that the pcq_t is
embedded into (e.g., socket lock, ifnet hwlock). These queues operate in a
first-in, first-out (FIFO) manner. The act of inserting or removing an item
from a pcq_t does not modify the item in any way.
pcq does not prevent an item from being inserted
multiple times into a single pcq_t.
pcq_create(maxlen,
kmflags)KM_SLEEP, if pcq_create()
is allowed to sleep until resources are available, or
KM_NOSLEEP if it should return
NULL immediately, if resources are
unavailable.pcq_destroy(pcq)pcq_get(pcq)NULL. The caller must
prevent concurrent gets from occurring.pcq_maxitems(pcq)pcq_peek(pcq)NULL.pcq_put(pcq,
item)false; otherwise, return
true. The item must not have the value of
NULL.Any memory operations sequenced before
pcq_put() of an item in one thread happen before all
memory operations with data dependencies on the item returned by
pcq_get() or pcq_peek() in
another thread. For example:
int mumble; /* producer */ mumble = 42; // A foo->x = 123; // B refcnt = foo->refcnt; // C pcq_put(pcq, foo); KASSERT(refcnt == 0); /* consumer */ foo = pcq_get(pcq); if (foo == NULL) return; atomic_inc_uint(&foo->refcnt); // D x = foo->x; // E if (x == 123) KASSERT(mumble == 42); // F
In this example, memory operations B and C
happen-before D and E. However, no ordering is guaranteed for A or F
relative to any other memory operations, because the memory location of
mumble is independent of the pointer
foo returned by
pcq_get().
If you must guarantee A happens before F, then on
the consumer side, after
pcq_get()
or
pcq_peek(),
you can call
membar_acquire()
to turn it into an acquire operation instead of a consume operation;
pcq_put()
serves as the matching release operation. (This is a little dicey. Perhaps
there should be separate
pcq_peek_acq()
and
pcq_get_acq()
operations if this semantics is necessary.)
The pcq interface is implemented within
the file sys/kern/subr_pcq.c.
The pcq interface first appeared in
NetBSD 6.0.
| January 22, 2012 | NetBSD 11.0 |