Semaphore in Operating System with it's own implementation in C.

Semaphore in Operating System with it's own implementation in C.

A semaphore is a synchronization construct used in operating systems to control access to shared resources among multiple processes or threads. It is essentially a variable or an abstract data type that provides two fundamental operations: "wait" and "signal" (also known as "P" and "V" operations, respectively).

Here's how semaphores work:

Wait Operation (P): When a process/thread wants to access a shared resource, it first performs a "wait" operation on the semaphore associated with that resource. If the semaphore value is positive (greater than 0), it decrements the value by 1 and continues accessing the resource. If the semaphore value is zero, indicating that the resource is currently being used by another process/thread, the process/thread is blocked or put to sleep until the semaphore becomes available.

Signal Operation (V): When a process/thread finishes using a shared resource, it performs a "signal" operation on the semaphore, which increments the semaphore value by 1. This operation indicates that the resource is now available for other processes/threads waiting on it. If there are any blocked processes/threads waiting for the semaphore, one of them is awakened and granted access to the resource.

Semaphores provide a mechanism to enforce mutual exclusion and prevent race conditions, ensuring that only one process/thread can access a shared resource at a time. They are commonly used in scenarios such as controlling access to critical sections of code, managing concurrent access to shared data structures, and implementing synchronization mechanisms like locks and barriers.

Semaphores can be implemented as either counting semaphores (allowing non-negative integer values) or binary semaphores (with values limited to 0 and 1), depending on the specific synchronization requirements.

Code:

#include <stdio.h>
#include <pthread.h>

typedef struct {
    pthread_mutex_t mutex;
    pthread_cond_t condition;
    int value;
} semaphore;

void semaphore_init(semaphore* sem, int initial_value) {
    pthread_mutex_init(&(sem->mutex), NULL);
    pthread_cond_init(&(sem->condition), NULL);
    sem->value = initial_value;
}

void semaphore_wait(semaphore* sem) {
    pthread_mutex_lock(&(sem->mutex));
    while (sem->value <= 0) {
        pthread_cond_wait(&(sem->condition), &(sem->mutex));
    }
    sem->value--;
    pthread_mutex_unlock(&(sem->mutex));
}

void semaphore_signal(semaphore* sem) {
    pthread_mutex_lock(&(sem->mutex));
    sem->value++;
    pthread_cond_signal(&(sem->condition));
    pthread_mutex_unlock(&(sem->mutex));
}

void* thread_function(void* arg) {
    semaphore* sem = (semaphore*)arg;

    printf("Thread waiting\n");
    semaphore_wait(sem);

    printf("Thread acquired the semaphore\n");
    // Perform some critical section or shared resource access here

    printf("Thread releasing the semaphore\n");
    semaphore_signal(sem);

    return NULL;
}

int main() {
    semaphore sem;
    semaphore_init(&sem, 1); // Initialize the semaphore with initial value 1

    pthread_t thread;
    pthread_create(&thread, NULL, thread_function, (void*)&sem);

    // Main thread also performs some work
    printf("Main thread performing some work\n");

    // Main thread waits for the semaphore
    printf("Main thread waiting\n");
    semaphore_wait(&sem);

    printf("Main thread acquired the semaphore\n");
    // Perform some critical section or shared resource access here

    printf("Main thread releasing the semaphore\n");
    semaphore_signal(&sem);

    pthread_join(thread, NULL);

    return 0;
}

Output:

Main thread performing some work
Main thread waiting
Main thread acquired the semaphore
Main thread releasing the semaphore
Thread waiting
Thread acquired the semaphore
Thread releasing the semaphore

Hope you liked this.