commit 92de58bdc273c8102c3e42f177353b298d7ddd51 Author: Hizenberg Date: Sun Apr 14 14:57:41 2024 +0530 Dining Philosopher Problem Implemented using pthread POSIX Library diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ef13456 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*vsidx +*.opendb +.vs +out \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..80ef37a --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,21 @@ +# CMakeList.txt : CMake project for DiningPhilosopher, include source and define +# project specific logic here. +# +cmake_minimum_required (VERSION 3.8) + +# Enable Hot Reload for MSVC compilers if supported. +if (POLICY CMP0141) + cmake_policy(SET CMP0141 NEW) + set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$,$>,$<$:EditAndContinue>,$<$:ProgramDatabase>>") +endif() + +project ("DiningPhilosopher") + +# Add source to this project's executable. +add_executable (DiningPhilosopher "DiningPhilosopher.c" "din_ph.h") + +if (CMAKE_VERSION VERSION_GREATER 3.12) + set_property(TARGET DiningPhilosopher PROPERTY CXX_STANDARD 20) +endif() + +# TODO: Add tests and install targets if needed. diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..f4bc98b --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,101 @@ +{ + "version": 3, + "configurePresets": [ + { + "name": "windows-base", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}", + "cacheVariables": { + "CMAKE_C_COMPILER": "cl.exe", + "CMAKE_CXX_COMPILER": "cl.exe" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "x64-debug", + "displayName": "x64 Debug", + "inherits": "windows-base", + "architecture": { + "value": "x64", + "strategy": "external" + }, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "x64-release", + "displayName": "x64 Release", + "inherits": "x64-debug", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "x86-debug", + "displayName": "x86 Debug", + "inherits": "windows-base", + "architecture": { + "value": "x86", + "strategy": "external" + }, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "x86-release", + "displayName": "x86 Release", + "inherits": "x86-debug", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "linux-debug", + "displayName": "Linux Debug", + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + }, + "vendor": { + "microsoft.com/VisualStudioRemoteSettings/CMake/1.0": { + "sourceDir": "$env{HOME}/.vs/$ms{projectDirName}" + } + } + }, + { + "name": "macos-debug", + "displayName": "macOS Debug", + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Darwin" + }, + "vendor": { + "microsoft.com/VisualStudioRemoteSettings/CMake/1.0": { + "sourceDir": "$env{HOME}/.vs/$ms{projectDirName}" + } + } + } + ] +} diff --git a/DiningPhilosopher.c b/DiningPhilosopher.c new file mode 100644 index 0000000..8d23d00 --- /dev/null +++ b/DiningPhilosopher.c @@ -0,0 +1,241 @@ +#include +#include +#include +#include +#include "din_ph.h" + + +#define N_PHILOSOPHER 5 + +static phil_t phil[N_PHILOSOPHER]; +static spoon_t spoon[N_PHILOSOPHER]; + +static spoon_t* phil_get_right_spoon(phil_t* phil) { + + int phil_id = phil->phil_id; + + if (phil_id == 0) { + return &spoon[N_PHILOSOPHER-1]; + } + + return &spoon[phil_id - 1]; +} + +static spoon_t* phil_get_left_spoon(phil_t* phil) { + + int phil_id = phil->phil_id; + return &spoon[phil_id]; +} + +void +phil_eat(phil_t* phil) { + + spoon_t* left_spoon = phil_get_left_spoon(phil); + spoon_t* right_spoon = phil_get_right_spoon(phil); + + /* + Check condition that Phil is eating with right set of spoons + */ + + assert(left_spoon->phil == phil); + assert(right_spoon->phil == phil); + assert(left_spoon->is_used == true); + assert(right_spoon->is_used == true); + + phil->eat_count++; + + printf("Phil %d eats with spoon [%d , %d] for %d times\n", + phil->phil_id, left_spoon->spoon_id, right_spoon->spoon_id, + phil->eat_count); + sleep(1); /* let the philosopher eat the cake for 1 second. */ + +} + +bool +philosopher_release_both_spoons(phil_t* phil) { + + spoon_t* left_spoon = phil_get_left_spoon(phil); + spoon_t* right_spoon = phil_get_right_spoon(phil); + + /* . . . */ + + pthread_mutex_lock(&left_spoon->mutex); + pthread_mutex_lock(&right_spoon->mutex); + + assert(left_spoon->phil == phil); + assert(left_spoon->is_used == true); + + assert(right_spoon->phil == phil); + assert(right_spoon->is_used == true); + + printf("phil %d releasing the left spoon %d\n", phil->phil_id, left_spoon->spoon_id); + + left_spoon->phil = NULL; + left_spoon->is_used = false; + + pthread_cond_signal(&left_spoon->cv); + printf("phil %d signalled the phil waiting for left spoon %d\n", + phil->phil_id, left_spoon->spoon_id); + + pthread_mutex_unlock(&left_spoon->mutex); + + printf("phil %d releasing the right spoon %d\n", + phil->phil_id, right_spoon->spoon_id); + + right_spoon->phil = NULL; + right_spoon->is_used = false; + pthread_cond_signal(&right_spoon->cv); + + printf("phil %d signalled the phil waiting for right spoon %d\n", + phil->phil_id, right_spoon->spoon_id); + + pthread_mutex_unlock(&right_spoon->mutex); +} + +bool +philosopher_access_both_spoons(phil_t* phil) { + + spoon_t* left_spoon = phil_get_left_spoon(phil); + spoon_t* right_spoon = phil_get_right_spoon(phil); + + /* before checking status of the spoon, lock it, While one + philosopher is inspecting the state of the spoon, no + other phil must change it */ + + printf("Phil %d waiting for lock on left spoon %d\n", + phil->phil_id, left_spoon->spoon_id); + + pthread_mutex_lock(&left_spoon->mutex); + printf("Phil %d inspecting left spoon %d state\n", + phil->phil_id, left_spoon->spoon_id); + + /* Case 1 : if spoon is begin used by some other phil, then wait */ + while (left_spoon->is_used && + left_spoon->phil != phil) { + + printf("phil %d blocks as left spoon %d is not available\n", + phil->phil_id, left_spoon->spoon_id); + + pthread_cond_wait(&left_spoon->cv, &left_spoon->mutex); + + printf("phil %d recvs signal to grab spoon %d\n", + phil->phil_id, left_spoon->spoon_id); + } + + + /* Case 2 : if spoon is available, grab it and try for another spoon */ + + printf("phil %d finds left spoon %d available, trying to grab it\n", + phil->phil_id, left_spoon->spoon_id); + left_spoon->is_used = true; + left_spoon->phil = phil; + printf("phil %d has successfully grabbed the left spoon %d\n", + phil->phil_id, left_spoon->spoon_id); + pthread_mutex_unlock(&left_spoon->mutex); + + /* Case 2.1 : Trying to grab the right spoon now */ + printf("phil %d now making an attempt to grab the right spoon %d\n", + phil->phil_id, right_spoon->spoon_id); + + /* lock the right spoon before inspecting its state */ + printf("phil %d waiting for lock on right spoon %d\n", + phil->phil_id, right_spoon->spoon_id); + + pthread_mutex_lock(&right_spoon->mutex); + + printf("phil %d inspecting right spoon %d state\n", + phil->phil_id, right_spoon->spoon_id); + + if (right_spoon->is_used == false) { + /* right spoon is also available, grab it and eat */ + right_spoon->is_used = true; + right_spoon->phil = phil; + pthread_mutex_unlock(&right_spoon->mutex); + return true; + } + else if (right_spoon->is_used == true) { + + if (right_spoon->phil != phil) { + printf("phil %d finds right spoon %d is already used by phil %d" + " realeasing the left spoon as well\n", + phil->phil_id, right_spoon->spoon_id, right_spoon->phil->phil_id); + + pthread_mutex_lock(&left_spoon->mutex); + assert(left_spoon->is_used == true); + + assert(left_spoon->is_used == true); + + left_spoon->is_used = false; + left_spoon->phil = NULL; + + printf("phil %d release the left spoon %d\n", + phil->phil_id, left_spoon->spoon_id); + + pthread_mutex_unlock(&left_spoon->mutex); + pthread_mutex_unlock(&right_spoon->mutex); + return false; + } + else { + printf("phil %d already has right spoon %d in hand\n", + phil->phil_id, right_spoon->spoon_id); + pthread_mutex_unlock(&right_spoon->mutex); + return true; + } + } + + assert(0); /* should be dead code */ + return false; +} + +void* +philosopher_fn(void* arg) { + + /* Start implementation of core logic from here */ + + phil_t* phil = (phil_t*)arg; + + while (1) { + + if (philosopher_access_both_spoons(phil)) { + + phil_eat(phil); + philosopher_release_both_spoons(phil); + sleep(1); /* Phil wait for 1 sec to again attempt to get the spoon */ + } + } + +} + +int +main(int argv, char* argc[]) { + + int i = 0; + pthread_attr_t attr; + + /*Initialize all spoons*/ + for (i = 0; i < N_PHILOSOPHER; i++) { + + spoon[i].spoon_id = i; + spoon[i].is_used = false; + spoon[i].phil = NULL; + pthread_mutex_init(&spoon[i].mutex, NULL); + pthread_cond_init(&spoon[i].cv, NULL); + + } + + /* Default attributes of all Philosopher threads */ + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + /* create philosopher threads */ + for (i = 0; i < N_PHILOSOPHER; i++) { + + phil[i].phil_id = i; + phil[i].eat_count = 0; + pthread_create(&phil[i].thread_handle, &attr, philosopher_fn, &phil[i]); + + } + + pthread_exit(0); + return 0; +} \ No newline at end of file diff --git a/din_ph.h b/din_ph.h new file mode 100644 index 0000000..527b488 --- /dev/null +++ b/din_ph.h @@ -0,0 +1,23 @@ +#ifndef __DIN_PH_H__ +#define __DIN_PH_H__ + +#include +#include + +typedef struct phil_ { + + int phil_id; + pthread_t thread_handle; + int eat_count; +} phil_t; + +typedef struct spoon_ { + + int spoon_id; + bool is_used; /* bool to indicate if the spoon is being used or not */ + phil_t* phil; /* If used, then which philosopher is using it */ + pthread_mutex_t mutex; /* For Mutual Exclusion */ + pthread_cond_t cv; /* For thread Co-ordination competing for this Resource */ +} spoon_t; + +#endif /* __DIN_PH_H__ */ \ No newline at end of file