15

There are many questions on stackoverflow about if a pthread mutex can be shared between processes, but I found no questions/answers regarding initialization of the shared mutex.

As far as I understand, the common way of using a process-shared mutex is the following: allocate a block of shared memory, initialize a pthread mutex on the shared memory block, use it.

In case of shared memory creation, it is handled by OS if multiple processes try to allocate a shared memory block with the same key ID. OK, but what I don't understand is how can I initialize a mutex on the shared memory block safely?

Am I right that the pthread_mutex_init doesn't provide any safe approach to initialize the pthread_mutex_t simultaneously from different processes? If yes, how can I provide exclusive access for processes to initialize a shared "mutual exclusion"? And how can I make sure if another process initialized the mutex successfully or not?

The second question relates to a case when a process blocking a mutex crashes. OK, there is a robust mutex which handles such cases and returns a corresponding error code. What about the shared memory block? It seems like a process should take care about if it is the last process which uses the shared memory to destroy it.

Rom098
  • 2,445
  • 4
  • 35
  • 52
  • 3
    Out of curiosity, why not use POSIX semaphores instead? http://man7.org/linux/man-pages/man7/sem_overview.7.html Unlike pthread_mutex, POSIX semaphores are _intended_ for inter-process communication. – Solomon Slow Mar 06 '17 at 18:01
  • 2
    @jameslarge This is a good question. Some people advise to use mutexes instead of semaphores. For example, http://stackoverflow.com/q/6477525/465662 I'm trying to find any real advantages – Rom098 Mar 06 '17 at 18:31
  • 1
    @Rom098, It is better in terms of OS scheduling perspective. Mutex has a clear ownership hence scheduler can know which process to schedule (schedule a process that has lock.) – rjhcnf Apr 07 '21 at 18:43

2 Answers2

13

Am I right that the pthread_mutex_init doesn't provide any safe approach to initialize the pthread_mutex_t simultaneously from different processes?

Correct. It is up to you to ensure that only one process calls pthread_mutex_init() on the mutex, and that no process tries to operate on the mutex until that call has successfully returned.

For example, with POSIX shm_open() shared memory regions, you can have the processes try to open the region with the O_CREAT and O_EXCL flags, so that exactly one process will succeed in creating it. This process is then responsible for resizing the shared memory region and initialising the mutex with pthread_mutex_init(). The other processes must then wait for some kind of notification from the initialising process before opening the shared memory region - eg you could have the processes block opening a FIFO O_RDONLY, and have the initialising process notify them by opening the FIFO O_WRONLY (which will cause the open to succeed).

Usually, a shared memory segment will not be the only communication channel between the processes. Typically you would bootstrap the communication through a UNIX domain socket and negotiate the setup of the shared memory region over it, probably even passing the shared memory region file descriptor through the socket with a SCM_RIGHTS message. The shared memory region would then be used to accelerate the performance-sensitive IPC.

caf
  • 233,326
  • 40
  • 323
  • 462
  • Can one not use the pthread_once(3) to achieve init safely? I know one can for threads of a given process; would it work across processes assuming the 'once_control' variable is also in shared memory? – kaiwan Sep 13 '18 at 11:58
  • 3
    @kaiwan: POSIX only says that `pthread_once()` works between threads of the same process. It does say that the `pthread_once_t` variable has to be statically initialized which is a problem for placing it in a shared memory area. – caf Sep 13 '18 at 12:48
  • We need to handle the case when process which created the shared memory crashes before notifying other processes. – abhiarora Jul 24 '20 at 08:58
  • What would happen when if system restarts? Then do we need to reinitialize that file containing the mutex? – abhiarora Jul 24 '20 at 09:02
6

I use mmap to create a shared memory block.

int fd = open(filename, O_RDWR | O_CLOEXEC);

when the file does not exist, then fd < 0, then I try to initialize a mutex memory.

fd = open(filename, O_RDWR | O_CREAT | O_CLOEXEC | O_TRUNC | O_EXCL,
            S_IRUSR);
if(fd < 0) {
     // two processes might try to create the file simultaneously.
     // one open is success, the other one fail because of O_EXCL.
     sleep_random_10ms();
     continue; // continue and try again.
}

Note: because of S_IRUSR, after open is success and the file is created, the file is still not writable. Any other processes will not be able to open the file with O_RDWR, so that the first open still fail. No process will use the mutex while we are initializing the mutex.

after the file is created, we initialize a mutex as usual

pthread_mutexattr_init(&att);
pthread_mutexattr_setrobust(&att);
pthread_mutexattr_setpshared(&att, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&mutex, &att)
write(fd, &mutex, sizeof(mutex))
fchmod(fd, S_IRUSR | S_IWUSR)
close(fd);

The last two steps make the file writable so that any other process will open the file successfully at the first attempt.

We start to use the mutex as usual

mutex = (pthread_mutex_t*) mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0);
pthread_mutex_lock(mutex);
....
pthread_mutex_unlock(mutex);

to clean up

munmap(&mutex);
close(fd);
Conrad Meyer
  • 2,851
  • 21
  • 24
wcy
  • 875
  • 11
  • 12
  • Looks pretty good solution to me. I hope if someone has used it and then they should provide their feedback. – abhiarora Jul 24 '20 at 09:00
  • 3
    Note you don't really need a file. `shm_open` would be better. – Louis Go Dec 30 '20 at 07:09
  • I suppose the processes using the shared-memory will lock up if the creator dies between open() and fchmod(). But I have yet to find an alternative that doesn't have that problem. – ulatekh Oct 08 '21 at 20:09
  • I wrote [an implementation](https://github.com/hnakamur/luajit-pshared-mmapf-experiment) in [LuaJIT](https://luajit.org/) and it seems working as expected. Thanks! I changed it a bit, used `O_SYNC` open flag and initialize a mutex on the shared memory instead of using `write` syscall. – hnakamur Dec 03 '22 at 17:16
  • I also wrote an example using `shm_open` (instead of `open`) and `pthread_rwlock_t` (instead of `pthread_mutex_t`). – hnakamur Dec 04 '22 at 11:53