Search code examples
c++boostboost-interprocessboost-mutex

Boost interprocess mutex crashes instead of waiting on a lock?


I've been at this for quite a few days (even posted on the boost forum) and being able to make a second process recognize a locked mutex just doesn't seem to work. Please help. This is the code:

A common header file: SharedObject.hpp

#ifndef SHAREDOBJECT_HPP 
#define SHAREDOBJECT_HPP 
#include <iostream>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/list.hpp>
#include <time.h>//for sleep 
//--------for mutexes 
#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>

#define MUTEX_SHARED_MEMORY_NAME "NavSharedMemoryMutex" 
#define DATAOUTPUT "OutputFromObject" 
#define INITIAL_MEM 650000 
using namespace std; 
namespace bip = boost::interprocess; 

class SharedMutex 
{ 
private: 
    typedef bip::interprocess_upgradable_mutex upgradable_mutex_type; 
    mutable upgradable_mutex_type mutex; 
    volatile int counter;     
public: 
    void lockWithReadLock() const { bip::sharable_lock<upgradable_mutex_type> lock(mutex); } 
    void lockWithWriteLock() { bip::scoped_lock<upgradable_mutex_type> lock(mutex); } 
}; 

//-------------------------------------- SharedMemoryObject 
class SharedObject 
{ 
public: 
    SharedMutex* sharedMutex;   
}; 

typedef bip::allocator<SharedObject, bip::managed_shared_memory::segment_manager> ShmemAllocator; 
typedef bip::list<SharedObject, ShmemAllocator> SharedMemData; 

#endif /* SHAREDOBJECT_HPP */ 

This is the first program:

#include "SharedObject.hpp" 

int main() 
{ 
    //-----------Create shared memory and put shared object into it 
    bip::managed_shared_memory* seg; 
    SharedMemData *sharedMemOutputList; 
    bip::shared_memory_object::remove(DATAOUTPUT); 
    seg = new bip::managed_shared_memory(bip::create_only, DATAOUTPUT, INITIAL_MEM); 
    const ShmemAllocator alloc_inst(seg->get_segment_manager()); 
    sharedMemOutputList = seg->construct<SharedMemData>("TrackOutput")(alloc_inst); 

    std::size_t beforeAllocation = seg->get_free_memory(); 
    std::cout<<"\nBefore allocation = "<< beforeAllocation <<"\n"; 
    SharedObject temp; 
    sharedMemOutputList->push_back(temp); 


    //-------------------Create a shared memory for holding a mutex 
    bip::shared_memory_object::remove(MUTEX_SHARED_MEMORY_NAME);//BUG: If another program also removes this, then there won't be any shared memory remaining. This problem has to be handled better. This is not the right way to deal with shared mutexes. 
    bip::shared_memory_object shm(bip::create_only, MUTEX_SHARED_MEMORY_NAME, bip::read_write); 
    shm.truncate(sizeof (SharedMutex)); //Allocate memory in shared memory for the mutex 
    bip::mapped_region region(shm, bip::read_write); // Map the whole shared memory into this process.         
    new (region.get_address()) SharedMutex; // Construct the SharedMutex using placement new 
    temp.sharedMutex = static_cast<SharedMutex *> (region.get_address()); //Store the mutex object address for future reference 

    { 
        std::cout<<"Program 1: Before first locking -------------------------- 1 v\n"; 
        temp.sharedMutex->lockWithWriteLock(); 
        const unsigned int SLEEP_TIME_IN_SECOND = 60; 
        std::cout<<"Program 1: sleep for "<< SLEEP_TIME_IN_SECOND << "\n"; 
        sleep(SLEEP_TIME_IN_SECOND); 
        std::cout<<"Program 1: Finished sleeping\n"; 
    } 
    std::cout<<"Program 1: unlocked -------------------------------------- 1 ^\n"; 


    seg->destroy<SharedMemData>("TrackOutput"); 
    delete seg;       
    return 0; 
}//main

And this is the second program:

#include "SharedObject.hpp" 
#define CREATE_SEPARATE_MUTEX 

int main() 
{ 
    bip::managed_shared_memory segment(bip::open_only, DATAOUTPUT); //Open the managed segment 

    SharedMemData* sharedMemoryTrackOutputList = segment.find<SharedMemData>("TrackOutput").first; //Find the list using the c-string name 
    assert(sharedMemoryTrackOutputList);     
    std::cout << "SharedMemoryData address found at = " << (void *) sharedMemoryTrackOutputList << "\n"; 
    std::cout << "size = " << sharedMemoryTrackOutputList->size() << "\n"; 
    SharedMemData::iterator iter = sharedMemoryTrackOutputList->begin(); 

#ifndef CREATE_SEPARATE_MUTEX 
    //-------------------Create a shared memory for holding a mutex 
    bip::shared_memory_object shm(bip::open_or_create, MUTEX_SHARED_MEMORY_NAME, bip::read_write); 
    shm.truncate(sizeof (SharedMutex)); //Allocate memory in shared memory for the mutex 
    bip::mapped_region region(shm, bip::read_write); // Map the whole shared memory into this process.         
    new (region.get_address()) SharedMutex; // Construct the SharedMutex using placement new 
    (*iter).sharedMutex = static_cast<SharedMutex *> (region.get_address()); //Store the mutex object address for future reference 
#endif 

    { 
        std::cout<<"Program 2: Before first locking -------------------------- 1 v\n"; 
        (*iter).sharedMutex->lockWithWriteLock(); 
        const unsigned int SLEEP_TIME_IN_SECOND = 1; 
        std::cout<<"Program 2: sleep for "<< SLEEP_TIME_IN_SECOND << "\n"; 
        sleep(SLEEP_TIME_IN_SECOND); 
        std::cout<<"Program 2: Finished sleeping\n"; 
    } 
    std::cout<<"Program 2: unlocked -------------------------------------- 1 ^\n";     

    return 0; 
}//main 

Program 1 works fine.
Program 2 gives this output:

SharedMemoryData address found at = 0x7f0a4c2b8118 
size = 1 
Program 2: Before first locking -------------------------- 1 v 
terminate called after throwing an instance of 'boost::interprocess::lock_exception' 
  what():  boost::interprocess::lock_exception 
Aborted (core dumped)

I'm running program1 first, which locks the mutex and keeps it locked for 60 seconds. During that time I run program 2 to see if it waits on the lock, but it crashes. How can I make program 2 wait on the mutex until program 1 has finished writing to shared memory?


Solution

  • Ok, here are some problems with your code.

    1. The SharedMutex is stored in a different memory region, which is not mapped in the second program.

    2. The mapped region can have a different base address, so raw pointers will not work in this environment. Use boost::interprocess::offset_ptr instead. Note, however, that those work for pointers to the same memory segment, so it would make sense to put your SharedMutex and SharedObject in the same segment (or instead of all this, use named_mutex).

    3. In main1, you copy temp to shared memory before modifying the member variable sharedMutex. You should ensure that in shared memory your pointer (offset_ptr-to-be) is actually valid.

    4. When you try to lock with scoped_lock using your lockWithWriteLock function, the lock is unlocked immediately at exit of the function. So once you fix all of the above and you get rid of the crash, you still won't get the expected output - the logic of your code should be changed.

    and the code:

    sharedObject.hpp

    #ifndef SHAREDOBJECT_HPP 
    #define SHAREDOBJECT_HPP 
    #include <iostream>
    #include <boost/interprocess/mapped_region.hpp>
    #include <boost/interprocess/managed_shared_memory.hpp>
    #include <boost/interprocess/containers/list.hpp>
    #include <time.h>//for sleep 
    //--------for mutexes 
    #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>
    
    #define MUTEX_SHARED_MEMORY_NAME "NavSharedMemoryMutex" 
    #define DATAOUTPUT "OutputFromObject" 
    #define INITIAL_MEM 650000 
    using namespace std;
    namespace bip = boost::interprocess;
    
    class SharedMutex
    {
    public:
        typedef bip::interprocess_upgradable_mutex upgradable_mutex_type;
        mutable upgradable_mutex_type mutex;
    };
    
    //-------------------------------------- SharedMemoryObject 
    class SharedObject
    {
    public:
        SharedMutex* sharedMutex;
    };
    
    typedef bip::allocator<SharedObject, bip::managed_shared_memory::segment_manager> ShmemAllocator;
    typedef bip::list<SharedObject, ShmemAllocator> SharedMemData;
    
    #endif /* SHAREDOBJECT_HPP */
    

    Program1:

    #include "SharedObject.hpp"
    
    int main()
    {
        //-----------Create shared memory and put shared object into it 
        bip::managed_shared_memory* seg;
        SharedMemData *sharedMemOutputList;
        bip::shared_memory_object::remove(DATAOUTPUT);
        seg = new bip::managed_shared_memory(bip::create_only, DATAOUTPUT, INITIAL_MEM);
        const ShmemAllocator alloc_inst(seg->get_segment_manager());
        sharedMemOutputList = seg->construct<SharedMemData>("TrackOutput")(alloc_inst);
    
        SharedObject temp;
    
        //-------------------Create a shared memory for holding a mutex 
        bip::shared_memory_object::remove(MUTEX_SHARED_MEMORY_NAME);//BUG: If another program also removes this, then there won't be any shared memory remaining. This problem has to be handled better. This is not the right way to deal with shared mutexes. 
        bip::shared_memory_object shm(bip::open_or_create, MUTEX_SHARED_MEMORY_NAME, bip::read_write);
        shm.truncate(sizeof(SharedMutex)); //Allocate memory in shared memory for the mutex 
        bip::mapped_region region(shm, bip::read_write); // Map the whole shared memory into this process.         
        new (region.get_address()) SharedMutex; // Construct the SharedMutex using placement new 
        temp.sharedMutex = static_cast<SharedMutex *> (region.get_address()); //Store the mutex object address for future reference 
    
        std::cout << "Region address " << region.get_address() << "\n";
    
        sharedMemOutputList->push_back(temp);
    
        //initiate scope for scoped mutex
        {
            std::cout << "Program 1: Going to do 1st locking -------------------------- 1 v\n";
            bip::scoped_lock<bip::interprocess_upgradable_mutex> lock(temp.sharedMutex->mutex);
            const unsigned int SLEEP_TIME_IN_SECOND = 10;
            std::cout<<"Program 1: locked. Now sleep for "<< SLEEP_TIME_IN_SECOND << "\n";
            sleep(SLEEP_TIME_IN_SECOND);
            std::cout << "Program 1: Finished sleeping\n";
        }
        std::cout << "Program 1: unlocked ----------------------------------------- 1 ^\n";
    
        //seg->destroy<SharedMemData>("TrackOutput");delete seg;
        return 0;
    }//main
    

    and Program2:

    #include "SharedObject.hpp"
    #define READ_LOCK_TESTING
    #ifndef READ_LOCK_TESTING
        #define WRITE_LOCK_TESTING
    #endif
    int main()
    {
        bip::managed_shared_memory segment(bip::open_only, DATAOUTPUT); //Open the managed segment 
    
        SharedMemData* sharedMemoryTrackOutputList = segment.find<SharedMemData>("TrackOutput").first; //Find the list using the c-string name 
        assert(sharedMemoryTrackOutputList);
        std::cout << "SharedMemoryData address found at = " << (void *)sharedMemoryTrackOutputList << "\n";
        std::cout << "size = " << sharedMemoryTrackOutputList->size() << "\n";
        SharedMemData::iterator iter = sharedMemoryTrackOutputList->begin();
    
        bip::shared_memory_object shm(bip::open_or_create, MUTEX_SHARED_MEMORY_NAME, bip::read_write);
        bip::mapped_region region(shm, bip::read_write); // Map the whole shared memory into this process.       
    
        std::cout << "Region address " << region.get_address() << "\n";
    
        //Initiate the scope for the scoped mutex
        {
            std::cout << "Program 2: Going to do 2nd locking -------------------------- 2 v\n";
            iter->sharedMutex = static_cast<SharedMutex*>(region.get_address());
    #ifdef WRITE_LOCK_TESTING        
            bip::scoped_lock<bip::interprocess_upgradable_mutex> lock((*iter).sharedMutex->mutex);//write lock
    #endif        
    #ifdef READ_LOCK_TESTING
            bip::sharable_lock<bip::interprocess_upgradable_mutex> lock((*iter).sharedMutex->mutex);//read lock
    #endif        
            const unsigned int SLEEP_TIME_IN_SECOND = 10;
            std::cout << "Program 2: locked. Now sleep for " << SLEEP_TIME_IN_SECOND << "\n";
            sleep(SLEEP_TIME_IN_SECOND);
            std::cout << "Program 2: Finished sleeping\n";
        }
        std::cout << "Program 2: unlocked ------------------------------------------ 2 ^\n";
    
        return 0;
    }//main