Welcome
» NERWous C
» Mel
Mel Arrays
So far in our previous examples, the mel variable is a single entity (for simplicity, we have chosen it to be an integer). Now let's explore how to handle a collection of mel entities of the same type using the mel array construct. In the new example below, we will have the
By using the collect-ENDED construct, the task
Mel Array Value Property
Let's now use the
Each
Since there is no exception handler, all
The
Mel Array Closures
Let's revisit the
Mel Array Status Property
There is an issue with the
Let's modify the
When an inline task
What would happen if the inline task did not check for the
Mel Array Exceptions
Let's now re-write the
Each inline task does an infinite
The
There are three ways for an inline task to end. It can
The third way is via a boundary exception which is the topic of the next section.
Mel Array Boundary Exception
There is a new exception associated with a mel array, the boundary exception, as seen in the VERSION 3 of
Although for the
Mel Array Count Property
The boundary exception depends on CHAOS knowing the items count in the mel array. Whenever a mel array argument is passed to a pel task, the items count is also passed along and cached locally as the
Mel Array Boundary Behaviors
The
A boundary behavior sets up an automatic default handling when
These are the attributes of the boundary behaviors:
This is the coding format for the boundary behaviors, usingstores[10] to
The
Mel Array Consecutive Constructs
When consecutive mel array items are used in an OR, AND or LIST reads or writes, instead of listing them individually, we can use the consecutive 2-dot construct (
The boundary behaviors can also be used with consecutive constructs. When specified, a behavior is applicable to all the mel array items in the consecutive construct. If different behaviors happen to be needed, the consecutive construct should not be used. Instead, the explicit list of the items will allow different behaviors to be assigned to different items.
The following code snippets use
Consecutive AND:
Consecutive LIST:
For a consecutive LIST, the unwieldy
Mel Array OR Wait Examples
The following examples show the use of mel arrays with OR waits:
Mel Array With OR Writes
Let's start with mel OR writes by modifying the
Mel Array With OR Reads
To illustrate the mel OR reads, let's modify the
Mel Array With Reader OR Zones
The previous
The
Mel Array With Writer OR Zones
Let's now modify
The
To handle boundary conditions, the wrap behavior starts to replace
When a 0 value is produced, the
If it happens that all the mel variables in the OR list have been previously closed, the mel zone wait statement will abort with the closure exception. Here, the exception handler just displays the message and implicitly ends the task.
The closure exception uses the AND consecutive construct for the mel array items (
Talking about implicit, there is an implict checkout of the writer zone at the closing
Each
When
Mel Array AND Wait Examples
Let's now modify
Mel Array with Reader AND Zones
Unlike previous versions, this new
The AND list above uses the flex behavior with stand-in value
Mel Array with Writer AND Zones
To illustrate the use of a writer AND zone, let's modify the previous version of
Inside the writer zone, the eponymous variables that stand for
The first task that
Mel Array LIST Wait Examples
As we have discussed OR and AND operations with mel arrays, we will now wrap up this chapter with a discussion on LIST operations. Again we will skip the LIST reads and LIST writes, and focus on LIST zones.
A LIST exclusive zone requests all the specified mel elements like the AND exclusive zone, but unlike the AND zone, it holds on to a mel element whenever it is available. This is efficient but can result in deadlocks or starvation if other tasks are also requesting the same elements.
Single mel elements required by a LIST zone are stringed together via the comma (either for reads or writes, while consecutive LIST elements in a mel array uses 3-point ellipsis construct (
Mel Array with Reader LIST Zones
Let's again modify the
Mel Array with Writer LIST Zones
We now replace the writer AND zone of the previous
Previous Next Top
- Mel Arrays
- Mel Array Value Property
- Mel Array Closures
- Mel Array Status Property
- Mel Array Exceptions
- Mel Array Boundary Exceptions
- Mel Array Count Property
- Mel Array Boundary Behaviors
- Mel Array Consecutive Constructs
- Mel Array OR Wait Examples
- Mel Array AND Wait Examples
- Mel Array LIST Wait Examples
Mel Arrays
So far in our previous examples, the mel variable is a single entity (for simplicity, we have chosen it to be an integer). Now let's explore how to handle a collection of mel entities of the same type using the mel array construct. In the new example below, we will have the
Producer
produces a series of products to be consumed:
#define NUMITEMS 30
#define NUMROUNDS 5
main () {
<mel> int stores[NUMITEMS];
<!>Producer (stores);
<!>Consumer (stores);
}
/* PRODUCER VERSION 1 */
void Producer (<mel> int stores[]) {
int round = 0;
while (1) {
for (int i=0; i<NUMITEMS; ++i)
<!collect> {
<?>stores[i] = produce();
} <? ENDED>;
printf ("Done with round [%d]\n", round);
if (++round >= NUMROUNDS) break;
}
<close>stores;
}
The main
program forks the task Producer
to run in parallel with the task Consumer
. Within Producer
, the tasks to produce
are also run in parallel via the for-pel loop. Unlike a serial for
loop, it is possible with the for-pel
loop to have a stores[i+1]
produce
'd before a stores[i]
since the produce
'ing tasks may be assigned to different cels with different performance and load.
The valueThe tasksi
is passed from theProducer
task to eachproduce
inline task via the automatic local variable import facility.
Producer
and Consumer
share the access to the mel array stores
. Each element within a mel array is an independent mel entity. They are produced and consumed separately from one another. For example, the for-pel
task for stores[i]
may be suspended to wait for a consumer to retrieve that value and free the mel array item for a new write, while the for-pel
task for stores[i+1]
, working with a faster consumer, has no problem depositing its product to its mel array item.
By using the collect-ENDED construct, the task
Producer
waits for all the inline tasks it pels to be finished. This marks the end of the first round. Producer
then starts the second round with a new set of for-pel
tasks. After NUMROUNDS
rounds, the Producer
task is done. Before ending itself, it calls the close
operation on the mel array stores
to inform any Consumer
tasks of no more forthcoming products.
Mel Array Value Property
Let's now use the
Consumer
task to explore the value property for a mel array.
/* CONSUMER VERSION 1 */
void Consumer (<mel> int stores[]) {
for (int i=0; i<NUMITEMS; ++i)
<!> {
while (1) {
consume (<?>stores[i]);
printf ("Just consumed item[%d] of value [%d]\n", i, stores[i]<value>);
}
}
}
Unlike the Producer
which creates a new set of NUMITEMS
tasks at each NUMROUND
iteration, the Consumer
uses a serial for
loop to create a fixed set of tasks, one for each store
element. Each task runs an infinite while
loop to keep consume
'ing its assigned store
item, no matter in what round in Producer
that that item has been produce
'd.
Each
while
-loop inline task invokes a read operation with <?>stores[i]
. Upon success, the read operation returns the read value as the result of the operation. This result is then consumed by the consume
function. It also saves the result in
the value property of the local cache representing the mel variable store[i]
. This value property is later used as the argument for the printf
statement.
Since there is no exception handler, all
while
loop inline tasks will automatically end via abortion when the mel array is closed by the Producer
. With all tasks ended, the program can exit.
The
while
loop can be replaced by the resume operation:
/* CONSUMER VERSION 2 */
void Consumer (<mel> int stores[]) {
for (int i=0; i<NUMITEMS; ++i)
<!> {
consume (<?>stores[i]);
printf ("Just consumed item[%d] of value [%d]\n", i, stores[i]<value>);
<resume>;
}
}
The resume
operation jumps back to the latest wait statement which is <?>stores[i]
, which is what the while(1)
loop was doing
Mel Array Closures
Let's revisit the
Producer
use of
<close>stores;
That statement is semantically equivalent to closing all the independent mel elements in the mel array in parallel:
for (int i=0; i<NUMITEMS; ++i) <!> { <close>stores[i];
}
It is also possible to close
a particular element separately:
/* PRODUCER VERSION 2 */
void Producer (<mel> int stores[]) {
int round = 0;
while (1) {
for (int i=0; i<NUMITEMS; ++i)
<!collect> {
int c = produce();
if ( c == 0 ) <close>stores[i];
else <?>stores[i] = c;
} <? ENDED>;
if (++round >= NUMROUNDS) break;
}
<close>stores;
}
Here, Producer
can prematurely close
an element in the mel array if the "product 0" condition is true for that element. After NUMROUNDS
rounds, the Producer
task closes the rest of the elements in the mel array. The <close>stores
statement skips any elements that have already closed.
Mel Array Status Property
There is an issue with the
Producer
code above. It runs the for-pel
loop NUMROUND
times. What if it closes an element of the mel array in an earlier round and then attempts to produce
for that item in the subsequent rounds? Since that element of the mel array has been closed, any subsequent write access to it will fail.
Let's modify the
Producer
to tackle this concern:
/* PRODUCER VERSION 3 */
void Producer (<mel> int stores[]) {
int round = 0;
for (int i=0; i<NUMITEMS; ++i)
<!collect> {
while ( ++round < NUMROUNDS ) {
if ( stores[i]<status> == NERW_STATUS_CLOSED ) <end>;
int c = produce();
if ( c == 0 ) <close>stores[i];
else <?>stores[i] = c;
if (++round >= NUMROUNDS) break;
}
} <? ENDED>;
<close>stores;
}
The VERSION 3 of Producer
flips the order of the for
and while
loops from the VERSION 2. In VERSION 2, we have a while
loop that runs NUMROUNDS
times, and each time, it pels NUMITEMS
inline tasks and waits for them to finish. The total number of tasks that are created is therefore NUMROUNDS
* NUMITEMS
. In the new VERSION 3, we have a for
loop that pels NUMITEMS
tasks. Each task then runs its own while
loop sequentially for NUMROUNDS
times. The number of inline tasks created in VERSION 3 is much smaller than in VERSION 2.
When an inline task
produce
s a 0
value, it close
s its mel array element <close>stores[i]
. We could end that inline task here. However for the sake of discussion, we allow it to loop back and does another round. The first thing it does is to check for the mel status
property since there is no point of produce
'ing if there is no stores
element to put it into. The status
propery value has been piggy-backed in the acknowledgement return package of the last operation to the mel entity, and saved in the local property cache. If the mel entity has been close
'd from the previous round, its status
will be NERW_STATUS_CLOSED
on this round.
What would happen if the inline task did not check for the
status
of the mel array element that it plans to use? It would run produce
. Then when it were to deposit that product to the mel array element with the mel write statement, <?>stores[i] = c
, it would get a stores[i]<CLOSED>
exception. Since it did not have a handler for this exception, it would also end, but now via a pel abortion.
Mel Array Exceptions
Let's now re-write the
Consumer
task to go with the above Producer
, highlighting the use of exceptions with mel arrays:
/* CONSUMER VERSION 3 */
void Consumer (<mel> int stores[]) {
int numitems = stores<count>;
for (int i=0; i<numitems; ++i) {
printf ("Task for [" + i + "] created");
<!> while (1) {
try { consume(<? timeout>stores[i]); }
catch ( stores[i]<TIMEDOUT> ) {
printf ("Keep trying");
<resume>;
}
catch ( stores[i]<CLOSED> ) {
printf ("stores[%d] has closed", i);
break;
}
catch ( stores<CLOSED> ) {
printf ("the full array stores has closed");
break;
}
catch ( stores[i]<BOUNDARY> ) {
printf ("Boundary error on [%d] - Abort! ", i);
break;
}
}
}
}
The above Consumer
uses a serial for
loop to print the "Task for ...
" message and then pel a number of inline tasks, one for each item of the stores
array. Once Consumer
has done all the task creations, it will end, leaving its inline tasks to end on their own terms via one of the exception handlers. Since Consumer
does not care when its inline tasks end, it does not have to wrap the for
loop in a collect-ENDED construct like the Producer
task.
Each inline task does an infinite
while
loop. For each iteration of the loop, the task waits for its own stores[i]
mel element. The wait is limited by a default timeout. When the mel is valued, it is retrieved and consume
'd. Then the wait resumes.
The
TIMEOUT
exception and the first CLOSED
exception handlers are applicable to the specific stores[i]
mel element. The second CLOSED
exception handler, stores<CLOSED>
, applies to the whole mel array.
There are three ways for an inline task to end. It can
break
out of the while
loop when its mel element is closed, or when the whole mel array is closed. After getting off the while
loop, with nothing else to do, the task ends.
The third way is via a boundary exception which is the topic of the next section.
Mel Array Boundary Exception
There is a new exception associated with a mel array, the boundary exception, as seen in the VERSION 3 of
Consumer
:
catch ( stores[i]<BOUNDARY> ) {
printf ("Boundary error on [%d] - Abort! ", i);
break;
}
This exception is raised if the mel element being accessed is beyond the mel array contents. For example, the mel array stores
with NUMITEMS
items contains stores[0]
to stores[NUMITEMS-1]
; accessing stores[-1]
or lower will trigger this out-of-bound exception at the lower end, and accessing stores[NUMITEMS]
or above will trigger this exception at the upper end.
Although for the
Consumer
version above, the for (int i=0; i<numitems; ++i)
does not go out-of-bounds, and thus Consumer
will not be hit by the Boundary
exception, it is still good to include this exception handler for defensive programming.
Mel Array Count Property
The boundary exception depends on CHAOS knowing the items count in the mel array. Whenever a mel array argument is passed to a pel task, the items count is also passed along and cached locally as the
count
property of the mel array. To properly iterate through the mel array, the task Consumer
makes use of the count
property, instead of using the constant NUMITEMS
as in the Producer
:
int numitems = stores<count>;
NERWous C library functions that pushes items in or pops items out of a mel array are required to update the count
value at the mel array itself. When such an action occurs, the count
properties locally cached in the tasks will become stale. These tasks need to refresh their property cache either by accessing the mel array again for reads or writes, or by using the snaphot operation.
Mel Array Boundary Behaviors
The
Consumer
VERSION 3 handles the mel array boundary in two ways. We make sure that stores[i]
does not go out of bounds by controlling the value of i
. We also handle any out-of-bound condition via the boundary exception handler. Boundary behaviors is the third way for handling boundary conditions.
A boundary behavior sets up an automatic default handling when
stores[i]
is out-of-bound so that a boundary exception can be either enforced or avoided. NERWous C supports the following behaviors:
- Strict behavior:
NERWous C strictly enforces the boundaries. If the item of the mel array is out of bound, the boundary exception is raised.
This is the default behavior if no behavior is specified, unless the out-of-bound condition happens in acatch
construct (see the flex behavior below).
- Flex behavior:
NERWous C tries to accommodate the out-of-bound conditions as much as possible to prevent the boundary exception. This is accomplished via two methods:- Ignoring out-of-bound items if there are other items to process. For example, out-of-bound items in an OR list wait can be ignored, and the result of the OR list wait depends on the in-bound items. This method is the default for both reading and writing.
- Stand-in values: this method is for reading only. When an item is out-of-bound, its value will be the stand-in value instead of causing an exception.
The flex behavior is the default behavior in acatch
construct as seen later in the Mel Array With Writer OR Zones example.
- Wrap and Wrapflex behaviors:
NERWous C wraps around the out-of-bound items. Out-of-bound items in the upper boundary will wrap around to the lower boundary, and vice versa. Boundary conditions can still occur if there are not enough items in the mel array to accommodate the wrap around. In this case, the wrap behavior falls back to the strict behavior, and the wrapflex behavior, to the flex behavior.
The wrapped-around item uses the same mel access as the out-of-bound item. For example, if the latter uses a mel readonly access, the former also uses readonly access.
These are the attributes of the boundary behaviors:
lbound | The specified behavior will be triggered on any array item that has its index equal to or lower than this lower boundary index. The default value is -1. |
---|---|
lval | The flex and wrapflex behaviors use this lower boundary value as the stand-in value for any array item at or below the lower boundary. If not specified, the default value is 0 for numerical items and null for non-numerical items. |
ubound | The specified behavior will be triggered on any array item that has its index equal to or greater than this upper boundary index. The default value is the array count property. |
uval | The flex and wrapflex behaviors use this upper boundary value as the stand-in value for any array item at or above the upper boundary. If not specified,the default value is 0 for numerical items and null for non-numerical items. |
This is the coding format for the boundary behaviors, using
stores[i]
:
mel stores[20];
/* strict behavior - explicit */
<?>stores[i]<strict lbound=5 ubound=10>;
/* flex behavior */
<?>stores[i]<flex lbound=5 ubound=10 lval="111" uval="999">;
/* wrap behavior */
<?>stores[i]<wrap lbound=5 ubound=10>;
/* wrapflex behavior */
<?>stores[i]<wrapflex lbound=5 ubound=10 lval="111" uval="999">;
Although the mel array stores
can contain 20
items, the above statements work on a subset of the array. With lbound
set to 5
, stores[0]
to stores[5]
will trigger the stated boundary behavior. Likewise, with ubound
set to 10
, stores[19]
will trigger the stated boundary behaviors. Either or both of the attributes can be omitted, and the boundary behaviors use the default value of -1
for the lower bound, and 20
for the upper bound, which is the full size of the mel array.
The
lval
and uval
attributes contain the stand-in values, and are needed only for the flex
and wrapflex
behaviors.
Mel Array Consecutive Constructs
When consecutive mel array items are used in an OR, AND or LIST reads or writes, instead of listing them individually, we can use the consecutive 2-dot construct (
..
), and specify only the first and last item.
The boundary behaviors can also be used with consecutive constructs. When specified, a behavior is applicable to all the mel array items in the consecutive construct. If different behaviors happen to be needed, the consecutive construct should not be used. Instead, the explicit list of the items will allow different behaviors to be assigned to different items.
The following code snippets use
stores[i]
Consecutive OR:
Explicit | <?>(stores[i] || stores[i+1] || stores[i+2]) |
Consecutive | <?>(stores[i] ..||.. stores[i+2]) |
with Strict | <?>(stores[i] ..||<strict lbound=5 ubound=10>.. stores[i+2]) |
with Flex | <?>(stores[i] ..||<flex lbound=5 ubound=10 lval="111" uval="999">.. stores[i+2]) |
with Wrap | <?>(stores[i] ..||<wrap lbound=5 ubound=10>.. stores[i+2]) |
with Wrapflex | <?>(stores[i] ..||<wrapflex lbound=5 ubound=10 lval="111" uval="999">.. stores[i+2]) |
---|
Consecutive AND:
Explicit | <?>(stores[i] && stores[i+1] && stores[i+2]) |
Consecutive | <?>(stores[i] ..&&.. stores[i+2]) |
with Strict | <?>(stores[i] ..&&<strict lbound=5 ubound=10>.. stores[i+2]) |
with Flex | <?>(stores[i] ..&&<flex lbound=5 ubound=10 lval="111" uval="999">.. stores[i+2]) |
with Wrap | <?>(stores[i] ..&&<wrap lbound=5 ubound=10>.. stores[i+2]) |
with Wrapflex | <?>(stores[i] ..&&<wrapflex lbound=5 ubound=10 lval="111" uval="999">.. stores[i+2]) |
---|
Consecutive LIST:
Explicit | <?>(stores[i], stores[i+1], stores[i+2]) |
Consecutive | <?>(stores[i] ... stores[i+2]) |
with Strict | <?>(stores[i] ..<strict lbound=5 ubound=10>.. stores[i+2]) |
with Flex | <?>(stores[i] ..<flex lbound=5 ubound=10 lval="111" uval="999">.. stores[i+2]) |
with Wrap | <?>(stores[i] ..<wrap lbound=5 ubound=10>.. stores[i+2]) |
with Wrapflex | <?>(stores[i] ..<wrapflex lbound=5 ubound=10 lval="111" uval="999">.. stores[i+2]) |
---|
For a consecutive LIST, the unwieldy
..,..
construct is shortened to an ellipsis, ...
. On the same vein, the comma is also omitted in consecutive LIST constructs with boundary behaviors.
Mel Array OR Wait Examples
The following examples show the use of mel arrays with OR waits:
- Mel Array with OR Writes
- Mel Array with OR Reads
- Mel Array with Reader OR Zones
- Mel Array with Writer OR Zones
Mel Array With OR Writes
Let's start with mel OR writes by modifying the
Producer
so that it can write to either stores[i]
or a common overflowstore
.
/* PRODUCER VERSION 4 */
void Producer (<mel> int stores[], <mel> int overflowstore) {
int round = 0;
for (int i=0; i<NUMITEMS; ++i)
<!collect> {
while ( ++round < NUMROUNDS ) {
int c = produce();
if ( c == 0 ) {
<close>stores[i];
break;
}
<?>(stores[i] || overflowstore) = c;
}
if (++round >= NUMROUNDS) break;
} <? ENDED>;
<close>stores;
}
This version of Producer
adds to the VERSION 3 the overflow
mel variable. If stores[i]
is full and cannot accept a new value, the new Producer
will try the overflow
variable. Unlike stores[i]
which is exclusive to a for
pel task, the overflow
mel variable is shared between all the for
pel tasks.
Mel Array With OR Reads
To illustrate the mel OR reads, let's modify the
Consumer
to read from either stores[i]
or if it is empty, from overflowstore
.
/* CONSUMER VERSION 4 */
void Consumer (<mel> int stores[], <mel> int overflowstore) {
int numitems = stores<count>;
for (int i=0; i<numitems; ++i) {
printf ("Task for [" + i + "] created");
<!> while (1) {
try {
consume(<? timeout>(stores[i] || overflowstore));
}
catch ( stores[i] || overflowstore)<TIMEDOUT> ) {
printf ("Keep trying");
<resume>;
}
catch ( stores[i]<CLOSED> ) {
printf ("Only me out!");
break;
}
catch ( stores<CLOSED> ) {
printf ("Everybody out!");
break;
}
catch ( stores<BOUNDARY> ) {
printf ("Boundary error - Abort! ");
break;
}
catch ( overflowstore<CLOSED> ) {
printf ("Keep trying with stores[i] only");
<resume>;
}
}
}
}
There is a flaw in the above Consumer
. A value in overflowstore
may be originally destined to any item in the mel array stores
, and not necessarily to stores[i]
. This is because the same overflowstore
is used by all the Producer
tasks to deposit their "overflown" product.
Mel Array With Reader OR Zones
The previous
Consumer
makes use of only one item in the stores
array. Let's modify Consumer
to use 3 consecutive items in the array, to illustrate the use of an reader OR zone in conjunction with a consecutive OR construct.
/* CONSUMER VERSION 5 */
void Consumer (<mel> int stores[], <mel> int overflowstore) {
int numitems = stores<count>;
for (int i=0; i<numitems; ++i)
<!> {
try <? as=consumable>(stores[i] ..||<flex>.. stores[i+2] || overflowstore) {
printf ("Item to be consumed [%s] of value [%d]\n", consumable<name>, consumable);
consume (consumable);
} <resume>; // use resume to loop back instead of while(1) loop
catch ( (stores[i] ..&&.. stores[i+2] && overflowstore)<CLOSED> ) { }
}
}
With the flex boundary behavior, if stores[i+1]
or stores[i+2]
are out-of-bound, they will be ignored in the processing of the OR list.
The
consumable
is the eponymous variable. It can be in the NERWous C mel format as in consumable<name>
or in the basic C data type format, consumable
. In fact, when used inside an exclusive zone, consumable
is a shortcut for consumable<value>
.
Mel Array With Writer OR Zones
Let's now modify
Producer
task to use a writer OR zone with consecutive mel array items.
/* PRODUCER VERSION 5 */
void Producer (<mel> int stores[], <mel> int overflowstore ) {
int round = 0;
while (1) {
for (int i=0; i<NUMITEMS; ++i)
<!collect> {
try <?writer mode=random as=product>
(stores[i] ..||<wrap>.. stores[i+2] || overflowstore) {
product = produce();
if ( product == 0 ) {
<close>product;
printf ("Item [%s] is closed\n", product<name>);
}
printf ("Item [%s] contains value [%d]\n", product<name>, product);
}
catch ( (stores[i] ..&&.. stores[i+2] && overflowstore)<CLOSED> ) {
printf ("All items in OR list have closed\n");
}
} <? ENDED>;
if (++round >= NUMROUNDS) break;
}
<close>stores;
<close>overflowstore;
}
The previous Producer
VERSION 4 has a while
loop inside a for
loop, and pels exactly NUMITEMS
tasks. For the sake of difference, this VERSION 5 reverses the order, and has a for
loop inside a while
loop.
It pels NUMITEMS
tasks every NUMROUNDS
iterations, for a total of NUMITEMS * NUMROUNDS
tasks for the life of Producer
.
The
writer
attribute in the mel wait statement indicates that this wait is for a writer zone. When a task is inside the writer zone, it will prevent other tasks to access the writer's end of the mel variable to deposit a new value.
Is using a writer zone for this version ofThe OR list on entrance of the writer zone is also used on the checkout of the zone. The eponymous variableProducer
an overkill, since a simple OR write statement can suffice?<?> (stores[i] ..||<wrap>.. stores[i+2] || overflowstore) = produce();
The main difference is that with the writer zone, the mel access for writing is held up during the time the product is beingproduce
'd, while with the simple write statement, the hold-up is only to deposit a value that has been alreadyproduce
'd.
product
has the value to be written into the mel variable. But which mel variable in the OR list? Since the random mode is used, the CHAOS runtime will randomly select either stores[i]
, stores[i+1]
, stores[i+2]
, or overflowstore
. If it the first randomly selected mel variable has an empty slot, it will be put on hold while the task is in the writer zone, and on checkout, it will receive the value of the eponymous variable. If that mel variable is still full, CHAOS will randomly look at another mel variable in the list. If none of the variables are available, CHAOS will force the task to wait at the mel wait statement <?> until one mel variable is available for writing.
To handle boundary conditions, the wrap behavior starts to replace
stores[i+1]
and stores[i+2]
with stores[0]
and stores[1]
when i
reaches the end of the stores
mel array (which is NUMITEMS-1
).
When a 0 value is produced, the
Producer
decides to close the mel variable that has been selected, and currently represented by the eponymous variable product
. Once that mel variable is closed, on the next round of the while
loop, CHAOS will skip looking at this mel variable during the processing of the OR list.
If it happens that all the mel variables in the OR list have been previously closed, the mel zone wait statement will abort with the closure exception. Here, the exception handler just displays the message and implicitly ends the task.
The closure exception uses the AND consecutive construct for the mel array items (
stores[i] ..&&.. stores[i+2]
). When used in the catch
construct, the flex behavior is implicit. This prevents a boundary exception when we are already handling a boundary condition.
Talking about implicit, there is an implict checkout of the writer zone at the closing
}
. This brings up the default behavior which is to update the writer's end of the selected mel variable with the value of the eponymous variable product
.
Each
while
loop iteration pels NUMITEMS
tasks and uses the collect-ENDED
construct to wait for all the pelled tasks to finish, before the next round is started. It is also possible to omit the collect-ENDED
construct. In that case, from NUMITEMS
to NUMITEMS * NUMROUNDS
tasks can exist at the same time and run in parallel. They will vie for the same stores
and overflowstore
shared resources but the writer zone wait will ensure that only one task can check out a resource, and the checkout follows the chronological order of the pel creations.
When
NUMROUNDS
rounds have completed, the Producer
tasks closes the mel array stores
and the mel variable overflow
. The corresponding Consumer
task will cue on these closures to end itself.
Mel Array AND Wait Examples
Let's now modify
Consumer
and Producer
to illustrate the use of the AND operation with mel arrays. We will skip the AND reads and AND writes discussion, and delve only with AND zones:
Mel Array with Reader AND Zones
Unlike previous versions, this new
Consumer
consume
s, not one but multiple items from the mel array store
on every run. These mel elements are waited on using the reader AND zone:
/* CONSUMER VERSION 6 */
void Consumer (<mel> int stores[], <mel> int overflowstore) {
int numitems = stores<count>;
for (int i=0; i<numitems; ++i)
<!> {
try <?>(stores[i] ..&&<flex uval=0>.. stores[i+2] && overflowstore) {
consume (stores[i]);
consume (stores[i+1]);
consume (stores[i+2]);
consume (overflowstore);
} <resume>; // loop back to zone wait
catch ( (stores[i] ..||.. stores[i+2] || overflowstore)<CLOSED> ) {
break;
}
}
}
There may be long waits at the reader AND zone entrance because each task iteration competes not only for its stores
element (stores[i]
) but also of the next two elements (stores[i+1]
and stores[i+2]
). And everybody competes for the same overflowstore
mel variable. Here overflowstore
acts as the serializer that allows only one task to be in the reader AND zone at a time. The mel AND wait will ensure that there is no deadlocks nor resource starvation, but the mechanism of an AND wait can impose performance penalty.
The AND list above uses the flex behavior with stand-in value
0
for any upper out-of-bound mel array items. For example, the last task that the for
loop pels has i
equals to NUMITEMS-1
. Thus, stores[i]
is in-bound, but stores[i+1]
and stores[i+2]
are out-of-bound. Instead of causing a boundary exception, they are assigned the stand-in value of 0
, which is then consume
'd inside the reader zone.
Mel Array with Writer AND Zones
To illustrate the use of a writer AND zone, let's modify the previous version of
Producer
:
/* PRODUCER VERSION 6 */
void Producer (<mel> int stores[], <mel> int overflowstore ) {
int round = 0;
while (1) {
for (int i=0; i<NUMITEMS; ++i)
<!> {
try <?writer>( stores[i] ..&&<flex>.. stores[i+2] && overflowstore) {
if ( ! (stores[i] = produce()) )
<close>stores[i];
if ( i < NUMITEMS-1 ) {
if (! (stores[i+1] = produce()) )
<close>stores[i+1];
}
if ( i < NUMITEMS-2 ) {
if ( ! (stores[i+2] = produce()) )
<close>stores[i+2];
}
if ( ! (overflowstore = produce()) )
<close>overflowstore;
}
catch ( (stores[i] ..||.. stores[i+2] || overflowstore)<CLOSED> ) {
printf ("A required item has been closed by another task -- Abort");
<end>;
}
}
if (++round >= NUMROUNDS) break;
}
<close>stores;
<close>overflowstore;
}
This Producer
also uses the flex behavior for its AND list. Since this is for a writer zone, there is no stand-in upper-bound value. Instead, any mel array item that is out-of-bound will be ignored. On entrance of the writer zone, the AND wait will skip checking stores[i+1]
and stores[i+2]
when i == NUMITEMS-1
. On the same vein, on checkout of the writer zone, stores[i+1]
and stores[i+2]
will not be updated by the checkout operation if they are out-of-bound.
Inside the writer zone, the eponymous variables that stand for
stores[i+1]
and stores[i+2]
can still be assigned values. But as said above, during the checkout, these values will not be used if the mel array items are out-of-bound. To prevent un-necessary produce
activities which may deplete production resources, there are checks to validate i
before produce
is invoked.
The first task that
produce
s a 0 value for overflowstore
will cause subsequent tasks to hit the CLOSED
exception. This is due to all the tasks include overflowstore
in their AND mel waiting list.
Mel Array LIST Wait Examples
As we have discussed OR and AND operations with mel arrays, we will now wrap up this chapter with a discussion on LIST operations. Again we will skip the LIST reads and LIST writes, and focus on LIST zones.
A LIST exclusive zone requests all the specified mel elements like the AND exclusive zone, but unlike the AND zone, it holds on to a mel element whenever it is available. This is efficient but can result in deadlocks or starvation if other tasks are also requesting the same elements.
Single mel elements required by a LIST zone are stringed together via the comma (either for reads or writes, while consecutive LIST elements in a mel array uses 3-point ellipsis construct (
...
).
Mel Array with Reader LIST Zones
Let's again modify the
Consumer
task, this time to use the mel array stores
with a reader LIST zone:
/* CONSUMER VERSION 7 */
void Consumer (<mel> int stores[], <mel> int overflowstore) {
int numitems = stores<count>;
for (int i=0; i<numitems; ++i)
<!> {
try <?>(stores[i] ..<flex uval=0>.. stores[i+2], overflowstore) {
consume (stores[i]);
consume (stores[i+1]);
consume (stores[i+2]);
consume (overflowstore);
} <resume>; // loop back to zone wait
catch ( (stores[i] ..||.. stores[i+2] || overflowstore)<CLOSED> ) {
break;
}
}
}
The difference between this LIST version and the "AND" version is the replacement of the consecutivve AND operator (..&&..
with the consecutive LIST operator (...
). The "LIST" version also waits for all the specified elements -- stores[i]
, stores[i+1]
, stores[i+2]
, and overflowstore
. However, while an AND wait requires all the elements to be available at the same time to prevent deadlock, the LIST wait allows the task to hold on a mel variable as it is made available. The task enters the reader LIST zone when it has accumulated holds on all the specified mel variables.
Mel Array with Writer LIST Zones
We now replace the writer AND zone of the previous
Producer
with a writer LIST zone to illustrate the use of such zone with mel arrays:
/* PRODUCER VERSION 7 */
void Producer (<mel> int stores[], <mel> int overflowstore ) {
int round = 0;
while (1) {
for (int i=0; i<NUMITEMS; ++i)
<!> {
try <?writer>( stores[i] ..<flex>.. stores[i+2], overflowstore) {
if ( ! (stores[i] = produce()) )
<close>stores[i];
if ( i < NUMITEMS-1 ) {
if (! (stores[i+1] = produce()) )
<close>stores[i+1];
}
if ( i < NUMITEMS-2 ) {
if ( ! (stores[i+2] = produce()) )
<close>stores[i+2];
}
if ( ! (overflowstore = produce()) )
<close>overflowstore;
}
catch ( (stores[i] ..||.. stores[i+2] || overflowstore)<CLOSED> ) {
printf ("A required item has been closed by another task -- Abort");
<end>;
}
}
if (++round >= NUMROUNDS) break;
}
<close>stores;
<close>overflowstore;
}
Near the boundary condition, when i == NUMITEMS-1
, the flex behavior instructs the LIST wait to ignore the out-of-the-bound mel array item, stores[i+2]
, and wait only on the inbound mel array items, stores[i]
and stores[i+1]
, plus the mel variable overflowstore
. Unlike an AND wait which waits for all these mel variables to be all available at the same time, the LIST zone wait allows the Producer
to accumulate the holds of these mel variables as they become available. This wait is more efficient but may introduce deadlocks if another task is doing the same wait.
Previous Next Top
No comments:
Post a Comment