Problem 2: ---------- This is a bit more complex. The main complexity arises due to the fact that a teller can get a customer either because she is assigned to him by the receptionist or because there is a queue of waiting customers. This note includes two solutions - in the first solution, the receptionist checks first for free tellers and reassigns them immediately; in the second solution, it always directs customers to the queue waiting for a free teller. The second solution is simpler and probably more intuitive. In these solutions, I use integer identifiers to represent both customers and tellers. ------------------------------------------------------------------------ Solution 1: ----------- /* queue of customers waiting for the receptionist */ queueOfPointersToInteger recepQ; /* lock for the queue */ Lock *recepQLock; /* queue of customers waiting for a teller */ queueOfPointersToInteger tellerQ; /* number of customers waiting for a teller */ int tellerQCount; /* lock for the above data structures */ Lock *tellerQLock; /* data structure to communicate between tellers and customers they are * serving. if nowServing[i] == NULL, the teller is free. */ int *nowServing[T]; /* locks to protect the individual entries */ Lock *servingLocks[T]; /* semaphore used by the receptionist to sleep */ Semaphore recepS = new Semaphore(0); /* procedure to implement a customer */ void customerProc(int cid) { /* this variable is used to hold the status of this customer. * status == WAITING => no teller has yet been assigned * status == DONE => the transaction has completed */ int status = WAITING; /* put myself in queue for the receptionist */ lock(recepQLock); append(recepQ,&status); unlock(recepLock); /* wake the receptionist, if need be */ recepS->V(); /* wait till the transaction is completed. Note that no lock is needed for * status as the assigned teller thread is the only one that will update * this value */ while (status != DONE) currentThread->yield(); /* the customer is done and can go home happy */ } void receptionistProc() { int *customer; int i; while (1) { /* sleep till at least one customer shows up */ recepS->P(); /* get the first customer from the queue - we are guaranteed to have * at least one */ lock(recepQLock); customer = Dequeue(recepQ); unlock(recepQLock); /* check if any of the tellers is free */ for (i = 0; i < T; i++) { lock(servingLocks[i]); if (nowServing[i] == NULL) { /* found a free teller, assign the customer to him */ nowServing[i] = customer; unlock(servingLocks[i]); /* assigned the customer - go back to top of while (1) */ continue; } unlock(servingLocks[i]); } /* if we reach here, none of the tellers are free, so queue the * customer */ lock(tellerQLock); append(tellerQ,customer); tellerQCount++; unlock(tellerQLock); } } void tellerProc(int tid) { int *customer; while (1) { /* first check if a customer has been assigned to me */ lock(servingLocks[tid]); if (nowServing[tid] != NULL) { customer = nowServing[tid]; doTransaction(); /* set the consumer status to indicate the transaction is complete */ *customer = DONE; /* free up the teller */ nowServing[tid] = NULL; unlock(servingLocks[tid]); /* completed handling this customer; * go back to top of while (1) loop */ continue; } else { /* no customer has been assigned to me */ /* Check queue for waiting customers. I am still holding my lock * so that the receptionist does not assign me a customer while I * am checking the queue */ lock(tellerQLock); if (tellerQCount > 0) { /* there is at least one waiting customer */ customer = Dequeue(tellerQ); tellerQCount--; unlock(tellerQLock); doTransaction(); /* set the consumer status to indicate the transaction is complete */ *customer = DONE; /* free up the teller */ nowServing[tid] = NULL; unlock(servingLocks[tid]); /* go back to the top of the while (1) loop */ continue; } unlock(servingLocks[tid]); /* if I got here, there must have been no customers waiting and * no consumer assigned to me. Let me go to sleep */ currentThread->yield(); } } ------------------------------------------------------------------------ Solution 2: ----------- /* queue of customers waiting for the receptionist */ queueOfPointersToInteger recepQ; /* lock for the queue */ Lock *recepQLock; /* queue of customers waiting for a teller */ queueOfPointersToInteger tellerQ; /* number of customers waiting for a teller */ int tellerQCount; /* lock for the above data structures */ Lock *tellerQLock; /* semaphore used by the receptionist to sleep */ Semaphore recepS = new Semaphore(0); /* procedure to implement a customer */ void customerProc(int cid) { /* this variable is used to hold the status of this customer. * status == WAITING => no teller has yet been assigned * status == DONE => the transaction has completed */ int status = WAITING; /* put myself in queue for the receptionist */ lock(recepQLock); append(recepQ,&status); unlock(recepLock); /* wake the receptionist, if need be */ recepS->V(); /* wait till the transaction is completed. Note that no lock is needed for * status as the assigned teller thread is the only one that will update * this value */ while (status != DONE) currentThread->yield(); /* the customer is done and can go home happy */ } void receptionistProc() { int *customer; int i; while (1) { /* sleep till at least one customer shows up */ recepS->P(); /* get the first customer from the queue - we are guaranteed to have * at least one */ lock(recepQLock); customer = Dequeue(recepQ); unlock(recepQLock); lock(tellerQLock); append(tellerQ,customer); tellerQCount++; unlock(tellerQLock); } } void tellerProc(int tid) { int *customer; while (1) { /* Check queue for waiting customers. */ lock(tellerQLock); if (tellerQCount > 0) { /* there is at least one waiting customer */ customer = Dequeue(tellerQ); tellerQCount--; unlock(tellerQLock); doTransaction(); /* set the consumer status to indicate the transaction is complete * no lock is needed as no one else will try to modify this * location */ *customer = DONE; /* go back to the top of the while (1) loop */ continue; } /* if I got here, there must have been no customers waiting and * no consumer assigned to me. Let me go to sleep */ currentThread->yield(); } } ------------------------------------------------------------------------ Grading: -------- Quick survey of the solutions handed in showed that the class as a whole hasn't done well on this problem. I had originally intended this problem to be 30 points with the following breakdown: Total points: 30 handling of customer by receptionist: 5 assignment of customer to teller: 5 queuing of customer if tellers busy: 5 teller checks for assigned customer before checking the queue: 5 teller checks queue before going to sleep: 5 teller indicates completion to customer: 5 Given the performance of the class, I have reduced the weight of this problem to 15 points with the following breakdown: Total points: 15 customer procedure: 5 receptionist procedure: 5 teller procedure: 5