Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ std lib <mutex>, <conditional_variable> libs and shared memory

The C API to POSIX threads requires a special flag to be set if you want to share a mutex between processes in shared memory - see sem_init(). I don't really know what the diff is but I'm having trouble trying to use C++ std::condition_variable in shared memory - its seg faulting. I can't see anything mentioning this in the C++ docs, or the constructors. I was wondering how to / if you can use C++ thread mutex in shared memory. Here is my test code for reference. Note squeue is just a simple (POD) static sized circular queue, and irrelevant stuff is omitted:

#include <iostream>
#include <sys/mman.h>
#include <sys/stat.h>        /* For mode constants */
#include <fcntl.h>           /* For O_* constants */
#include "squeue.h"
#define SHM_FILENAME "/shimmy-foo"
#define SQUEUE_LENGTH 10

typedef struct {
        squeue<int,SQUEUE_LENGTH> queue;
        std::mutex mutex;
        std::condition_variable_any condvar;
} SHM;

int main() {
        int shm_fd = 0;
        SHM * shm_ptr = NULL;
        squeue<int,SQUEUE_LENGTH> * queue = NULL;
        std::mutex * mutex;
        std::condition_variable_any * condvar;

        // Init SHM. ftruncate() will zero area.
        if((shm_fd = shm_open(SHM_FILENAME, O_CREAT|O_RDWR|O_EXCL, S_IREAD|S_IWRITE)) == -1 ) {
            fprintf (stderr, "Could not open shm object. %s\n", strerror(errno));
            return errno;
        }
        else {
            fprintf (stderr, "Open shm OK. %d\n", shm_fd);
        }
        ftruncate(shm_fd, sizeof(SHM));

        // Connect the shmptr pointer to set to the shared memory area,
        // with desired permissions
        if((shm_ptr = (SHM*)mmap(0, sizeof(SHM), PROT_READ|PROT_WRITE, MAP_SHARED, shm_fd, 0)) == MAP_FAILED) {
            fprintf (stderr, "Could not map shm. %s\n", strerror(errno));
            return errno;
        }
        else {
        fprintf(stderr, "Mapped shm OK. %p\n", shm_ptr);
        }

        // Create queue and mutex.
        queue = new(&shm_ptr->queue) squeue<int,SQUEUE_LENGTH>();
        mutex = new(&shm_ptr->mutex) std::mutex();
        condvar = new(&shm_ptr->condvar) std::condition_variable_any();

        srand(time(NULL));
        while(true) {
            cout << "Waiting on lock" << endl;
            mutex->lock();
            if(!queue->full()) {
                int value = rand()%100;
                queue->push(value);
                cout << "Pushed " << value << endl;
            } else {
                cout << "Que is full!" << endl;
            };
            condvar->notify_all(); //Seg fault.
            mutex->unlock();
            sleep(1);
        }
    }
like image 556
Francis M. Bacon Avatar asked Jun 23 '14 07:06

Francis M. Bacon


1 Answers

I use a similar pattern, however, the standard mutex and condition variables are not designed to be shared between processes. The reason for that is that POSIX requires PTHREAD_PROCESS_SHARED attribute set on process shared mutexes and condition variables but the standard C++ primitives do not do that. On Windows it might be more complicated than that.

You can try using boost process shared mutexes and process shared condition variables instead. Or create your own wrappers for POSIX interfaces.


It could also be that squeue corrupts memory beyond its buffer overwriting the mutex and the condition variable that lay above in memory in struct SHM. I would try commenting out the code that pushes into the queue and see if you still get that crash. I tried your code with queue code commented out and it works as expected.


You may also like to use condition_variable instead of condition_variable_any, because the latter one maintains its own mutex but that mutex is not needed if you notify that condition variable while having the associated mutex locked (as you do).

like image 85
Maxim Egorushkin Avatar answered Sep 30 '22 00:09

Maxim Egorushkin