2

I'm having trouble with shared memory using condition variables and mutexes accross files.

My research led me to here Share condition variable & mutex between processes: does mutex have to locked before? The solution that the OP posted, doesn't work if you run two completely separate executables. I tried his methods towards my own problem and two seperate processes just would not signal to each other. So to confirm that the OPs code actually works, as seen below i have copied his code and added a #define midway so that you can compile and start as the father, change the define and start as the son. If you run the code as the OP had it with just a fork, it works. if you run as two seperate executables it doesn't work...... Does anyone have any ideas?

Background Question This started with my previous question POSIX Shared Memory Sync Across Processes C++/C++11

Test Code

#include <QCoreApplication>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>

#define OKTOWRITE "/condwrite"
#define MESSAGE "/msg"
#define MUTEX "/mutex_lock"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    pthread_cond_t* condition;
    pthread_mutex_t* mutex;
    char* message;
    int des_cond, des_msg, des_mutex;
    int mode = S_IRWXU | S_IRWXG;

    des_mutex = shm_open(MUTEX, O_CREAT | O_RDWR | O_TRUNC, mode);

    if (des_mutex < 0) {
        perror("failure on shm_open on des_mutex");
        exit(1);
    }

    if (ftruncate(des_mutex, sizeof(pthread_mutex_t)) == -1) {
        perror("Error on ftruncate to sizeof pthread_cond_t\n");
        exit(-1);
    }

    mutex = (pthread_mutex_t*) mmap(NULL, sizeof(pthread_mutex_t),
            PROT_READ | PROT_WRITE, MAP_SHARED, des_mutex, 0);

    if (mutex == MAP_FAILED ) {
        perror("Error on mmap on mutex\n");
        exit(1);
    }

    des_cond = shm_open(OKTOWRITE, O_CREAT | O_RDWR | O_TRUNC, mode);

    if (des_cond < 0) {
        perror("failure on shm_open on des_cond");
        exit(1);
    }

    if (ftruncate(des_cond, sizeof(pthread_cond_t)) == -1) {
        perror("Error on ftruncate to sizeof pthread_cond_t\n");
        exit(-1);
    }

    condition = (pthread_cond_t*) mmap(NULL, sizeof(pthread_cond_t),
            PROT_READ | PROT_WRITE, MAP_SHARED, des_cond, 0);

    if (condition == MAP_FAILED ) {
        perror("Error on mmap on condition\n");
        exit(1);
    }

//#define father
#ifdef father


    /* HERE WE GO */
    /**************************************/

        /* set mutex shared between processes */
    pthread_mutexattr_t mutexAttr;
    pthread_mutexattr_init(&mutexAttr);
    pthread_mutexattr_setpshared(&mutexAttr, PTHREAD_PROCESS_SHARED);
    pthread_mutex_init(mutex, &mutexAttr);

    /* set condition shared between processes */
    pthread_condattr_t condAttr;
    pthread_condattr_init(&condAttr);
    pthread_condattr_setpshared(&condAttr, PTHREAD_PROCESS_SHARED);
    pthread_cond_init(condition, &condAttr);

    /*************************************/


    printf("father waits on condition\n");

     pthread_mutex_lock(mutex);
     pthread_cond_wait(condition, mutex);
     pthread_mutex_unlock(mutex);

     printf("Signaled by son process, wake up!!!!!!!!\n");

    pthread_condattr_destroy(&condAttr);
    pthread_mutexattr_destroy(&mutexAttr);
    pthread_mutex_destroy(mutex);
    pthread_cond_destroy(condition);

    shm_unlink(OKTOWRITE);
    shm_unlink(MESSAGE);
    shm_unlink(MUTEX);
#else

//    if (!fork()) {

//        sleep(3);

        pthread_mutex_lock(mutex);
        pthread_cond_signal(condition);
        printf("son signaled\n");
        pthread_mutex_unlock(mutex);
        exit(0);
//    }

//    else {

#endif

//    }

    exit(0);

    return a.exec();
}
Community
  • 1
  • 1
Asvaldr
  • 197
  • 3
  • 15

1 Answers1

3

You're truncating the shared memory for mutex and conditional variable unnecessarily in the son process. Since this part used to happen before fork() in the original code you were doing it only once for the couple. But in two separate processes you're initializing the shared memory with cross-process pthread mutex and conditional variable respectively in father and then destroying everything that pthreads put in there by using O_TRUNC in the son. The correct code is following:

father.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>

#define OKTOWRITE "/condwrite"
#define MESSAGE "/msg"
#define MUTEX "/mutex_lock"

int main(int argc, char *argv[])
{
    pthread_cond_t* condition;
    pthread_mutex_t* mutex;
    char* message;
    int des_cond, des_msg, des_mutex;
    int mode = S_IRWXU | S_IRWXG;

    des_mutex = shm_open(MUTEX, O_CREAT | O_RDWR | O_TRUNC, mode);

    if (des_mutex < 0) {
        perror("failure on shm_open on des_mutex");
        exit(1);
    }

    if (ftruncate(des_mutex, sizeof(pthread_mutex_t)) == -1) {
        perror("Error on ftruncate to sizeof pthread_cond_t\n");
        exit(-1);
    }

    mutex = (pthread_mutex_t*) mmap(NULL, sizeof(pthread_mutex_t),
            PROT_READ | PROT_WRITE, MAP_SHARED, des_mutex, 0);

    if (mutex == MAP_FAILED ) {
        perror("Error on mmap on mutex\n");
        exit(1);
    }

    des_cond = shm_open(OKTOWRITE, O_CREAT | O_RDWR | O_TRUNC, mode);

    if (des_cond < 0) {
        perror("failure on shm_open on des_cond");
        exit(1);
    }

    if (ftruncate(des_cond, sizeof(pthread_cond_t)) == -1) {
        perror("Error on ftruncate to sizeof pthread_cond_t\n");
        exit(-1);
    }

    condition = (pthread_cond_t*) mmap(NULL, sizeof(pthread_cond_t),
            PROT_READ | PROT_WRITE, MAP_SHARED, des_cond, 0);

    if (condition == MAP_FAILED ) {
        perror("Error on mmap on condition\n");
        exit(1);
    }

    /* HERE WE GO */
    /**************************************/

        /* set mutex shared between processes */
    pthread_mutexattr_t mutexAttr;
    pthread_mutexattr_init(&mutexAttr);
    pthread_mutexattr_setpshared(&mutexAttr, PTHREAD_PROCESS_SHARED);
    pthread_mutex_init(mutex, &mutexAttr);

    /* set condition shared between processes */
    pthread_condattr_t condAttr;
    pthread_condattr_init(&condAttr);
    pthread_condattr_setpshared(&condAttr, PTHREAD_PROCESS_SHARED);
    pthread_cond_init(condition, &condAttr);

    /*************************************/


    printf("father waits on condition\n");

     pthread_mutex_lock(mutex);
     pthread_cond_wait(condition, mutex);
     pthread_mutex_unlock(mutex);

     printf("Signaled by son process, wake up!!!!!!!!\n");

    pthread_condattr_destroy(&condAttr);
    pthread_mutexattr_destroy(&mutexAttr);
    pthread_mutex_destroy(mutex);
    pthread_cond_destroy(condition);

    shm_unlink(OKTOWRITE);
    shm_unlink(MESSAGE);
    shm_unlink(MUTEX);

    exit(0);
}

son.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>

#define OKTOWRITE "/condwrite"
#define MESSAGE "/msg"
#define MUTEX "/mutex_lock"

int main(int argc, char *argv[])
{
    pthread_cond_t* condition;
    pthread_mutex_t* mutex;
    char* message;
    int des_cond, des_msg, des_mutex;
    int mode = S_IRWXU | S_IRWXG;

    des_mutex = shm_open(MUTEX, O_CREAT | O_RDWR, mode);

    if (des_mutex < 0) {
        perror("failure on shm_open on des_mutex");
        exit(1);
    }

    mutex = (pthread_mutex_t*) mmap(NULL, sizeof(pthread_mutex_t),
            PROT_READ | PROT_WRITE, MAP_SHARED, des_mutex, 0);

    if (mutex == MAP_FAILED ) {
        perror("Error on mmap on mutex\n");
        exit(1);
    }

    des_cond = shm_open(OKTOWRITE, O_CREAT | O_RDWR, mode);

    if (des_cond < 0) {
        perror("failure on shm_open on des_cond");
        exit(1);
    }

    condition = (pthread_cond_t*) mmap(NULL, sizeof(pthread_cond_t),
            PROT_READ | PROT_WRITE, MAP_SHARED, des_cond, 0);

    if (condition == MAP_FAILED ) {
        perror("Error on mmap on condition\n");
        exit(1);
    }

    pthread_mutex_lock(mutex);
    pthread_cond_signal(condition);
    printf("son signaled\n");
    pthread_mutex_unlock(mutex);
    exit(0);
}

Testing:

1st terminal: $ ./father
father waits on condition

2nd terminal: $ ./son
son signaled

1st terminal: Signaled by son process, wake up!!!!!!!!
$
datenwolf
  • 159,371
  • 13
  • 185
  • 298
ALGOholic
  • 644
  • 3
  • 7
  • Thank you so much @ALGOholic, i didn't see that. So i see the ftruncate after the shm_open is not required for the son process either. So that means fttruncate is operating on the shared memory itself, not sizing the individual processes virtual addr space to match? – Asvaldr Feb 17 '16 at 21:48
  • The way I look at it - shm_open() returns a "file" handle, therefore logically it should behave +- as a file. ftruncate() simply sets that "file" size - nothing to do with virtual memory of a process. The latter part is handled by the mmap() call where you specify the size of memory and establish a link between virtual memory pages and their backing store (the shm "file"). Behind the scenes, kernel recognizes common backing store for both mmap() calls and assigns same VM pages to both processes. The short answer is - yes, ftruncate() operates on the "shm" file and is not necessary in son. – ALGOholic Feb 18 '16 at 07:05
  • Perfect code, I have been trying to figure this out for a while. None of the documentation is clear on how to do this in shared memory context. Is there a way to do this with single memory mapped file rather than 2 – webjockey Mar 27 '18 at 17:02