0

I have 2 processes (producer and consumer) sharing an int deque in shared memory, I have the producer process put 2 numbers in the deque and then it gets in a wait state losing its mutex lock. I then have the consumer process removing the numbers and printing them. It then does a notify on the condition which the producer is waiting on. The consumer then goes on its own wait on a second condition. After this case the producer does not wake up. I am using the same mutex between the processes. Please find all code below.

Include file shared_memory.h:

#ifndef SharedMemory_h
#define SharedMemory_h

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/deque.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <boost/interprocess/offset_ptr.hpp>
#include <boost/interprocess/sync/named_condition.hpp>

using namespace boost::interprocess;

typedef allocator<offset_ptr<int>, managed_shared_memory::segment_manager> ShmemAllocator;
typedef deque<offset_ptr<int>, ShmemAllocator> Deque;

#endif 

Producer process:

#include "shared_memory.h"

struct shm_remove
{
    shm_remove() { shared_memory_object::remove("MySharedMemory"); }
    ~shm_remove() { shared_memory_object::remove("MySharedMemory"); }
} remover;

struct mutex_remove
{
    mutex_remove() { named_mutex::remove("MyMutex"); }
    ~mutex_remove() { named_mutex::remove("MyMutex"); }
} mutex_remover;

//Create shared memory, mutex and condtion
managed_shared_memory segment(create_only, "MySharedMemory", 10000000);
named_mutex mutex(create_only,"MyMutex");
named_condition full(open_or_create,"FullCondition");
named_condition empty(open_or_create,"EmptyCondition");

const ShmemAllocator alloc_inst (segment.get_segment_manager());


int main() 
{
    Deque* MyDeque;
    offset_ptr<int> a, b;
    try{
        MyDeque = segment.construct<Deque>("MyDeque")(alloc_inst);
        try{
           a = static_cast<int*> (segment.allocate(sizeof(int)));
           b = static_cast<int*> (segment.allocate(sizeof(int)));
        }catch(bad_alloc &ex){
             std::cout << "Could not allocate int" << std::endl;
        }
    }catch(bad_alloc &ex){
        std::cout << "Could not allocate queue" << std::endl;
    }
    scoped_lock<named_mutex> lock(mutex);
    while(1)
    {
        while (MyDeque->size() == 2)
        {
            full.wait(lock);
            std::cout << "unlocked producer" << std::endl;
        }

        if (MyDeque->size() == 0)
        {
            *a = 2;
            MyDeque->push_back(a);
        }

        if (MyDeque->size() == 1)
        {
            *b = 4;
            MyDeque->push_back(b);
            empty.notify_one();
        }
    }
}

Consumer process:

#include "shared_memory.h"

managed_shared_memory segment(open_only, "MySharedMemory");
Deque* MyDeque = segment.find<Deque>("MyDeque").first;

named_mutex mutex(open_only, "MyMutex");
named_condition full(open_only, "FullCondition");
named_condition empty(open_only, "EmptyCondition");

int main()
{
    scoped_lock<named_mutex> lock(mutex);
    while(1)
    {

         //volatile int size = MyDeque->size();
         while (MyDeque->size() == 0)
         {
             empty.wait(lock);
         }

         if (MyDeque->size() == 2)
         {
             std::cout << "Consumer: " << *MyDeque->front() << std::endl;
             MyDeque->pop_front();
         }

         if (MyDeque->size() == 1)
         {
             std::cout << "Consumer: " << *MyDeque->front() << std::endl;
             MyDeque->pop_front();
             full.notify_one();
         }
    }
}

While debugging things seem to go ok for the first iteration, the producer puts numbers 2 and 4 on the deque then waits on the full condition. The Consumer then gets the lock, prints these numbers, does a notify_one on the full condition and then goes into a wait. After this the producer does not wake up.

rudasi
  • 185
  • 2
  • 14
  • I'm not familiar with boost interprocess, but is there a reason to think that the `mutex` in shared memory is functional in both processes? – Yakk - Adam Nevraumont Jul 18 '13 at 18:31
  • 1
    Please, specify explicitly that all the synchronization objects belong to Boost.Interprocess (i.e. `boost::interprocess` namespace), because similar objects having a different functionality exist in Boost.Thread library as well. – Igor R. Jul 18 '13 at 18:34
  • I would like to think it is functional as when you try to get a named mutex an exception is thrown if it is not available. Also when I debug through the code with breakpoints there are times when the consumer tries to get the mutex but cannot as the producer has it. Only when the producer does a wait and loses the mutex does the consumer get it. – rudasi Jul 18 '13 at 18:35
  • I have a using namespace boost::interprocess. I should have put that up in the code. – rudasi Jul 18 '13 at 18:36
  • @rudasi I think you are going to have to show us the code where you "create the mutex and conditions." – doctorlove Jul 18 '13 at 19:39
  • Probably unrelated, but it would be good style to lock the mutex before creating or opening the shared memory segment to ensure that the Producer has a chance to initialize everything before the Consumer accesses it. – Casey Jul 18 '13 at 20:16
  • I tried similar code but without using named mutexes and named conditions, instead I used regular boost thread mutexes and conditions. I created a producer and consumer thread, everything seemed to be synchronized correctly and worked well. It seems either I am doing something wrong with setting up the shared memory and mutex/conditions or maybe there is an issue with named mutex/conditions in boost. Any suggestions? – rudasi Jul 19 '13 at 16:00
  • shm_remove and mutex_remove seem like odd classes? They remove the target objects when they are created and remove them again when they are destroyed. This is similar to the RAII pattern http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization but different. In any case they are not used in this code, so I'll ignore them. One more point, though, the named condition variables are also persistent beyond the lifetime of any process and probably need a removal mechanism, too. – Dale Wilson Aug 07 '13 at 21:18
  • Why create_only on the named mutex? That will throw if the mutex already exists (remember it's persistent). I would expect open_or_create. – Dale Wilson Aug 07 '13 at 21:23
  • It wouldn't let me edit the comment above when I figured out what shm_remove & mutex_remove are doing: Static "wrappers" around main(). – Dale Wilson Aug 07 '13 at 21:26
  • What do the static wrappers do, my understanding is that they are called each time the process is started and exited. They remove the shared memory and mutex, but I thought they can only remove them if no other process is connected to them? – rudasi Aug 08 '13 at 15:28

1 Answers1

0

The mutex must not be locked across notifying. This is the reason of the deadlock.

Lukas Pauk
  • 19
  • 2