8

I'm trying to use interprocess_mutex with managed_windows_shared_memory. In my project, multiple processes create an instance of class A in the following code.

using namespace boost::interprocess;

class A
{
    managed_windows_shared_memory* _shm;
    interprocess_mutex* _mtx;
}
A::A()
{
    _shm = new managed_windows_shared_memory{ open_or_create, "shm", 1024 };
    _mtx = _shm->find_or_construct<interprocess_mutex>("mtx")();
}
A::~A()
{
    delete _mtx;
    delete _shm;
}

I can see that it is safe to call delete _shm; in ~A(), since managed_windows_shared_memory will be destroyed only when every process using it destroys the managed_windows_shared_memory object, as written in the doc.

However, I'm not sure if it is safe to call delete _mtx; in ~A(). In the doc for interprocess_mutex, it is not mentioned that whether it is still destroyed or not even if other processes have objects referring to it.

I've searched this and I'm guessing that my option is to use boost::interprocess::shared_ptr in this case. Am I right here? Is this the option that I should take?

nglee
  • 1,913
  • 9
  • 32
  • It is an OS implementation detail, the kind of detail that Boost docs carefully avoid mentioning. You can safely assume that the OS uses reference-counting on the underlying named mutex kernel object. Only the last call made by the last running process counts it down to 0 and gets it destroyed. You don't have a problem. – Hans Passant Jun 08 '18 at 10:23
  • Thanks for your comment. In my case, however, I'm using an anonymous mutex, not a named mutex. The term _anonymous mutex_ and _named mutex_ is taken from [this boost doc](https://www.boost.org/doc/libs/1_60_0/doc/html/interprocess/synchronization_mechanisms.html#interprocess.synchronization_mechanisms.mutexes.mutexes_interprocess_mutexes). I've performed some tests and it seems that deleting an `interprocess_mutex` which is held by other process results in error. I'm going to post my test code. Please correct me if I'm mistaken. – nglee Jun 12 '18 at 11:19
  • According to boost: *If any process uses the mutex after the destructor is called the result is undefined*. I guess that if you have reference count you can delete when have the only object, so using shared_ptr mapped on shared memory may be the answer. – SHR Jun 12 '18 at 11:27

2 Answers2

3

From the documentation:

Both processes share the same object

(emphasis in the original).

Obviously you cannot destroy an object while some other code might still access it, even if it's code in another process, because both processes share the same object.

Note the situation with the shared memory pointer is different. The shared memory object is not shared between processes. The region of memory it manages is, but the C++ object itself is private to the process.

The mutex, on the other hand, lives in that shared memory region and so is shared.

Unless you want to reuse the memory occupied by the mutex, you don't need to delete _mtx at all, so don't bother with shared pointers or reference counters. Deleting the shared memory object will just unmap everything inside the corresponding memory segment, as if it was never there.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
0

Firstly, the pointer that points to an interprocess_mutex allocated on a shared memory can't be destroyed with delete directly, since the pointer points to some address in the process' address space that maps to the shared memory region where the mutex is actually located. Therefore, in the following code, executing the line delete mtx crashes.

#include <boost/interprocess/managed_windows_shared_memory.hpp>

using namespace boost::interprocess;

int main()
{
    managed_windows_shared_memory shm{ open_or_create, "shm", 1024 };

    interprocess_mutex *mtx = shm.find_or_construct<interprocess_mutex>("gMutex")();

    delete mtx; // ***** CRASH *****
}

To correctly destroy the created object via pointer, call shm.destroy_ptr(mtx) instead of delete mtx.


Secondly, by compiling the following code and running it twice on two separate consoles, it can be checked that destroying interprocess_mutex with destroy_ptr while other process is locking the mutex does not crash. Also, the process holding the lock can safely unlock the mutex afterwards without crashing. (Tested on Windows 10, Boost 1.60.0, Visual Studio 2015)

#include <boost/interprocess/sync/interprocess_semaphore.hpp>
#include <boost/interprocess/managed_windows_shared_memory.hpp>

using namespace boost::interprocess;
typedef managed_windows_shared_memory   smem_t;
typedef interprocess_mutex              mutex_t;
typedef interprocess_semaphore          sema_t;

void first_process(smem_t *shm);
void second_process(smem_t *shm);

int main()
{
    smem_t *shm = nullptr;

    try {
        // launching this program for the first time
        // successfully creates a shared memory region
        shm = new smem_t{ create_only, "shm", 1024 };
        first_process(shm);
    } catch (interprocess_exception& e) {
        // launching this program again, it fails to create shared memory
        // since it already exists
        second_process(shm);
    }

    return EXIT_SUCCESS;
}

void first_process(smem_t *shm)
{
    mutex_t *mtx = shm->find_or_construct<mutex_t>("gMutex")();
    sema_t *sema1 = shm->find_or_construct<sema_t>("gSema1")(0);
    sema_t *sema2 = shm->find_or_construct<sema_t>("gSema2")(0);

    sema1->wait();          // wait until the second process locks the mutex
    shm->destroy_ptr(mtx);  // destroy the mutex, doesn't crash (delete mtx crashes)
    sema2->post();          // signal the second process to unlock the mutex
}

void second_process(smem_t *shm)
{
    try {
        shm = new smem_t{ open_only, "shm" };
    } catch (std::exception& e) {
        exit(EXIT_FAILURE);
    }

    mutex_t *mtx = shm->find_or_construct<mutex_t>("gMutex")();
    sema_t *sema1 = shm->find_or_construct<sema_t>("gSema1")(0);
    sema_t *sema2 = shm->find_or_construct<sema_t>("gSema2")(0);

    mtx->lock();
    sema1->post();  // signal the first process that the mutex is locked
    sema2->wait();  // wait until the first process calls destroy_ptr
    mtx->unlock();  // doesn't crash
}
nglee
  • 1,913
  • 9
  • 32