Pages

Saturday, March 30, 2013

NOTHING Mel Exception

Welcome » NERWous C » Exceptions

The NOTHING mel exception is the ying-yang of the NOROOM exception. While NOROOM checks if the mel buffer is full, the NOTHING exception is raised when the mel buffer is empty. Like NOROOM, the NOTHING is raised immediately by the application itself when it accesses the mel variable via the wait expression ?.

The NOTHING exception has the same relationship with the NOIN exception as between NOROOM and NOOUT exceptions. While NOTHING and NOIN both check that the mel buffer does not have any product, the NOIN exception is raised by the run-time environment after a configurable amount of time to instill confidence that no Producers will fill the buffer again. As said previously, the NOTHING is raised by the application immediately upon detection of the empty condition.

Let now rewrite the Producer / Consumer example to have two Producers and one Consumer:
main() {
    pel Producer() p1;
    pel Producer() p2;
    pel Consumer (p1, p2) c;
}
(int mel val) Producer () {
    int product;
    for (int i=0; i<10; ++i)
       ?val = produce();
}
void Consumer (pel Producer p1, pel Producer p2) {
    while ( 1 )
        try {
           consume (?p1!val);
        }
        catch (p1!val!NOTHING) {
           consume (?p2!val);
        }
     }
}
The Consumer will first try to get the product from the Producer p1. If this Producer does not have a product ready yet, the NOTHING exception is raised on its mel return port, and Consumer instead of waiting for p1, now attempts to get a product out of the other Producer, p2.

In the above example, if p2 also does not have a product, the Consumer is stuck waiting for it, even if by that time the first Producer, p1 has a product out. To allow the Consumer to keep checking both Producers until it gets a product from any of them, we can rewrite the Consumer:
void Consumer (pel Producer p1, pel Producer p2) {
    while ( 1 )
        consume ( (?p1!val NOTHING ?p2!val) );
 }
One flaw on the above code is that when both Producers have terminated, the Consumer keeps checking ad infinitum. To break out of such loop, the Consumer must also check for the NOIN exception:
void Consumer (pel Producer p1, pel Producer p2) {
    while ( 1 ) {
      try {
        consume ( (?p1!val NOTHING ?p2!val)<somebody_give_me> );
      } catch (somebody_give_me!NOIN) {
          printf ("Both Producers have terminated. I go home too.");
          break;    // out of while loop
      }
    }
 }
Here, the Consumer checks ?p1!val first. If this mel port has NOTHING, it checks ?p2!val. If this mel port also has NOTHING, it goes back to check ?p1!val. This continuous checking is programmatically tagged with the tag somebody_give_me. This tagged checking continues until either Producers has a product for the Consumer, or a NOIN condition has been raised on both Producer mel ports.

We can pass an array of many Producers to the Consumer,too:
void Consumer (pel Producer p[]) {
    while ( 1 ) {
      try {
        consume ( (?p!val NOTHING)<somebody_give_me> );
      } 
      catch (somebody_give_me!NOIN) {
          printf ("All Producers have terminated. I go home too.");
          break;    // out of while loop
      }
    }
}


If in the above example, the first Producer in the array is somehow special, we can specifically watch for its termination in addition to all Producer terminations:
void Consumer (pel Producer p[]) {
    while ( 1 ) {
      try {
        consume ( (?p!val NOTHING)<somebody_give_me> );
      } 
      catch (?p[0]!val!NOIN) {
          printf ("The leader Producer has terminated. I follow the leader and go home.");
          break;    // out of while loop
      }
      catch (somebody_give_me!NOIN) {
          printf ("All Producers have terminated. I go home too.");
          break;    // out of while loop
      }
    }
}

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.