Welcome
» NERWous C
» Mel
Global Variable Scope
In the Mel Basic chapter, we have touched on the topic of mel scope. Let's revisit the example used:
We have seen local mel variables in use extensively in the previous chapter on Mel Operations. In this chapter, we focus on mel variables as global variables.
Note that the remote mel elements are always "global", in the sense they can be accessed at run-time anywhere in a NERWous C program. The scope rules apply only at compile time and only to the mel variables, and not the mel elements. When mel variables have local scope, they appear only inside a function or block of code. When they have global scope, they can appear anywhere in the program, but only with the help of the <extern> statement. Incorrect appearances will trigger errors during compilation.
The CORE properties of a mel variable allow a task to reach out to a mel element. For local mel variables, the CORE properties are passed by value from the parent task to the children tasks via function arguments. For a global mel variable, the CORE properties can also be passed from the parent task to the children tasks, but only if the parent task knows about this particular global mel variable. If it does not, the children tasks have to query the CHAOS runtime for the CORE properties to initialize their own global mel variables to reach out to the mel elements. In NERWous C, there is a single mel element that resides somewhere on the NERW network, but there is no single global mel variable. Each task maintains its own global mel variable in its own individual context.
Global Variable Usage
We can see the following uses for global mel variables: Semantic Sharing
In the example above, although both the
Code Sharing
Let's write a new Producer/Consumer example using only the global mel variable
When invoked from the
Name Discovery
All mel variables, declared with either global or local scope, are reachable by any task in the NERWous C program, but only if the tasks know about them. For local mel variables, this knowledge comes from their explicit passing from parent tasks to children tasks as functional arguments. The global mel variables are also passed in a similar way, but implicitly via the <extern> statement. However, global mel variables have a feature that local mel variables does not, which is name discovery.
The CHAOS runtime environment keeps track of mel elements with global scope: their names and their contact information (such as the CORE properties). This allows tasks that do not get the mel element information passed to them from the parent task to ask CHAOS for the contact information so they can reach out to the mel element by themselves. This is done via the
No Global Variables
Some programmers do not like the global-variable "cleaner" look. As a matter of style, they prefer not to deal with global variables at all, be it regular C variables or NERWous C mel variables. The above Producer/Consumer example can be rewritten by replacing global mel variables with local mel variables:
Global Variable Creation
When a local mel variable is created, it is under the context of the task that runs the mel variable declaration statement. What is the task context for a global mel variable? Let's modify our Producer/Consumer example again:
With the global mel variable
The hidden startup task is not meant to run a programmer's custom code. Therefore, if a programmer needs to check the result of a global mel creation, especially over an unstable runtime environment such as a wide area network (WAN) environment, this check has to be done in an explicit task. The
Multiple Global Variables
Multiple global mel variables can be declared for compile-time scope and run-time creation:
No Exception Handling
The result of a local mel variable creation can be checked against the <error> property or caught via a mel exception. However due to the global mel creation being outside of any code block, the exception method is not available for global mel variables.
In the code snippet below, we can use the exception handling for the local mel creation but not for the global one:
No Extern Statement
As said above, global mel variables are created under the context of the hidden startup task and then "passed naturally" to the
Global Variable Extern Statement
Except for the
Let's modify our Producer/Consumer example to illustrate the <extern> use:
Extern Statement Signature
The
The <extern> statement is both declarative and operative. During compilation time, the <extern> declaration collects the type of the mel variable (such as
In the latest example, there are five separate
All the versions of the global mel variable
All tasks share the same serial function
Extern Operation Translated
NERWous C seeks to process global mel variables as much the same way as local mel variables. In fact, a NERWous C compilation suite may have a translation pass to transform global mel variables into local mel variables. A hypothetical translation could be as follows:
An actual NERWous C compilation suite may or may not do the above translation. Nonetheless, the above translation describes the conceptual behavior of a global mel variable in NERWous C, as a roadmap for actual implementations.
Extern Statement Chain
The behavior of a global mel variable in NERWous C is like the behavior a local mel variable that is passed from task to task. This forms an extern statement chain. When the chain is broken, like the
This discovery process is done under-the-hood by the CHAOS runtime via the Name Discovery process. This process could be quite expensive. If this were to impact the performance of a NERWous C program, it could be better to change the signature of the
Extern Statement Short Passing
At run time, the <extern> statement re-creates the global mel variable in the context of the resident task. By default, this global mel variable will be created in full with the <value> and associated properties copied from the parent task (via explicit passing) or from the mel element itself by the CHAOS runtime (via the Name Discovery process). In many times, this copy is wasted since the resident task only needs some basic contact information to reach out to the remote mel element.
Like the short passing for local mel variables, a global mel variable can be initialized with short passing, by using the pass attribute to the
For the
The
Extern Statement Multiple Values
Multiple global mel variables of the same data type and passing attribute can be grouped with the same <extern> statement.
After forking the twowhile loop via the CLOSURE exception.
Getting back to the
Extern Statement Macro
To keep the Extern Statement Chain unbroken, they need to be repeated in child tasks. To alleviate typing repetitions, C language macros can be used to group same-data-type global mel variables.
Global Variable Closure
Any task that maintains its own resident global mel variable can invoke the <close> operation to close that global mel element. The first task that invokes the <close> operation removes all the resources that the CHAOS runtime has allocated to support shared access to the global-scoped mel element. Any subsequent access to the closed mel element by this task or any other task will result in a CLOSED mel operation exception.
We have seen the use of global mel closure in the
The
The
Previous Next Top
- Global Variable Scope
- Global Variable Usage
- Global Variable Creation
- Global Variable Extern Statement
- Global Variable Closure
Global Variable Scope
In the Mel Basic chapter, we have touched on the topic of mel scope. Let's revisit the example used:
<mel> int GlobalStore; /* global variable */
main () {
<mel> int localStore; /* local variable */
<!> Producer (localStore);
<!> Consumer ();
}
void Producer (<mel> int store) {
<extern> GlobalStore;
<?>GlobalStore = Produce(); /* OK - GlobalStore is global */
<?>store = Produce(); /* OK - store is local */
<?>localStore = Produce(); /* ERROR - localStore is not in scope */
}
void Consumer () {
Consume (<?>GlobalStore); /* ERROR - GlobalStore is not in scope */
Consume (<?>store); /* ERROR - store is not defined */
Consume (<?>localStore); /* ERROR - localStore is not in scope */
}
Mel variables, like GlobalStore
and localStore
above, appear in NERWous C programs based on their scope rules: either local or global. The enforcement of the scope rules is during compile time. The programmer has to resolve the ERRORs flagged in the above program before it can be compiled for execution.
We have seen local mel variables in use extensively in the previous chapter on Mel Operations. In this chapter, we focus on mel variables as global variables.
Note that the remote mel elements are always "global", in the sense they can be accessed at run-time anywhere in a NERWous C program. The scope rules apply only at compile time and only to the mel variables, and not the mel elements. When mel variables have local scope, they appear only inside a function or block of code. When they have global scope, they can appear anywhere in the program, but only with the help of the <extern> statement. Incorrect appearances will trigger errors during compilation.
The CORE properties of a mel variable allow a task to reach out to a mel element. For local mel variables, the CORE properties are passed by value from the parent task to the children tasks via function arguments. For a global mel variable, the CORE properties can also be passed from the parent task to the children tasks, but only if the parent task knows about this particular global mel variable. If it does not, the children tasks have to query the CHAOS runtime for the CORE properties to initialize their own global mel variables to reach out to the mel elements. In NERWous C, there is a single mel element that resides somewhere on the NERW network, but there is no single global mel variable. Each task maintains its own global mel variable in its own individual context.
Global Variable Usage
We can see the following uses for global mel variables: Semantic Sharing
In the example above, although both the
GlobalStore
and localStore
mel elements are accessible by any task, their meanings are different. The GlobalStore
mel element is meant to be a shared resource that is common to all tasks. On the other hand, the localStore
mel element is controlled regionally by the main
task. Only tasks that main
wants them to work on localStore
, will know about localStore
that main
explicitly passes as argument.
Code Sharing
Let's write a new Producer/Consumer example using only the global mel variable
GlobalStore
:
<mel> int GlobalStore; /* global variable */
main () {
<!> Producer ();
<!> Consumer ();
printReport ();
}
void Producer () {
<extern> GlobalStore;
while ( <?>GlobalStore = Produce() );
printReport ();
}
void Consumer () {
<extern> GlobalStore;
while ( <?>GlobalStore )
Consume ( GlobalStore<value> );
printReport ();
}
void printReport () {
printf ("[GlobalStore] status [%d], condition [%d]\n",
GlobalStore<status>, GlobalStore<condition>);
}
The function printReport
is a serial function. It is shared by all three tasks in the program (main
, Producer
and Consumer
) to print out the status and condition of the global mel variable GlobalStore
.
When invoked from the
main
task, printReport
makes use of the global mel variable GlobalStore
that is resident in the main
context. This global mel variable is different from the global mel variables GlobalStore
that are used by printReport
invoked from the Producer
and Consumer
tasks. As said previously, each task maintains its own global mel variables in its own context. By using mel variables with the global scope, they do not need to explicitly pass them around to subsequent shared functions, giving the code a "cleaner" look.
Name Discovery
All mel variables, declared with either global or local scope, are reachable by any task in the NERWous C program, but only if the tasks know about them. For local mel variables, this knowledge comes from their explicit passing from parent tasks to children tasks as functional arguments. The global mel variables are also passed in a similar way, but implicitly via the <extern> statement. However, global mel variables have a feature that local mel variables does not, which is name discovery.
The CHAOS runtime environment keeps track of mel elements with global scope: their names and their contact information (such as the CORE properties). This allows tasks that do not get the mel element information passed to them from the parent task to ask CHAOS for the contact information so they can reach out to the mel element by themselves. This is done via the
on
attribute of the <snapshot>
operation. We will see this use in the Extern Statement Translated section.
No Global Variables
Some programmers do not like the global-variable "cleaner" look. As a matter of style, they prefer not to deal with global variables at all, be it regular C variables or NERWous C mel variables. The above Producer/Consumer example can be rewritten by replacing global mel variables with local mel variables:
main () {
<mel> int localStore; /* local variable */
<!> Producer (localStore);
<!> Consumer (localStore);
printReport (localStore);
}
void Producer (<mel> int store) {
while ( <?>store = Produce() );
printReport (store);
}
void Consumer (<mel> int store) {
while ( <?>store )
Consume ( store<value> );
printReport (store);
}
void printReport (<mel> int store) {
printf ("[%s] status [%d], condition [%d]\n",
store<name>, store<status>, store<condition>);
}
Compared with the earlier global-variable version, the local-variable version looks a little more verbose with all the local mel variables being passed around. Interestingly, the local-variable version can be used to explain in detail the behavior of a global mel variable from creation to closure. As a matter of fact, a NERWous C compilation suite may support global mel variables by translating them into local mel variables that are passed around tasks behind the scene.
Global Variable Creation
When a local mel variable is created, it is under the context of the task that runs the mel variable declaration statement. What is the task context for a global mel variable? Let's modify our Producer/Consumer example again:
<mel> int GlobalStore; /* global variable */
main () {
if ( GlobalStore<error> ) {
printf ("[GlobalStore] creation failed on error [%d] due to [%s]\n",
GlobalStore<error>, GlobalStore<why>);
exit (911);
}
<!> Producer ();
<!> Consumer ();
}
void Producer () {
<extern> GlobalStore;
while ( <?>GlobalStore = Produce() );
}
void Consumer () {
<extern> GlobalStore;
while ( <?>GlobalStore )
Consume ( GlobalStore<value> );
}
Like a local mel variable, a global mel variable declaration: <mel> int GlobalStore;
is not only a compile-time statement but also a run-time operation to allocate shared resource to set up a mel element at a cel location. (In the above example, since the at attribute is not specified, the CHAOS runtime will allocate the mel element at a default cel that hosts generic shared resources.) This remote mel element is then represented for program use by the eponymous mel variable GlobalStore
.
With the global mel variable
GlobalStore
defined outside of any function, its resident context is of a hidden startup task. This is the one that the CHAOS runtime uses to invoke the main
task. It also passes to the main
task any global mel variables that it creates. The properties of these global mel variables have the same initial property values for newly created local mel variables.
The hidden startup task is not meant to run a programmer's custom code. Therefore, if a programmer needs to check the result of a global mel creation, especially over an unstable runtime environment such as a wide area network (WAN) environment, this check has to be done in an explicit task. The
main
task is logically the closest one to the global mel creations, so it is it that usually does the checking, as in the example above. However any task that makes use of a global mel variable, can also does its own checking before use.
Multiple Global Variables
Multiple global mel variables can be declared for compile-time scope and run-time creation:
<mel> int GlobalStore1, GlobalStore2[5];
main () {
if ( GlobalStore1<error> ) {
printf ("[GlobalStore1] creation failed on error [%d] due to [%s]\n",
GlobalStore1<error>, GlobalStore1<why>);
exit (911);
}
if ( GlobalStore2<error> ){
printf ("[GlobalStore2] creation failed on error [%d] due to [%s]\n",
GlobalStore2<error>, GlobalStore2<why>);
exit (911+1);
}
}
No Exception Handling
The result of a local mel variable creation can be checked against the <error> property or caught via a mel exception. However due to the global mel creation being outside of any code block, the exception method is not available for global mel variables.
In the code snippet below, we can use the exception handling for the local mel creation but not for the global one:
<mel> int GlobalStore;
main () {
if ( GlobalStore<error> ) {
printf ("[GlobalStore] global creation failed on error [%d] due to [%s]\n",
GlobalStore<error>, GlobalStore<why>);
exit (911);
}
try { <mel> int localStore;
} catch ( localStore<...> ) {
printf ("[localStore] local creation failed on error [%d] due to [%s]\n",
localStore<error>, localStore<why>);
exit (911+2);
}
}
For the global mel variable, we can only use the <error> property checking.
No Extern Statement
As said above, global mel variables are created under the context of the hidden startup task and then "passed naturally" to the
main
task. There is no need for an <extern>
statement in the main
task to receive all the declared global mel variables. This is not true when main
needs to "pass" the global mel variables to its children tasks, as we will see next.
Global Variable Extern Statement
Except for the
main
task which receives the global mel variables from the hidden startup task, other tasks that need to use a global mel variable, have to explicitly invoke an <extern> statement on that global mel variable. Unlike the C language extern
statement which is solely a compile-time statement to resolve scope, the NERWous C <extern> statement is also a run-time statement. It creates in the context of the task that runs that statement, a new mel variable that points to the same remote mel element as any other mel variable of the same name that appears in the context of any other task in that particular NERWous C program.
Let's modify our Producer/Consumer example to illustrate the <extern> use:
<mel> int GlobalStore;
main () {
<!> Producer ();
<!> DuoConsumers ();
printReport ();
}
void Producer () {
<extern> GlobalStore;
while ( <?>GlobalStore = Produce() );
printReport ();
}
void DuoConsumers () {
<!> Consumer();
<!> Consumer();
}
void Consumer () {
<extern> GlobalStore;
while ( <?>GlobalStore )
Consume ( GlobalStore<value> );
printReport ();
}
void printReport () {
printf ("[GlobalStore] status [%d], condition [%d]\n",
GlobalStore<status>, GlobalStore<condition>);
}
As said above, the <extern> statement in Producer
creates a new GlobalStore
mel variable in that task's context. How does that mel variable get initialized?
Extern Statement Signature
The
Producer
task indicates its intention to access the global mel variable GlobalStore
via the statement <extern> GlobalStore;
This statement updates the signature of the task Producer
. As we will see in the pel basics chapter, a signature of a task consists of:
- Return values
- Explicit arguments
- Extern global variables
Producer
task requires any parent task that invokes it, to pass the global mel variable that the parent task maintains in its own context. Extern global variables can be viewed as implicit arguments, to complement the explicit functional arguments. (Neither the Producer
nor Consumer
versions here have functional arguments, see this example for the versions that have store
as the functional argument.)
The <extern> statement is both declarative and operative. During compilation time, the <extern> declaration collects the type of the mel variable (such as
int
), and checks for the scope rules. During run time, the <extern> operation re-creates the global mel variable for the resident task. Like the local mel variables passed as functional arguments, the global mel variables created by the <extern> operation, will be initialized
by value from the parent task.
In the latest example, there are five separate
GlobalStore
global mel variables. The first one is created in the context of the hidden startup task. It is then passed to the main
task. This second version and the first version could be the same, depending on the NERWous C compilation suite. The third version is in the context of the Producer
task. The fourth and fifth versions are in the context of each of the two Consumer
tasks.
All the versions of the global mel variable
GlobalStore
have the same CORE properties since they access the same remote mel element. The other properties may diverge depending on what interactions the individual tasks have with the remote mel element.
All tasks share the same serial function
printReport
. It prints out the status and condition of the global mel variable in the context of the invoking task. It is invoked four times in the above example by different tasks, and the printf
outputs can all be different.
Extern Operation Translated
NERWous C seeks to process global mel variables as much the same way as local mel variables. In fact, a NERWous C compilation suite may have a translation pass to transform global mel variables into local mel variables. A hypothetical translation could be as follows:
<mel> int GlobalStore;
main (int argc, char **argv, char **envp, char **nerw) {
<mel> int __GlobalStore = nerw[0];
<!> __Producer (__GlobalStore);
<!> DuoConsumers ();
__printReport (__GlobalStore);
}
void __Producer (<mel> int __GlobalStore) {
if ( __GlobalStore == null ) <snapshot on="GlobalStore"> __GlobalStore;
while ( <?>__GlobalStore = Produce() );
__printReport (__GlobalStore);
}
void DuoConsumers () {
<!> __Consumer(null);
<!> __Consumer(null);
}
void __Consumer (<mel> int __GlobalStore) {
if ( __GlobalStore == null ) <snapshot on="GlobalStore"> __GlobalStore;
while ( <?>__GlobalStore )
Consume ( __GlobalStore<value> );
__printReport (__GlobalStore);
}
void __printReport (<mel> int __GlobalStore) {
printf ("[GlobalStore] status [%d], condition [%d]\n",
__GlobalStore<status>, __GlobalStore<condition>);
}
The hypothetical translation can help explain the working of the <extern> mel operation:
- The CHAOS runtime allocates shared resources for the mel element
GlobalStore
, and creates the eponymous mel variable with global scope.
- The CHAOS runtime passes this mel variable to the
main
task via the hiddennerw
argument. This argument is similar to the customaryargc
,argv
orenvp
that a C language runtime passes to themain
function.
- The mel variable
__GlobalStore
that is passed to themain
task contains properties with the creation initial values.
- With the appearance of the
<extern>
statement, the task functionProducer
is translated to be__Producer
with<mel> int __GlobalStore
as an explicit argument.
- When the
main
task invokes__Producer
, it passes by value its__GlobalStore
local variable to__Producer
via the functional argument conduit.
- The task
__Producer
re-creates the__GlobalStore
local mel variable in its own context, with properties set to passing initial values.
- When the task
__Producer
runs, it detects that there is a__GlobalStore
mel argument passed to it, disowning the check for thenull
value and thus avoiding the call to the<snapshot>
operation.
- The hypothetical NERWous C translator also detects the use of the global variable
GlobalStore
in the serial functionprintReport
. It translates it to__printReport
with the local mel variable__GlobalStore
passed as argument. The serial function__printReport
runs in the same task context as__Producer
, and thus receives__GlobalStore
by reference.
- The task function
DuoConsumer
does not have an<extern>
statement. The hypothetical NERWous C translator does not rename it, and themain
task invokes it without passing its local mel variable__GlobalStore
over.
- On the other hand, the task function
Consumer
does include the<extern>
statement, and has been translated it to be__Consumer
with<mel> int __GlobalStore
as its functional argument.
- Since
DuoConsumer
does not have a__GlobalStore
local mel variable in its context, it invokes its two__Consumer
tasks with thenull
value for the function argument.
- When each
__Consumer
task runs, it detects that there is no__GlobalStore
passed to it. The check for thenull
value becomes affirmative, requiring the<snapshot>
operation to be invoked with theon
attribute.
- The value of the
on
attribute isGlobalStore
, the official name of the globally scoped mel element. This instructs the CHAOS runtime to scan the NERWous C program's universe for a mel variable of the same name. If one is found, the local mel variable,__GlobalStore
, is initialized with the snapshot values of that mel element.
An actual NERWous C compilation suite may or may not do the above translation. Nonetheless, the above translation describes the conceptual behavior of a global mel variable in NERWous C, as a roadmap for actual implementations.
Extern Statement Chain
The behavior of a global mel variable in NERWous C is like the behavior a local mel variable that is passed from task to task. This forms an extern statement chain. When the chain is broken, like the
Duoconsumer
above that skips the <extern>
statement, the subsequent downstream task (here it is Consumer
) has to run a discovery process to find out how to connect to the remote mel element represented by the global mel variable.
This discovery process is done under-the-hood by the CHAOS runtime via the Name Discovery process. This process could be quite expensive. If this were to impact the performance of a NERWous C program, it could be better to change the signature of the
DuoConsumer
task function so that the extern statement chain is maintained:
void DuoConsumers () {
<extern> GlobalStore;
<!> Consumer();
<!> Consumer();
}
Extern Statement Short Passing
At run time, the <extern> statement re-creates the global mel variable in the context of the resident task. By default, this global mel variable will be created in full with the <value> and associated properties copied from the parent task (via explicit passing) or from the mel element itself by the CHAOS runtime (via the Name Discovery process). In many times, this copy is wasted since the resident task only needs some basic contact information to reach out to the remote mel element.
Like the short passing for local mel variables, a global mel variable can be initialized with short passing, by using the pass attribute to the
<extern>
statement:
<mel> int GlobalStore;
main () {
<!> Producer ();
<!> DuoConsumers ();
printReport ();
}
void Producer () {
<extern pass="short"> GlobalStore;
while ( <?>GlobalStore = Produce() );
printReport ();
}
void DuoConsumers () {
<!> Consumer();
<!> Consumer();
}
void Consumer () {
<extern short> GlobalStore;
while ( <?>GlobalStore )
Consume (GlobalStore<value>);
printReport ();
}
void printReport () {
printf ("[GlobalStore] status [%d], condition [%d]\n",
GlobalStore<status>, GlobalStore<condition>);
}
The term pass
is optional, as seen in the <extern>
statement for the task function Consumer
.
For the
Producer
task, the main
task is the parent task, and will pass the contents of its resident global mel variable GlobalStore
over without the <value> and associated properties. The Producer
task now has its own global mel variable GlobalStore
re-created with the Short Passed Mel Argument Initial Property Values.
The
Consumer
task has its resident global mel variable GlobalStore
re-created by the CHAOS runtime, also with the Short Passed Mel Argument Initial Property Values. This time, the initial values come fresh from the mel element itself, and not potentially stale from any parent task.
Extern Statement Multiple Values
Multiple global mel variables of the same data type and passing attribute can be grouped with the same <extern> statement.
<mel> int GlobalStore1, GlobalStore2, GlobalStore3;
main () {
<!> Producer ();
<!> DuoConsumers ();
printReport ();
}
void Producer () {
<extern> GlobalStore1, GlobalStore2;
<extern pass=short> GlobalStore3;
while ( <?>GlobalStore1 = Produce() &&
<?>GlobalStore2 = Produce() );
<?>GlobalStore3 = CloseEndProduce();
printReport ();
}
void DuoConsumers () {
<extern short> GlobalStore1, GlobalStore2, GlobalStore3;
<!> Consumer(GlobalStore1);
<!> Consumer(GlobalStore2);
CloseEndConsume (<?>GlobalStore3);
printReport ();
}
void CloseEndConsume (lastitem) {
Consume (lastitem);
<close> GlobalStore1;
<close> GlobalStore2;
}
void Consumer (<mel> int store) {
while ( <?>store )
Consume ( store<value> );
}
void printReport () {
printf ("[GlobalStore1] status [%d], condition [%d]\n",
GlobalStore1<status>, GlobalStore1<condition>);
printf ("[GlobalStore2] status [%d], condition [%d]\n",
GlobalStore2<status>, GlobalStore2<condition>);
printf ("[GlobalStore3] status [%d], condition [%d]\n",
GlobalStore3<status>, GlobalStore3<condition>);
}
On each iteration, the single Producer
task Produce
s two items and deposits them into the global mel elements GlobalStore1
and GlobalStore2
. There are two Consumer
tasks: one to Consume
from GlobalStore1
, and the other from GlobalStore2
. When Producer
ends its run, it generates a last product via CloseEndProduce
. This product is not consumed by a Consume
task, but by the DuoConsumers
task.
After forking the two
Consumer
children tasks, the DuoConsumers
task waits for the Producer
task's CloseEndProduce
run, via the GlobalStore3
global variable. It the invokes the CloseEndConsume
serial function, that Consume
s this last product, and invokes closure on the global variables that its Consumer
child tasks are waiting for. This allows the Consumer
tasks to break out of their infinite Getting back to the
Producer
task, even though it only needs short passing for all three global mel variables, for illustration purpose, the above example has it make use of the full value passing for GlobalStore1
and GlobalStore2
, and only short passing for GlobalStore3
.
Extern Statement Macro
To keep the Extern Statement Chain unbroken, they need to be repeated in child tasks. To alleviate typing repetitions, C language macros can be used to group same-data-type global mel variables.
#define MYGLOBALS GlobalStore11,GlobalStore2,GlobalStore3
<mel> int MYGLOBALS;
main () {
<!> Producer ();
<!> DuoConsumers ();
printReport ();
}
void Producer () {
<extern> MYGLOBALS;
while ( <?>GlobalStore1 = Produce() &&
<?>GlobalStore2 = Produce() );
<?>GlobalStore3 = CloseEndProduce();
printReport ();
}
void DuoConsumers () {
<extern short> MYGLOBALS;
<!> Consumer(GlobalStore1);
<!> Consumer(GlobalStore2);
CloseEndConsume (<?>GlobalStore3);
printReport ();
}
For illustration purpose, the Producer
task requests full value passing from the parent task, while the DuoConsumer
task only requests short passing. Both tasks make use of the macro MYGLOBALS
to list the needed global mel variables.
Global Variable Closure
Any task that maintains its own resident global mel variable can invoke the <close> operation to close that global mel element. The first task that invokes the <close> operation removes all the resources that the CHAOS runtime has allocated to support shared access to the global-scoped mel element. Any subsequent access to the closed mel element by this task or any other task will result in a CLOSED mel operation exception.
We have seen the use of global mel closure in the
CloseEndConsume
function of an earlier example. Let's discuss closure with this simpler example:
<mel> int GlobalStore;
main () {
<!> Producer ();
<!> Consumer ();
}
void Producer () {
<extern> GlobalStore;
int c;
while ( c = Produce() )
<?>GlobalStore = c;
<close>GlobalStore;
}
void Consumer () {
<extern> GlobalStore;
try {
while ( 1 )
Consume(<?>GlobalStore);
} catch ( GlobalStore<CLOSED> ) {
printf ("Consumer ends due to GlobalStore closed by Producer");
return;
}
}
The Producer
and Consumer
tasks communicate via the global mel element GlobalStore
. Each maintains its own resident global mel variable that connects to the same remote mel element. When the Producer
generates a 0-valued product, it stops Produce
'ing more items. To inform the Consumer
task that there are no more forthcoming products, it invokes the <close> operation on GlobalStore
.
The
Consumer
keeps Consume
'ing whatever coming up at the global mel element GlobalStore
, until it gets hit by a <CLOSED> exception. With its understanding of the Producer
behavior, the Consumer
task knows that there will be no more products for it to Consume
; it thus makes a printf
notification, and then ends itself via the return statement.
The
main
task forks the Producer
and Consumer
tasks, then ends. The Producer
task after closing GlobalStore
also ends. The Consumer
task after handling the <CLOSED> exception, also ends. With all the tasks having ended, the CHAOS runtime terminates the NERWous C program.
Previous Next Top