I do have to dive into concurrency with C++11 at the moment and I am having a very hard time to get a basic producer/consumer to work with Boost.Interprocess using shared memory. I've already tried a lot of different things, but I can't make two simple programs (1x producer, 1x consumer) work in a reliable manner, because they run into deadlocks.
I have the following requirements:
The example shall work regardless of the order of process execution. It should not make a functional difference if either
consumer
orproducer
is started first.Note: For this reason I'm using
boost::interprocess::open_or_create
in both processes (maybe that cause problems too).- The producer shall construct a shared memory object (in this case an
int
) if notified by the consumer. - The consumer shall read the shared memory object if notified by the producer, print the read data and destroy the shared memory object.
- Each process shall not "poll", but block/wait until it is allowed to do work (the allowance comes from the other process).
Here is my current C++ code which causes a deadlock without producing and/or consuming even one object. After looking a long time at the code, I'm not be able to see the forest for the trees.
producer.cpp
// Standard Library
#include <iostream>
#include <thread>
// Boost.Interprocess
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/sync/named_condition.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
int main() {
std::cout << "Started Producer" << std::endl;
try {
std::cout << "Producer: Open/Create shared memory segment" << std::endl;
boost::interprocess::managed_shared_memory managed_shm{
boost::interprocess::open_or_create, "SharedMemory_Segment", 1024};
std::cout << "Producer: Open/Create mutex" << std::endl;
boost::interprocess::named_mutex mutex{boost::interprocess::open_or_create,
"SharedMemory_Mutex"};
std::cout << "Producer: Open/Create condition" << std::endl;
boost::interprocess::named_condition condition_read{
boost::interprocess::open_or_create, "SharedMemory_ConditionRead"};
boost::interprocess::named_condition condition_write{
boost::interprocess::open_or_create, "SharedMemory_ConditionWrite"};
std::cout << "Producer: Start work" << std::endl;
for (int i = 0; i < 10; ++i) {
std::cout << "Producer: Try to get the lock for the shared mutex "
"(potential blocking)"
<< std::endl;
boost::interprocess::scoped_lock<boost::interprocess::named_mutex> lock{
mutex};
std::cout << "Producer: Wait for Consumer" << std::endl;
condition_read.wait(lock);
std::cout << "Producer: Construct data" << std::endl;
managed_shm.construct<int>("SharedMemory_Data")(i);
std::cout << "Producer: Unlock the mutex" << std::endl;
lock.unlock();
std::cout << "Producer: Notify Consumer" << std::endl;
condition_write.notify_one();
}
} catch (const boost::interprocess::interprocess_exception& ex) {
std::cerr << "boost::interprocess::interprocess_exception: " << ex.what()
<< std::endl;
} catch (const std::exception& ex) {
std::cerr << "std::exception: " << ex.what() << std::endl;
} catch (...) {
std::cerr << "unhandled exception\n";
}
}
consumer.cpp
// Standard Library
#include <iostream>
#include <thread>
#include <utility>
// Boost.Interprocess
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/sync/named_condition.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
int main() {
std::cout << "Started Consumer" << std::endl;
try {
std::cout << "Consumer: Open/Create shared memory segment" << std::endl;
boost::interprocess::managed_shared_memory managed_shm{
boost::interprocess::open_or_create, "SharedMemory_Segment", 1024};
std::cout << "Consumer: Open/Create mutex" << std::endl;
boost::interprocess::named_mutex mutex{boost::interprocess::open_or_create,
"SharedMemory_Mutex"};
std::cout << "Consumer: Open/Create condition" << std::endl;
boost::interprocess::named_condition condition_read{
boost::interprocess::open_or_create, "SharedMemory_ConditionRead"};
boost::interprocess::named_condition condition_write{
boost::interprocess::open_or_create, "SharedMemory_ConditionWrite"};
std::cout << "Consumer: Start work" << std::endl;
do {
std::cout << "Consumer: Try to get the lock for the shared mutex "
"(potential blocking)"
<< std::endl;
boost::interprocess::scoped_lock<boost::interprocess::named_mutex> lock{
mutex};
std::cout << "Consumer: Wait for Producer" << std::endl;
condition_write.wait(lock);
std::cout << "Consumer: Read data" << std::endl;
std::pair<int*, int> p = managed_shm.find<int>("SharedMemory_Data");
if (p.first) {
std::cout << "Consumer: Found data = " << *p.first << std::endl;
std::cout << "Consumer: Destroy data" << std::endl;
managed_shm.destroy<int>("SharedMemory_Data");
}
std::cout << "Consumer: Unlock the mutex" << std::endl;
lock.unlock();
std::cout << "Consumer: Notify Producer" << std::endl;
condition_read.notify_one();
} while (true);
} catch (const boost::interprocess::interprocess_exception& ex) {
std::cerr << "boost::interprocess::interprocess_exception: " << ex.what()
<< std::endl;
} catch (const std::exception& ex) {
std::cerr << "std::exception: " << ex.what() << std::endl;
} catch (...) {
std::cerr << "unhandled exception\n";
}
}
My questions are:
- Are there any problems regarding the four requirements stated above?
- What are the problems in my code? What is unnecessary, too complicated or simply plain wrong?
(e.g. Are two
condition_variables
required?, Do both processes need to callnotify_one
andwait
? ...)
Any guidance would be very helpful to me. I'm feeling kind of dumb at the moment.
I am using Boost 1.59.0, Microsoft Visual C++ (MSVC) 19.11.25547 and Windows 10.