Pages

Sunday, April 10, 2016

Mel Subscriptions

Welcome » NERWous C » Mel
  1. Writers Subscriptions
  2. Quit vs. Close
  3. Readers Subscriptions
  4. Dual Subscriptions
  5. Quit Attributes


Writers Subscriptions

The previous example introduces the NOWRITERs exception. This exception makes use of the writers subscription feature of the mel.

In the beginning, when a mel is created, its writers subscription is undefined. The first producer task that executes a write operation (such as <?>store = 10) is said to have subscribed to the mel for writing. The writers subscription is now 1. Other producer tasks that come along later will add 1 each to the writers subscription.

For each task, the writers subscription is a one-time event. As said above, the first write operation by the task will record its subscription. Any subsequent write operations by the same task will not add another subscription. New subscriptions only occur when new producer tasks access the mel for writing the first time to that mel.

When the task ends, the CHAOS runtime environment will automatically unsubscribe the task from the mel. If the task has a writers subscription, this action will decrease the writers subscription of the mel by 1. A task can also explicitly unsubscribe by invoking the quit operation, as shown in the example below.

Any consumer task that accesses the mel entity for reading will receive the writers subscription number from the mel entity. This number is cached locally in the mel proprety, writers. If the consumer has a catch on the NOWRITERS exception, several things happen when the consumer does a wait operation for reading. If the writers subscription is 0, the wait is aborted and the consumer task will have the NOWRITERS exception raised with the mel property writers updated to 0. If the writers subscription is undefined or greater than 0, the consumer task will stand in the mel queue and continues with the mel read wait process. If the wait becomes successful and the consumer task gets a value, the local mel property writers is updated to the actual writers subscription number at that time. If during the wait, the writers subscription becomes 0, then the wait is broken and the consumer task will have the NOWRITERS exception raised with the mel property writers updated to 0.

It is worthwhile to re-emphasize that the NOWRITERS exception is optional. If the reader task does not define a NOWRITERS exception handler, than even when the mel property writers is found to be 0, no NOWRITERS exception will be raised for the task. Such a reader task expects writer tasks to come and go, and there will be periods of times where there are no writers at all to deposit new values. In those times, the reader task simply waits at the mel read wait statement.

The following example shows the use of the quit operation, the NOWRITERS exception, and the writers property:
main () {
   <mel> int store;
   <! name="Producer1"> Producer (store);
   <! name="Producer2"> Producer (store);
   <!> Consumer (store);
}
void Producer (<mel> int store) {
   var n = random();
   while ( --n ) {
      <?>store = Produce();
   }
   <quit>store;
   DoSomethingElse();
}
void Consumer (<mel> int store) {
   while (1) {
      try {
         Consume (<?>store);
         printf ("There are %d producers serving me", store<writers>);
      }
      catch ( store<NOWRITERS> ) {
         printf ("No more producers - let's exit the grind");
         break;
      }
   }
   printf ("All consumption done");
}
We have two Producers running the same code. They Produce a random number of items then stop and DoSomethingElse. We have one common Consumer that gets whatever the Producers generate via the shared mel store.

The Consumer watches for the NOWRITERS exception. Without it, it will while loop forever after both Producers having stopped Produce'ing.

The Producer makes use of the quit operation to increase concurrency. Without this operation, the unsubscription to the mel store has to wait for the DoSomethingElse to be done and the Producer task to stop execution for the automatic unsubscription to take place. Here, the NOWRITERS exception will be raised as soon as both Producers have quittted, and the All consumption done message can be printed sooner.


Quit vs. Close

The mel close operation is a more drastic and global way for the Producer to mark that it is done with the mel store. If it is used in the above example instead of quit, the first Producer that ends will cause the other Producer task to get a mel CLOSED exception on the write operation operation and stop Produce'ing. The Consumer task will also get a CLOSED exception on its read operation operation, forcing it to abort abnormally. The use of close will prevent Consumer to consume whatever products the remaining Producer has left to Produce.

The first Producer that calls quit will have no impact on Consumer. The writers subscription of store will be down to 1, which is still greater than 0, so the NOWRITERS exception is not raised on Consumer. When the remaining Producer also calls quit or is terminated, then the NOWRITERS exception is raised and Consumer can wrap up within the NOWRITERS exception handler and then ends itself.


Readers Subscriptions

The readers subscription is similar to the writers subscription, but for the read operations.

A mel when first created has its readers subscription undefined. Any task that makes use of the read operation (such as c=<?>store) will increase the readers subscription by 1. The readers subscription is a one-time event. Any subsequent read operations by the same task will not add another subscription. When a subscribed task calls quit or has stopped running, the readers subscription is decremented by 1.

Any producer task that accesses the mel entity for writing will receive the readers subscription number from the mel entity. This number is cached locally in the mel proprety, readers. Any producer task that has the NOREADERS exception set up, will have this exception raised when it detects that the readers subscription has become 0.

Like the NOWRITERS exception, the NOREADERS exception is optional. If the writer task does not define a NOREADERS exception handler, than even when the mel property readers goes down to 0, no NOREADERS exception will be raised for the writer task. Such a writer task expects reader tasks to come and go, and there will be periods of times where there are no readers to empty the mel buffers so that it can deposit new products. In those times, the writer task just waits at the mel write wait statement.

The following example shows the use of the quit operation, the NOREADERS exception, and the readers property:
main () {
   <mel> int store;
   <!> Producer (store);
   <! name="Consumer1"> Consumer (store);
   <! name="Consumer2"> Consumer (store);
}
void Producer (<mel> int store) {
   while (1) {
      try {
         <?>store = Produce();
         printf ("There are %d consumers serving me", store<readers>);
      }
      catch ( store<NOREADERS> ) {
         printf ("No more consumers - let's exit the grind");
         break;
      }
   }
   printf ("All consumption done");
}
void Consumer (<mel> int store) {
   var n = random();
   while ( --n ) {
      Consume (<?>store);
   }
   <quit>store;
   DoSomethingElse();
}
We have above 2 Consumer tasks that Consume a random number of produced items, before quitting. Once both Consumer tasks have quitted, the readers subscription to the mel store becomes 0, causing the NOREADERS exception to be raised, allowing Producer to wrap up.


Dual Subscriptions

A task can have both readers and writers subscriptions.

We will now modify the example above by introducing an Inspector task that randomly withdraws the next produced item from the pipeline, examines it, and puts it back for consumption if it passes inspection.
main () {
   <mel> int store;
   <!> Producer (store);
   <! name="Consumer1"> Consumer (store);
   <! name="Consumer2"> Consumer (store);
   <!> Inspector (store);
}
void Inspector (<mel> int store) {
   int c;
   while ( IsMyShift() ) {
      c = <?>store;      // withdraw product from pipeline
      if ( !Examine (c) ) {   // examine it
        printf ("Stop the line -- value [%d] does not pass inspection", c);
        <close>store;
        return;
      }
      printf ("Value [%d] is good for consumption", c);
      <?>store = c;     // put it back to pipeline
      sleep (random());      // to do the inspection randomly
   }
   <quit>store;
   WriteShiftReport ();
}
Because the Inspector has a read operation and write operation, it has both the readers and writers subscriptions. At the end of its shift, it calls quit to unsubscribe to both subscriptions. However, during its shift, if it detects a bad product, it will close the mel, triggering CLOSED exceptions to all running processes that access the mel store. Since neither Producer nor Consumer has code to handle the CLOSED exception, this exception will abort those tasks. With all tasks not ended, the program also exits, causing the "line to be stopped" just as Inspector wants it.


Quit Attributes

The above implementation of Inspector has a big flaw. It prevents Producer to detect the NOREADERS exception, since Inspector itself is a readers subscriber. As long as Inspector is still in its shift, it will prevent the readers subscription number to go to 0 even if all the Consumer tasks have already quittted. Let's modify Inspector:
void Inspector (<mel> int store) {
   int c;
   while ( IsMyShift() ) {
      c = <?>store;     // withdraw product from pipeline
      <quit readers>;
      if ( !Examine (c) ) {   // examine it
        printf ("Stop the line -- value [%d] does not pass inspection", c);
        <close>store;
        return;
      }
      printf ("Value [%d] is good for consumption", c);
      <?>store = c;    // put it back to pipeline
      <quit writers>;
      sleep (random());      // to do the inspection randomly
   }
   WriteShiftReport ();
}
After a read operation, Inspector unsubscribes to the readers subscription; and after a write operation, to the writers subscription. If all Consumer tasks have quitted, and there is a Producer waiting at the mel queue for a buffer slot to free up so it can deposit its product, the <quit readers> command from Inspector will allow the readers subscription number to go to 0. This will relieve Producer from waiting and the NOREADERS exception can be raised.

The same argument applies similarly for the <quit writers> command, any waiting Consumer tasks and their NOWRITERS exception handling.

Astute readers of the Inspector code above may claim that we would not need the attribute readers and writers to the quit operation. And they are right. When Inspector starts, it does not have any subscriptions. On its first read operation statement (c = <?>store;), it gains a readers subscription. Since it has only one subscription, the more generic <quit> statement will generate the same result as the specific <quit readers> statement used here. The same logic applies for the <quit writers> statement.


Previous Next Top

No comments:

Post a Comment