The Cogent IPC layer provides many advanced services that augment the basic send/receive/reply protocol. This section describes those services.
The Cogent IPC layer provides a messaging protocol that is easier to use and different in format from raw QNX 4 send/receive/reply.
Messages between Cogent IPC-enabled tasks are very similar to function calls. A message is constructed and sent, and the task on the other end evaluates the message. The return value of the evaluation of the message is transmitted to the originating task in the reply.
Consider two Gamma modules using the following code:
Task A:
#!/usr/cogent/bin/gamma
init_ipc("task_a");
while (t)
{
next_event();
}
The function init_ipc is called first to initialize
Cogent interprocess communication. For more details, see IPC Initialization below.
Task B:
#!/usr/cogent/bin/gamma
init_ipc("task_b");
function ask_taska_date ()
{
local result,tp;
if (tp = locate_task("task_a",nil))
result = send(tp,#date());
else
result = "could not locate task A";
}
every(1.0,#princ(ask_taska_date(),"\n"));
while (t)
{
next_event();
}
Of specific note in this example is the format of the message in the
send function. The first argument to the Cogent
IPC function send is a task. The
locate_task function, along with the
nserve module provides the name lookup. The
second argument is an expression for the receiver to evaluate. For
simple send's an unevaluated Gamma expression
(using #) will suffice. For more complex send's,
such as when a partially evaluated list of arguments need to be passed,
the format of the send command should be Lisp.
This code gives a good example of using the Cogent IPC layer as an RPC (Remote Procedure Call) mechanism.
To use the Cogent IPC layer for transferring data between tasks, use
the Lisp expression for assignment: setq. An
example is:
Task C:
#!/usr/cogent/bin/gamma
init_ipc("task_c");
add_set_function(#x,#princ("Task C reports x=",x,"\n"));
while (t)
{
next_event();
}
Task D:
#!/usr/cogent/bin/gamma
init_ipc("task_d");
function inc_x ()
{
local result,tp;
x++;
if (tp = locate_task("task_c",nil))
result = send(tp,list(#setq, #x, x));
}
x = 0;
every(0.1,#inc_x());
while (t)
{
next_event();
}
In this example task C sets up a set_function
before starting its event loop. The set function will print out the
value of x if it changes. Task D initializes x to 0 and then starts a
timer to run every tenth of a second to increment x and send
setq expressions to task C.
(setq x 1)
(setq x 2)
(setq x 3)
(setq x 4)
These expressions are in Lisp format because all messages between processes use the Lisp internal representation for efficiency.
The setq function is evaluated in task C. Any
side effects of the function, for example the setting of the variable x,
happens in task C. The return value of the function is the content of
the reply message. The return value of the send
function can be found by evaluating the 'result' variable in the
inc_x function.
Consider the inc_x function re-written as:
function inc_x ()
{
local result,tp;
x++;
if (tp = locate_task("task_c",nil))
{
result = send(tp,list(#setq, #x, x));
princ("task D result of send: ",result,"\n");
}
}
When this example is run the return value of the send is shown to be the
result of the setq function. Obviously, task D must
wait for task C to receive and evaluate the message before sending back
the response.