Welcome
» NERWous C
» Mel
Reader Zones
The NERWous C examples we have seen so far have the tasks getting a lock on the mel variable long enough to put in a new value (
Let's modify our
The task
When a task uses a mel (exclusive) reader zone, the following behind-the-scene process occurs:
Conditions
Let us repeat the conditions that allow the task to get into the reader zone:
Eponymous Variables
When a task checks into the exclusive reader zone, the value of the mel variable is automatically copied to a local variable in the task's environment. This local variable is called the eponymous variable since it has the same name as the mel variable. Within the reader zone, the task does reads and writes to the local eponymous variable instead of to the remote mel variable. Since the access is local, there is no need for the wait operation (<?>).
When the task is in the exclusive reader zone, it can read from the eponymous variable as we see with the
When the task checks out of the reader zone, what happens to the value contained in the eponymous variable depends on the mode of the
Checkout Operation
A task checks into a reader zone exclusive to a mel variable, say
On checking into the reader zone, the value of the mel variable is copied to the eponymous variable maintained locally at the task. When the task checks out of the reader zone, what happens to the local mel properties and the remote mel variable depends on the
Let's take a look at this example, and compare the local <value> and <sequence> properties against the remote mel variable's value and sequence number, at the checkout of the reader zone.
Checkout Writeover
When a
The
The following code samples on the mel variable
Reader Zone Attributes
Like any mel wait operation, a mel reader zone wait has the following attributes:
To illustrate the use of the priority and timeout attributes, let's introduce a new task called the
Priority Attribute
The two
When
For all the three tasks, the local <sequence> property of the mel variable is updated, allowing them to wait for a new mel
Timeout Attribute
The
The timeout exception as used in this example may allow a
Mode Attribute
If a task checks into a reader zone and knows that most of the ways that it will check out of the zone is via a certain checkout mode (be it
When specifying the
Read Mode Attribute:
This is not necessary because the default mode is already the
Skip Mode Attribute:
Now let's rewrite the
Writeover Mode Attribute:
The
Reader Zone Resumptions
An astute reader will notice that there is a flaw in the VERSION 1 of the
In VERSION 1, after the reader zone checkout,
To resolve this flaw, we will use the
By invoking
Again the astute reader will notice a processing gap between the
Previous Next Top
Reader Zones
The NERWous C examples we have seen so far have the tasks getting a lock on the mel variable long enough to put in a new value (
Producer
tasks), or read out the deposited mel value (Consumer
tasks). We now introduce a new NERWous C construct called exclusive zones where the tasks can hold on to the mel variables as long as it needs to. We start with the reader zone in this chapter, and will explore the writer zone in a future chapter.
Let's modify our
Consumer
task so that it only consumes an odd or even product depending on an argument request. To make sure that all products are consume
'd, we will create two Consumer
tasks, one for the even products, and the other for the odd products:
#define DO_EVEN 0
#define DO_ODD 1
main () {
<mel> int store;
<!>Producer (store);
<!>Consumer (store, DO_EVEN);
<!>Consumer (store, DO_ODD);
}
void Producer (<mel> int store) {
int c;
while ( 1 ) {
c = produce();
if ( c == 0 ) {
<close>store;
break;
}
<?>store = c;
}
}
void Consumer (<mel> int store, int evenodd) {
while ( 1 ) {
try {
printf ("Waiting to enter reader zone");
<?>store {
if ( ((evenodd == DO_EVEN) && (store % 2) == 0))
|| ((evenodd == DO_ODD) && (store % 2) != 0)) ) {
consume (store);
printf ("Consumed product [%d]", store);
}
else {
printf ("Skipped product [%d]", store);
<checkout skip>;
}
}
printf ("Stepped back from reader zone with value [%d]", store<value>);
}
catch ( store<CLOSED> ) {
printf ("Exit because the Store is closed");
break;
}
}
}
There is nothing special about Producer
. It keeps produce
'ing until a zero product is detected. It then close
s the mel store
and break
s from the infinite while
loop. As it runs out of code to run, the task ends itself.
The task
Consumer
is the more interesting one. The task main
pels two Consumer
tasks, one to consume
the even products and the other, the odd products. The task Consumer
implements its logic inside an exclusive reader zone code block, <?>store { ... }
.
When a task uses a mel (exclusive) reader zone, the following behind-the-scene process occurs:
- The task puts itself in the mel readers' queue.
- In due course, the task moves up to the top position of the readers' queue. If the mel is still empty, the task will wait at this position, until the mel becomes valued.
- If and when the mel is valued, the task checks the mel sequence number. If this number is the same as the locally cached <sequence> property that the task keeps from the last access, the mel is stale, and the task continues to wait at the top of the queue position. During this wait, the task allows another reader task behind it to "jump the line" to get the stale mel.
-
When the task detects that the mel is valued with a new value, the task is ready to check in into the exclusive zone. See summary of conditions.
- With the task still keeping its top of the queue position, other incoming reader tasks must stand in line in various mel readers' priority queues. At this stage, the task does not allow subsequent reader tasks to "jump the line" to get to the mel value. The task now has exclusive read access to the mel variable.
- The check-in process has the task copy the mel value into a local eponymous variable but leave the mel value remaining at the remote mel variable.
Instead of working with the remote mel which requires potentially expensive network communication, the task works with the eponymous local variable. This is the reason within the reader zone, the variablestore
can be accessed without the <?> wait operation -- we are accessing the eponymous local variable of the same name, and not the remote mel variable.
-
While the task keeps other reader tasks at bay, it allows readonly access to the mel value left at the top slot of the mel buffer. If the readonly task returns for another readonly request, it may find that the mel value is still the same. In this case, the readonly task deems the mel variable stale and will wait at the readonly queue for a new value.
- If a writer task comes along, it may still able to deposit its item if the mel variable is buffered. When the mel buffer is full, the writer task will wait at the mel writers' queue until the exclusive zone task checks out off the exclusive zone and allows itself or another reader task to empty the top slot of the mel buffer.
- When the task is done with the reader zone, it either falls out of the reader zone's code block at the closing
}
, or invokes thecheckout
operation. The default behavior in both cases is to remove the mel variable off the top of the buffer. The value of the eponymous variable, that has been updated by the code inside the reader zone, is copied to the mel local <value> property. In addition, the mel local <sequence> property is also updated with the sequence number of the mel value that has been removed.
- Besides the default behavior, the task may choose other behaviors by calling the
checkout
operation with an attribute. In the example above, theDO_EVEN
Consumer
task does not process an odd product, and theDO_ODD
one, an even product. The<checkout skip>
operation has the task getting out of the reader zone without removing the top value from the mel variable buffer. Although there is no removal, the <value> property is still updated with the value of the eponymous variable. The <sequence> property is also updated with the sequence number of the mel variable (that has not been removed form the mel buffer) to prevent the task to re-process the just processed value in case of an iteration.
- After checking out off the reader zone, the task drops itself out of the mel readers' queue. Any reader task that has been waiting in the various mel priority queues can now has the chance to move up the queues and access the mel variable. Any writer task that has been waiting on the mel writers' queue because the buffer is full, can deposit its value if there is a new slot opened up from the default checkout behavior.
- After the reader zone checkout, our
Consumer
task iterates the infinitewhile
loop and vies with other reader tasks to get access to the mel variable. During this wait, thestore<CLOSED>
exception can happen if the taskProducer
has closed the mel variablestore
. At the end of the exception handler, the taskbreak
s out of thewhile
loop, and with nothing else to do, ends itself.
main
after pelling the three tasks has already ended. With the Producer
and the two Consumer
tasks also ended, there are no more running tasks. The program itself then exits.
Conditions
Let us repeat the conditions that allow the task to get into the reader zone:
- The task must be at the top position of the reader's queue
- The mel variable has a value at the reader's end of the mel buffer
- This value is not stale
Eponymous Variables
When a task checks into the exclusive reader zone, the value of the mel variable is automatically copied to a local variable in the task's environment. This local variable is called the eponymous variable since it has the same name as the mel variable. Within the reader zone, the task does reads and writes to the local eponymous variable instead of to the remote mel variable. Since the access is local, there is no need for the wait operation (<?>).
When the task is in the exclusive reader zone, it can read from the eponymous variable as we see with the
Consumer
task that consume
s what it reads. It can also make change to the eponymous variable as we will see in the upcoming Manipulator
task. Thus, the value of the eponymous variable can diverge from the original value of the mel variable.
When the task checks out of the reader zone, what happens to the value contained in the eponymous variable depends on the mode of the
checkout
operation.
Checkout Operation
A task checks into a reader zone exclusive to a mel variable, say
store
, via the <?>store {
opening construct. It checks out of the zone at the closing curly bracket (}
) or by explicitly invoking the checkout
operation. Checking out via the closing bracket is the same as implicitly invoking the checkout
operation without any attribute.
On checking into the reader zone, the value of the mel variable is copied to the eponymous variable maintained locally at the task. When the task checks out of the reader zone, what happens to the local mel properties and the remote mel variable depends on the
checkout
operation attributes:
Operation | Synopsis |
---|---|
<checkout> |
Checkout with default mode. If the default mode is not specified on check-in of the exclusive zone, the <checkout read> is assumed.
|
<checkout read> | This is the default behavior. The task removes the mel value off the mel buffer when checking out of the reader zone. This is similar to a mel read operation. The removal of the mel value frees the top slot of the mel buffer, allowing the next item in the buffer to move up and a writer task to deposit a new value at the writer's end of the buffer. |
<checkout skip> | The task checks out of the reader zone without removing the value of the mel variable. The value the mel variable has on entry of the reader zone is retained. This value is then available for another reader task. Since the mel buffer is not emptied of its top slot, any writer task that is waiting due to a full buffer, will continue to wait. |
<checkout writeover> | The task checks out of the reader zone without removing the value of the mel variable. However the value of the mel variable is updated with the value of the eponymous variable. This updated value is then available for another reader task. Since the mel buffer is not emptied of its top slot, any writer task that is waiting due to a full buffer, will continue to wait.
The sequence number is increased at both the remote mel variable and the local mel property <sequence> of the checkout task.
|
Let's take a look at this example, and compare the local <value> and <sequence> properties against the remote mel variable's value and sequence number, at the checkout of the reader zone.
extern int testcase;
<?>store = 10; /* mel variable */
printf ("Before value [%d], seqno [%lld]", store<value>, store<sequence>);
<> store {
store++; /* eponymous variable */
if ( testcase == 1 ) <checkout read>;
if ( testcase == 2 ) <checkout skip>;
if ( testcase == 3 ) <checkout writeover>;
}
printf ("After value [%d], seqno [%lld]", store<value>, store<sequence>);
On check-in to the reader zone, the value of the mel variable is set to 10
, and let's assume that its sequence number is the number X. On checkout, we have this matrix, depending on the testcase
taken:
Local Properties | Mel Variable | |
---|---|---|
<checkout read> | The store<value> property has the value of the eponymous variable (hence, 11 ). The store<sequence> property has the value of the sequence number of the remote mel variable on check-in of the reader zone (hence X). |
The value of the remote mel variable is undefined since it has been removed by the <checkout read> . The sequence number of the remote mel variable does not change from the reader zone entry (hence X). |
<checkout skip> | The store<value> property has the value of the mel variable on entry of the reader zone (hence, 10 ). The store<sequence> property also retains the sequence number got by reading the mel variable on check-in of the reader zone (hence X). |
The remote mel variable retains the same original mel value (hence 10 ) and sequence number (hence X) as on entrance of the reader zone. |
<checkout writeover> | The store<value> property has the value of the eponymous variable (hence, 11 ). The store<sequence> property has the value of the sequence number of the remote mel variable on check-in of the reader zone plus one (hence X+1). |
The remote mel variable has the value of the eponymous variable (hence, 11 ). Its sequence number has been increased by one (hence X+1). |
Checkout Writeover
When a
checkout writeover
is invoked, the sequence number of the remote mel variable is increased. This allows reader tasks that wait for a new mel value to get out of their slumber. The increased sequence number is also copied to the local <sequence> property of the task that does the checkout writeover
. Identical sequence numbers indicate staleness, and this prevents this task to reprocess the mel variable in case the exclusive zone wait is resumed in an iterative loop. As a reminder, the local <sequence> property associated with this mel variable in other tasks is not updated until that particular task has successfully done a read or write operation to the mel variable; thus staleness does not apply to those other tasks -- they are free to read the new mel value.
The
checkout writeover
mode is used frequently enough in NERWous C programs that it has a shortcut for a particular case. If a task enters a reader zone and knows that most of the ways that it will checkout
of the zone is via the checkout writeover
mode, it can specify this mode as the default mode on entrance of the reader zone.
The following code samples on the mel variable
store
are equivalent:
/* Standard version for writeover mode */
<?>store {
store += 10; /* no matter what, alwsys increase store by 10 */
if ( !checkformore(store) ) <checkout writeover>;
store += 10; /* store can get another 10 */
<checkout writeover>;
}
---
/* Writeover mode default version */
<? mode=writeover>store {
store += 10; /* no matter what, alwsys increase store by 10 */
if ( !checkformore(store) ) <checkout>; /* writeover is assumed */
store += 10; /* store can get another 10 */
} /* <checkout writeover> is assumed
---
/* Omitted mode keyword */
<?writeover>store {
store += 10; /* no matter what, alwsys increase store by 10 */
if ( !checkformore(store) ) <checkout>; /* writeover is assumed */
store += 10; /* store can get another 10 */
} /* <checkout writeover> is assumed
Like the writeover mode for a mel variable wait, the mode
keyword can be omitted for a mel zone wait.
Reader Zone Attributes
Like any mel wait operation, a mel reader zone wait has the following attributes:
Attribute | Synopsis |
---|---|
mode | Default mode on checkouts |
priority | Wait to check in using this priority |
timeout | Wait to check in up to this timeout value. If the timeout is reached, a <TIMEOUT> exception will be raised. |
To illustrate the use of the priority and timeout attributes, let's introduce a new task called the
Manipulator
. whose purpose is to surreptitiously convert any products from the Producer
into an even number.
#include "nerw.h"
#define DO_EVEN 0
#define DO_ODD 1
main () {
<mel> int store;
<pel> p = <!>Manipulator (store);
<!>Producer (store);
<!>Consumer (store, DO_EVEN);
<!>Consumer (store, DO_ODD);
}
/* VERSION 1 */
void Manipulator(<mel> int store) {
while (1) {
try {
printf ("Yet another round of waiting...");
<? priority=NERW_PRIORITY_HIGHEST timeout+timeout>store {
store += store; // double store to make it even
<checkout writeover>;
}
}
catch ( store<CLOSED> ) { break; }
catch ( store<TIMEOUT> ) {
printf ("Is something wrong with Producer?");
continue;
}
}
}
The task main
pels the task Manipulator
and makes sure that is running by using the pel variable assignment. Then the task main
pels the Producer
and the two Consumer
tasks like before. The codes for Producer
and Consumer
are the same as in the original example, and are not repeated here.
-
Side note:
The use of the pel variablep
forManipulator
is to prevent a potential race condition. A pel operation (<!>) is only a request to run the code in a separate task, not an assurance that the task has actually run. Without the pel variable assignment, there is no guarantee that theManipulator
will run before aConsumer
task. If aConsumer
gets assigned to faster resources and starts to execute beforeManipulator
, some initial products may go fromProducer
toConsumer
without beingManipulate
d.
Priority Attribute
The two
Consumer
tasks and the Manipulator
task vie to enter the exclusive reader zone. Since the Manipulator
task waits at the highest possible priority for the run-time environment in use, it will be given first access. The Consumer
tasks do not specify a priority and thus will be waiting in the default priority queue. The priority queues used for the reader zones are the same priority queues used for the regular mel reads.
When
Manipulator
is in the reader zone, it turns the eponymous store
value into an even number by doubling it. It then gets out of the zone with a <checkout writeover>
operation, resulting in the mel variable value being replaced with the new double even value. The Consumer
task that is first in line in the default queue is now given exclusive access to the mel variable. If this is the DO_EVEN
task, it will consume
the manipulated value, and removes that value off the mel variable. If this is the DO_ODD
task, it will get off the reader zone with a <checkout skip> operation, leaving the mel variable value untouched for the upcoming DO_EVEN
task.
For all the three tasks, the local <sequence> property of the mel variable is updated, allowing them to wait for a new mel
store
value from the Producer
instead of re-processing a stale value.
Timeout Attribute
The
Manipulator
also makes use of the timeout attribute during the reader zone wait. If it cannot get into the zone after double the default system timeout (timeout + timeout
), it will jump into the store<TIMEOUT>
exception handler, prints a message, and iterates back to the while
loop to wait again.
The timeout exception as used in this example may allow a
Consumer
task to sneak in while the Manipulator
is running the exception handler, and process an un-manipulated product from Producer
.
Mode Attribute
If a task checks into a reader zone and knows that most of the ways that it will check out of the zone is via a certain checkout mode (be it
read
, skip
or writeover
), it can specify this mode on the check-in of the reader zone, using the mode
attribute.
When specifying the
mode
attribute, the keyword mode
can be ignored. Instead, just the value of the mode (either read
, skip
or writeover
) is specified. In the examples below, we will use this condensed short-form format.
Read Mode Attribute:
This is not necessary because the default mode is already the
read
mode. However a programmer may feel compel to mention it explicitly for emphasis, especially if one of the checkout cases will be in a different mode. Let's rewrite the Consumer
example above with an explicit read
mode:
void Consumer (<mel> int store, int evenodd) {
while ( 1 ) {
try {
printf ("Waiting to enter reader zone");
<?read>store {
if ( ((evenodd == DO_EVEN) && (store % 2) == 0))
|| ((evenodd == DO_ODD) && (store % 2) != 0)) ) {
consume (store);
printf ("Consumed product [%d]", store);
}
else {
printf ("Skipped product [%d]", store);
<checkout skip>;
}
}
printf ("Stepped back from reader zone with value [%d]", store<value>);
}
catch ( store<CLOSED> ) {
printf ("Exit because the Store is closed");
break;
}
}
}
Skip Mode Attribute:
Now let's rewrite the
Consumer
example with the skip
mode as the default mode:
void Consumer (<mel> int store, int evenodd) {
while ( 1 ) {
try {
printf ("Waiting to enter reader zone");
<?skip>store {
if ( ((evenodd == DO_EVEN) && (store % 2) == 0))
|| ((evenodd == DO_ODD) && (store % 2) != 0)) ) {
consume (store);
printf ("Consumed product [%d]", store);
}
else {
printf ("Skipped product [%d]", store);
<checkout>; /* this is now checkout skip */
}
}
printf ("Stepped back from reader zone with value [%d]", store<value>);
<checkout read>; /* need this */
}
catch ( store<CLOSED> ) {
printf ("Exit because the Store is closed");
break;
}
}
}
We need to add a checkout read
statement before getting out of the reader zone because the implicit checkout
now means checkout skip
due to the default mode now set to skip
.
Writeover Mode Attribute:
The
writeover
mode has been discussed in the Checkout Writeover section:
void Consumer (<mel> int store, int evenodd) {
while ( 1 ) {
try {
printf ("Waiting to enter reader zone");
<?writeover>store {
if ( ((evenodd == DO_EVEN) && (store % 2) == 0))
|| ((evenodd == DO_ODD) && (store % 2) != 0)) ) {
consume (store);
printf ("Consumed product [%d]", store);
}
else {
printf ("Skipped product [%d]", store);
<checkout skip>; /* need to be specific */
}
}
printf ("Stepped back from reader zone with value [%d]", store<value>);
<checkout read>; /* need to be specific */
}
catch ( store<CLOSED> ) {
printf ("Exit because the Store is closed");
break;
}
}
}
While the default mode is writeover
, none of the checkouts are writeover
, thus the checkout modes must be specifically designated.
Reader Zone Resumptions
An astute reader will notice that there is a flaw in the VERSION 1 of the
Manipulator
above. This flaw may allow a Consumer
to access the store
variable before Manipulator
has a chance to modify it, even though Manipulator
is on a higher priority queue.
In VERSION 1, after the reader zone checkout,
Manipulator
is not on any mel waiting queue. It jumps back to the beginning of the while
loop, checks the while
loop condition (which is always true), prints to the console the "Yet another round of waiting..."
message, and then finally gets back to the high priority mel waiting queue. During this interval, it may happen that the waiting Consumer
task is a DO_EVEN
one. It will get into the reader zone and processes the mel value that Manipulator
has changed, and removes the mel value on checkout. If the mel variable is buffered, the Producer
may have feed the subsequent slots. The processing gap that Manipulator
has left open, may be enough for the DO_ODD
Consumer
that is next in line in the default queue to grasp the next item in the mel buffer. If this item is odd, it will be consume
'd, defeating Manipulator
's goal to manipulate all products to be even.
To resolve this flaw, we will use the
resume
operation.
/* VERSION 2 */
void Manipulator(<mel> int store) {
while (1) {
try {
printf ("Yet another round of waiting...");
<? priority=NERW_PRIORITY_HIGHEST timeout+timeout>store {
store += store; // double store to make it even
<checkout writeover>;
} <resume>;
}
catch ( store<CLOSED> ) { break; }
catch ( store<TIMEOUT> ) {
printf ("Is something wrong with Producer?");
continue;
}
}
}
The <resume>
operation must be invoked at the closing bracket of the reader zone code block. This instructs the CHAOS run-time to put the task right back to the waiting queue for re-entry to the reader zone instead of having it totally leave the queue. The task is put at the end of the waiting queue to give tasks already in the queue the chance to get into the reader zone.
By invoking
<resume>
, the Manipulator
task bypasses the while
loop iteration and the "Yet another round of waiting..."
printing. If this message is not necessary, we can simplify Manipulator
by omitting the while
loop and use only the resumption for iterations:
/* VERSION 3 */
void Manipulator(<mel> int store) {
try {
<? priority=NERW_PRIORITY_HIGHEST timeout+timeout>store {
store += store; // double store to make it even
<checkout writeover>;
} <resume>;
}
catch ( store<CLOSED> ) { }
catch ( store<TIMEOUT> ) {
printf ("Is something wrong with Producer?");
<resume>;
}
}
With the removal of the while
loop, the break
statement inside the <CLOSED>
exception handler is unnecessary and is thus removed. On the other hand, the continue
statement inside the <TIMEOUT>
handler is replaced by a <resume>
statement to allow Manipulator
to resume its attempt to check into the reader zone again.
Again the astute reader will notice a processing gap between the
TIMEOUT
exception occurrence and the resume
invocation. When an exception occurs. the task is moved out of the waiting queue. By the time Manipulator
puts itself back on the highest priority queue via resumption, one or more buffered store
products may have been processed by the Consumer
tasks. This is the characteristic of a resumption within an exception handler - NERWous C programmers must be aware of it and code the logic of their programs accordingly.
Previous Next Top
No comments:
Post a Comment