- Notifications
You must be signed in to change notification settings - Fork68
Lock-free ring buffer (MPSC)
License
rmind/ringbuf
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
Lock-free multi-producer single-consumer (MPSC) ring buffer which supportscontiguous range operations and which can be conveniently used for messagepassing. The implementation is written in C11 and distributed under the2-clause BSD license.
int ringbuf_setup(ringbuf_t *rbuf, unsigned nworkers, size_t length)- Setup a new ring buffer of a givenlength. The
rbufis a pointerto the opaque ring buffer object; the caller is responsible to allocatethe space for this object. Typically, the object would be allocateddynamically if using threads or reserved in a shared memory blocked ifusing processes. The allocation size for the object shall be obtainedusing theringbuf_get_sizesfunction. Returns 0 on success and -1on failure.
- Setup a new ring buffer of a givenlength. The
void ringbuf_get_sizes(unsigned nworkers, size_t *ringbuf_obj_size, size_t *ringbuf_worker_size)- Returns the size of the opaque
ringbuf_tand, optionally,ringbuf_worker_tstructures.The size of theringbuf_tstructure depends on the number of workers,specified by thenworkersparameter.
- Returns the size of the opaque
ringbuf_worker_t *ringbuf_register(ringbuf_t *rbuf, unsigned i)- Register the current worker (thread or process) as a producer. Eachproducer MUST register itself. The
iis a worker number, startingfrom zero (i.e. shall be thannworkersused in the setup). On success,returns a pointer to an opaqueringbuf_worker_tstructured, which isa part of theringbuf_tmemory block. On failure, returnsNULL.
- Register the current worker (thread or process) as a producer. Eachproducer MUST register itself. The
void ringbuf_unregister(ringbuf_t *rbuf, ringbuf_worker_t *worker)- Unregister the specified worker from the list of producers.
ssize_t ringbuf_acquire(ringbuf_t *rbuf, ringbuf_worker_t *worker, size_t len)- Request a space of a given length in the ring buffer. Returns theoffset at which the space is available or -1 on failure. Once the datais ready (typically, when writing to the ring buffer is complete), the
ringbuf_producefunction must be called to indicate that. Nestedacquire calls are not allowed.
- Request a space of a given length in the ring buffer. Returns theoffset at which the space is available or -1 on failure. Once the datais ready (typically, when writing to the ring buffer is complete), the
void ringbuf_produce(ringbuf_t *rbuf, ringbuf_worker_t *worker)- Indicate that the acquired range in the buffer is produced and is readyto be consumed.
size_t ringbuf_consume(ringbuf_t *rbuf, size_t *offset)- Get a contiguous range which is ready to be consumed. Returns zeroif there is no data available for consumption. Once the data isconsumed (typically, when reading from the ring buffer is complete),the
ringbuf_releasefunction must be called to indicate that.
- Get a contiguous range which is ready to be consumed. Returns zeroif there is no data available for consumption. Once the data isconsumed (typically, when reading from the ring buffer is complete),the
void ringbuf_release(ringbuf_t *rbuf, size_t nbytes)- Indicate that the consumed range can now be released and may now bereused by the producers.
The consumer will return a contiguous block of ranges produced i.e. theringbuf_consume call will not return partial ranges. If you think ofproduced range as a message, then consumer will return a block of messages,always ending at the message boundary. Such behaviour allows us to usethis ring buffer implementation as a message queue.
The implementation was extensively tested on a 24-core x86 machine,seethe stress test for the details on the technique.It also provides an example how the mechanism can be used for messagepassing.
This ring buffer implementation always provides a contiguous range ofspace for the producer. It is achieved by an early wrap-around if therequested range cannot fit in the end. The implication of this is thattheringbuf_acquire call may fail if the requested range is greaterthan half of the buffer size. Hence, it may be necessary to ensure thatthe ring buffer size is at least twice as large as the maximum productionunit size.
It should also be noted that one of the trade-offs of such design is thatthe consumer currently performs an O(n) scan on the list of producers.
Producers:
if ((w=ringbuf_register(r,worker_id))==NULL)err(EXIT_FAILURE,"ringbuf_register")...if ((off=ringbuf_acquire(r,w,len))!=-1) {memcpy(&buf[off],payload,len);ringbuf_produce(r,tls);}
Consumer:
if ((len=ringbuf_consume(r,&off))!=0) {process(&buf[off],len);ringbuf_release(r,len);}
About
Lock-free ring buffer (MPSC)
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.