13.3.2. Cogent IPC Advanced services

The Cogent IPC layer provides many advanced services that augment the basic send/receive/reply protocol. This section describes those services.

13.3.2.1. Cogent IPC Messages

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.