Welcome
» NERWous C
» Mel
Resume Operation
Let's take a look at this
Let's enhance the code so that we also get a longer timeout on resumed waits:
Resume With Attribute
In the example above, if there is no forthcoming producer, the combination of
With Assignment vs. Mel Assignment
Let's compare the use of the
Another difference is that a mel assignment means a trip to the remote mel variable to deposit the
Resume With vs. Code Duplication
Instead of doing a
Resume In Exceptions
When a task waits for multiple mels, it can also use the
Once
On the first wait of
The
On the
The
Resume, Break and Continue
We started with VERSION 1 of the
With the
While waiting on the mel
The most likely scenario is that
The last exception is the catch-all exception,
Of course the C language also offers the
Resume In Exclusive Zones
See exclusive zones.
Previous Next Top
- Resume Operation
- Resume With Attribute
- Resume In Exceptions
- Resume, Break and Continue
- Resume In Exclusive Zones
Resume Operation
Let's take a look at this
Consumer
task that only needs to Consume
once:
/* VERSION 1 */
void Consumer (<mel> int store) {
int myprio = store<priority>;
try {
Consume(<? timeout priority=myprio>store);
} catch ( store<TIMEDOUT> ) {
++myprio;
}
}
When the default timeout expires, the TIMEDOUT
exception is raised. The Consumer
adds one to its mel wait priority to increase its chance to get a value on its next try. The problem is there is no loop in the logic to support that trial iteration. We could re-introduce a loop but that loop would be artificial. Instead we will use the resume mel operation:
/* VERSION 2 */
void Consumer (<mel> int store) {
int myprio = store<priority>;
try {
Consume(<? timeout priority=myprio>store);
} catch ( store<TIMEDOUT> ) {
++myprio;
<resume>;
}
}
The <resume>
mel operation retries the latest <?>
mel wait. In the above example, the resumption is at the mel wait for the Consume
function. The newly resumed mel wait has the same default timeout value but with its priority increased by one by the exception handler.
Let's enhance the code so that we also get a longer timeout on resumed waits:
/* VERSION 3 */
void Consumer (<mel> int store) {
int myprio = store<priority>;
int mywait = store<timeout>;
try {
Consume(<? timeout=mywait priority=myprio>store);
} catch ( store<TIMEDOUT> ) {
++myprio;
mywait += 10;
<resume>;
}
}
The mel wait at the statement Consume
starts with the default timeout and priority. After the first TIMEDOUT
exception, the program retries the mel wait statement with a timeout increased by 10 msec and a priority increased by 1. After the second TIMEDOUT
exception, the wait will have 10 msec more and a priority increased one more notch. Hopefully after awhile, the mel wait will be successful and a retrieved value can be given to Consume
.
Resume With Attribute
In the example above, if there is no forthcoming producer, the combination of
TIMEDOUT
exception and resume
operation will cause the Consumer
task to loop forever. Let's handle that condition:
/* VERSION 4 */
#define DUMMYVALUE 0
void Consumer (<mel> int store) {
int myprio = store<priority>;
int mywait = store<timeout>;
try {
Consume(<? timeout=mywait priority=myprio>store);
}
catch ( store<TIMEDOUT> ) {
++myprio;
mywait += 10;
<resume>;
}
catch ( store<CLOSED || NOWRITERS> ) {
<resume with=DUMMYVALUE>; // last resumption
}
return; // exit the task with consumption
}
If the producer is done and has closed the mel, the CLOSED
exception is raised. If there is no more producers for the mel, the NOWRITERS
exception is also raised. We catch either exception. We could flow out of the catch
code and return
ing to close the Consumer
task. Instead Consumer
does one more retry.
Consumer
wants to give a dummy value to the function Consume
for its own closure (such as for Consume
to print "no value found"). It accomplishes this by invoking the resume
operation with the with
attribute set to the dummy value. The Consumer
task jumps back to the mel wait statement, and runs the Consume
function with the dummy value as argument. Upon consumption, the Consumer
task flows out of the try / catch
setup and return
s for the task closure.
With Assignment vs. Mel Assignment
Let's compare the use of the
resume
with the with
attribute with a mel assignment followed by a simple resume
:
catch ( store<CLOSED || NOWRITERS> ) {
<?>store = DUMMYVALUE;
<resume>;
}
The mel assignment way above is fraught with gotcha's. It is possible that after the store
mel variable gets filled with the DUMMYVALUE
, and before the Consumer
task gets resume
'd back to the Consume
statement, another task that is waiting on the store
read queue will pick up that value, leaving Consumer
with nothing for its Consume
function.
Another difference is that a mel assignment means a trip to the remote mel variable to deposit the
DUMMYVALUE
. The mel sequence property is also updated. On the other hand, a with
assignment is local to the Consumer
task. As far as the store
mel variable is concerned, it never gets this DUMMYVALUE
.
Resume With vs. Code Duplication
Instead of doing a
resume-with
, we can also run the Consume
function directly in the catch
block:
catch ( store<CLOSED || NOWRITERS> ) {
Consume(DUMMYVALUE);
}
This is definitely more efficient, since there is no resumption jump-back. However, let's look at another version of Consumer
:
/* VERSION 5 */
#define DUMMYVALUE 0
void Consumer (<mel> int store) {
int myprio = store<priority>;
int mywait = store<timeout>;
try {
Consume(<? timeout=mywait priority=myprio>store);
ReportConsumptionToHigherAuthority();
}
catch ( store<TIMEDOUT> ) {
++myprio;
mywait += 10;
<resume>;
}
catch ( store<CLOSED || NOWRITERS> ) {
<resume with=DUMMYVALUE>; // last resumption
}
return; // exit the task with consumption
}
With the resume-with
method, the ReportConsumptionToHigherAuthority
will also be executed for the DUMMYVALUE
. This allows all the logic for the consumption to be in the try
block, instead of being duplicated in a catch
block for the special value of DUMMYVALUE
:
try {
Consume(<? timeout=mywait priority=myprio>store);
ReportConsumptionToHigherAuthority();
}
catch ( store<CLOSED || NOWRITERS> ) {
Consume(DUMMYVALUE);
ReportConsumptionToHigherAuthority();
}
Of course if DUMMYVALUE
needs to be treated differently, then its logic should be coded in the catch
block, and resume-with
should not be used:
catch ( store<CLOSED || NOWRITERS> ) {
Consume(DUMMYVALUE);
LetCelebrateOfJobWellDone();
}
Resume In Exceptions
When a task waits for multiple mels, it can also use the
resume
operation. Let's look at this new Consumer
task which consumes two mel variables, store1
and store2
:
/* VERSION 6 */
int Consumer (<mel> int store1, <mel> int store2) {
try {
int retries;
string steps = "CONS_BOTH";
ConsumeBoth (<? timeout>(store1 && store2));
steps += "-CONS_ONE"; retries = null;
Consume (<? timeout>store1);
steps += "-CONS_TWO";
Consume (<? timeout>store2);
steps += "-CONS_ONE_AGAIN"; retries = 0;
Consume (<? timeout>store1);
return SUCCESS;
}
catch ( (store1 && store2)<TIMEDOUT> ) {
printf ("Can't get both values in time - Try again");
<resume>;
}
catch ( (store1 && store2)<CLOSED> ) {
return RESOURCE_FAILURE;
}
catch ( store1<TIMEDOUT> ) {
printf ("Can't get [store1] at steps [%s] in time - Try again", steps);
if ( retries == null ) <resume>;
else if ( ++retries < 5 ) <resume>;
else <resume with=DUMMYVALUE>;
}
catch (store2<TIMEDOUT> ) {
printf ("Can't get [store2] in time - Try again");
<resume>;
}
catch ( (store1 || store2)<CLOSED> ) {
<resume with=null>;
}
}
The ConsumeBoth
expects two arguments, and waits for them at the mel AND wait. If this wait times out, the catch ( (store1 && store2)<TIMEDOUT> )
exception is invoked causing the "Can't get both values in time - Try Again" message to be shown. The resume
statement will return the processing flow back to the mel AND wait for ConsumeBoth
. This looping continues until both store1
and store2
are valued, or until both of them are closed. In the latter case, the code will continue at the catch ( (store1 && store2)<CLOSED> )
exception code, where the Consumer
will exit with a RESOURCE_FAILURE
.
Once
ConsumeBoth
is satisfactorily run, the Consumer
task waits for and Consume
store1
, then store2
, and finally store1
again.
On the first wait of
store1
(i.e. at steps CONS_ONE
), if the wait times out, the task will fall into the catch ( store1<TIMEDOUT> )
exception code block. For CONS_ONE
, the local variable retries
is always null, which instructs the Consumer
to resume
the wait. This timeout repeats until store1
is either valued or closed. For the first case, Consumer
goes to step CONS_TWO
for the consumption of store2
. For the closed case, Consumer
falls into the catch ( (store1 || store2)<CLOSED> )
exception code, where it resume
s with a null
value. The Consume
function at the CONS_ONE
mel wait is entered with this null value. It does whatever it does with this value, and then returns to the main flow, which then goes to step CONS_TWO
.
The
Consumer
task then waits on store2
until this mel variable is valued or closed. In either case, similarly to the analysis of store1
in step CONS_ONE
above, the task will continue to step CONS_ONE_AGAIN
.
On the
CONS_ONE_AGAIN
wait on store1
, the task has set the local variable retries
to 0. This allows the task to do only 5 resume
operations on timeout. On the 6th timeout, the Consumer
will abort the wait by seeding the DUMMYVALUE
to the Consume
function so that the CONS_ONE_AGAIN
step can finish.
The
Consumer
task then returns SUCCESS
.
Resume, Break and Continue
We started with VERSION 1 of the
Consumer
which does a one-shot consumption. Let's modify Consumer
to have it continuously Consume
so that we can explore the difference between resume
, continue
and break
:
/* VERSION 7 */
void Consumer (<mel> int store) {
while (1) {
printf ("Wait and consume ...");
int myprio = store<priority>;
try {
Consume(<? timeout priority=myprio>store);
}
catch ( store<TIMEDOUT> ) {
++myprio;
<resume>; /* resume at the wait statement */
}
catch ( store<FAILED> ) {
continue; /* iterate the while loop */
}
catch ( store<CLOSED> ) {
break; /* break out of the while loop */
}
catch ( store ) {
printf ("Unexpected exception [%s] ...", store));
}
printf ("One consumed\n");
}
printf ("All done!");
}
With the
while (1)
loop, the Consumer
task keeps Consume
'ing until the mel store
is closed by another process. When this happens, the catch ( store<CLOSED> )
exception handler will be entered. This handler ends with the standard C language break
statement, allowing the program to break out of the while (1)
loop. The "All done!"
message is shown, and the task Consumer
closes.
While waiting on the mel
store
, the Consumer
task may get hit by a FAILED
exception. This exception may be caused by a permanent or temporary hiccup in accessing the remote mel location. Our Consumer
task decides to retry by invoking the C language continue
statement. This re-iterates the while (1)
loop. The "Wait and consume ..."
message is shown again.
The most likely scenario is that
Consumer
will get hit by the TIMEOUT
exception during its mel wait. The exception handler makes use of the NERWous C <resume>
statement. Unlike the continue
statement which iterates back to the while
loop, the <resume>
statement iterates back to the mel wait statement <? timeout priority=myprio>store
. Any code inside the while
loop before the mel wait statement will not be executed, including the printf
for the "Wait and consume ..."
message.
The last exception is the catch-all exception,
catch ( store )
. After doing a printf
of the unexpected exception, the code just flows through, the One consumed
statement will be shown, and the program re-iterates to the while (1)
loop. This differs from the continue
statement which jumps back directly to the while (1)
loop, bypassing the "Once consumed"
message.
Of course the C language also offers the
goto
statement to redirect the code flow. However we are not going there.
Resume In Exclusive Zones
See exclusive zones.
Previous Next Top
No comments:
Post a Comment