CS 133 Discussion: Week 4


There was a problem in my buffer's get function in Wednesday's lecture. It did read (more or less):
  P(S);
  items--;
  V(S);
  return buffers[items];
It should have read
  P(S);
  BufferItem item = buffers[items-1];
  items--;
  V(S);
  return item;
The change makes sure the correct item is copied out of the buffer while still in the critical section.


Homework Clarification

Fairness Problem

MPC is optimized to process local messages first. If there's an enabling message from another entity on the same processor, that message will be executed in favor of a message from a remote processor. This leads to the unfortunate possibility of an infinite loop.

#include 

message Ping{int id; ename reply_to;};
message Die{};

entity Remote (ename target1, ename target2) {
  send Die{} to target1;
  send Die{} to target2;
}

entity Local (int myno) {

  int i = 0;

  while (1) {
    receive (Die d) {
        break;
    }
    or receive (Ping p) {
      i++;
      printf("\n Entity No %d: message %d, number = %d", myno, p1.id, i); 
      send Ping{p.id, self} to p.reply_to;
    }
  }
}

entity driver (int argc, char** argv) {
  ename e1, e2, e3;
  e1 = new Local(1) at 0;
  e2 = new Local(2) at 0;
  e3 = new Remote(e1, e2) at 1;
  send Ping{1, e2} to e1;
}

If we run this program on a single processor, the sequence of events is roughly as follows:

  1. The driver runs to completion.
  2. Entity e1 is created, receives a Ping message sent by the driver, and sends it to e2.
  3. Entity e2 is created, receives a Ping message sent by e1, and sends it back to e1.
  4. Entity e3 is created, and sends Die messages to both e1 and e2, then terminates.
  5. Entities e1 and e2 receive the Die messages and terminate.
If, however, we run the program on two processors, the sequence of events is different in a small, but significant way.
  1. same
  2. same
  3. same
  4. Entity e3 is created on a remote processor, sends Die messages to e1 and e2, and terminates. Because of the transmission delay, this may take some time.
  5. In the meantime, e1 and e2 Ping each other mercilessly.
  6. Eventually, the Die messages arrive at processor 0 and are temporarily placed in a special queue of remote messages.
  7. Entity e1 (or e2) is activated. It has a message waiting from another local entity e2 (respectively e1), therefore it accepts this message without checking the remote queue.
  8. Repeat step 7.
Because the remote queue is not checked unless there are no local messages (with true guards), the remote queue is never checked and the program never terminates. This has important consequences.

In some sense, the program behavior is still legal. Parsec/MPC semantics state that if two enabling messages of different types (i.e. Die and Ping) are available, a non-deterministic choice will be made between them. However, suppose we change the code to read:

or receive (Ping p) when (qempty(Die)) {
Now the program is no longer non-deterministic. It must choose a Die message if one is available. (qempty is a predefined boolean function in Parsec/MPC which checks the input queue for a particular message type.) Unfortunately, qempty also does not check the remote queue, and the program still doesn't terminate. This leads me to believe that the program behaviour is incorrect. Professor Bagrodia argues that the program behavior is still technically correct (at least not provably wrong) because Parsec only guarantees that remote messages may be delivered "eventually," and the definition of "eventual" can be stretched to an arbitrary time less than infinity. Nevertheless, this anomolous behavior will cause a large class of programs to act in unexpected, if not literally incorrect, ways.