Lab Report: Classical Problems of Synchronization
using Mutex and Semaphore
Date of Completion: 11 September 2025
Objectives: Implement and demonstrate solutions to Producer-Consumer and Dining Philosophers using
POSIX mutexes and semaphores. Demonstrate avoidance of race conditions, deadlock, and starvation.
Theory (expanded): In concurrent systems multiple threads access shared data. Without coordination,
race conditions occur where outcome depends on timing. Synchronization primitives: mutex (binary lock),
semaphore (counting/binary), condition variables. Strategies to avoid deadlock: ordering resources, using
an odd-even pick strategy for dining philosophers, using try-lock and back-off, or resource hierarchy.
Starvation can be avoided by fair locking or using semaphores that ensure FIFO ordering.
Algorithms & Design Decisions:
Producer-Consumer (bounded buffer): Use a circular buffer, a mutex to protect buffer, 'empty' semaphore
initialized to buffer_size and 'full' initialized to 0. Producers wait(empty), lock mutex, insert, unlock,
post(full). Consumers wait(full), lock, remove, unlock, post(empty).
Flowchart (Producer):
[Start] -> Produce Item -> wait(empty) -> lock(mutex) -> Insert into buffer -> unlock(mutex) -> post(full) ->
Repeat
Dining Philosophers (avoid deadlock): Use an array of mutexes for forks and a semaphore limiting
maximum philosophers who may try to pick forks (e.g., N-1) to prevent cyclical wait.
Program (synchronization.c)
/* synchronization.c
Producer-Consumer and Dining Philosophers examples using pthreads, mutex and semaphores.
Compile: gcc -o synchronization synchronization.c -lpthread -lrt
*/
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
/* -------- Producer-Consumer (bounded buffer) -------- */
#define BSIZE 5
int buffer[BSIZE];
int in = 0, out = 0;
pthread_mutex_t mutex;
sem_t empty, full;
void *producer(void *arg) {
int id = *(int*)arg;
for (int i=0;i<10;i++) {
int item = id*100 + i;
sem_wait(&empty);
pthread_mutex_lock(&mutex);
buffer[in] = item;
in = (in+1) % BSIZE;
printf("P%d produced %d\\n", id, item);
pthread_mutex_unlock(&mutex);
sem_post(&full);
usleep(100000);
}
return NULL;
}
void *consumer(void *arg) {
int id = *(int*)arg;
for (int i=0;i<10;i++) {
sem_wait(&full);
pthread_mutex_lock(&mutex);
int item = buffer[out];
out = (out+1) % BSIZE;
printf("C%d consumed %d\\n", id, item);
pthread_mutex_unlock(&mutex);
sem_post(&empty);
usleep(150000);
}
return NULL;
}
/* -------- Dining Philosophers (avoid deadlock by allowing max N-1 eaters) -------- */
#define NPHIL 5
pthread_mutex_t forks[NPHIL];
sem_t room; // allows up to N-1 philosophers into room
void *philosopher(void *arg) {
int id = *(int*)arg;
int left = id;
int right = (id+1) % NPHIL;
for (int i=0;i<5;i++) {
// thinking
printf("Philosopher %d thinking\\n", id);
usleep(100000 + (rand()%200000));
// enter room (prevent deadlock)
sem_wait(&room);
// pick forks (lock smaller id first to avoid deadlock further)
if (left < right) { pthread_mutex_lock(&forks[left]); pthread_mutex_lock(&forks[right]); }
else { pthread_mutex_lock(&forks[right]); pthread_mutex_lock(&forks[left]); }
printf("Philosopher %d eating\\n", id);
usleep(200000);
pthread_mutex_unlock(&forks[left]);
pthread_mutex_unlock(&forks[right]);
sem_post(&room);
}
return NULL;
}
int main() {
/* Producer-consumer demo */
pthread_t p1,p2,c1,c2;
int id1=1,id2=2;
pthread_mutex_init(&mutex,NULL);
sem_init(&empty,0,BSIZE);
sem_init(&full,0,0);
pthread_create(&p1,NULL,producer,&id1);
pthread_create(&p2,NULL,producer,&id2);
pthread_create(&c1,NULL,consumer,&id1);
pthread_create(&c2,NULL,consumer,&id2);
pthread_join(p1,NULL); pthread_join(p2,NULL); pthread_join(c1,NULL); pthread_join(c2,NULL);
pthread_mutex_destroy(&mutex); sem_destroy(&empty); sem_destroy(&full);
/* Dining philosophers demo */
pthread_t ph[NPHIL];
int ids[NPHIL];
sem_init(&room,0,NPHIL-1);
for (int i=0;i<NPHIL;i++) { pthread_mutex_init(&forks[i],NULL); ids[i]=i; }
for (int i=0;i<NPHIL;i++) pthread_create(&ph[i],NULL,philosopher,&ids[i]);
for (int i=0;i<NPHIL;i++) pthread_join(ph[i],NULL);
for (int i=0;i<NPHIL;i++) pthread_mutex_destroy(&forks[i]);
sem_destroy(&room);
return 0;
}
Sample Output (truncated):
P1 produced 100
P2 produced 200
C1 consumed 100
P1 produced 101
...
Philosopher 0 thinking
Philosopher 0 eating
...