Pages

Saturday, March 30, 2013

NOROOM Mel Exception

Welcome » NERWous C

In the previous Producer/Consumer example, we have two Producers vying for the same mel. If they come to the mel at the same time, the run-time environment will allow one Producer to enter the mel, while the other waits at the ?. If the Consumer is slow to empty the mel, the mel can get full, and the Producers have to wait for room to be made available again on the mel. Instead of waiting, the Producers have the option to check for this no-room condition through the mel exception NOROOM.

Let's modify the previous Producer/Consumer example by introducing a second Consumer:
main() {
    pel Consumer() c1;
    pel Consumer() c2;
    pel Producer1(c1, c2);
    pel Producer2(c1);
}
void Producer1 (pel Consumer c1, pel Consumer c2) {
    int product;
    for (int i=0; i<10; ++i) {
       product = produce();
       try {
           ?c1!val = product;
       }
       catch (c1!val!NOROOM) {
           ?c2!val = product;
       }
    }
}
void Producer2 (pel Consumer c) {
    for (int i=0; i<70; ++i)
       ?c!val = produce();
}
void Consumer (mel int val) {
    try {
       while ( 1 )
           consume (?val);
     }
     catch (val!NOIN) {
       printf ("No more producers to me");
     }
}
The first Producer makes use of both Consumers. The second Producer uses only the first Consumer. The first Consumer's input mel val can be slow to drain because of its serving of two Producers. If things buffer up and this mel has no room, the first Producer detects this condition via the NOROOM mel exception and switch to the second Consumer. On the other hand, the second Producer sticks with its Consumer and opts for waiting at the ?c!val until the mel buffer is freed up for it to deposit the product.

Unlike the temporaneous mel exception NOOUT, the NOROOM exception is immediate when the Producer checks the mel variable at ?c!val. On the other hand, the NOOUT exception depends on the run-time environment to monitor all consumers of the mel variable and raise that exception if after a configurable amount of time it has not detected any consumption.

With the existing NOROOM exception, there is no need for Producer1 to add the NOOUT checking to its mel communication between itself and Consumer1. However we can enhance the 2nd Producer so that it will not wait forever if its Consumer1 partner has terminated, and there is no consumer for its product:
void Producer2 (pel Consumer c) {
    for (int i=0; i<70; ++i) {
      try {
        ?c!val = produce();
      }
      catch (?c!val!NOOUT) {
        print ("My consumer has gone away. I go home too.");
        break;    // out of for loop
      }
   }
}
It is worth repeating that NOROOM is different from NOOUT. If Producer2 were using NOROOM, it would exit at the first detection of the buffer-full condition at the mel variable ?c!val. With the use of NOOUT, Producer2 will wait for a configurable amount of time for the Consumer to catch up on its consumption and clear out the mel buffer val

In NERWous C, there is a better way to write Producer1 so that it will keep trying between Consumer1 and Consumer2 if their mel ports are in the NOROOM mode (i.e. full), until either one port can accept the product or until the NOOUT conditions have been detected at both ports (i.e. both Consumers have terminated):
void Producer1 (pel Consumer c1, pel Consumer c2) {
    int product;
    for (int i=0; i<10; ++i) {
       product = produce();
       try {
           (?c1!val NOROOM ?c2!val)<somebody_take_me> = product;
       }
       catch (?c1!val!NOOUT) {
           printf ("Consumer1 has terminated. I continue with Consumer2 only.");
           nobreak;   // continue waiting with c2 only
       }
       catch (?c2!val!NOOUT) {
           printf ("Consumer2 has terminated; I continue with Consumer1 only.");
           nobreak;  // continue waiting with c1 only
       }
       catch (somebody_take_me!NOOUT) {
           printf ("Both consumers have terminated. I go home too.");
           break;     // out of for loop
       }
    }
}
Here, Producer1 checks ?c1!val first. If this mel has NOROOM, it checks ?c2!val. If this mel also has NOROOM, it goes back to check ?c1!val. This continuous checking is programmatically tagged with the tag somebody_take_me. This tagged checking continues until either Consumer mel takes in the product, or a NOOUT condition has been raised on both Consumer mels.

We can pass an array of many Consumers to Producer1, too:
void Producer1 (pel Consumer c[]) {
    int product;
    for (int i=0; i<10; ++i) {
       product = produce();
       try {
           (?c[]!val NOROOM)<somebody_take_me> = product;
       }
       catch (somebody_take_me!NOOUT) {
           printf ("All consumers have terminated. I go home too.");
           break;     // out of for loop
       }
    }
}
In the above example, the Producer1 will try all consumers in the array c. If the first consumer is full (i.e. NOROOM), then Producer1 will try the next consumer in the array. If none of the consumers in the array are available, it will loop back and check the first one again. This busy check will continue forever until one consumer can take the product (and Producer1 can go on and produce another product until 10 are produced), or the exception NOOUT is raised when the running system detects that all the consumers in the array have terminated. If some consumers have terminated and some consumers are still active, then the NOOUT exception is not raised.

No comments:

Post a Comment