How it works...

In our application, we reuse the templated SharedMem class we introduced in Chapter 6Memory Management. This class is used to store an element of a specific type in a shared memory region. Let's quickly recap how it works.

The SharedMem class is a wrapper on top of the Portable Operating System Interface (POSIX) shared memory API. It defines three private data fields to hold system-specific handlers and pointers, and exposes a public interface consisting of two functions:

  • A constructor function that accepts the name of a shared region and the ownership flag
  • get method that returns a reference to the object stored in shared memory

The class also defines a destructor that performs all operations needed to properly close the shared object. As a result, the SharedMem class can be used for safe resource management using the C++ RAII idiom.

The SharedMem class is a templated class. It is parameterized by the data type we want to store in the shared memory. For this purpose, we define a structure called Payload:

struct Payload {
std::atomic_bool data_ready;
std::atomic_bool data_processed;
int index;
};

It has an index integer variable that we are going to use as a data exchange field, and two atomic Boolean flags, data_ready and data_processed, that are used for data synchronization.

We also define two functions, producer and consumer, that will work in separate processes and exchange data between each other using a shared memory region.

The producer function is producing data chunks. Firstly, it creates an instance of the SharedMem class, parametrized by the Payload data type. It passes a path to the shared memory region to the SharedMem constructor:

SharedMem<Payload> writer(kSharedMemPath);

After the shared memory instance is created, it gets the reference to the payload data stored there and checks whether any of the atomic flags we defined in the Payload data type are lock-free:

if (!pw.data_ready.is_lock_free()) {
throw std::runtime_error("Flag is not lock-free");
}

The function produces 10 chunks of data in a loop. An index of the chunk is put into the index field of the payload:

pw.index = i;

However, besides putting the data into shared memory, we need to synchronize access to this data. This is when we use our atomic flags.

For each iteration, before updating the index field, we reset the data_processed flag. After the index is updated, we set the data ready flag, which is an indicator to the consumer that a new chunk of data is ready, and wait till the data is processed by the consumer. We loop until the data_processed flag becomes true, and then go to the next iteration:

pw.data_ready.store(true);
while(!pw.data_processed.load());

The consumer function works in a similar way. Since it works in a separate process, it opens the same shared memory region by creating an instance of the SharedMem class using the same path. We also make the consumer function the owner of the shared memory instance. It means it is responsible for removing the shared memory region after its instance of SharedMem is destroyed:

SharedMem<Payload> point_reader(kSharedMemPath, true);

Similarly to the producer function, the consumer function checks whether an atomic flag is lock-free, and enters the loop of data consumption.

For each iteration, it waits in a tight loop until the data is ready:

while(!pr.data_ready.load());

After the producer function sets the data_ready flag to true, the consumer function can safely read and process data. In our implementation, it only prints the index field to the console. After the data is processed, the consumer function indicates this by setting the data_processed flag to true:

pr.data_processed.store(true);

This triggers the next iteration of data production on the producer function side:

As a result, we can see a deterministic output of processed data chunks, with no omissions or duplications; this is common in cases where data access is not synchronized.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.245.196