The agent places two of the ingredients on the table. The smoker who has the remaining ingredient then makes and smokes a cigarette, signaling the agent on completion. The agent then puts out another two of the three ingredients, and the cycle repeats.
Assume the agent calls the procedure
void chooseIngredients(int *paper, int *tobacco, int *match);to randomly select 2 of the 3 ingredients. The routine randomly sets 2 of the values to "1" and one of the values to "0". You don't have to write this routine.
Write a program to synchronize the agent and smokers.
Variable Name Variable Type Initial Value Description
Solution:
1.
| Variable Name | Variable Type | Initial Value | Description |
| lock | Mutex Lock |
- |
To protect shared state variables associated with condition variables(
e.g.,.Tobacco, Match, Paper) |
| IngredientReady | ConditionVariable |
|
ConditionVariable to coordinate when smokers
consume Ingredients |
| IngredientConsumed | ConditionVariable |
|
ConditionVariable to coordinate when agent creates new Ingredients |
| Tobacco | int |
|
indicate if Tobacco on the table is created or consumed |
| Match | int |
|
indicate if Match on the table is created or consumed |
| Paper | int |
|
indicate if Paper on the table is created or consumed |
2.
void Agent()
{
lock.acquire();
while(1){
while (!((Tobacco == 0) && (Match == 0)
&& (Paper == 0))){
IngredientConsumed.wait(&lock);
}
chooseIngredients(&Tobacco, &Match,
&Paper);
IngredientReady.broadcast(&lock);
}
lock.release();
}
void matchSmoker()
{
lock.acquire();
while(1){
while( !( (Tobacco ==1) && (Paper ==1)
)){
IngredientReady.wait(&lock);
}
// smoking;
Tobacco = 0;
Paper = 0;
IngredientConsumed.signal(&lock);
}
lock.release();
}
/*
* Each philosopher is a thread that does the following:
*/
philosopherMain(int myId, Table *table){
while(1){
table->startEating(myId);
eat();
table->doneEating(myId);
}
}
typedef enum stick_status{STICK_FREE, STICK_USED} stick_status;
/*
* First solution. It is safe but not completely
* live in that it does not guarantee freedom
* from starvation.
*/
public class Table{
public:
Table(int nSeats);
void startEating(int philosopherId);
void doneEating(int philosopherId);
private:
mutex lock;
cond cv;
int nseats;
stick_status stickStatus[];
}
Table::Table(int ns)
{
int ii;
nseats = ns;
stickStatus = (stick_status *)malloc(nseats * sizeof(stick_status));
for(ii = 0; ii < nseats; ii++){
stickStatus[ii] = STICK_FREE;
}
}
void
Table::startEating(int id)
{
lock.acquire();
while(stickStatus[stickLeft(id)] == STICK_USED ||
stickStatus[stickRight(id)] == STICK_USED){
cv.wait(&lock);
}
stickStatus[stickLeft(id)] = STICK_USED;
stickStatus[stickRight(id)] = STICK_USED;
lock.release();
}
void
Table::doneEating(int id)
{
lock.acquire();
stickStatus[stickLeft(id)] = STICK_FREE;
stickStatus[stickRight(id)] = STICK_FREE;
cv.broadcast(&lock);
lock.release();
}
/*
* Second solution. A philosopher should eat if there
* is no conflict for the chopsticks. But, if two philosophers
* are waiting for the same chopstick, the one that
* has been waiting longer should go.
*
*/
const static int NOT_WAITING = -999;
public class Table{
public:
Table(int nSeats);
void startEating(int philosopherId);
void doneEating(int philosopherId);
private:
mutex lock;
cond cv;
int nseats;
stick_status stickStatus[];
int entryTime[];
int currentTime;
int okToGo(int id);
}
Table::Table(int ns)
{
int ii;
nseats = ns;
stickStatus = (stick_status *)malloc(nseats * sizeof(stick_status));
entryTime = (int *)malloc(nseats * sizeof(int));
for(ii = 0; ii < nseats; ii++){
stickStatus[ii] = STICK_FREE;
entryTime[ii] = NOT_WAITING;
}
currentTime = 0;
}
void
Table::startEating(int id)
{
lock.acquire();
entryTime[id] = currentTime;
currentTime++;
while(!okToGo(id)){
cv.wait(&lock);
}
stickStatus[stickLeft(id)] = STICK_USED;
stickStatus[stickRight(id)] = STICK_USED;
entryTime[id] = NOT_WAITING;
lock.release();
}
void
Table::doneEating(int id)
{
lock.acquire();
stickStatus[stickLeft(id)] = STICK_FREE;
stickStatus[stickRight(id)] = STICK_FREE;
cv.broadcast(&lock);
lock.release();
}
int
Table::okToGo(int id)
{
assert(lock is held on entry);
/*
* OK to go if both left and right sticks
* are free AND for each stick my neighbor
* is not waiting, or my neighbor's waiting
* number is larger than mine
*/
if(stickStatus[stickLeft(id)] != STICK_FREE){
return 0;
}
if(stickStatus[stickRight(id)] != STICK_FREE){
return 0;
}
if(entryTime[seatRight(id)] != NOT_WAITING
&&
entryTime[seatRight(id)] < entryTime[id]){
return 0;
}
if(entryTime[seatLeft(id)] != NOT_WAITING
&&
entryTime[seatLeft(id)] < entryTime[id]){
return 0;
}
return 1;
}
void parallelDo(int n, void *(function(void *)), void *arg[])parallelDo spawns off N worker threads. function is a pointer to a function, and arg[] is an array of arguments to that function (e.g., worker thread i is to execute (*function)(arg[i])). parallelDo returns once all worker threads have finished running the function.
Assume you are given mutex locks and condition variables with standard public interfaces.
Assume that you are given functions creating and destroying threads that are similar to those discussed in class:
void sthread_create(sthread_t *thrd,
void sthread_exit(void);
Implement this parallelDo function. Follow the coding standards specified in the handout. Important: follow an object-oriented style of programming - encapsulate all shared states in an object that manages access to it.
Solution: As PDF file As PS file