Skip to content
Jonathan Beard edited this page Aug 7, 2021 · 9 revisions

What is ipc::buffer and how to use

This is a sketch on how to use the buffer. Eventually we'll finish the full documentation, until then, this serves as a high-level on how to use this piece of software. The test cases and README documentation also serve as points of reference for syntax and behavior.

What is a buffer object

  • a buffer object is a multi-channel, multi-process, multi-threaded dynamic allocator that allows the programmer to allocate an object, then create channels.
auto *buffer = ipc::buffer::initialize( "thehandle"  );

where the programmer creates a buffer object that has the name "thehandle".

Per thread data structures

Each thread in each process should create a thread-local-storage (TLS) structure using this function and syntax:

ipc::buffer::get_tls_structure( buffer, thread_id );

Each thread should have it's own TLS, these are opened for each thread within each process.

Creating channels

The programmer creates a buffer object, then creates channels on top of that object, to create a single-producer, single consumer channel:

ipc::buffer::add_spsc_lf_channel( tls_producer, 
                                          channel_id_a )

Allocating memory

Once channels are created, memory can be allocated in each process.

int *output = 
   (int*) ipc::buffer::allocate_record( tls_producer, 
                                        sizeof( int ), 
                                        channel_id_a );

This memory is allocated dynamically from the shared memory heap. Memory can be allocated in any order from any channel. A return of nullptr indicates that the buffer object is out of memory.

Each channel is a single-producer, single-consumer lock-free queue that can be multi-thread or multi-process.

Sending memory

Memory is sent and received from producers on a channel (e.g., channel_id_a). As an example, a producer would

while( ipc::buffer::send_record(    tls_producer, 
                                    channel_id_a, 
                                    (void**)&output ) != ipc::tx_success );

The while loop is needed because send_record can fail due to the lock_free point-to-point connection. The send_record function sends data from the producer on a channel to the waiting consumer on a channel. This is queued up within the buffer for the target to receive.

Receiving data

To receive data, a consumer would do:

while( ipc::buffer::receive_record(     tls_consumer, 
                                        channel_id, 
                                        &record ) != ipc::tx_success );

where the consumer waits on data from the target channel and the given buffer registered with tls_consumer.

Freeing data

On either the producer or consumer side, freeing data back to the buffer is accomplished via ipc::buffer::free_record( tls, record ) where tls is the thread local storage struct (TLS) and record is the pointer returned by allocate_record.

Removing channels

Removing individual channels to free up internally allocated memory is preferred when channels are no longer being used by the calling thread. To do this, call

ipc::buffer::remove_channel( tls, channel_id );

where tls is a valid thread local storage object and channel_id is the specific channel you want to remove from this thread local storage object, and globally (note: channels are reference counted, which means if someone is using the channel, it won't be immediately deleted).

Freeing thread local storage

On thread exit, they should close the TLS structure that they've allocated. closing the TLS structure will deallocate any pre-allocations that the thread/channel combo has made on your behalf and it will also unlink all channels associated with this thread.

ipc::buffer::close_tls_structure( tls_consumer );

Destructing the buffer

On exit (per process), the programmer should call the destruct function that will decrease the reference count on the shared memory buffer and if the reference count is zero then it will unlink and clear the shared memory region that backs this buffer.

ipc::buffer::destruct( buffer, "thehandle", true );