← Back to all projects

POSIX Semaphores Roller Coaster

C · Systems · Open Source

Simulates a roller coaster loading system using C, pthreads and semaphores, prioritizing different passenger types (premium, groups, loners) while preventing starvation.

It demonstrates advanced concurrency control, queue management and fairness strategies in a multithreaded environment.

Simulation output

Source · 1 file

Semaphores.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>

#define buffLonersSize 10
#define buffGroupSize 10
#define buffPremiumSize 10

// The amount of seats offered for each group. Row 1 would be the premium spaces. Row 2 and 3 are normal spaces.
#define normalSpaces 6
#define premiumSpaces 3

// The amount of time each thread sleeps after inputting.
#define sleepLoners 2
#define sleepGroups 1
#define sleepPremium 2
#define sleepCart 10

sem_t riders, spacesGroups,  spacesLoners, spacesPremium, mutex;
int groupBuffer[buffGroupSize] = {0};
int lonerBuffer[buffLonersSize] = {0};
int premiumBuffer[buffPremiumSize] = {0};

int inGroup = 0, inLoner = 0, outGroup = 0, outLoner = 0, inPremium = 0, outPremium = 0; // Used to track indexes
int lonersStarving = 0; // Increases every time a cart leaves without loners

// To track queue sizes
int currentPremiumCount = 0;
int currentGroupCount = 0;
int currentLonerCount = 0;

void insertPremium(int *pickedPremium, int *room) {
    while (*pickedPremium < 3 && *room >= 1 && currentPremiumCount > 0) {
        int groupSize = premiumBuffer[outPremium];
        if (*room >= groupSize && groupSize >= 1) {
            *room -= groupSize;
            premiumBuffer[outPremium] = 0;
            outPremium = (outPremium + 1) % buffPremiumSize;
            currentPremiumCount--;
            sem_post(&spacesPremium);
            (*pickedPremium)++;
            printf(" a premium group of %d,", groupSize);
        } else break;
    }
}

void insertGroups(int *pickedGroups, int *room) {
    while (*pickedGroups < 3 && *room >= 2 && currentGroupCount > 0) {
        int groupSize = groupBuffer[outGroup];
        if (*room >= groupSize && groupSize >= 2) {
            *room -= groupSize;
            groupBuffer[outGroup] = 0;
            outGroup = (outGroup + 1) % buffGroupSize;
            currentGroupCount--;
            sem_post(&spacesGroups);
            (*pickedGroups)++;
            printf(" a group of %d,", groupSize);
        } else break;
    }
}

void insertLoners(int *pickedLoners, int *room) {
    while (*room > 0 && currentLonerCount > 0) {
        int lonerSize = lonerBuffer[outLoner];
        if (lonerSize == 1) {
            *room -= 1;
            lonerBuffer[outLoner] = 0;
            outLoner = (outLoner + 1) % buffLonersSize;
            currentLonerCount--;
            sem_post(&spacesLoners);
            (*pickedLoners)++;
        } else break;
    }
}

void *cartThread() {
    while (1) {
        sleep(sleepCart);

        sem_wait(&riders);
        sem_wait(&mutex);

        int room = normalSpaces;
        int premiumRoom = premiumSpaces;

        int pickedLoners = 0;
        int pickedGroups = 0;
        int pickedPremium = 0;

        if (lonersStarving >= 2) { // 2 carts left with no loners
            room = 1;
            printf("\nLoners were starved! Attempting to insert at least 1 loner.");
            insertLoners(&pickedLoners, &room);
            room = normalSpaces - pickedLoners; // 1 loner should have been added if there is any on the queue
            if (pickedLoners != 0) lonersStarving = 0; // Reset the signal only if a loner was added.
        }

        printf("\nThe cart departed with:");

        insertPremium(&pickedPremium, &premiumRoom);

        // If we want to fill the whole cart, in case the premium queue is empty
//		if (pickedPremium == 0) {
//			room += premiumRoom; // Keep in mind I'm still only picking 3 groups max, so a lot of loners might enter if this is enabled
//		}

        insertGroups(&pickedGroups, &room);
        insertLoners(&pickedLoners, &room);

        if (pickedGroups > 0 || pickedPremium > 0) printf(" and");
        if (pickedLoners == 0) lonersStarving++;
        else lonersStarving = 0;

        printf(" %d loner(s). \n\n",  pickedLoners);

        printf("The queues values at this moment are: \nPremium Queue: %d\nGroups Queue: %d\nLoners Queue: %d\n\n",
               currentPremiumCount,
               currentGroupCount,
               currentLonerCount);

        sem_post(&mutex);
    }

    pthread_exit(0);
}

void *groupThread() {
    while (1) {
        int r = rand() % (5 + 1 - 2) + 2; // From 2-5

        sem_wait(&spacesGroups);
        sem_wait(&mutex);

        groupBuffer[inGroup] = r;
        currentGroupCount++;
        printf("[GROUP] GROUP OF %d ENTERED.\n", r);
        inGroup = (inGroup + 1) % buffGroupSize;

        sem_post(&mutex);
        sem_post(&riders);

        sleep(sleepGroups);
    }

    pthread_exit(0);
}

void *lonerThread() {
    while (1) {
        sem_wait(&spacesLoners);
        sem_wait(&mutex);

        lonerBuffer[inLoner] = 1;
        currentLonerCount++;
        printf("[LONER] LONER ENTERED.\n");
        inLoner = (inLoner + 1) % buffLonersSize;

        sem_post(&mutex);
        sem_post(&riders);
        sleep(sleepLoners);
    }

    pthread_exit(0);
}

void *premiumThread() {
    while (1) {
        int r = rand() % (3 + 1 - 1) + 1; // Can be 1 user or a group up to 3

        sem_wait(&spacesPremium);
        sem_wait(&mutex);

        premiumBuffer[inPremium] = r;
        currentPremiumCount++;
        printf("[PREMIUM] GROUP OF %d ENTERED.\n", r);
        inPremium = (inPremium + 1) % buffPremiumSize;

        sem_post(&mutex);
        sem_post(&riders);

        sleep(sleepPremium);
    }

    pthread_exit(0);
}

int main(int argc, char * argv[]) {
    int targ[4];
    pthread_t thread[4];

    sem_init(&mutex, 0, 1);
    sem_init(&spacesGroups, 0, buffGroupSize);
    sem_init(&spacesLoners, 0, buffLonersSize);
    sem_init(&spacesPremium, 0, buffPremiumSize);
    sem_init(&riders, 0, 0);

    pthread_create(&thread[0], NULL, &groupThread, (void *) &targ[0]);
    pthread_create(&thread[1], NULL, &lonerThread, (void *) &targ[1]);
    pthread_create(&thread[2], NULL, &premiumThread, (void *) &targ[2]);
    pthread_create(&thread[3], NULL, &cartThread,(void *) &targ[3]);

    for(int i = 0; i < 4; i++) {
        pthread_join(thread[i], NULL);
    }

    sem_destroy(&spacesGroups);
    sem_destroy(&spacesLoners);
    sem_destroy(&spacesPremium);
    sem_destroy(&riders);
    sem_destroy(&mutex);

    return 0;
}
← Back to all projects