Lock-Free Multi-Producer-Single-Consumer-Ringpuffer (MPSC), der zusammenhängende Bereichsvorgänge unterstützt und für das Bestehen von Nachrichten bequem verwendet werden kann. Die Implementierung ist in C11 geschrieben und unter der 2-Klausel-BSD-Lizenz verteilt.
int ringbuf_setup(ringbuf_t *rbuf, unsigned nworkers, size_t length)
rbuf ist ein Zeiger auf das undurchsichtige Ringpufferobjekt; Der Anrufer ist dafür verantwortlich, den Raum für dieses Objekt zuzuweisen. Normalerweise wird das Objekt dynamisch zugewiesen, wenn Threads verwendet oder in einem gemeinsam genutzten Speicher reserviert werden, wenn es mit Prozessen blockiert ist. Die Zuordnungsgröße für das Objekt muss mit der Funktion ringbuf_get_sizes erhalten werden. Gibt 0 über den Erfolg und -1 zurück. void ringbuf_get_sizes(unsigned nworkers, size_t *ringbuf_obj_size, size_t *ringbuf_worker_size)
ringbuf_t und optional ringbuf_worker_t Strukturen zurück. Die Größe der ringbuf_t -Struktur hängt von der Anzahl der Arbeitnehmer ab, die vom Parameter nworkers angegeben ist. ringbuf_worker_t *ringbuf_register(ringbuf_t *rbuf, unsigned i)
i ist eine Arbeiternummer, beginnend von Null (dh im Setup verwendet als nworkers ). Beim Erfolg gibt ein Zeiger auf einen undurchsichtigen ringbuf_worker_t strukturiert zurück, der Teil des Speicherblocks ringbuf_t ist. Rücksende kehrt NULL zurück. void ringbuf_unregister(ringbuf_t *rbuf, ringbuf_worker_t *worker)
ssize_t ringbuf_acquire(ringbuf_t *rbuf, ringbuf_worker_t *worker, size_t len)
ringbuf_produce muss aufgerufen werden, um dies anzuzeigen. Verschachtelte Erwerbsanrufe sind nicht erlaubt. void ringbuf_produce(ringbuf_t *rbuf, ringbuf_worker_t *worker)
size_t ringbuf_consume(ringbuf_t *rbuf, size_t *offset)
ringbuf_release -Funktion aufgerufen werden, um dies anzuzeigen. void ringbuf_release(ringbuf_t *rbuf, size_t nbytes)
Der Verbraucher gibt einen zusammenhängenden Block der produzierten Bereiche zurück, dh der ringbuf_consume -Anruf gibt keine Teilbereiche zurück. Wenn Sie den produzierten Bereich als Nachricht betrachten, gibt der Verbraucher einen Nachrichtenblock zurück und endet immer an der Nachrichtengrenze. Ein solches Verhalten ermöglicht es uns, diese Ringpufferimplementierung als Nachrichtenwarteschlange zu verwenden.
Die Implementierung wurde ausführlich auf einer 24-Kern-X86-Maschine getestet, siehe den Spannungstest für Details zur Technik. Es liefert auch ein Beispiel, wie der Mechanismus für das Bestehen von Nachrichten verwendet werden kann.
Diese Implementierung von Ringpuffer bietet immer einen zusammenhängenden Raumbereich für den Hersteller. Es wird durch einen frühen Wickel erreicht, wenn der angeforderte Bereich nicht am Ende passen kann. Dies impliziert, dass der ringbuf_acquire -Aufruf möglicherweise fehlschlägt, wenn der angeforderte Bereich größer als die Hälfte der Puffergröße ist. Daher kann es erforderlich sein, sicherzustellen, dass die Ringpuffergröße mindestens doppelt so groß ist wie die maximale Produktionseinheitgröße.
Es ist auch zu beachten, dass einer der Kompromisse eines solchen Designs darin besteht, dass der Verbraucher derzeit einen O (N) -Scan auf der Liste der Hersteller durchführt.
Produzenten:
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 );
}Verbraucher:
if (( len = ringbuf_consume ( r , & off )) != 0 ) {
process ( & buf [ off ], len );
ringbuf_release ( r , len );
}