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:
named_upgradable_mutex
but I am not quite sure) and Comments or good solutions are still welcome.
Interprocess simplifies the use of common interprocess communication and synchronization mechanisms and offers a wide range of them: Shared memory. Memory-mapped files. Semaphores, mutexes, condition variables and upgradable mutex types to place them in shared memory and memory mapped files.
Boost Interprocess is a header-only library, so all you need to do is include the appropriate header in your sources and make the compiler aware of the include path.
A readers/writer lock regulates access to a set of data. The readers/writer lock is so called because many threads can hold the lock simultaneously for reading, but only one thread can hold the lock for writing. Most device drivers do not use readers/writer locks. These locks are slower than mutexes.
An RW lock allows concurrent access for read-only operations, write operations require exclusive access. This means that multiple threads can read the data in parallel but an exclusive lock is needed for writing or modifying data.
The Boost.Interprocess documentation describes the so-called upgradable mutexes it supports and the upgradable mutex operations for the two supported upgradable mutex types:
boost::interprocess::interprocess_upgradable_mutex
, a non-recursive, anonymous upgradable mutex that can be placed in shared memory or memory mapped files.boost::interprocess::named_upgradable_mutex
, a non-recursive, named upgradable mutex.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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With