Welcome
» NERWous C
» Mel
A Serial Program
Let's start with a Producer / Consumer example written serially in standard C language:
A Concurrent Program
Let's make the above program run in concurrency by using NERWous C. Its constructs are highlighted below:
The
The
When all the tasks in the program (
Mel Elements
A mel element (or just mel) is the "shared data". The NERWous C mel element is introduced by the mel construct
A mel is created by default with a 1-slot buffer. It can also be created with additional buffer to optimize throughput. When a mel is requested for reading or writing, the request can be prioritized to minimize latency.
Any task that requests access to the mel must first establish a connection to that element and subsequently maintain a session with it. This session is represented by the mel variable. A NERWous C program has multiple tasks accessing the same shared mel element. Each of these tasks maintain their own resident mel variable. Let's get to know more about mel variables next.
Mel Variables
A mel element is requested via the mel creation statement:
The other part of the mel variable is the information about the specific request that the task makes against the mel element. The task provides the IN properties of the mel variable to make a request. The task gets the result and status data back via the OUT properties. The mel element also piggy-backs with the OUT data some useful configuration information for the task to cache in the SET properties.
Each task maintains its own resident version of the mel variable. If there are three tasks that access the same mel element such as in the Producer/Consumer example above (i.e., the
Mel Creation
The
Since it is a run-time operation, the mel creation can fail. In this case, the mel variable <error> property can be used for defensive coding:
Another way to check for failure is to catch a mel operation exception:
Mel Passing
Like any C language variable, a mel variable can be passed to functions as arguments. One thing special about mel variables is that the way they are passed depends on the destination they are passed to. If they are passed to another task, they are passed by value. If they are passed to a function within the same task, they are passed by reference.
Unlike a normal C variable where passing by reference is an option requiring the use of a pointer, there is no such thing as a pointer to a mel variable. NERWous C does not allow a programmer to choose between passing a mel variable by value or by reference. It always enforces its passing rule: if a mel variable is passed to another task, it is always passed by value; and if passed within a task, it is always passed by reference.
This example shows the passing of mel variables as arguments:
Within the
Mel Scope
NERWous C mel variables follow the same scope rules of C variables. They are either global or local, as seen in the following example:
The mel variable
The
Mel Life
The life of a mel element and the life of a mel variable are different. Let's consider the following example:
When the mel element is closed, the CHAOS runtime recollects the allocated shared resource and closes the open connections. A mel element is closed implicitly when the NERWous C program exits, or explicitly when the <close> operation is invoked on the mel variable. Any task that has access to the mel element via its resident mel variable can invoke the <close> operation, like the
While there is only one remote mel element
When the
Mel Queues
The three basic operations on a mel element is creation, reading and writing:
In the second statement above, the task requests a READ access:
For reading:
For writing:
The default readers' queue and writers' queue are created by default with the mel creation operation. The other queues are created on demand. All queue creations are done by the CHAOS runtime. Not all CHAOS implementations support all the queue creations; however all of them are required to support at least the creation of the default queues, either physically or virtually.
Mel Operations
A mel operation is a request that a task sends to the CHAOS runtime for some action on a mel element. In NERWous C, an operation is specified in lower case within the < > markers, and placed before a mel variable. An operation can contain attributes to customize the request. The attributes are specified between the < and > markers.
These are the mel operations and their attributes, using the mel variable
Since a mel operation is a run time activity, it can fail. In NERWous C, such failure can be detected by the programmer checking the
Mel Properties
A property of a mel variable is a local value that a task caches from its most recent interaction with a mel element. In NERWous C, a property is specified in lower case within the < > markers, and placed after a mel variable.
The value of a mel property is specific to a task. Two tasks that access the same mel element will have two different mel variables to represent their own access to the mel. These mel variables may have different values for the mel properties depending on the circumstance the tasks access the mel element. For example, the
Accessing a mel variable by itself, such as
Let's take the mel variable
IN properties
are cached values of the attributes of the last mel operation that the task requests against the mel element:
OUT properties
are the results received back from the last mel operation:
SET properties
are cached values of the mel element's configuration information. Any change in the mel configuration will be propagated to a mel variable by piggy-backing to the last mel operation return:
If the remote mel configuration information has been changed but the task has not made any mel operation request, the SET properties in its mel variable keep the old values and do not reflect the new changes. They are deemed stale.
CORE properties
are system values assigned by the CHAOS runtime to allow a task to reach out to a mel element. These values cannot be changed once a mel element has been created.
Unlike the other properties that may be different between mel variables, the CORE properties are the same for all mel variables that represent the session between the tasks and the mel element.
A mel variable is basically a collection of properties. The properties that are visible to a NERWous C programmer are listed above. A NERWous C implementation can have additional custom properties that are particular to that implementation and hidden from the application-level programming.
Previous Next Top
- A Serial Program
- A Concurrent Program
- Mel Elements
- Mel Variables
- Mel Creation
- Mel Passing
- Mel Scope
- Mel Life
- Mel Queues
- Mel Operations
- Mel Properties
A Serial Program
Let's start with a Producer / Consumer example written serially in standard C language:
main () {
int c;
while (c = Produce())
Consume(c);
}
The main
task works serially. It calls Produce
to get a value to c
, then calls Consume
to consume it. The cycle repeats until there is no more products (with Produce
returning 0).
A Concurrent Program
Let's make the above program run in concurrency by using NERWous C. Its constructs are highlighted below:
main () {
<mel> int store;
<!> Producer (store);
<!> Consumer (store);
}
void Producer (<mel> int store) {
while ( <?>store = Produce() );
}
void Consumer (<mel> int store) {
while ( int c = <?>store )
Consume(c);
}
The main
task creates a mel
variable, store
, as the communication channel between the Producer and Consumer. Then it creates a task to run the Producer
code via the pel construct <!>
, and another task to run the Consumer
code. The main
task then ends itself.
The
Producer
task keeps producing until Produce
returns 0. Every time it has a product, it will see if the mel store
is available for it to deposit the product. By default, the mel store
has one slot. If a slower Consumer
task has not consumed the previous product in that slot, the Producer
will wait at the mel store
via the <?> wait operator. Eventually the previous product will be Consume
'd, the mel store
will be emptied, and the CHAOS runtime will allow the Producer
to access the mel store
. Once it has successfully deposited its new product into the mel store
, the Producer
resumes its Produce
chore. When it cannot Produce
any more, it will deposit a 0 value to the mel store
to inform the Consumer
task. The Producer
task then breaks out of the while
loop and ends itself.
The
Consumer
task runs in parallel with the Producer
task. The two synchronize via the mel store
. If store
is empty (due to a slower Producer
) or is locked by the Producer
while it deposits a new product, the Consumer
task will wait at the <?> wait operator. Once it can access the mel store
and has detected that the mel store
has a value, it will empty the mel store
and moves the value to the local variable c
. If c
is not 0, it will be Consume
d. Otherwise, the Consumer
task breaks out of the while
loop and ends itself.
When all the tasks in the program (
main
, Producer
and Consumer
) have ended, the program itself exits.
Mel Elements
A mel element (or just mel) is the "shared data". The NERWous C mel element is introduced by the mel construct
<mel>
. It is the NERWous C implementation of the Memory Element of the NERW Concurrency Model. The mel element resides somewhere on the model nerw network, hosted in a model computer element (cel). We can imagine the cel as maintaining a pool of shared data, and requesting tasks asking for a piece of this shared data.
A mel is created by default with a 1-slot buffer. It can also be created with additional buffer to optimize throughput. When a mel is requested for reading or writing, the request can be prioritized to minimize latency.
Any task that requests access to the mel must first establish a connection to that element and subsequently maintain a session with it. This session is represented by the mel variable. A NERWous C program has multiple tasks accessing the same shared mel element. Each of these tasks maintain their own resident mel variable. Let's get to know more about mel variables next.
Mel Variables
A mel element is requested via the mel creation statement:
<mel> int store;
Once a mel element has been created, it is represented by a mel variable. The mel variable by itself does not contain the actual shared data. The shared data remains at the remote mel element. What the mel variable contains is the information on how to access the mel element to get hold of the shared data. This information is maintained in the CORE properties within the mel variable.
The other part of the mel variable is the information about the specific request that the task makes against the mel element. The task provides the IN properties of the mel variable to make a request. The task gets the result and status data back via the OUT properties. The mel element also piggy-backs with the OUT data some useful configuration information for the task to cache in the SET properties.
Each task maintains its own resident version of the mel variable. If there are three tasks that access the same mel element such as in the Producer/Consumer example above (i.e., the
main
task, the Producer
task, and the Consumer
task), then there are three separate mel variables with the same name (i.e. store
). Each mel variable resides in the context of its specific task. They have the same values for the CORE properties, usually the same values for the SET properties, and most likely different values for the IN and OUT properties due to their different interactions with the mel element.
Mel Creation
The
main
task creates the mel
variable, store
, via the NERWous C statement:
<mel> int store;
While the above statement looks like a compile-time declaration, it is actually a run-time operation that invokes the underlying CHAOS runtime to secure the resources necessary for accessing the remote shared data. A mel creation statement can contain attributes to let the programmer specify how and where the mel element will be created.
Since it is a run-time operation, the mel creation can fail. In this case, the mel variable <error> property can be used for defensive coding:
main () {
<mel> int store;
if ( store<error> ) {
printf ("Creation of [store] failed on [%s]", store<why>);
exit 411;
}
<!> Producer (store);
<!> Consumer (store);
}
The reason for a mel creation failure can be found in the <why> property.
Another way to check for failure is to catch a mel operation exception:
main () {
try {
<mel> int store;
}
catch ( store<...> ) {
printf ("Creation of [store] failed on [%s]", store<why>);
exit 411;
}
<!> Producer (store);
<!> Consumer (store);
}
Mel Passing
Like any C language variable, a mel variable can be passed to functions as arguments. One thing special about mel variables is that the way they are passed depends on the destination they are passed to. If they are passed to another task, they are passed by value. If they are passed to a function within the same task, they are passed by reference.
Unlike a normal C variable where passing by reference is an option requiring the use of a pointer, there is no such thing as a pointer to a mel variable. NERWous C does not allow a programmer to choose between passing a mel variable by value or by reference. It always enforces its passing rule: if a mel variable is passed to another task, it is always passed by value; and if passed within a task, it is always passed by reference.
This example shows the passing of mel variables as arguments:
main () {
<mel> int store;
<!> Producer (store);
<!> Consumer (store);
}
void Producer (<mel> int store) {
if ( store<error> & NERW_ERROR_FAILED ) {
printf ("Mel [store] invalid due to [%s]", store<why>);
return;
}
while ( <?>store = Produce() );
}
void Consumer (<mel> int store) {
if ( store<error> & NERW_ERROR_FAILED ) {
printf ("Mel [store] invalid due to [%s]", store<why>);
return;
}
while ( 1 ) {
DoConsume (store);
printf ("Status in Consumer: %d", store<status>);
if ( store<value> == 0 ) break;
}
}
function DoConsume (<mel> int store) {
Consume (<?>store);
printf ("Status in DoConsume: %d", store<status>);
}
There are three different mel variables of the same name in the code above. The task main
creates the first mel variable store
to request the CHAOS runtime to set up the store
mel element for sharing, and to maintain its own session with that shared data. It then passes that mel variable by value to the tasks Producer
and Consumer
. These tasks duplicate the property values of the parent mel variable into their own resident mel variables. As the processing goes on, some property values of the mel variable in Producer
will diverge from those of the mel variable maintained by Consumer
and the main
task, while still sharing some common properties like the location of the mel element.
Within the
Consumer
task, the DoConsume
serial function runs in the context of the task Consumer
. It takes the mel variable store
as argument by reference. Thus DoConsume
is using the same mel variable as set up by Consumer
, and whatever changes DoConsume
makes on the mel variable store
will be reflected back to the Consumer
code. For example, DoConsume
reads the value of the mel element and caches the <value> and >status< properties in the mel variable <store>. These properties are then used by the parent function Consumer
appropriately.
Mel Scope
NERWous C mel variables follow the same scope rules of C variables. They are either global or local, as seen in the following example:
<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 localStore passed by value */
<?>localStore = Produce(); /* ERROR - localStore is not in scope */
}
void Consumer () {
Consume (<?>GlobalStore); /* ERROR - GlobalStore is not declared */
Consume (<?>store); /* ERRROR - store is not defined */
Consume (<?>localStore); /* ERROR - localStore is not in scope */
}
Any scope error in the above example is flagged during compile time. The NERWous C programmer has to fix all the scope related errors before the program can be compiled for execution.
The mel variable
GlobalStore
has a global scope. It is first created in the context of the main
task. The Producer
task re-creates it with the <extern>
statement as its own resident mel variable. The NERWous C program now has two separate global mel variables, both pointing to the same GlobalStore
mel element: one in the context of the main
task and the other, in the context of the Producer
task.
The
Consumer
task omits the <extern>
operation before making use of the GlobalStore
. This results in a compile-time error. We will learn more about the <extern>
statement later: it is both a global scope facilitator during compile time and a global variable duplicator during run time.
Mel Life
The life of a mel element and the life of a mel variable are different. Let's consider the following example:
main () {
<mel> int store;
<!> Producer (store);
<!> Consumer (store);
}
void Producer (<mel> int store) {
while ( <?>store = Produce() );
<close> store;
printf ("Status of mel store [%d]\n", store<status>); /* OK */
}
void Consumer (<mel> int store) {
try {
while ( 1 ) Consume(<?>store);
} catch ( store<CLOSED> ) {
store = null;
printf ("Status of mel store [%d]\n", store<status>); /* ERROR */
}
}
The life of a mel element is from the time it is created until it is closed. In the above example, the mel element store
comes into life at the mel creation statement as initiated by the main
task
<mel> int store;
This statement requests the CHAOS runtime to allocate shared data for the int
storage of the mel store
, and to open the connections on the cel that hosts that mel element to all tasks that request access (main
, Producer
, Consumer
).
When the mel element is closed, the CHAOS runtime recollects the allocated shared resource and closes the open connections. A mel element is closed implicitly when the NERWous C program exits, or explicitly when the <close> operation is invoked on the mel variable. Any task that has access to the mel element via its resident mel variable can invoke the <close> operation, like the
Producer
task above. Afterwards, any task that attempts to read or write to the closed mel element will encounter the CLOSED
exception, as happens to the Consumer
task above.
While there is only one remote mel element
store
, there are three mel variables store
, resident in each of the task. Like all C variables, the life of the mel variables is dictated by their scope (local or global). For the mel variable store
in the main
task, it is between the mel creation statement and the end of the task. For the Producer
task, its mel variable store
is active from the task starts until the task ends. After Produce
'ing a zero value, it writes that value to the remote mel element, gets off the while
loop and closes the mel element. Even though CHAOS is removing the resources and accesses to the mel element store
, the Producer
still has full control of its resident mel variable. It can access the mel variable's properties at will from its own local context, such as the <status> property for the printf
statement.
When the
Consumer
task detects that the remote mel store
has been closed, it jumps into the exception handler, and explicitly deactivates its mel variable store
by setting it to null
. This frees any resource in the context of the Consumer
task that has been allocated to the resident mel variable store
. When Consumer
tries to printf
the property <status>, this property does not exist any more, and the Consumer
task will end abnormally via a run-time error.
On a side noteIn summary, the scope of a mel variable dictates its access during compile time, while the life of a mel variable dictates its access during run time.
Is the 0 value which is the last valueProduce
'd byProducer
getConsume
'd byConsumer
? IfConsumer
gets hold to this value before the <close> statement is issued and processed, then yes, it does. IfConsumer
has been busy running theConsume
on the previous-to-last value, the <close> statement fromProducer
has the chance to be fully processed by the CHAOS runtime. WhenConsumer
goes back to the mel elementstore
for the next value, it will be served by the <CLOSED> exception instead of getting the 0 value. If the logic of the program requires the 0 value to beConsume
'd, then it should be the responsibility of theConsumer
task to <close> the melstore
.
Mel Queues
The three basic operations on a mel element is creation, reading and writing:
<mel>int store;
int c = <?>store;
<?>store = c;
In the first statement above, the mel element represented by the mel variable store
is created. As mentioned in the Mel Creation section, this declarative-looking statement is actually a run-time operation. The CHAOS runtime is instructed to collect all the necessary resources to allow tasks to access the shared data. The main resources are two queues: the reader's queue for read access, and the writer's queue for write access. NERWous C does not define where those queues are created, or even if these queues are physical. NERWous C programmers have to trust CHAOS to set up these shared queues properly, or to return a failure condition that allows them to check for creation errors.
In the second statement above, the task requests a READ access:
- The reader task comes to the mel element and stands in line at the default readers' queue.
- Whenever the task reaches the top of the default readers' queue, it waits for the queue's turn to read off the mel element. This is because there can be multiple queues with different reading priorities.
- The task then examines the mel element from the reading side. If the mel is buffered, reading is done on one side of the buffer and writing is done at the other side. By default, a mel element is unbuffered, or looking from a different angle, is buffered with one slot.
- If the mel element does not contain any value on the reading side of the mel buffer, it is said to be empty. The task keeps waiting at the top of the default readers' queue, until the mel element receives a value from a writer task.
- If the mel element contains a value on the reading side of the mel buffer, it is said to be filled. When this happens, the task removes the value from the mel element, and gets off the readers' queue with the retrieved value.
- The task then stores the retrieved value in the <value> property of the associated mel variable.
- The task resumes its program execution past the mel read statement.
- The writer task comes to the mel element and stands in line at the default writers' queue.
- Whenever the task reaches the top of the default writers' queue, it waits for the queue's turn to write to the mel element. This is because there can be multiple queues with different writing priorities.
- The task then looks at the mel element from the writing side of the mel buffer. If the mel is not buffered, there is only one slot to write to.
- If there is no available slot in the mel element to write another value, the mel element is said to be full. The task keeps waiting at the top of the default writers' queue, until a reader task reads out the mel element, making a slot available for the writer task.
- If there is an available slot to write another value, the mel element is said to be vacant. The task deposits its value into the available slot, and gets off the writers' queue.
- The task resumes its program execution past the mel write statement.
For reading:
Readers' Queues | Synopsis |
---|---|
Default queue | Tasks with default priority wait in this queue for the mel element to be filled. |
Priority queues | Tasks with higher priorities wait in these queues instead of the default readers' queue. |
Read-only queue | Tasks wait in this queue for read-only requests. |
For writing:
Writers' Queues | Synopsis |
---|---|
Default queue | Tasks with default priority wait in this queue for the mel element to be vacant. |
Priority queues | Tasks with higher priorities wait in these queues instead of the default writers' queue. |
Write-Over queue | Tasks wait in this queue for writeoever requests. |
The default readers' queue and writers' queue are created by default with the mel creation operation. The other queues are created on demand. All queue creations are done by the CHAOS runtime. Not all CHAOS implementations support all the queue creations; however all of them are required to support at least the creation of the default queues, either physically or virtually.
Mel Operations
A mel operation is a request that a task sends to the CHAOS runtime for some action on a mel element. In NERWous C, an operation is specified in lower case within the < > markers, and placed before a mel variable. An operation can contain attributes to customize the request. The attributes are specified between the < and > markers.
These are the mel operations and their attributes, using the mel variable
store
as a stand-in:
Operation | Attributes | Synopsis |
---|---|---|
<mel> int store |
Mel creation operation | |
buffer =n |
Create a buffered mel with n slots | |
at =cel |
Create the mel at the specific cel location | |
mode =setting |
Create the mel with this mode setting:
readwrite : this default mode allows the mel to be read and written to.
readonly : the mel access is always readonly.
|
|
<?>store |
Mel read operation | |
priority =n |
Access the mel for reading with priority n | |
timeout =msec |
Wait for the mel to be read for msec milliseconds before aborting | |
as =var |
Used to represent the mel variable selected in an exclusive reader OR zone | |
mode =setting |
Request read access with this mode setting:
reader : request a read access (default)
readonly : request read-only access
|
|
<?>store = |
Mel write operation | |
priority =n |
Access the mel for writing with priority n | |
timeout =msec |
Wait for the mel to be written for msec milliseconds before aborting | |
mode =setting |
Request write access with this mode setting:
writer : request write access (default)
writeover : request overwrite access
|
|
<close>store | Mel closure request | |
<resume> | Mel wait resumption | |
<quit>store | Mel unsubscription request | |
<rebuffer>store | Mel buffer resize request | |
<snapshot>store | Mel properties refresh |
Since a mel operation is a run time activity, it can fail. In NERWous C, such failure can be detected by the programmer checking the
<error>
property of the mel variable immediately after the mel operation, and investigating the <why> property for the long-text description. Another way is to request an exception to be raised on mel operation failures. The NERWous C language borrows the C++ try/catch
construct to support exceptions. A mel exception name is specified within the < > markers, and placed after a mel variable. Unlike mel properties which are lower cased, mel exceptions are upper cased.
Mel Properties
A property of a mel variable is a local value that a task caches from its most recent interaction with a mel element. In NERWous C, a property is specified in lower case within the < > markers, and placed after a mel variable.
The value of a mel property is specific to a task. Two tasks that access the same mel element will have two different mel variables to represent their own access to the mel. These mel variables may have different values for the mel properties depending on the circumstance the tasks access the mel element. For example, the
<value>
property, which represents the value of a mel upon a successful mel read operation, will likely be different for each reader task since each task reads a different value (unless the producer task produces the same value over and over).
Accessing a mel variable by itself, such as
c = <?>store
or <?>sotre = c
, is a mel operation, and thus requires the mel wait operator (<?>). On the other hand, a mel property is like any C language variable, it resides in the context of the task, and accessing one does not require mel waiting.
Let's take the mel variable
store
as the stand-in. These are its properties:
IN properties
are cached values of the attributes of the last mel operation that the task requests against the mel element:
Property | Synopsis |
---|---|
store<priority> | Priority value used by the last mel operation |
store<timeout> | Timeout value used by the last mel operation |
store<name> | Scoped name of the mel variable set during creation. |
store<agent> | Entity that executes a requested mel operation |
OUT properties
are the results received back from the last mel operation:
Property | Synopsis |
---|---|
store<value> | Value returned by the mel read operation. This value can be a simple entity (like an int ), an array or a structured entity |
store<count> | Number of items in an array value |
store<sequence> | Version identifier of the value returned from a mel read operation |
store<status> | Status of the mel element after a mel operation |
store<condition> | Status of the mel element before a mel operation |
store<error> | Error code of the last mel operation. A value of 0 means success and non-0 values mean failures. |
store<why> | Reason of a non-zero <error> value |
SET properties
are cached values of the mel element's configuration information. Any change in the mel configuration will be propagated to a mel variable by piggy-backing to the last mel operation return:
Property | Synopsis |
---|---|
store<buffer> | Number of buffer slots created for the mel element |
store<location> | Cel location of the mel element |
store<readers> | Number of reader tasks subscribing to the mel element |
store<writers> | Number of writer tasks subscribing the mel element |
If the remote mel configuration information has been changed but the task has not made any mel operation request, the SET properties in its mel variable keep the old values and do not reflect the new changes. They are deemed stale.
CORE properties
are system values assigned by the CHAOS runtime to allow a task to reach out to a mel element. These values cannot be changed once a mel element has been created.
Property | Synopsis |
---|---|
store<id> | Mel element numerical identification |
store<url> | Mel element string identifier |
Unlike the other properties that may be different between mel variables, the CORE properties are the same for all mel variables that represent the session between the tasks and the mel element.
A mel variable is basically a collection of properties. The properties that are visible to a NERWous C programmer are listed above. A NERWous C implementation can have additional custom properties that are particular to that implementation and hidden from the application-level programming.
Previous Next Top
No comments:
Post a Comment