193 lines
4.9 KiB
C++
193 lines
4.9 KiB
C++
// Filename: test_diners.cxx
|
|
// Created by: cary (16Sep98)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
|
//
|
|
// All use of this software is subject to the terms of the revised BSD
|
|
// license. You should have received a copy of this license along
|
|
// with this source code in a file named "LICENSE."
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
// A solution to the famous dining philosophers, implemented using the
|
|
// threading abstraction. This program exercises thread creation and
|
|
// destruction, mutexes, and condition variables.
|
|
|
|
#include "pandabase.h"
|
|
#include "thread.h"
|
|
#include "conditionVar.h"
|
|
#include "mutexHolder.h"
|
|
#include "pointerTo.h"
|
|
#include "referenceCount.h"
|
|
#include "trueClock.h"
|
|
|
|
#ifdef WIN32_VC
|
|
// Under Windows, the rand() function seems to return a sequence
|
|
// per-thread, so we use this trick to set each thread to a different
|
|
// seed.
|
|
static int last_rand = 0;
|
|
#endif /* __WIN32__ */
|
|
|
|
Mutex rand_mutex;
|
|
|
|
static double random_f(double max)
|
|
{
|
|
MutexHolder l(rand_mutex);
|
|
int i = rand();
|
|
#ifdef WIN32_VC
|
|
last_rand = i;
|
|
#endif /* __WIN32__ */
|
|
return max * (double)i / (double)RAND_MAX;
|
|
}
|
|
|
|
#define PRINTMSG(x) { MutexHolder l(Mutex::_notify_mutex); x << flush; }
|
|
|
|
// n philosophers sharing n chopsticks. Philosophers are poor folk and can't
|
|
// afford luxuries like 2 chopsticks per person.
|
|
#define N_DINERS 5
|
|
|
|
class ChopstickMutex : public Mutex {
|
|
public:
|
|
void output(ostream &out) const {
|
|
out << "chopstick " << _n;
|
|
}
|
|
int _n;
|
|
};
|
|
|
|
|
|
ChopstickMutex chopsticks[N_DINERS];
|
|
|
|
// At most n philosophers are allowed into the room, others would have to
|
|
// wait at the door. This restriction demonstrates the use of condition
|
|
// variables.
|
|
|
|
Mutex room_mutex;
|
|
|
|
ConditionVar room_condition(room_mutex);
|
|
int room_occupancy = 0;
|
|
|
|
class philosopher;
|
|
PT(Thread) phils[N_DINERS];
|
|
|
|
class philosopher : public Thread {
|
|
private:
|
|
int _id;
|
|
void thread_main() {
|
|
#ifdef WIN32_VC
|
|
rand_mutex.lock();
|
|
srand(last_rand);
|
|
rand_mutex.release();
|
|
random_f(1.0);
|
|
#endif /* __WIN32__ */
|
|
int l = _id;
|
|
int r = l+1;
|
|
if (r == N_DINERS)
|
|
r = 0;
|
|
if (l & 1) {
|
|
int t = l;
|
|
l = r;
|
|
r = t;
|
|
}
|
|
PRINTMSG(cerr << "Philosopher #" << _id << " has entered the room."
|
|
<< "\n");
|
|
int count = (int)random_f(10.0) + 1;
|
|
while (--count) {
|
|
chopsticks[r].lock();
|
|
Thread::sleep(1);
|
|
chopsticks[l].lock();
|
|
PRINTMSG(cerr << "Philosopher #" << _id
|
|
<< " is eating spaghetti now.\n");
|
|
Thread::sleep(random_f(3.0));
|
|
chopsticks[l].release();
|
|
chopsticks[r].release();
|
|
PRINTMSG(cerr << "Philosopher #" << _id
|
|
<< " is pondering about life.\n");
|
|
Thread::sleep(random_f(3.0));
|
|
}
|
|
room_mutex.lock();
|
|
--room_occupancy;
|
|
cerr << "clearing philosopher " << _id << "\n";
|
|
phils[_id] = (philosopher*)0L;
|
|
room_condition.signal();
|
|
room_mutex.release();
|
|
PRINTMSG(cerr << "Philosopher #" << _id << " has left the room ("
|
|
<< room_occupancy << " left).\n");
|
|
}
|
|
|
|
inline void* make_arg(const int i) { return (void*)new int(i); }
|
|
public:
|
|
philosopher(const int id) : Thread("philosopher", "a") {
|
|
_id = id;
|
|
}
|
|
|
|
virtual void output(ostream &out) const {
|
|
out << "philosopher " << _id;
|
|
}
|
|
};
|
|
|
|
int
|
|
main(int argc, char *argv[]) {
|
|
double has_run_time = false;
|
|
double run_time = 0.0;
|
|
if (argc > 1) {
|
|
run_time = atof(argv[1]);
|
|
cerr << "Running for " << run_time << " seconds\n";
|
|
has_run_time = true;
|
|
} else {
|
|
cerr << "Running indefinitely\n";
|
|
}
|
|
|
|
int i;
|
|
room_mutex.lock();
|
|
for (i=0; i<N_DINERS; ++i) {
|
|
chopsticks[i]._n = i;
|
|
}
|
|
for (i=0; i<N_DINERS; ++i) {
|
|
phils[i] = new philosopher(i);
|
|
phils[i]->start(TP_normal, false);
|
|
}
|
|
room_occupancy = N_DINERS;
|
|
|
|
TrueClock *clock = TrueClock::get_global_ptr();
|
|
double start_time = clock->get_short_time();
|
|
double end_time = start_time + run_time;
|
|
|
|
while (!has_run_time || clock->get_short_time() < end_time) {
|
|
if (room_occupancy == N_DINERS) {
|
|
PRINTMSG(cerr << "main thread about to block " << room_occupancy
|
|
<< "\n");
|
|
while (room_occupancy == N_DINERS) {
|
|
room_condition.wait();
|
|
}
|
|
}
|
|
// hmm.. someone left the room.
|
|
room_mutex.release();
|
|
// sleep for a while and then create a new philosopher
|
|
PRINTMSG(cerr << "main thread sleep\n");
|
|
Thread::sleep(2.0);
|
|
PRINTMSG(cerr << "main thread wake up\n");
|
|
room_mutex.lock();
|
|
for (i=0; i<N_DINERS; ++i)
|
|
if (phils[i] == (philosopher*)0L)
|
|
break;
|
|
assert(i != N_DINERS);
|
|
phils[i] = new philosopher(i);
|
|
phils[i]->start(TP_normal, false);
|
|
++room_occupancy;
|
|
}
|
|
|
|
PRINTMSG(cerr << "Waiting for philosophers to finish\n");
|
|
|
|
while (room_occupancy != 0) {
|
|
room_condition.wait();
|
|
}
|
|
|
|
room_mutex.release();
|
|
|
|
cerr << "Exiting.\n";
|
|
return 0;
|
|
}
|