diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..7ca4ac2 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,64 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "(gdb) Unittest Launch", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/code/bin/unittest", + "args": [], + "stopAtEntry": false, + "cwd": "${fileDirname}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, + { + "name": "(gdb) Attach", + "type": "cppdbg", + "request": "attach", + "program": "${workspaceFolder}/code/bin/dining-philosophers", + "processId": "${command:pickProcess}", + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, + { + "name": "(gdb) Launch", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/code/bin/dining-philosophers", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing -gdb-set follow-fork-mode child", + "ignoreFailures": true + }, + { + "text": "-gdb-set follow-fork-mode child" + } + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..440fd4a --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,52 @@ +{ + "files.associations": { + "*.tcc": "cpp", + "limits": "cpp", + "type_traits": "cpp", + "iostream": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "cctype": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "map": "cpp", + "set": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "string": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "utility": "cpp", + "fstream": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "istream": "cpp", + "new": "cpp", + "ostream": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "typeinfo": "cpp" + } +} \ No newline at end of file diff --git a/README.md b/README.md index 6ddcabb..e951559 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Implementation on Dining Philosophers problem on C++ ## Problem Statement -A certain number of philosophers spend their lives alternating between thinking and eating. They are seated around a circular table. There is a fork placed between each pair of neighboring philosophers. Each philosopher has access to the forks at her left and right. In order to eat, a philosopher must be in possession of both forks. A philosopher may only pick up one fork at a time. Each philosopher attempts to pick up the left fork first and then the right fork. When done eating, a philosopher puts both forks back down on the table and begins thinking. Since the philosophers are sharing forks, it is not possible for all of them to be eating at the same time. +A certain number of philosophers spend their lives alternating between thinking and eating. They are seated around a circular table. There is a chopstick placed between each pair of neighboring philosophers. Each philosopher has access to the chopsticks at her left and right. In order to eat, a philosopher must be in possession of both chopsticks. A philosopher may only pick up one chopstick at a time. Each philosopher attempts to pick up the left chopstick first and then the right chopstick. When done eating, a philosopher puts both chopsticks back down on the table and begins thinking. Since the philosophers are sharing chopsticks, it is not possible for all of them to be eating at the same time. This was originally formulated in 1965 by Edsger Dijkstra as a student exam exercise. @@ -15,7 +15,7 @@ This was originally formulated in 1965 by Edsger Dijkstra as a student exam exer ### Waiting State - The philosopher enters into a waiting state when neighbors are eating - - When its neighbors are done, go for the forks + - When its neighbors are done, go for the chopsticks - Its neighbors can't enter into the waiting state if there is a neighbor already waiting ## Tests diff --git a/code/kill-ipcs.sh b/code/kill-ipcs.sh new file mode 100755 index 0000000..f0146f7 --- /dev/null +++ b/code/kill-ipcs.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +ME=`whoami` + +IPCS_S=`ipcs -s | egrep "0x[0-9a-f]+ [0-9]+" | grep $ME | cut -f2 -d" "` +IPCS_M=`ipcs -m | egrep "0x[0-9a-f]+ [0-9]+" | grep $ME | cut -f2 -d" "` +IPCS_Q=`ipcs -q | egrep "0x[0-9a-f]+ [0-9]+" | grep $ME | cut -f2 -d" "` + + +for id in $IPCS_M; do + ipcrm -m $id; +done + +for id in $IPCS_S; do + ipcrm -s $id; +done + +for id in $IPCS_Q; do + ipcrm -q $id; +done + +ipcs \ No newline at end of file diff --git a/code/makefile b/code/makefile index a64da3d..4d31db8 100644 --- a/code/makefile +++ b/code/makefile @@ -58,7 +58,6 @@ $(OUTFILE): $(OBJFILES) $(CXX) -o $(BINDIR)/$@ $^ $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) $(TESTFILE): $(TESTOBJFILES) - echo $(TESTOBJFILES) mkdir -p $(BINDIR) $(CXX) -o $(BINDIR)/$@ $^ $(CXXFLAGS) $(LDFLAGS) $(TSTLIBS) diff --git a/code/src/constants.h b/code/src/constants.h new file mode 100644 index 0000000..48ea946 --- /dev/null +++ b/code/src/constants.h @@ -0,0 +1,21 @@ +#ifndef CONSTANTS_H +#define CONSTANTS_H + +// #ifdef __cplusplus +// extern "C" { +// #endif +#include + +namespace WaitingPhilosopher +{ + #define SEMKEYPATH "/tmp" /* Path used on ftok for semget key */ + #define SHMKEYPATH SEMKEYPATH /* Path used on ftok for shmget key */ + #define OUTPUTPATH "/tmp" /* Path used on ftok for semget key */ + + #define NUMSEMS 1 + #define SIZEOFSHMSEG 2048 +} + +#endif /* PHILOSOPHER_H_ */ + +// EOF \ No newline at end of file diff --git a/code/src/main.cpp b/code/src/main.cpp index 3551fbb..771ffbf 100644 --- a/code/src/main.cpp +++ b/code/src/main.cpp @@ -1,6 +1,99 @@ -#include "waiting-state/philosopher.h" +#include "waiting-state/diningTable.h" -int main() +#include +#include +#include +#include +#include /* read, write, pipe, _exit */ +#include + + +using namespace std; + +/* Flag set by ‘--verbose’. */ +static int verbose_flag; + + + +void displayUsage() +{ + cout << + "-n/--number : Number 'n' of philosophers. \n" + "-h/--help: Show help\n"; + exit(EXIT_SUCCESS); +} + +int main (int argc, char **argv) { - return 0; + int c; + int n = 5; + + while (true) + { + static struct option long_options[] = + { + /* These options set a flag. */ + {"verbose", no_argument, &verbose_flag, 1}, + {"brief", no_argument, &verbose_flag, 0}, + /* These options don’t set a flag. + We distinguish them by their indices. */ + + {"number", required_argument, 0, 'n'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0} + }; + + /* getopt_long stores the option index here. */ + int option_index = 0; + + c = getopt_long (argc, argv, "hn:", + long_options, &option_index); + + + + /* Detect the end of the options. */ + if (c == -1) + break; + + switch (c) + { + case 0: + /* If this option set a flag, do nothing else now. */ + if (long_options[option_index].flag != 0) + break; + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case 'n': + n = atoi(optarg); + break; + case 'h': + case '?': + default: + displayUsage(); + abort (); + } + } + + /* Instead of reporting ‘--verbose’ + and ‘--brief’ as they are encountered, + we report the final status resulting from them. */ + if (verbose_flag) + puts ("verbose flag is set"); + + /* Print any remaining command line arguments (not options). */ + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + putchar ('\n'); + } + + WaitingPhilosopher::DiningTable::startDinner(n); + + exit (EXIT_SUCCESS); } \ No newline at end of file diff --git a/code/src/waiting-state/chopstick.cpp b/code/src/waiting-state/chopstick.cpp new file mode 100644 index 0000000..6d1a7f5 --- /dev/null +++ b/code/src/waiting-state/chopstick.cpp @@ -0,0 +1,62 @@ +#include "chopstick.h" +#include "constants.h" + +#include +#include +#include + +#include + +#include + +using namespace WaitingPhilosopher; + +Chopstick::Chopstick(int index) +{ + int rc= 0; + m_index = index; + + /* Generate an IPC key for the semaphore set and the shared */ + /* memory segment. Typically, an application specific path and */ + /* id would be used to generate the IPC key. */ + m_semkey = ftok(SEMKEYPATH,m_index); + if ( m_semkey == (key_t)-1 ) + { + std::string error = "ID: " + std::to_string(m_index) + " ftok() for sem failed\n"; + perror(error.c_str()); + abort(); + } + + + /* Create a semaphore set using the IPC key. The number of */ + /* semaphores in the set is two. If a semaphore set already */ + /* exists for the key, return an error. The specified permissions*/ + /* give everyone read/write access to the semaphore set. */ + + m_semid = semget( m_semkey, NUMSEMS, 0666 | IPC_CREAT | IPC_EXCL ); + if ( m_semid == -1 ) + { + std::string error = "ID: " + std::to_string(m_index) + " semget() failed\n"; + perror(error.c_str()); + abort(); + } + + // Initialize the semaphore in the set to 0 + rc = semctl( m_semid, 0, SETVAL, 0); + if(rc == -1) + { + std::string error = "ID: " + std::to_string(m_index) + " semctl() initialization failed\n"; + perror(error.c_str()); + + } +} + +void Chopstick::putAway() +{ + int rc = semctl( m_semid, 0, IPC_RMID ); + if (rc==-1) + { + std::string error = "ID: " + std::to_string(m_index) + " semctl() remove id failed\n"; + perror(error.c_str()); + } +} diff --git a/code/src/waiting-state/chopstick.h b/code/src/waiting-state/chopstick.h new file mode 100644 index 0000000..ecf45e4 --- /dev/null +++ b/code/src/waiting-state/chopstick.h @@ -0,0 +1,25 @@ +#ifndef CHOPSTICK_H_ +#define CHOPSTICK_H_ + +// #ifdef __cplusplus +// extern "C" { +// #endif +#include + +namespace WaitingPhilosopher +{ + class Chopstick + { + public: + Chopstick(int index); + void putAway(); + protected: + int m_index; + int m_semid; + key_t m_semkey; + }; +} + +#endif /* CHOPSTICK_H_ */ + +// EOF \ No newline at end of file diff --git a/code/src/waiting-state/diningTable.cpp b/code/src/waiting-state/diningTable.cpp new file mode 100644 index 0000000..88201b8 --- /dev/null +++ b/code/src/waiting-state/diningTable.cpp @@ -0,0 +1,63 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include "diningTable.h" +#include "philosopher.h" +#include "chopstick.h" +#include "constants.h" + +using namespace WaitingPhilosopher; +using namespace std; + +void DiningTable::startDinner(int numberPhilosophers) +{ + Philosopher* philosophers = (Philosopher*)malloc(sizeof(Philosopher) * numberPhilosophers); + Chopstick* chopsticks = (Chopstick*)malloc(sizeof(Chopstick) * numberPhilosophers);; + pid_t pids[numberPhilosophers]; + + printf("\n------ Dining Philosophers ------\n\n\n"); + printf("%i philosophers dining\n\n", numberPhilosophers); + for (int i = 0; i < numberPhilosophers; i++) + { + philosophers[i] = Philosopher(i, numberPhilosophers); + chopsticks[i] = Chopstick(i); + } + + + for (int i = 0; i < numberPhilosophers; i++) + { + if ((pids[i] = fork()) < 0) + { + perror("fork"); + abort(); + } + else if (pids[i] == 0) + { + philosophers[i].dine(); + exit(0); + } + } + + int status; + pid_t pid; + int n = numberPhilosophers; + while (n > 0) + { + pid = wait(&status); + //printf("Child with PID %ld exited with status 0x%x.\n", (long)pid, status); + --n; // TODO(pts): Remove pid from the pids array. + } + + for (int i = 0; i < numberPhilosophers; i++) + { + chopsticks[i].putAway(); + } + + printf("\nFinished Diner\n"); +} \ No newline at end of file diff --git a/code/src/waiting-state/diningTable.h b/code/src/waiting-state/diningTable.h new file mode 100644 index 0000000..2f3c864 --- /dev/null +++ b/code/src/waiting-state/diningTable.h @@ -0,0 +1,20 @@ +#ifndef DININGTABLE_H_ +#define DININGTABLE_H_ + +// #ifdef __cplusplus +// extern "C" { +// #endif +#include + +namespace WaitingPhilosopher +{ + class DiningTable + { + public: + static void startDinner(int numberPhilosophers); + }; +} + +#endif /* DININGTABLE_H_ */ + +// EOF \ No newline at end of file diff --git a/code/src/waiting-state/fork.cpp b/code/src/waiting-state/fork.cpp deleted file mode 100644 index f725c60..0000000 --- a/code/src/waiting-state/fork.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "fork.h" - -using namespace WaitingPhilosopher; - -Fork::Fork() -{ - -} - -bool Fork::isBeingUsed() -{ - return IsUsed; -} - diff --git a/code/src/waiting-state/fork.h b/code/src/waiting-state/fork.h deleted file mode 100644 index d49f296..0000000 --- a/code/src/waiting-state/fork.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef FORK_H_ -#define FORK_H_ - -// #ifdef __cplusplus -// extern "C" { -// #endif -#include - -namespace WaitingPhilosopher -{ - class Fork - { - public: - Fork(); - bool isBeingUsed(); - protected: - bool IsUsed = true; - - - - }; -} - -#endif /* FORK_H_ */ - -// EOF \ No newline at end of file diff --git a/code/src/waiting-state/philosopher.cpp b/code/src/waiting-state/philosopher.cpp index 3ac89c8..c2a5ef5 100644 --- a/code/src/waiting-state/philosopher.cpp +++ b/code/src/waiting-state/philosopher.cpp @@ -1,22 +1,125 @@ #include "philosopher.h" +#include "constants.h" + +#include +#include +#include + +#include + +#include using namespace WaitingPhilosopher; -Philosopher::Philosopher() +Philosopher::Philosopher(int index, int numPhilosophers) { - + m_index = index; + m_numPhilosophers = numPhilosophers; } + void Philosopher::think() -{ +{ + printf("ID: %i - Thinking\n",m_index); } -void Philosopher::wait() +void Philosopher::eat() { + printf("ID: %i - Eating\n",m_index); + usleep(500); + printf("ID: %i - Done eating\n",m_index); +} +void Philosopher::dine() +{ + + int chopstick1 = m_index; + int chopstick2 = (m_index + 1) % m_numPhilosophers ; + + printf("ID: %i - Right Chopstick: %i, Left Chopstick: %i\n",m_index,chopstick1,chopstick2); + + think(); + + waitChopstick(chopstick1); + pickupChopstick(chopstick1); + waitChopstick(chopstick2); + pickupChopstick(chopstick2); + + eat(); + + putdownChopstick(chopstick1); + putdownChopstick(chopstick2); + + + printf("ID: %i - Done\n",m_index); + // std::cout<<"Me: "< + +#include +#include + +#include +#include +#include + #include "waiting-state/philosopher.h" -#include "waiting-state/fork.h" +#include "waiting-state/chopstick.h" +#include "waiting-state/diningTable.h" +#include "constants.h" using namespace WaitingPhilosopher; using namespace std; + +union semun +{ + int val; /* value for SETVAL */ + struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */ + unsigned short int *array; /* array for GETALL & SETALL */ + struct seminfo *__buf; /* buffer for IPC_INFO */ +}; + class WaitingPhilosopherTest : public testing::Test { public: @@ -30,14 +49,89 @@ class WaitingPhilosopherTest : public testing::Test } }; +TEST_F(WaitingPhilosopherTest, chopstickCreation) +{ + int id = 17; + key_t semKey; + int semId; + + semKey = ftok(SEMKEYPATH,id); + ASSERT_FALSE( semKey == (key_t)-1 ); -TEST_F(WaitingPhilosopherTest, evenPhilosophers) + semId = semget( semKey, NUMSEMS, 0666); + ASSERT_TRUE ( semId == -1 ); + + Chopstick testChopstick = Chopstick(id); + + semKey = ftok(SEMKEYPATH,id); + ASSERT_FALSE( semKey == (key_t)-1 ); + + semId = semget( semKey, NUMSEMS, 0666); + ASSERT_FALSE ( semId == -1 ); + + testChopstick.putAway(); + + semKey = ftok(SEMKEYPATH,id); + ASSERT_FALSE( semKey == (key_t)-1 ); + + semId = semget( semKey, NUMSEMS, 0666); + ASSERT_TRUE ( semId == -1 ); +} + +TEST_F(WaitingPhilosopherTest, chopstickOperations) { - int numPh = 6; - Philosopher phs[numPh]; - Fork forks[numPh*2 -1]; - ASSERT_TRUE(forks[0].isBeingUsed()); + int id = 17; + key_t semKey; + int semId; + struct sembuf sempar; + int semval; + union semun arg; + + Chopstick testChopstick = Chopstick(id); + Philosopher aristotle = Philosopher(id, id+1); + semKey = ftok(SEMKEYPATH,id); + ASSERT_FALSE( semKey == (key_t)-1 ); + semId = semget( semKey, NUMSEMS, 0666); + ASSERT_FALSE ( semId == -1 ); + + semval = semctl( semId, 0, GETVAL , arg ); + ASSERT_EQ ( semval, 0 ); + + aristotle.pickupChopstick(id); + + semval = semctl( semId, 0, GETVAL , arg ); + ASSERT_EQ ( semval, 1 ); + + aristotle.pickupChopstick(id); + + semval = semctl( semId, 0, GETVAL , arg ); + ASSERT_EQ ( semval, 2 ); + + aristotle.putdownChopstick(id); + + semval = semctl( semId, 0, GETVAL , arg ); + ASSERT_EQ ( semval, 1 ); + + + testChopstick.putAway(); + + semKey = ftok(SEMKEYPATH,id); + ASSERT_FALSE( semKey == (key_t)-1 ); + + semId = semget( semKey, NUMSEMS, 0666); + ASSERT_TRUE ( semId == -1 ); +} + + +TEST_F(WaitingPhilosopherTest, DISABLED_evenPhilosophers) +{ + DiningTable::startDinner(6); +} + +TEST_F(WaitingPhilosopherTest, DISABLED_oddPhilosophers) +{ + DiningTable::startDinner(5); } diff --git a/docs/index.md b/docs/index.md index 460c766..01e8e48 100644 --- a/docs/index.md +++ b/docs/index.md @@ -5,6 +5,6 @@ ## Problem Statement -A certain number of philosophers spend their lives alternating between thinking and eating. They are seated around a circular table. There is a fork placed between each pair of neighboring philosophers. Each philosopher has access to the forks at her left and right. In order to eat, a philosopher must be in possession of both forks. A philosopher may only pick up one fork at a time. Each philosopher attempts to pick up the left fork first and then the right fork. When done eating, a philosopher puts both forks back down on the table and begins thinking. Since the philosophers are sharing forks, it is not possible for all of them to be eating at the same time. +A certain number of philosophers spend their lives alternating between thinking and eating. They are seated around a circular table. There is a chopstick placed between each pair of neighboring philosophers. Each philosopher has access to the chopsticks at her left and right. In order to eat, a philosopher must be in possession of both chopsticks. A philosopher may only pick up one chopstick at a time. Each philosopher attempts to pick up the left chopstick first and then the right chopstick. When done eating, a philosopher puts both chopsticks back down on the table and begins thinking. Since the philosophers are sharing chopsticks, it is not possible for all of them to be eating at the same time. This was originally formulated in 1965 by Edsger Dijkstra as a student exam exercise.