One Size Fits All: Linked Ring instead of a Ring Buffer

It's just in place replacement for ring buffer, but without taking up all that extra space for multiply buffers used by different producers-consumers.

In this article, we explore the benefits of using Linked Ring buffers over traditional Circular Buffers. We delve into the topic of memory allocation and discuss how Linked Ring buffers can efficiently handle multiple producers. The Linked Ring buffer implementation can be found here.

Memory Allocation for Linked Ring Buffers

Before we jump into the specifics, let’s clarify one thing: Linked Ring Buffers will consume more memory than Circular Buffers, but the magic unfolds when multiple producers get involved.

Now, let’s say you are dealing with multiple (owners_nr) consumers. You just have multiple Circular Buffers of CELLS_NR cells (owners_nr * CELLS_NR). You might wonder about memory allocation for Linked Ring Buffer in multiple producers scenario. The beauty is that it remains the same.

Spaghetti code that allocate a lot of ring buffers

The Backbone of Linked Ring: Linkages

The backbone of Linked Ring? Linkages! Imagine a circle where each point is connected to the next, forming a ring-like data structure. However, the twist in the tale — instead of an array storing items one by one like the traditional ring buffer comrades, our hero uses a linked list.

Linked Ring initialization

Dual Role of Allocated Cells

And that’s not all! Besides data storage, the allocated cells play another vital role — they store owner information. Essentially, each cell doubles up as a data store and a mini-directory, helping us keep track of the owners and their corresponding data.

Adding data to a buffer

Here’s how it works: Each owner cell carries the information about its owner and sports a link to the last added cell. This nifty feature allows us to quickly compute the head and tail of each owner’s list. So, in one compact package, we have both the data and the necessary map to navigate through it — quite a space and computation-time saver, don’t you think?

Benefits of Linked Ring Buffers

Why is this remarkable? In traditional ring buffer systems, every producer required a separate buffer. As we had more and more producers, the complexity grew like a weed, hogging memory and making it a nightmare to manage.

Adding data from a second producer

In contrast, Linked Ring treats all producers equally and gives them a shared space in our single buffer ring — no more wasteful heap of separate buffers for each producer. Everyone gets to share the buffer equivalent of community gardens, making it a far more collaborative, efficient, and harmonious data environment.

Adding data from a third producer

Let’s bring some more clarity by visualizing a scenario. Assume we have three owners with the Linked Ring buffer. For each data addition, the producer’s corresponding owner cell updates with new data and the link to the newly added cell. This way, fetching the data becomes a breeze. All we need to do is access the owner cell, and we can swiftly traverse through all the data points belonging to that producer.

In essence, the Linked Ring buffer transforms the daunting task of managing multiple producer data requirements into an effortless walk in the park, saving significantly on memory, enhancing computational efficiency, and guaranteeing systematic and well-organized data handling.

Linked Ring Implementation in C

If you’re interested in exploring a ready-to-use open-source implementation of the Linked Ring buffer in C, you can check out the my GitHub repository: Linked Ring — C Implementation.

Library Description

The library provides a C implementation of the Linked Ring buffer. It includes two important data structures:

  1. struct linked_ring: This struct represents the Linked Ring buffer itself. It contains pointers to the cells that make up the buffer, information about the buffer's size, the current write positions, and the owners of the elements within the buffer.

  2. struct lr_cell: This struct represents an element in the buffer. Each element consists of a data field and a pointer to the next element in the buffer.

Functionality and Utility

The library offers several functions to initialize and manipulate linked ring buffers. Some of these functions include:

  • lr_init(): This function initializes a new linked ring buffer.

  • lr_put(): It adds an element to the end of the buffer.

  • lr_get(): This function removes an element from the front of the buffer for a specific owner.

Additionally, the library provides utility functions like:

  • lr_count(): It returns the number of elements currently present in the buffer.

  • lr_exists(): This function checks whether an element with a specific owner is present in the buffer.

  • lr_set_mutex(): It sets the mutex for thread-safe operations, ensuring the buffer can be safely used in multi-threaded environments.

Feel free to explore the repository for more details and examples on how to use the Linked Ring buffer implementation in C.