MsgDeliverEvent(), MsgDeliverEvent_r()

Deliver an event through a channel

Synopsis:

#include <sys/neutrino.h>

int MsgDeliverEvent( int rcvid,
                     const struct sigevent* event );

int MsgDeliverEvent_r( 
                   int rcvid,
                   const struct sigevent* event );

Arguments:

rcvid
The value returned to the server when it receives a message from a client using MsgReceive*().
event
A pointer to a sigevent structure that contains the event you want to send. These events are defined in <sys/siginfo.h>. The type of event is placed in event.sigev_notify.

Library:

libc

Use the -l c option to qcc to link against this library. This library is usually included automatically.

Description:

The MsgDeliverEvent() and MsgDeliverEvent_r() kernel calls deliver an event from a server to a client through a channel connection. They're typically used to perform async IO and async event notification to clients that don't want to block on a server.

These functions are identical except in the way they indicate errors. See the Returns section for details.

Although the server can explicitly send any event it desires, it's more typical for the server to receive a struct sigevent in a message from the client that already contains this data. The message also contains information for the server indicating the conditions on when to notify the client with the event. The server then saves the rcvid from MsgReceive*() and the event from the message without needing to examine the event in any way. When the trigger conditions are met in the server, such as data becoming available, the server calls MsgDeliverEvent() with the saved rcvid and event.

You can use the SIGEV_SIGNAL set of notifications to create an asynchronous design in which the client is interrupted when the event occurs. The client can make this synchronous by using the SignalWaitinfo() kernel call to wait for the signal. Where possible, you should use an event-driven synchronous design that's based on SIGEV_PULSE. In this case, the client sends messages to servers, and requests event notification via a pulse.

You're not likely to use the event types SIGEV_UNBLOCK and SIGEV_INTR with this call.

You should use MsgDeliverEvent() when two processes need to communicate with each other without the possibility of deadlock. The blocking nature of MsgSend*() introduces a hierarchy of processes in which “sends” flow one way and “replies” the other way.

In the following diagram, processes at the A level can send to processes at the B or C level. Processes at the B level can send to the C level but they should never send to the A level. Likewise, processes at the C level can never send to those at the A or B level. To A, B and C are servers. To B, A is a client and C is a server.


Client-server connections


A hierarchy of processes.

These hierarchies are simple to establish and ensure a clean deadlock-free design. If these rules are broken then deadlock can occur as shown below:


Deadlocks


A deadlock when sending messages improperly among processes.

There are common situations which require communication to flow backwards through the hierarchy. For example, A sends to B requesting notification when data is available. B immediately replies to A. At some point in the future, B will have the data A requested and will inform A. B can't send a message to A because this might result in deadlock if A decided to send to B at the same time.

The solution is to have B use a nonblocking MsgDeliverEvent() to inform A. A receives this pulse and sends a message to B requesting the data. B then replies with the data. This is the basis for asynchronous IO. Clients send to servers and where necessary, servers use pulses to request clients to resend to them as needed. This is illustrated below:

Message Use
A sends to → B Async IO request
B replies to → A Request acknowledged
B sends pulse to → A Requested data available
A sends to → B Request for the data
B replies to → A Reply with data

Note: In client/server designs, you typically use MsgDeliverEvent() in the server, and MsgSendPulse() in the client.

Blocking states

None. In the network case, lower priority threads may run.

Native networking

When you use MsgDeliverEvent() to communicate across a network, the return code isn't “reliable”. In the local case, MsgDeliverEvent() always returns a correct success or failure value. But since MsgDeliverEvent() must be nonblocking, in the networked case, the return value isn't guaranteed to reflect the actual result on the client's node. This is because MsgDeliverEvent() would have to block waiting for the communications between the two lsm-qnet.so objects.

Generally, this isn't a problem, because MsgDeliverEvent() is for the benefit of the client anyway — if the client no longer exists, then the client obviously doesn't care that it didn't get the event. The server usually delivers the event and then goes about its business, regardless of the success or failure of the event delivery.

Returns:

The only difference between these functions is the way they indicate errors:

MsgDeliverEvent()
If an error occurs, -1 is returned and errno is set. Any other value returned indicates success.
MsgDeliverEvent_r()
EOK is returned on success. This function does NOT set errno. If an error occurs, any value in the Errors section may be returned.

Errors:

EAGAIN
The kernel has insufficient resources to enqueue the event.
EBADF
The thread indicated by rcvid had its connection detached.
EFAULT
A fault occurred when the kernel tried to access the buffers provided.
EINVAL
The given event isn't valid. For example, the type of notification might be invalid, or the signal for a SIGEV_SIGNAL event might be out of range.
ESRCH
The thread indicated by rcvid doesn't exist.
ESRVRFAULT
A fault occurred in the server's address space when it tried to write the pulse message to the server's receive message buffer (SIGEV_PULSE only).

Examples:

The following example demonstrates how a client can request a server to notify it with a pulse at a later time (in this case, after the server has slept for two seconds). The server side notifies the client using MsgDeliverEvent().

Here's the header file that's used by client.c and server.c:

struct my_msg
{
   short type;
   struct sigevent event;
};

#define MY_PULSE_CODE _PULSE_CODE_MINAVAIL+5
#define MSG_GIVE_PULSE _IO_MAX+4
#define MY_SERV "my_server_name"

Here's the client side that fills in a struct sigevent and then receives a pulse:

/* client.c */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/neutrino.h>
#include <sys/iomsg.h>

#include "my_hdr.h"

int main( int argc, char **argv)
{
  int chid, coid, srv_coid, rcvid;
  struct my_msg msg;
  struct _pulse pulse;

  /* we need a channel to receive the pulse notification on */
  chid = ChannelCreate( 0 ); 

  /* and we need a connection to that channel for the pulse to be
     delivered on */
  coid = ConnectAttach( 0, 0, chid, _NTO_SIDE_CHANNEL, 0 );

  /* fill in the event structure for a pulse */
  SIGEV_PULSE_INIT( &msg.event, coid, SIGEV_PULSE_PRIO_INHERIT, 
                    MY_PULSE_CODE, 0 );
  msg.type = MSG_GIVE_PULSE;

  /* find the server */
  if ( (srv_coid = name_open( MY_SERV, 0 )) == -1)
  {
     printf("failed to find server, errno %d\n", errno );
     exit(1);
  }

  /* give the pulse event we initialized above to the server for
     later delivery */
  MsgSend( srv_coid, &msg, sizeof(msg), NULL, 0 );

  /* wait for the pulse from the server */
  rcvid = MsgReceivePulse( chid, &pulse, sizeof( pulse ), NULL );
  printf("got pulse with code %d, waiting for %d\n", pulse.code, 
         MY_PULSE_CODE );

  return 0;
}

Here's the server side that delivers the pulse defined by the struct sigevent:

/* server.c */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/neutrino.h>
#include <sys/iomsg.h>
#include <sys/iofunc.h>
#include <sys/dispatch.h>

#include "my_hdr.h"

int main(int argc, char **argv) {
  int             rcvid;
  union {
      struct my_msg   mine;
      struct _pulse   pulse;
  }           msg;
  name_attach_t  *attach;

  /* Attach the name the client will use to find us. */
  /* Our channel will be in the attach structure */
  if ((attach = name_attach(NULL, MY_SERV, 0)) == NULL) {
     printf("server:failed to attach name, errno %d\n", errno);
     exit(1);
  }

  /* Wait for the message from the client. */
  for( ;; ) {
     rcvid = MsgReceive(attach->chid, &msg, sizeof(msg), NULL);
     switch(msg.mine.type) {
        case _PULSE_CODE_DISCONNECT:
           ConnectDetach(msg.pulse.scoid);
           break;
        case _IO_CONNECT:   
           MsgReply(rcvid, 0, NULL, 0);
           break;
        case MSG_GIVE_PULSE:    
           /* Wait until it is time to notify the client */
           sleep(2);

           /*
            * Deliver notification to client that client
            * requested
            */
           MsgDeliverEvent(rcvid, &msg.mine.event);
           printf("server:delivered event\n");
           return 0;
        default:    
           printf("server: unexpected message %d\n", msg.mine.type);
           return 0;
     }
  }

  return 0;
}

Classification:

QNX Neutrino

Safety:
Cancellation point No
Interrupt handler No
Signal handler Yes
Thread Yes

Caveats:

In the case of a pulse event, if the server faults on delivery, the pulse is either lost or an error is returned.

See also:

MsgReceive(), MsgReceivev(), MsgSend(), MsgSendPulse(), MsgSendv(), sigevent, SignalWaitinfo()

Message Passing chapter of Getting Started with QNX Neutrino