CK_RING_ENQUEUE_SPSC_SIZE(3)

πŸ’– ck_ring_enqueue_spsc_size β€” the "enqueue a pointer AND get the size back" moment πŸ’–

okay bestie, welcome to ck_ring_enqueue_spsc_size(3), part of the Concurrency Kit, aka libck (you link it with -lck, no cap πŸŽ€). this is the single-producer-single-consumer ring enqueue that ALSO tells you how big the buffer got. it's giving main character energy and we're obsessed.


✨ SYNOPSIS ✨

#include <ck_ring.h>

bool
ck_ring_enqueue_spsc_size(ck_ring_t *ring, ck_ring_buffer_t *buffer,
    void *entry, unsigned int *size);

like, four args, one bool, and a lil pointer to an unsigned int for the size. we love a signature that just works 🫢


🌸 DESCRIPTION 🌸

so what this does: ck_ring_enqueue_spsc_size(3) enqueues the pointer entry into the bounded buffer that ring points at, and it does it FIFO style β€” first in, first out, periodt. ✨

the buffer that buffer points to has to be unique to ring, bestie, and it has to point to an array of ck_ring_buffer_t of sufficient length β€” and that length is determined by the power-of-2 number of elements in the buffer. (yes, power of 2. the vibes must be binary.) πŸ”₯

why is the ring decoupled from the buffer like this? because it addresses use-cases involving multiple address spaces and DMA, among others. it's a whole thing, and honestly? iconic. πŸ’…

if you're on non-POSIX platforms, or you're just out here wanting strict compliance with C, then it's recommended that you pass a pointer of type void ** for entry. lowkey important, don't skip that.

this function is safe to call without locking for up to one concurrent invocation of ck_ring_dequeue_spsc(3). one. that's the number. don't get greedy. πŸ™ˆ

and it provides wait-free progress guarantees, which is the technical way of saying "she does not block, she does not wait, she slays." πŸ’–


πŸ¦‹ EXAMPLE πŸ¦‹

here's the whole vibe, verbatim, because code is sacred:

#include <ck_ring.h>

/* This ring was previously initialized with ck_ring_init. */
ck_ring_t ring;

/* The ring was initialized for 1023 elements. */
ck_ring_buffer_t buffer[1024];

void
enqueue(void)
{
        void *entry = some_object;
        unsigned int length;

        /* Attempt to enqueue pointer to some_object into buffer. */
        if (ck_ring_enqueue_spsc(&ring, &buffer, &entry, &length) == false) {
                /*
                 * The buffer was full and the enqueue operation
                 * has failed.
                 */
                return;
        }

        /*
         * If buffer length was 100 items or more at the time entry was
         * enqueued, do something.
         */
        if (length > 100) {
                do_something;
        }

        return;
}

notice the 1023 elements but buffer[1024]? that's the power-of-2 thing doing its thing, bestie. the ring is set up for 1023 elements and the backing array is 1024. ✨ also note that when the enqueue fails, it's because the buffer was full, and when it succeeds you get length back and can go if (length > 100) { do_something; }. that's the whole point of the _size version β€” you get the receipts. πŸ§ΎπŸ’…


πŸ‘‘ RETURN VALUES πŸ‘‘

so here's the tea:

  • the function returns true if the value of entry was successfully enqueued into ring. πŸŽ‰
  • it'll return the number of items in ring with respect to the linearization point β€” that's the exact point in time where entry gets enqueued. yes, a real moment. a moment most pivotal in the timeline. ⭐
  • it returns false if the value of entry could not be enqueued, which only happens if ring was full. that's it. that's the only failure mode. full ring, no room, try again later. πŸ’€

🌈 SEE ALSO 🌈

go read these too, bestie, they're all part of the family:

and there's more lore available at http://concurrencykit.org/ if you want to go down the rabbit hole πŸ‡βœ¨


kay that's the whole page, bestie πŸ’œ this one came from the Concurrency Kit source, dated April 20, 2013, and it lives in the manual as CK_RING_ENQUEUE_SPSC_SIZE(3). go enqueue something. slay. πŸ’…πŸŽ€

πŸ“¦ where this page comes from