Interprocess reader/writer lock with Boost

10,027

The Boost.Interprocess documentation describes the so-called upgradable mutexes it supports and the upgradable mutex operations for the two supported upgradable mutex types:

EDIT: I believe this works:

#include <iostream>
#include <string>
#include <unistd.h>

#include <boost/scope_exit.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/sync/interprocess_upgradable_mutex.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <boost/interprocess/sync/sharable_lock.hpp>
#include <boost/interprocess/sync/upgradable_lock.hpp>

// http://stackoverflow.com/questions/12439099/interprocess-reader-writer-lock-with-boost/

#define SHARED_MEMORY_NAME "SO12439099-MySharedMemory"

struct shared_data {
private:
    typedef boost::interprocess::interprocess_upgradable_mutex upgradable_mutex_type;

    mutable upgradable_mutex_type mutex;
    volatile int counter;

public:
    shared_data()
        : counter(0)
    {
    }

    int count() const {
        boost::interprocess::sharable_lock<upgradable_mutex_type> lock(mutex);
        return counter;
    }

    void set_counter(int counter) {
        boost::interprocess::scoped_lock<upgradable_mutex_type> lock(mutex);
        this->counter = counter;
    }
};

int main(int argc, char *argv[])
{
    using namespace boost::interprocess;

    if (argc != 2) {
        std::cerr << "Usage: " << argv[0] << " WHICH" << std::endl;
        return 1;
    }

    const std::string which = argv[1];
    if (which == "parent") {
        shared_memory_object::remove(SHARED_MEMORY_NAME);
        shared_memory_object shm(create_only, SHARED_MEMORY_NAME, read_write);

        BOOST_SCOPE_EXIT(argc) {
            shared_memory_object::remove(SHARED_MEMORY_NAME);
        } BOOST_SCOPE_EXIT_END;

        shm.truncate(sizeof (shared_data));

        // Map the whole shared memory into this process.
        mapped_region region(shm, read_write);

        // Construct the shared_data.
        new (region.get_address()) shared_data;

        // Go to sleep for a minute.
        sleep(60);

        return 0;
    } else if (which == "reader_child") {
        shared_memory_object shm(open_only, SHARED_MEMORY_NAME, read_write);

        mapped_region region(shm, read_write);
        shared_data& d = *static_cast<shared_data *>(region.get_address());

        for (int i = 0; i < 100000; ++i) {
            std::cout << "reader_child: " << d.count() << std::endl;
        }
    } else if (which == "writer_child") {
        shared_memory_object shm(open_only, SHARED_MEMORY_NAME, read_write);

        mapped_region region(shm, read_write);
        shared_data& d = *static_cast<shared_data *>(region.get_address());

        for (int i = 0; i < 100000; ++i) {
            d.set_counter(i);
            std::cout << "writer_child: " << i << std::endl;
        }
    }
}

I tried this on a Mac with the following script:

#!/usr/bin/env sh
./a.out reader_child &
./a.out reader_child &
./a.out writer_child &
./a.out reader_child &
./a.out reader_child &

(You have to start the parent first: ./a.out parent)

The output showed interleaving of "reader_child" and "writer_child" lines (with all of the "reader_child" lines showing a non-zero value after the first "writer_child" line), so it appears to be working.

Share:
10,027

Related videos on Youtube

wpfwannabe
Author by

wpfwannabe

Updated on September 15, 2022

Comments

  • wpfwannabe
    wpfwannabe over 1 year

    This thread is gold when it comes to explaining how to implement reader/writer locks with Boost. It seems relatively simple and I really love it but it also seems to be using a non-named lock and I need an interprocess solution (doesn't need to be portable, can be Windows-only).

    Is there a way to have an interprocess shared_mutex? I see there is a named_mutex but I can't get it to work with shared_lock ot other locks.

    Any pointers are appreciated.

    [EDIT]

    In the meantime, I have come across this thread which almost hits the nail on the head. I have two issues:

    1. it doesn't show complete code (I am guessing I need to use named_upgradable_mutex but I am not quite sure) and
    2. I don't like the answer for the modified "writer" which uses no off the shelf class that does unlocking in destructor but a sequence of 3 raw calls on the mutex.

    Comments or good solutions are still welcome.

  • wpfwannabe
    wpfwannabe over 11 years
    Thanks! Does the trick for me even though it is not exactly what I was looking for it cleared some doubts.
  • Bryan Fok
    Bryan Fok almost 8 years
    why do you use volatile when counter is guarded by a lock?
  • Daniel Trebbien
    Daniel Trebbien almost 8 years
    @BryanFok: I used volatile because I didn't see any memory barrier guarantees when locking an interprocess_upgradable_mutex. There are three implementations: posix, windows, and spin. If using the posix or windows implementations, I don't think volatile is needed because pthread_mutex_lock() / WaitForSingleObject() is called behind the scenes. The spin implementation uses an atomic CAS operation, and I think that there are architectures where CAS does not have memory barrier guarantees. See this answer, for example.
  • Tobi Akinyemi
    Tobi Akinyemi almost 4 years