0
votes

I'm trying to create a simple shared mutex between processes. The idea is that I will run the same executable twice to create two processes and the first process should create a shared mutex and lock it and go to sleep for some time, meanwhile the other process should find the shared mutex locked and should report the same. For this I've written the below code -

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

#define MYMUTEX "/mymutex"

int main(int argc, char* argv[])
{
    pthread_cond_t *cond;
    pthread_mutex_t *mutex;
    int cond_id, mutex_id;
    int mode = S_IRWXU | S_IRWXG;
    /* mutex */
    mutex_id = shm_open(MYMUTEX, O_CREAT | O_RDWR, mode);
    printf("Mutex Id : %d\n", mutex_id);
    if (mutex_id < 0) {
        printf("shm_open failed\n");
        return -1;
    }
    if (ftruncate(mutex_id, sizeof(pthread_mutex_t)) == -1) {
        printf("ftruncate failed\n");
        return -1;
    }
    mutex = (pthread_mutex_t *) mmap(NULL, sizeof(pthread_mutex_t), PROT_READ | PROT_WRITE, MAP_SHARED, mutex_id, 0);
    if (mutex == MAP_FAILED) {
        printf("mmap failed\n");
        return -1;
    }
    printf("Mutex : %p\n", mutex);
    pthread_mutexattr_t mattr;
    pthread_mutexattr_init(&mattr);
    pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
    pthread_mutex_init(mutex, &mattr);
    pthread_mutexattr_destroy(&mattr);

    if (pthread_mutex_trylock(mutex)) {
        printf("Cannot acquire Lock. Some instance might be already running\n");
    } else {
        printf("Acquired Lock now sleeping...\n");
        sleep(25);
        pthread_mutex_unlock(mutex);
        pthread_mutex_destroy(mutex);
        shm_unlink(MYMUTEX);
    }
    return 0;
}

As far as I understand for creating a mutex shared between processes, it should be mapped to a shared memory and it should have the attribute PTHREAD_PROCESS_SHARED set. But the result I get when I run the program from two separate console is -

CONSOLE 1:

[gbose@seadev:~/mutlti-threaded-client-server$]./mutex
Mutex Id : 3
Mutex : 0x7faf2c57a000
Acquired Lock now sleeping...

CONSOLE 2:

[gbose@seadev20:~/mutlti-threaded-client-server$]./mutex
Mutex Id : 3
Mutex : 0x7f6d5766a000
Acquired Lock now sleeping...

What am I missing here? My hunch is that initializing the mutex both the time is the root cause of this unexpected behavior - if that is correct then what else should have been done to check if the mutex is already initialized?

UPDATE : Handled the case where file for shared memory already exists by adding the O_EXCL as per comment by @Shawn but still I see that the memory address returned by mmap() is a new one as pasted above console output.

    mutex_id = shm_open(MYMUTEX, O_CREAT | O_RDWR | O_EXCL, mode);
    if (mutex_id < 0) {
        mutex_id = shm_open(MYMUTEX, O_RDWR, mode);
    } else {
        if (ftruncate(mutex_id, sizeof(pthread_mutex_t)) == -1) {
            printf("ftruncate failed\n");
            return -1;
        }
    }
1
Only one process should create the shared memory and initialize the mutex (and clean up at the end). The other should map the shared memory and use the mutex. - Shawn
A shared semaphore with a count of 1 is easier to use in cases like this, imo. - Shawn
@Shawn Thanks! But I'm using shm_open with O_CREATE so shouldn't that automatically take care of the case that if shared memory object already exists then not to create one again? The mmap part remains the same then... please correct me if I'm wrong - Goutam Bose
Even if the shared memory exists it always goes on to re-initialize the mutex. You could use logic involving adding O_EXCL and doing the initialization if that succeeds and opening an existing one if it fails. - Shawn
Try opening it again without O_CREAT. - Shawn

1 Answers

0
votes

Thanks @Shawn for pointing out to check if the shared memory exists or not first. Checking if the shared memory object already exists and if exists then not initializing the mutex again worked. Summary is if we reinitialize a mutex, then its lock information gets overridden hence it was not working.

    mutex_id = shm_open(MYMUTEX, O_CREAT | O_RDWR | O_EXCL, mode);
    if (mutex_id < 0) {
        mutex_id = shm_open(MYMUTEX, O_RDWR, mode);
        mutex = (pthread_mutex_t *) mmap(NULL, sizeof(pthread_mutex_t), PROT_READ | PROT_WRITE, MAP_SHARED, mutex_id, 0);
        if (mutex == MAP_FAILED) {
            printf("mmap failed\n");
            return -1;
        }
    } else {
        if (ftruncate(mutex_id, sizeof(pthread_mutex_t)) == -1) {
            printf("ftruncate failed\n");
            return -1;
        }
        mutex = (pthread_mutex_t *) mmap(NULL, sizeof(pthread_mutex_t), PROT_READ | PROT_WRITE, MAP_SHARED, mutex_id, 0);
        if (mutex == MAP_FAILED) {
            printf("mmap failed\n");
            return -1;
        }
        pthread_mutexattr_t mattr;
        pthread_mutexattr_init(&mattr);
        pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
        pthread_mutex_init(mutex, &mattr);
        pthread_mutexattr_destroy(&mattr);
    }
    printf("Mutex Id : %d\n", mutex_id);
    printf("Mutex : %p\n", mutex);

    if (pthread_mutex_trylock(mutex)) {
        printf("Cannot acquire Lock. Some instance might be already running\n");
    } else {
        printf("Acquired Lock now sleeping...\n");
        sleep(25);
        pthread_mutex_unlock(mutex);
        pthread_mutex_destroy(mutex);
        shm_unlink(MYMUTEX);
    }

CONSOLE 1

[gbose@seadev:~/mutlti-threaded-client-server$]./mutex
Mutex Id : 3
Mutex : 0x7f3cd591c000
Acquired Lock now sleeping...

CONSOLE 2

[gbose@seadev2:~/mutlti-threaded-client-server$]./mutex
Mutex Id : 3
Mutex : 0x7f09db159000
Cannot acquire Lock. Some instance might be already running