PCQ(9)

PCQ(9) | Kernel Developer's Manual | PCQ(9)

# NAME

`pcq` — producer/consumer queue

# SYNOPSIS

`#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);

# DESCRIPTION

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.

# FUNCTIONS

`pcq_create`(maxlen, kmflags)
  Create a queue that can store at most maxlen items at one time. kmflags should be either `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)
  Free the resources held by pcq.
`pcq_get`(pcq)
  Remove the next item to be consumed from the queue and return it. If the queue is empty, return `NULL`. The caller must prevent concurrent gets from occurring.
`pcq_maxitems`(pcq)
  Return the maximum number of items that the queue can store at any one time.
`pcq_peek`(pcq)
  Return the next item to be consumed from the queue but do not remove it from the queue. If the queue is empty, return `NULL`.
`pcq_put`(pcq, item)
  Place an item at the end of the queue. If there is no room in the queue for the item, return `false`; otherwise, return `true`. The item must not have the value of `NULL`.

## Memory ordering

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.)

# CODE REFERENCES

The `pcq` interface is implemented within the file sys/kern/subr_pcq.c.

# SEE ALSO

atomic_ops(3), queue(3)

# HISTORY

The `pcq` interface first appeared in NetBSD 6.0.

January 22, 2012 | NetBSD 11.0