Master the fundamental concepts of inter-process communication through this focused micro-challenge.
You have read the whole brief, and the concepts above stay free on every task. Writing and running the code needs a plan.
Three hints are available for this task, revealed one at a time inside the code workspace so you can struggle productively before seeing them.
Every task includes starter code, theory, and hidden tests so you can implement and verify locally in the browser.
How it worksShared mappings remove copy cost but introduce data races. Producers and consumers must synchronize access to indices and payloads with semaphores, mutexes, or atomics in the shared segment.
Shared control block:
For example, a ring in shared memory with two semaphores (items, spaces) mirrors kernel pipe backpressure without syscalls per byte.
PostgreSQL and Oracle's shared-memory buffer pools pair exactly this SysV semaphore pattern with shared segments because raw shared memory has zero built-in coordination, meaning concurrent writers can interleave byte-by-byte and corrupt a struct mid-update. The unmatched wait/signal bug this task guards against is a real, common source of production hangs: a process that crashes while holding a semaphore leaves every future waiter blocked forever unless the semaphore has SEM_UNDO semantics.
Before you call the implementation done, walk failure modes on purpose. Test empty structures, single-element edge cases, maximum concurrency, and errno paths that must not crash the program. OS code usually fails in production when happy-path tests pass but invariants break under contention or memory pressure.
Keep structures small and name fields after kernel counterparts when possible. That lets you read man pages and kernel source side by side while you work. Print observable events during development; remove noisy logs once tests pass reliably.
You will pair your shared memory segment with semaphores for a bounded buffer. This exercise requires proving no torn reads occur under concurrent load.
Protect shared memory with System V semaphores. The task has two parts.
First, several worker processes increment one shared counter, and a binary semaphore used as a mutex makes every read-modify-write atomic, so no increment is lost.
Second, a classic bounded buffer: a producer process and a consumer process share a ring of K slots. Three semaphores coordinate it: mutex (1), empty (K, free slots) and full (0, filled slots). Then nobody spins and nobody overruns the ring.
cLoading…
shmget + shmat, or mmap(MAP_SHARED | MAP_ANONYMOUS)), created before any fork, holding the counter and the ring.semget(IPC_PRIVATE, 3, IPC_CREAT | 0600): semaphore 0 = mutex, 1 = empty, 2 = full, initialised with semctl(SETVAL). P/V are semop with -1/+1.N workers. Each does M times: P(mutex); counter = counter + 1; V(mutex), then exits. The parent waits for all of them.P(empty); P(mutex); put; V(mutex); V(full), and it finishes by putting the sentinel. The parent consumes with P(full); P(mutex); take; V(mutex); V(empty) until the sentinel, printing each value. Use a separate "last item" flag for the sentinel, so any integer can be sent.semctl(GETVAL), then remove the set (IPC_RMID) and the segment.cLoading…
Only the parent prints. Children must not write to stdout.
Input:
cLoading…
Output:
cLoading…
union semun yourself (glibc doesn't); semctl(id, n, SETVAL, arg) takes it by value.P is matched by a V. The final values printed prove it: mutex=1, empty=K, full=0.Hidden tests cover many workers with many increments, a ring of one slot, more values than slots, negative values, and no values at all.