How to do it...

In this recipe, we will modify an application we created in Chapter 6Memory Management, for exchanging data between two processors using a shared memory region.

  1. In your ~/test working directory, create a subdirectory called shmatomic.
  2. Use your favorite text editor to create a shmatomic.cpp file in the shmatomic subdirectory.
  3. We reuse the shared memory data structure we created in the shmem application. Put the common headers and constants into the shmatomic.cpp file: 
#include <atomic>
#include <iostream>
#include <chrono>
#include <thread>

#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>

const char* kSharedMemPath = "/sample_point";
  1. Next, start defining the templated SharedMem class:
template<class T>
class SharedMem {
int fd;
T* ptr;
const char* name;

public:
  1. The class will have a constructor, a destructor, and a getter method. Let's add the constructor:
    SharedMem(const char* name, bool owner=false) {
fd = shm_open(name, O_RDWR | O_CREAT, 0600);
if (fd == -1) {
throw std::runtime_error("Failed to open a shared
memory region");
}
if (ftruncate(fd, sizeof(T)) < 0) {
close(fd);
throw std::runtime_error("Failed to set size of a shared
memory region");
};
ptr = (T*)mmap(nullptr, sizeof(T), PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (!ptr) {
close(fd);
throw std::runtime_error("Failed to mmap a shared memory
region");
}
this->name = owner ? name : nullptr;
}
  1. The simple destructor and the getter follow:
~SharedMem() {
munmap(ptr, sizeof(T));
close(fd);
if (name) {
std::cout << "Remove shared mem instance " << name << std::endl;
shm_unlink(name);
}
}

T& get() const {
return *ptr;
}
};
  1. Now, we define the data type we will use for data exchange and synchronization:
struct Payload {
std::atomic_bool data_ready;
std::atomic_bool data_processed;
int index;
};
  1. Next, we define a function that will generate data:
void producer() {
SharedMem<Payload> writer(kSharedMemPath);
Payload& pw = writer.get();
if (!pw.data_ready.is_lock_free()) {
throw std::runtime_error("Flag is not lock-free");
}
for (int i = 0; i < 10; i++) {
pw.data_processed.store(false);
pw.index = i;
pw.data_ready.store(true);
while(!pw.data_processed.load());
}
}
  1. It is followed by the function that consumes the data:
void consumer() {
SharedMem<Payload> point_reader(kSharedMemPath, true);
Payload& pr = point_reader.get();
if (!pr.data_ready.is_lock_free()) {
throw std::runtime_error("Flag is not lock-free");
}
for (int i = 0; i < 10; i++) {
while(!pr.data_ready.load());
pr.data_ready.store(false);
std::cout << "Processing data chunk " << pr.index << std::endl;
pr.data_processed.store(true);
}
}
  1. Finally, we add our main function, which ties everything together:
int main() {

if (fork()) {
consumer();
} else {
producer();
}
}
  1. Create a file called CMakeLists.txt in the loop subdirectory, with the following content:
cmake_minimum_required(VERSION 3.5.1)
project(shmatomic)
add_executable(shmatomic shmatomic.cpp)

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)

SET(CMAKE_CXX_FLAGS "--std=c++11")
target_link_libraries(shmatomic pthread rt)

set(CMAKE_CXX_COMPILER /usr/bin/arm-linux-gnueabi-g++)

You can build and run the application.