Why doesn't my mutex work properly in a multi-process C application?
You should have error-checking on your semaphore calls. Use perror()
to display the error if sem_wait()
, sem_init()
or sem_post()
returns non-zero.
Secondly, you a creating more processes than you think. Your first fork()
results in a parent (with producer
non-zero) and a child (with producer
zero). Both processes then execute the second fork()
, so you now have four processes.
Thirdly, the sem_t
variable must be shared between the processes, so it must be stored in a shared memory region. The easiest way to achieve this is with mmap()
:
sem_t *sem = mmap(NULL, sizeof *sem, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
(Execute that prior to the sem_init()
and the first fork()
).
Forthly, it's not defined which process will run first, so you can't rely on the producer thread calling sem_wait()
before the consumer does. Instead, initialise the semaphore to zero with sem_init()
, and only call sem_wait()
in the consumer - this will block the consumer. The producer executes, and calls sem_post()
when it is done, which allows the consumer to proceed.
The sem_init()
call should specify pshared
as non-zero and value
as 0, so it should look like:
if (sem_init(sem, 1, 0) != 0) { perror("sem_init"); exit(1);}
Fifth, wait(NULL)
only waits for a single child process to exit. Call it twice to wait for two child processes.
Just because you fork
the producer thread first doesn't mean the OS will schedule it to run first - its quite possible that the consumer actually runs and gets the lock first.
Also, you should check the return value of sem_wait
- it is posible to return from it without holding the semaphore.
It is also quite possible (as several people have noted in comments) that semaphores may simply not work across fork
ed processes
EDIT - if you pass a non-zero value to argument 2 of sem_init(sem_t *sem, int pshared, unsigned value)
when initializing posix semaphores will work across processes
EDIT - See here for a much better explanation than i could give, complete with source code to do nearly exactly what you want
Have you provided complete code in the question?
If so, you are missing semaphore initialization. You have to call either sem_init
or sem_open
prior to using the semaphore.
Read here.
EDIT You are specifying pshared
= 0 in the sem_init
call. This makes the semaphore process-local (i.e. it can be used only to synchronize threads of one process). fork
creates a child process, so the semaphore does not do anything useful.
If pshared has value 0, then the semaphore is shared between the threads of a process. If pshared is non-zero, then the semaphore is shared between processes.
(the quote is from the link above)