This program implements the producer-consumer problem on a table in shared memory that holds 2 items and a semaphore.
To watch the shared memory change as the program runs, run:
make watch
Or you can do the same thing manually by running
watch -n .2 xxd -e /dev/shm/producerConsumerTable
in one terminal and running
./producer 1.0 &
./consumer 1.0
in another. 1.0
sets the delay in the main loop of each program to 1s.
/dev/shm/producerConsumerTable
contains 36 bytes:
- 00-31: semaphore
- 32-33: item 1
- 34-35: item 2
./[consumer | producer] [delay [num_threads]]
After running make
, run either ./producer
or ./consumer
. Both take optional arguments delay (default 0.01) and number of threads. If number of threads is specified, delay must be specified.
The program is composed of 3 files: producer.c
, consumer.c
, and shared.h
, the latter of which contains functionality shared by both. In particular, shared.h
contains a run
function that accepts a function pointer as argument. producer.c
and consumer.c
define produceItem
and consumeItem
, respectively, and pass those functions to run
along with the command-line arguments.
run
calls initializeSharedMemory
to open the shared memory table, parses optional arguments, and then creates num_threads
threads (default 1) that each run the function passed to run
.
Run make test
to build and run both producer and consumer. Producer produces 14 items and exits. Consumer is killed after by Make after an appropriate delay. This is repeated up to 100 times, and if none match the expected output, the test fails. The reason for repeating is because the scheduler can cause some uncertainty in ordering. It is possible to mitigate this by running the programs with larger delays, but the current testing setup usually succeeds within a few loops, which is quicker than increasing the delay.
make test
first tests with single threads, and then tests the case where producer and consumer each run with two threads.
Valgrind confirmed that no memory leaks are possible when the programs exit normally, but handling memory when the program is interrupted was not made robust.
- Restrict syscalls with SECCOMP for security.
- Ensure the mutex is released and all file descriptors are closed if the program is interrupted.
- Implement custom semaphores.
<semaphore.h>
is overkill for what we need and uses 32 bytes of memory!