Process J - A Concurrent Language

Selecting Input


← Previous (Communicating Processes) Selecting Input Next (Building Processes as a Network of Processes) →

As described in the previous section, a read to a channel (we ought to say a read from a channel's read-end, but it is understood that a read from a channel will have to be from the reading end of it) and a write to a channel are both synchronous blocking actions. This of course means that once either is called, the process with the call may block (if no writer is present when read() is called, or if no reader is present when write() is called). There is no way to uncommit to a read or a write. This can make programming more dynamic systems very complicated, if not impossible. Consider, for example, a multiplexing process $P$ that reads input from two channels and writes output to a single channel. We could try to write kbd that alternates between the input channels, but if one of the channels carry a lot more data items than the other, then we are unnecessarily slowing down the process. What we need is a mechanism for alternating between channels. Such a primitive is built in to ProcessJ, and it is called an alt. An alt consists of a number of guarded statements. Each guard (for now) is a channel-read expression. When execution reaches an alt, all guards that are ready (that is, all guards that represent communications that can complete) are marked, and one is chosen at random. Once one has been chosen, the communication represented by the guard takes place, and the associated statement is executed. Here is an example of the aforementioned multiplexing process:

Alt statement in ProcessJ
public proc void multiplexer(chan<int>.read in1,
                             chan<int>.read in2,
                             chan<int>.write out) {
  while (true) {
    int x;
    alt {
      x = in1.read() : out.write(x);
      x = in2.read() : out.write(x);
    }
  }
}

If no input is ready on neither in1 nor in2 the alt statement will be block until at least one of the guards becomes ready. We can avoid that by adding a skip guard. A skip guard is where the communication guard is replaced by the word skip, which represents a guard who is always ready.

Alt Statement with Skip Guard
public proc void relay(chan<int>.read in,
                       chan<int>.write out) {
  while (true) {

    // do some processing

    int x;
    alt {
      x = in.read() : { out.write(x); }
      skip : { skip; }
    }
  }
}

skip is also a statement by it self; a statement that does nothing; in the example above the second skip could have been replaced by a semicolon (;) or an empty block ({ }).

Apart from channel-read guards and skip guards, one additional type of guard exists, namely a timeout guard, but we will come back to that when we talk about timers.

The semantics of an alt statement states that of the guards that are ready, one is chosen at random, and its corresponding statement is executed. If we wish to prioritize this selection process, we can use a prioritized alt, which is just the regular alt prefixed with the keyword pri. In the code above, when we introduced that skip guard, the skip can be chosen (at random) even if there is input ready on the in channel. If we wish to favor reading input from the in channel over the skip, then we use a pri alt. The guard that appears first has highest priority, the second one has second highest priority and so on until the one that appears last, which has lowest priority.

Pri Alt Statement
public proc void relay-prioritized(chan<int>.read in,
                                   chan<int>.write out) {
  while (true) {

    // do some processing

    int x;
    pri alt {
      x = in.read() : { out.write(x); }
      skip : { skip; }
    }
  }
}


← Previous (Communicating Processes) Selecting Input Next (Building Processes as a Network of Processes) →