Pages

Sunday, May 20, 2012

Wait Exception (NOMORE / NOOUT / NOIN)

In the previous Producer/Consumer example, the Producer indicates that it is done producing by generating a NULL item as the last item. Many Producers are not that nice. When they are done producing, they just terminate. What happens to the Consumer then? Will it wait forever? Let's look at this new code:
main() {
    pel Producer() p;
    pel Consumer(p);
}
(mel item val) Producer () {
    while ( !timeToPunchOut() )
      ?val = produce();
}
void Consumer (Producer p) {
    try {
      while ( 1 )
          consume(?p!val);
    }
    catch (p!NOMORE){
      printf ("Producer has done producing. I go home too");
    }
}
Here, the Consumer keeps consuming until its wait for the mel val of the pel p fails due to the NOMORE pel exception. This exception is raised by the Producer p as it terminates (normally or abnormally). This allows the Consumer to clean itself up (here by printing a message) and terminate too. When both Producer and Consumer have terminated, the whole program terminates.

Now, let us look at the reverse problem, where the Consumer terminates before consuming all the produced items. The Producer keeps producing and deposits into its mel variable val, which acts like a buffer between the Producer and Consumer. If the mel val is full and cannot accept any more items, the Producer then waits at the ? for the Consumer to clear the mel val. If the Consumer has terminated (normally or abnormally), the Producer will wait forever. To break out of such potentially infinite wait, the Producer can check for the NOOUT mel exception:
(mel item val) Producer () {
    try {
        while ( !timeToPunchOut() )
           ?val = produce();
    }
    catch (val!NOOUT) {
        printf ("Consumer has done consuming. I go home too");
    }
}
The NOOUT mel exception is raised when there is no reader subscribing to the mel and the mel buffer stays full for some time (this "some time" is configurable).

There is a corresponding NOIN mel exception for the Consumer to check in case there are no Producer subscribing to the mel to deposit new values. Let's rewrite the example so that we now have one Consumer but two Producers:
main() {
    mel int Val;
    pel Producer1();
    pel Producer2();
    pel Consumer();
}
void Producer1 () {
    for (int i=0; i<10; ++i)
       ?Val = produce();
}
void Producer2 () {
    for (int i=0; i<70; ++i)
       ?Val = produce();
}
void Consumer () {
    try {
       while ( 1 )
           consume (?Val);
     }
     catch (Val!NOIN) {
      printf ("No more producers");
     }
}
The mel Val is now used by both Producers and the single Consumer. Producer1 produces 10 times then terminates. Producer2 does 70 times and terminates. After both of them have terminated, the Consumer will wait forever at the ?Val. Here the Consumer protects itself from a forever wait by checking the NOIN mel exception. This exception is raised by the run-time environment when it detects that there are no pel processes that register to produce for the mel variable Val for some time (this "some time" is configurable).

No comments:

Post a Comment