Created
October 25, 2017 11:05
-
-
Save celestialphineas/e347b70be7002192b980f08b64373fa6 to your computer and use it in GitHub Desktop.
Simulation of traffic scheduling on a crossroad with multiple threads (OS lab)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <cstdio> | |
#include <cstdlib> | |
#include <cstring> | |
#include <deque> | |
#include <unistd.h> | |
#include <pthread.h> | |
#include <algorithm> | |
// #define TEST_OUTPUT | |
using std::deque; | |
#ifdef TEST_OUTPUT | |
pthread_mutex_t outputLock; | |
#endif | |
// Base class entrance | |
// Entrances of the road is modeled with this class | |
// Derivants of the class must be singleton | |
class Entrance { | |
public: | |
// Entrance mutexes | |
pthread_mutex_t mutex; | |
// Entrance mutex for cond | |
pthread_mutex_t condMutex; | |
// Conditions for changes of the waiting queue | |
pthread_cond_t changes; | |
// Queue of cars waiting for their turn to head towards a direction | |
deque<int> waitingCars; | |
protected: | |
Entrance() { | |
pthread_mutex_init(&mutex, NULL); | |
pthread_mutex_init(&condMutex, NULL); | |
pthread_cond_init(&changes, NULL); | |
} | |
}; | |
class NorthEntrance: public Entrance { | |
private: | |
static Entrance *instance; | |
NorthEntrance(): Entrance() {} | |
public: | |
static Entrance &getInstance() { | |
if(instance) return *instance; | |
instance = new NorthEntrance(); | |
return *instance; | |
} | |
}; | |
Entrance *NorthEntrance::instance = NULL; | |
class WestEntrance: public Entrance { | |
private: | |
static Entrance *instance; | |
WestEntrance(): Entrance() {} | |
public: | |
static Entrance &getInstance() { | |
if(instance) return *instance; | |
instance = new WestEntrance(); | |
return *instance; | |
} | |
}; | |
Entrance *WestEntrance::instance = NULL; | |
class SouthEntrance: public Entrance { | |
private: | |
static Entrance *instance; | |
SouthEntrance(): Entrance() {} | |
public: | |
static Entrance &getInstance() { | |
if(instance) return *instance; | |
instance = new SouthEntrance(); | |
return *instance; | |
} | |
}; | |
Entrance *SouthEntrance::instance = NULL; | |
class EastEntrance: public Entrance { | |
private: | |
static Entrance *instance; | |
EastEntrance(): Entrance() {} | |
public: | |
static Entrance &getInstance() { | |
if(instance) return *instance; | |
instance = new EastEntrance(); | |
return *instance; | |
} | |
}; | |
Entrance *EastEntrance::instance = NULL; | |
// Abstract base class Road | |
// Road's derivants must be a singleton | |
class Road { | |
public: | |
// Car mutexes | |
pthread_mutex_t mutex; | |
// Mutex for the condition | |
pthread_mutex_t condMutex; | |
// Conditions for changes of the queue | |
pthread_cond_t changes; | |
// Show which car is occupying the road | |
int car; | |
bool isBusy() {return !!car;} | |
// Queue of cars waiting for the road's occupation | |
deque<int> waitingCars; | |
virtual bool findDeadlock() = 0; | |
protected: | |
Road(): car(0) { | |
pthread_mutex_init(&mutex, NULL); | |
pthread_cond_init(&changes, NULL); | |
} | |
}; | |
class RoadA: public Road { | |
private: | |
static Road *instance; | |
RoadA(): Road() {} | |
public: | |
static Road &getInstance() { | |
if(instance) return *instance; | |
instance = new RoadA(); | |
return *instance; | |
} | |
// Override | |
bool findDeadlock(); | |
}; | |
class RoadB: public Road { | |
private: | |
static Road *instance; | |
RoadB(): Road() {} | |
public: | |
static Road &getInstance() { | |
if(instance) return *instance; | |
instance = new RoadB(); | |
return *instance; | |
} | |
// Override | |
bool findDeadlock(); | |
}; | |
class RoadC: public Road { | |
private: | |
static Road *instance; | |
RoadC(): Road() {} | |
public: | |
static Road &getInstance() { | |
if(instance) return *instance; | |
instance = new RoadC(); | |
return *instance; | |
} | |
// Override | |
bool findDeadlock(); | |
}; | |
class RoadD: public Road { | |
private: | |
static Road *instance; | |
RoadD(): Road() {} | |
public: | |
static Road &getInstance() { | |
if(instance) return *instance; | |
instance = new RoadD(); | |
return *instance; | |
} | |
// Override | |
bool findDeadlock(); | |
}; | |
// Definitions of the class members | |
Road *RoadA::instance = NULL; | |
bool RoadA::findDeadlock() { | |
return RoadB::getInstance().isBusy() | |
&& RoadC::getInstance().isBusy() | |
&& RoadD::getInstance().isBusy(); | |
} | |
Road *RoadB::instance = NULL; | |
bool RoadB::findDeadlock() { | |
return RoadA::getInstance().isBusy() | |
&& RoadC::getInstance().isBusy() | |
&& RoadD::getInstance().isBusy(); | |
} | |
Road *RoadC::instance = NULL; | |
bool RoadC::findDeadlock() { | |
return RoadA::getInstance().isBusy() | |
&& RoadB::getInstance().isBusy() | |
&& RoadD::getInstance().isBusy(); | |
} | |
Road *RoadD::instance = NULL; | |
bool RoadD::findDeadlock() { | |
return RoadA::getInstance().isBusy() | |
&& RoadB::getInstance().isBusy() | |
&& RoadC::getInstance().isBusy(); | |
} | |
// Base class for cars | |
// +---+---+ | |
// | 3 | 2 | | |
// +---+---+ | |
// | 4 | 1 | | |
// +---+---+ | |
// ^ Entrance of the car | |
class Car { | |
public: | |
int id; | |
Car(int id_): id(id_) {} | |
virtual ~Car() {} | |
virtual Entrance &getEntrance() = 0; | |
virtual Road &getRoad1() = 0; | |
virtual Road &getRoad2() = 0; | |
virtual Road &getRoad3() = 0; | |
virtual Road &getRoad4() = 0; | |
virtual std::string getDirectionStr() = 0; | |
virtual std::string getLeftStr() = 0; | |
}; | |
class NorthCar: public Car { | |
public: | |
NorthCar(int id_): Car(id_) {} | |
~NorthCar() {} | |
Entrance &getEntrance() { return NorthEntrance::getInstance(); } | |
Road &getRoad1() { return RoadA::getInstance(); } | |
Road &getRoad2() { return RoadB::getInstance(); } | |
Road &getRoad3() { return RoadC::getInstance(); } | |
Road &getRoad4() { return RoadD::getInstance(); } | |
std::string getDirectionStr() { return "north"; } | |
std::string getLeftStr() { return "east"; } | |
}; | |
class WestCar: public Car{ | |
public: | |
WestCar(int id_): Car(id_) {} | |
~WestCar() {} | |
Entrance &getEntrance() { return WestEntrance::getInstance(); } | |
Road &getRoad1() { return RoadB::getInstance(); } | |
Road &getRoad2() { return RoadC::getInstance(); } | |
Road &getRoad3() { return RoadD::getInstance(); } | |
Road &getRoad4() { return RoadA::getInstance(); } | |
std::string getDirectionStr() { return "west"; } | |
std::string getLeftStr() { return "north"; } | |
}; | |
class SouthCar: public Car { | |
public: | |
SouthCar(int id_): Car(id_) {} | |
~SouthCar() {} | |
Entrance &getEntrance() { return SouthEntrance::getInstance(); } | |
Road &getRoad1() { return RoadC::getInstance(); } | |
Road &getRoad2() { return RoadD::getInstance(); } | |
Road &getRoad3() { return RoadA::getInstance(); } | |
Road &getRoad4() { return RoadB::getInstance(); } | |
std::string getDirectionStr() { return "south"; } | |
std::string getLeftStr() { return "west"; } | |
}; | |
class EastCar: public Car{ | |
public: | |
EastCar(int id_): Car(id_) {} | |
~EastCar() {} | |
Entrance &getEntrance() { return EastEntrance::getInstance(); } | |
Road &getRoad1() { return RoadD::getInstance(); } | |
Road &getRoad2() { return RoadA::getInstance(); } | |
Road &getRoad3() { return RoadB::getInstance(); } | |
Road &getRoad4() { return RoadC::getInstance(); } | |
std::string getDirectionStr() { return "east"; } | |
std::string getLeftStr() { return "south"; } | |
}; | |
#ifdef TEST_OUTPUT | |
void printConfig(void); | |
#endif | |
// Implement the car behavior | |
void carBehavior(Car &car) { | |
// While I'm not the front of the entering cars | |
while(car.getEntrance().waitingCars.front() != car.id) { | |
// Wait for the condition | |
pthread_cond_wait(&car.getEntrance().changes, | |
&car.getEntrance().condMutex); | |
} | |
// Now that I'm the front of the entering cars | |
// Lock the entering queue | |
pthread_mutex_lock(&car.getEntrance().mutex); | |
// Wait for the mutex of road 1 | |
pthread_mutex_lock(&car.getRoad1().mutex); | |
// Detect deadlock | |
// If a deadlock occurs | |
if(car.getRoad1().findDeadlock()) { | |
// Show that the deadlock is being solved | |
#ifndef TEST_OUTPUT | |
std::cout << "DEADLOCK: car jam detected, signaling " | |
<< car.getLeftStr() << " to go." << std::endl; | |
#endif | |
// Allow the left car to pass first | |
car.getRoad1().waitingCars.push_back(car.id); | |
// car.getRoad1().waitingCars.push_back(car.id); | |
} | |
else | |
{ | |
// Push myself in the waiting queue for road 1 | |
// Because right-handed cars have greater priority to the road's occupation | |
// push myself to the front | |
car.getRoad1().waitingCars.push_front(car.id); | |
} | |
// Push myself in the waiting queue for 2 | |
// as soon as I have asked for road 1 | |
car.getRoad2().waitingCars.push_back(car.id); | |
// Unlock the mutex | |
pthread_mutex_unlock(&car.getRoad1().mutex); | |
// Wait until I am the front of the queue | |
while(car.getRoad1().waitingCars.front() != car.id) { | |
// Wait for the condition | |
pthread_cond_wait(&car.getRoad1().changes, &car.getRoad1().condMutex); | |
} | |
// Get the mutex lock | |
car.getRoad1().car = car.id; | |
#ifdef TEST_OUTPUT | |
pthread_mutex_lock(&outputLock); | |
printConfig(); | |
pthread_mutex_unlock(&outputLock); | |
#endif | |
pthread_mutex_lock(&car.getRoad1().mutex); | |
// Show that I've arrived | |
#ifndef TEST_OUTPUT | |
printf("car %d heading %s arrives at crossing.\n", | |
car.id, car.getDirectionStr().c_str()); | |
# endif | |
// Since I've entered road, quit from the entering queue | |
// Delete myself from the entering queue | |
car.getEntrance().waitingCars.erase( | |
std::find(car.getEntrance().waitingCars.begin(), | |
car.getEntrance().waitingCars.end(), car.id)); | |
// Unlock the entering queue | |
pthread_mutex_unlock(&car.getEntrance().mutex); | |
// And signal this | |
pthread_cond_broadcast(&car.getEntrance().changes); | |
pthread_mutex_unlock(&car.getEntrance().condMutex); | |
// Pass the road | |
usleep(1000000); | |
// While I'm not the front of the waiting queue for road 2 | |
while(car.getRoad2().waitingCars.front() != car.id) { | |
// Wait for the condition | |
pthread_cond_wait(&car.getRoad2().changes, &car.getRoad2().condMutex); | |
} | |
// Get the mutex lock | |
car.getRoad2().car = car.id; | |
#ifdef TEST_OUTPUT | |
pthread_mutex_lock(&outputLock); | |
printConfig(); | |
pthread_mutex_unlock(&outputLock); | |
#endif | |
pthread_mutex_lock(&car.getRoad2().mutex); | |
// Pop myself from the waiting queue for road 1 | |
car.getRoad1().waitingCars.erase( | |
std::find(car.getRoad1().waitingCars.begin(), | |
car.getRoad1().waitingCars.end(), car.id)); | |
// And signal this | |
pthread_cond_broadcast(&car.getRoad1().changes); | |
pthread_mutex_unlock(&car.getRoad1().condMutex); | |
// Unlock the mutex | |
car.getRoad1().car = 0; | |
#ifdef TEST_OUTPUT | |
pthread_mutex_lock(&outputLock); | |
printConfig(); | |
pthread_mutex_unlock(&outputLock); | |
#endif | |
// Unlock road 1 | |
pthread_mutex_unlock(&car.getRoad1().mutex); | |
// Pop myslf from the waiting queue for road 2 | |
car.getRoad2().waitingCars.erase( | |
std::find(car.getRoad2().waitingCars.begin(), | |
car.getRoad2().waitingCars.end(), car.id)); | |
// Sleep for a while, I'm passing | |
usleep(1000000); | |
// And signal this | |
pthread_cond_broadcast(&car.getRoad2().changes); | |
pthread_mutex_unlock(&car.getRoad2().condMutex); | |
// Now I'm gonna leave | |
car.getRoad2().car = 0; | |
#ifdef TEST_OUTPUT | |
pthread_mutex_lock(&outputLock); | |
printConfig(); | |
pthread_mutex_unlock(&outputLock); | |
#endif | |
pthread_mutex_unlock(&car.getRoad2().mutex); | |
// And show this | |
#ifndef TEST_OUTPUT | |
printf("car %d heading %s leaves the crossing.\n", | |
car.id, car.getDirectionStr().c_str()); | |
#endif | |
return; | |
} | |
// Car behavior adapters | |
void *headNorth(void *id) { | |
Car *car = new NorthCar(*(reinterpret_cast<int*>(id))); | |
carBehavior(*car); | |
delete car; | |
return NULL; | |
} | |
void *headWest(void *id) { | |
Car *car = new WestCar(*(reinterpret_cast<int*>(id))); | |
carBehavior(*car); | |
delete car; | |
return NULL; | |
} | |
void *headSouth(void *id) { | |
Car *car = new SouthCar(*(reinterpret_cast<int*>(id))); | |
carBehavior(*car); | |
delete car; | |
return NULL; | |
} | |
void *headEast(void *id) { | |
Car *car = new EastCar(*(reinterpret_cast<int*>(id))); | |
carBehavior(*car); | |
delete car; | |
return NULL; | |
} | |
int main(int argc, char **argv) | |
{ | |
// Handle the command line arguments | |
// which indicates the creation order of the processes | |
if(argc < 2) return 0; | |
// Thread ids | |
pthread_t *tids = new pthread_t[strlen(argv[1])]; | |
memset(reinterpret_cast<void*>(tids), 0, strlen(argv[1])); | |
// Car ids | |
int *cids = new int[strlen(argv[1])]; | |
// Return values of the threads | |
void **texits = new void*[strlen(argv[1])]; | |
// Entrance objects | |
Entrance &northEntrance = NorthEntrance::getInstance(); | |
Entrance &westEntrance = WestEntrance::getInstance(); | |
Entrance &southEntrance = SouthEntrance::getInstance(); | |
Entrance &eastEntrance = EastEntrance::getInstance(); | |
#ifdef TEST_OUTPUT | |
pthread_mutex_init(&outputLock, NULL); | |
#endif | |
// Create the waiting queue by the argument | |
for(int i = 0; argv[1][i]; i++) { | |
cids[i] = i + 1; | |
switch(argv[1][i]) { | |
case 'n': | |
northEntrance.waitingCars.push_back(i + 1); | |
pthread_create(tids + i, NULL, | |
headNorth, reinterpret_cast<void*>(cids + i)); | |
break; | |
case 'w': | |
westEntrance.waitingCars.push_back(i + 1); | |
pthread_create(tids + i, NULL, | |
headWest, reinterpret_cast<void*>(cids + i)); | |
break; | |
case 's': | |
southEntrance.waitingCars.push_back(i + 1); | |
pthread_create(tids + i, NULL, | |
headSouth, reinterpret_cast<void*>(cids + i)); | |
break; | |
case 'e': | |
eastEntrance.waitingCars.push_back(i + 1); | |
pthread_create(tids + i, NULL, | |
headEast, reinterpret_cast<void*>(cids + i)); | |
break; | |
default: break; | |
} | |
} | |
// Join the threads | |
for(unsigned i = 0; i < strlen(argv[1]) && tids[i] != 0; i++) { | |
pthread_join(tids[i], texits + i); | |
} | |
// Free spaces | |
delete[] tids; delete[] cids; delete[] texits; | |
return 0; | |
} | |
#ifdef TEST_OUTPUT | |
void printConfig(void) { | |
try { | |
printf(" "); | |
for(deque<int>::iterator i = RoadC::getInstance().waitingCars.begin(); | |
i != RoadC::getInstance().waitingCars.end(); i++) printf("%d ", *i); | |
printf("\n"); | |
printf("+--------+--------+\n"); | |
printf("| | |\n"); | |
printf("| %3d | %3d | ", RoadC::getInstance().car, RoadB::getInstance().car); | |
for(deque<int>::iterator i = RoadB::getInstance().waitingCars.begin(); | |
i != RoadB::getInstance().waitingCars.end(); i++) printf("%d ", *i); | |
printf("\n"); | |
printf("| | |\n"); | |
printf("+--------+--------+\n"); | |
printf("| | |\n"); | |
printf("| %3d | %3d | ", RoadD::getInstance().car, RoadA::getInstance().car); | |
for(deque<int>::iterator i = RoadA::getInstance().waitingCars.begin(); | |
i != RoadA::getInstance().waitingCars.end(); i++) printf("%d ", *i); | |
printf("\n"); | |
printf("| | |\n"); | |
printf("+--------+--------+\n"); | |
printf(" "); | |
for(deque<int>::iterator i = RoadD::getInstance().waitingCars.begin(); | |
i != RoadD::getInstance().waitingCars.end(); i++) printf("%d ", *i); | |
printf("\n"); | |
printf("North waiting: "); | |
for(deque<int>::iterator i = NorthEntrance::getInstance().waitingCars.begin(); | |
i != NorthEntrance::getInstance().waitingCars.end(); i++) printf("%d ", *i); | |
printf("\n"); | |
printf("West waiting: "); | |
for(deque<int>::iterator i = WestEntrance::getInstance().waitingCars.begin(); | |
i != WestEntrance::getInstance().waitingCars.end(); i++) printf("%d ", *i); | |
printf("\n"); | |
printf("South waiting: "); | |
for(deque<int>::iterator i = SouthEntrance::getInstance().waitingCars.begin(); | |
i != SouthEntrance::getInstance().waitingCars.end(); i++) printf("%d ", *i); | |
printf("\n"); | |
printf("East waiting: "); | |
for(deque<int>::iterator i = EastEntrance::getInstance().waitingCars.begin(); | |
i != EastEntrance::getInstance().waitingCars.end(); i++) printf("%d ", *i); | |
printf("\n\n"); | |
} | |
catch(std::exception) {} | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment