CS372: Solutions for Homework 7

Problem 1:

Consider a system with three smoker processes and one agent process. Each smoker continuously rolls a cigarette and then smokes it. But to roll and smoke a cigarette, the smoker needs three ingredients: tobacco, paper, and matches. One of the smoker processes has paper, another has tobacco, and the third has matches. The agent has an infinite supply of all three materials.

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.

  1. What synchronization and state variables will you use in this problem? (For each variable, indicate the variable's type, the variable's name, its initial value (if any), and a short comment describing the variable's purpose.

    Variable Name        Variable Type        Initial Value        Description

  2. Write the routines Agent() and matchSmoker() (the routine for the smoker that has lots of matches.) You don't have to write the routines paperSmoker() or tobaccoSmoker(), but your solution should be general enough so that those routines would be simple variations of matchSmoker().

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
0
indicate if Tobacco on the table is created or consumed
Match int
0
indicate if Match on the table is created or consumed
Paper int
0
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();
}

Problem 2:

Write a solution to the dining philosophers using locks and condition variables. Your solution must prevent philosopher starvation. For your convenience sthread.h provides a simple threads process.

/*
 * 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;
}

Problem 3

Implement the function
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 *(start_routine(void*)),
void *arg0ToStartRoutine,
void *arg1ToStartRoutine,
...); // Pass as many args as you like
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