Boost Library

They have Expected.

Mainly used Boost when I tried to write ROS from scratch, trying to create an IPC framework.

Why did the Boost library come from? Why is it not part of STL?

  • The Boost library originated as a collection of high-quality, peer-reviewed C++ libraries aimed at extending the functionality of the Standard Template Library (STL) and providing solutions to common programming problems. It was established in 1998 by a group of C++ developers who saw a need for reusable components that were not covered by the STL or the C++ Standard Library at the time.

Boost serves as a testing ground for new features that might later be considered for inclusion in the C++ Standard Library. For example:

  • Smart pointers (shared_ptr, unique_ptr) first appeared in Boost and were later incorporated into C++11.
  • Regex library was in Boost before becoming part of the standard in C++11.
  • Boost.Asio (for asynchronous I/O) influenced the std::async features introduced in later versions of C++.

Resources

Sharing information between processes

  • In the traditional programming model an operating system has multiple processes running and each process has its own address space. To share information between processes we have several alternatives:

Two processes share information using a file. To access to the data, each process uses the usual file read/write mechanisms. When updating/reading a file shared between processes, we need some sort of synchronization, to protect readers from writers. Two processes share information that resides in the kernel of the operating system. This is the case, for example, of traditional message queues. The synchronization is guaranteed by the operating system kernel. Two processes can share a memory region. This is the case of classical shared memory or memory mapped files. Once the processes set up the memory region, the processes can read/write the data like any other memory segment without calling the operating system’s kernel. This also requires some kind of manual synchronization between processes.

Video quality is bad but code is easy to understand

#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <cstring>
#include <iostream>
 
int main() {
    using namespace boost::interprocess;
 
    // Parent process: Write to shared memory
    shared_memory_object shm(create_only, "MySharedMemory", read_write);
    shm.truncate(1024);
 
    mapped_region region(shm, read_write);
    std::strcpy(static_cast<char*>(region.get_address()), "Hello from parent!");
 
    // Child process: Read from shared memory
    shared_memory_object shm_child(open_only, "MySharedMemory", read_only);
    mapped_region region_child(shm_child, read_only);
    std::cout << "Child read: " << static_cast<char*>(region_child.get_address()) << std::endl;
 
    shared_memory_object::remove("MySharedMemory");  // Clean up
    return 0;
}

Example

struct shared_memory_buffer {
    char messages[1024];  // To modify based on needs
    interprocess_mutex mutex;  // Mutex for synchronization
};

Shared Memory Object

You need to use this in combination with mappedRegion.

Some

  • shared_memory_object class from Boost Interprocess is used to represent a portion of shared memory that can be accessed by multiple processes.
  • The truncate method is called to allocate memory for the shared memory object.
  • The mapped_region class from Boost Interprocess is used to map a region of the shared memory object into the process’s address space

Publisher

#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/sync/interprocess_condition.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <chrono>
#include <cstring>
#include <iostream>
#include <string>
#include <thread>
 
#include "shared_memory/shared_memory_buffer.hpp"
 
using namespace boost::interprocess;
 
int main() {
    // Remove shared memory on exit
    struct shm_remove {
        shm_remove() { shared_memory_object::remove("SharedMemory"); }
        ~shm_remove() { shared_memory_object::remove("SharedMemory"); }
    } remover;
 
    shared_memory_object shm(create_only, "SharedMemory", read_write);
    shm.truncate(sizeof(shared_memory_buffer));
    mapped_region region(shm, read_write);
    void *addr = region.get_address();
 
    shared_memory_buffer *buffer = new (addr) shared_memory_buffer;
 
    // Continuously publish messages
    int message_count = 0;
    while (true) {
        std::this_thread::sleep_for(std::chrono::milliseconds(100));  // Simulate delay between messages
 
        std::string message = "Message " + std::to_string(message_count++);
        std::cout << message << std::endl;
 
        // Lock the buffer for writing
        scoped_lock<interprocess_mutex> lock(buffer->mutex);
 
        // // Wait if buffer is full (circular buffer)
        // while ((buffer->next_write + 1) % shared_memory_buffer::BufferSize == buffer->next_read) {
        //     buffer->cond_write.wait(lock);  // Wait until there is space to write
        // }
 
        // Write message to the buffer
        std::memset(buffer->messages, 0, sizeof(buffer->messages));  // Clear message buffer
        std::strcpy(buffer->messages, message.c_str());
        std::cout << "Publisher: Sent - " << message << std::endl;
 
        // // Update the next write index
        // buffer->next_write = (buffer->next_write + 1) % shared_memory_buffer::BufferSize;
 
        // Notify subscriber
        // buffer->cond_read.notify_one();
    }
 
    return 0;
}

Subscriber

#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/sync/interprocess_condition.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <cstring>
#include <iostream>
#include <thread>
 
#include "shared_memory/shared_memory_buffer.hpp"
 
using namespace boost::interprocess;
 
int main() {
    try {
        shared_memory_object shm(open_only, "SharedMemory", read_write);
 
        // Map the shared memory into this process
        mapped_region region(shm, read_write);
        void *addr = region.get_address();
        shared_memory_buffer *buffer = static_cast<shared_memory_buffer *>(addr);
 
        // Continuously receive messages
        while (true) {
            // Lock the buffer for reading
 
            std::cout << "reached here" << buffer->messages << std::endl;
 
            scoped_lock<interprocess_mutex> lock(buffer->mutex);
 
            // Wait if buffer is empty
            // while (buffer->next_read == buffer->next_write) {
            //     buffer->cond_read.wait(lock);  // Wait for new messages
            // }
 
            // Read the message from the buffer
            std::string message = buffer->messages;
            std::cout << "Subscriber: Received - " << message << std::endl;
 
            // // Update the next read index
            // buffer->next_read = (buffer->next_read + 1) % shared_memory_buffer::BufferSize;
 
            // // Notify publisher that space is available
            // buffer->cond_write.notify_one();
        }
    } catch (interprocess_exception &ex) {
        std::cout << "Error: " << ex.what() << std::endl;
        return 1;
    }
 
    return 0;
}

I was running into bus errors with this implementation and could attribute it to

scoped_lock<interprocess_mutex> lock(buffer->mutex);

when it came to the subscriber.

  • Ahh, it’s beca

I don’t run into this issue when using a managed shared memory object.

Managed Shared Memory

Tip

Use this if you need to dynamically allocate shared memory. However, if you don’t need, just use shared memory object.

  • The managed_shared_memory object has an internal memory allocator that automatically manages how space is allocated and reused.
  • You can dynamically allocate and deallocate memory within the segment using construct() and destroy()
    • For each segment, you need up with a new name that you use to query

Notice below that we have 2 names:

  • "SharedMemory": Shared memory name
  • "SharedBuffer": The name of the buffer

This is exactly how the memory alignment issue is abstracted away from you.

The difference

With shared_memory_object, the entire shared memory segment is treated as a raw block of memory.

In contrast, managed_shared_memory is more like a memory manager for shared memory. Instead of just being a raw block of bytes, it allows you to dynamically allocate and deallocate memory within the shared segment, similar to how a heap manager works in a regular program.

  • Other difference: When you use managed_shared_memory, you don’t explicitly specify read_write or read_only like you do with shared_memory_object. Instead, you only specify whether you’re creating or opening the shared memory segment, and Boost internally handles the memory access permissions for you.

Publisher

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/sync/interprocess_condition.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <chrono>
#include <cstring>
#include <iostream>
#include <string>
#include <thread>
 
#include "shared_memory/shared_memory_buffer.hpp"
 
using namespace boost::interprocess;
 
int main() {
    // Remove shared memory on exit
    struct shm_remove {
        shm_remove() { shared_memory_object::remove("MySharedMemory"); }
        ~shm_remove() { shared_memory_object::remove("MySharedMemory"); }
    } remover;
 
    // Create managed shared memory
    managed_shared_memory segment(create_only, "MySharedMemory", 65536);  // 64 KB
 
    // Construct shared memory buffer in managed shared memory
    shared_memory_buffer *buffer = segment.construct<shared_memory_buffer>("SharedBuffer")();
 
    // Continuously publish messages
    int message_count = 0;
    while (true) {
        std::this_thread::sleep_for(std::chrono::milliseconds(100));  // Simulate delay between messages
 
        std::string message = "Message " + std::to_string(message_count++);
        std::cout << message << std::endl;
 
        // Lock the buffer for writing
        scoped_lock<interprocess_mutex> lock(buffer->mutex);
 
        // Wait if buffer is full (you can implement this logic if necessary)
        // while ((buffer->next_write + 1) % shared_memory_buffer::BufferSize == buffer->next_read) {
        //     buffer->cond_write.wait(lock);  // Wait until there is space to write
        // }
 
        // Write message to the buffer
        std::memset(buffer->messages, 0, sizeof(buffer->messages));  // Clear message buffer
        std::strcpy(buffer->messages, message.c_str());
        std::cout << "Publisher: Sent - " << message << std::endl;
 
        // Notify subscriber (if implemented)
        // buffer->cond_read.notify_one();
    }
 
    return 0;
}

Subscription

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/sync/interprocess_condition.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <cstring>
#include <iostream>
#include <thread>
 
#include "shared_memory/shared_memory_buffer.hpp"
 
using namespace boost::interprocess;
 
int main() {
    try {
        // Open the managed shared memory
        managed_shared_memory segment(open_only, "MySharedMemory");
 
        // Find the shared buffer in the managed shared memory
        shared_memory_buffer *buffer = segment.find<shared_memory_buffer>("SharedBuffer").first;
 
        // Continuously receive messages
        while (true) {
            // Lock the buffer for reading
            // Simulate some processing delay
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
            scoped_lock<interprocess_mutex> lock(buffer->mutex);
 
            // Wait if buffer is empty (this logic can be extended as needed)
            // while (buffer->next_read == buffer->next_write) {
            //     buffer->cond_read.wait(lock);  // Wait for new messages
            // }
 
            // Read the message from the buffer
            std::string message = buffer->messages;
            std::cout << "Subscriber: Received - " << message << std::endl;
 
            // Notify publisher that space is available (if using circular buffer logic)
            // buffer->cond_write.notify_one();
        }
    } catch (interprocess_exception &ex) {
        std::cout << "Error: " << ex.what() << std::endl;
        return 1;
    }
 
    return 0;
}