ChorusOS 5.0 Application Developer's Guide

ChorusOS Message Queues (MIPC)

The MIPC API is an optional feature that facilitates message queues in ChorusOS systems. This feature enables an application, composed of one or more actors, to create a shared communication environment -- often referred to as a message space. Within this message space, actors can exchange messages efficiently. Supervisor and user actors of the same application can use MIPC to exchange messages. Furthermore, messages may be initially allocated and sent by interrupt handlers to be processed later by threads.

The MIPC API includes the following system calls:

msgAllocate()

Allocate a message

msgFree()

Free a message

msgGet()

Get a message

msgGetAny()

Get a message from any declared queues

msgPoolStat()

Retrieve statistics about a message pool

msgPut()

Post a message

msgQueueStat()

Retrieve statistics about a message queue

msgRemove()

Remove a message from a queue

msgSpaceCreate()

Create a message space

msgSpaceOpen()

Open a message space.

Message queues are designed around the concept of a message space which encapsulates within a single entity:

A message space is a temporary resource that must be created explicitly by one actor within the application. Once created, a message space can be opened by other actors within the application. Actors that have opened the same message space are said to share the message space. A message space is deleted automatically when the actor is deleted.

A message pool is defined by two parameters (message size and number of messages) provided by the application when it creates the message space. The configuration of the set of message pools defined within a message space depends on the application requirements.

A message is an array of bytes which can be structured and used at application level through any appropriate convention. Messages are presented to actors as pointers within their address space.

Messages are posted to message queues belonging to the same message space. All actors sharing a message space can allocate messages from the message pools. In the same way, all actors sharing a message space have send and receive rights on each queue of the message space.

Even though most applications need to create only a single message space, the message queue feature is designed to enable an application to create or open multiple message spaces. However, messages allocated from one message space cannot be sent to a queue of a different message space. The following example illustrates the typical use of message spaces:

  1. The first actor, aware of the overall requirements of the application, creates the message space.

  2. Other actors of the application open the shared message space.

  3. An actor allocates a message from a message pool, and fills it with the data to be sent.

  4. The actor which allocated the message then posts it to the appropriate queue and assigns a priority to the message.

  5. The destination actor obtains the message from the queue. At this point, the message is removed from the queue.

  6. After the destination actor has processed the message, the message can be freed. It is then available to be reallocated by the application. Alternatively, the destination actor may modify the message and post it to another queue.

To make the service as efficient as possible, physical memory is allocated for all messages and data structures of the message space at message space creation. At message space open time, this memory is transparently mapped by the system into the actor address space. Further operations such as posting and receiving a message are performed without any copy.

You can create a message space as follows:

#include <mipc/chMipc.h>
int msgSpaceCreate (KnMsgSpaceId     spaceGid,
                    unsigned int     msgQueueNb,
                    unsigned int     msgPoolNb,
                    const KnMsgPool* msgPools); 

The spaceGid parameter is an unique global identifier assigned by the application to the message space being created. This identifier is also used by other actors of the application to open the message space. Therefore, the identifier serves to bind actors participating in the application to the same message space. The K_PRIVATEID predefined global identifier indicates that the message space created will be private to the invoking actor. This means that its queues and message pools will only be accessible to threads executing within this actor. No other actor will be able to open the message space. The message space is described by the following three parameters:

Figure 11-1 shows an example of a message space recently created by an actor.

Figure 11-1 Creating a Message Space

Graphic

The created message space is assigned a local identifier which is returned to the invoking actor as the return value of the msgSpaceCreate(2K). The scope of this local identifier is the invoking actor.

A message space may be opened by other actors through the following call:

#include <mipc/chMipc.h>
int msgSpaceOpen(KnMsgSpaceId spaceGid); 

The message space assigned with the spaceGid unique global identifier must have been created previously by a call to msgSpaceCreate(2K). A local identifier is returned to the invoking actor. This message space local identifier can then be used to manipulate messages and queues within the message space. Figure 11-2 shows an example of a message space recently opened by a second actor.

Figure 11-2 Opening a Message Space

Graphic

A message may be allocated by the following call:

#include <mipc/chMipc.h>
int msgAllocate(int             spaceLid,
                unsigned int    poolIndex,
                unsigned int    msgSize,
                KnTimeVal*      waitLimit,
                char**          msgAddr);

msgAllocate(2K) attempts to allocate a message from the appropriate pool of the message space identified by the spaceLid return value of a previous call to msgSpaceOpen(2K), or to msgSpaceCreate(2K). If poolIndex is not set to K_ANY_MSGPOOL, the allocated message is the first free (unallocated) message of the pool defined by poolIndex, regardless of the value specified by the msgSize parameter. Otherwise, if poolIndex is set to K_ANY_MSGPOOL, the message is allocated from the first pool for which the message size fits the requested msgSize. In this context, first pool means the pool with the lowest index in the set of pools defined at message space creation time. If the pool is empty, no attempt is made to allocate a message from another pool.

If the message pool is empty (all messages have been allocated and none are free), msgAllocate(2K) will block, waiting for a message in the pool to be freed. The invoking thread is blocked until the wait condition defined by waitLimit expires.

If successful, the address of the allocated message is stored at the location defined by msgAddr. The returned address is the address of the message within the address space of the actor. Remember that a message space is mapped to the address space of the actors sharing it. However, the message spaces (and therefore the messages themselves), may be mapped to different addresses in different actors. This is especially true for message spaces shared between supervisor and user actors.

Figure 11-3 illustrates two actors allocating two messages from two different pools of the same message space.

Figure 11-3 Allocating Messages From Pools

Graphic

After a message has been allocated and initialized by the application, it may be posted to a message queue with:

#include <mipc/chMipc.h> 
int msgPut(int             spaceLid,
           unsigned int    queueIndex,
           char*           msg,
           unsigned int    prio);

msgPut(2K) posts the message (the address of which is msg) to the message queue queueIndex within the message space (the local identifier of which is spaceLid). The message has already been allocated previously by a call to msgAllocate(2K). The message is inserted into the queue according to its priority, prio. Messages with a high priority are taken from the queue first.

A message is posted to a queue without any message copy. Additionally, the message may be posted within an interrupt handler, or with preemption disabled.

Figure 11-4 illustrates how the actors from the previous figure post their messages to different queues.

Figure 11-4 Posting Messages To Queues

Graphic

To obtain a message from a queue (if any) use the following call:

#include <mipc/chMipc.h>
int msgGet(int             spaceLid,
           unsigned int    queueIndex,
           KnTimeVal*      waitLimit,
           char**          msgAddr,
           KnUniqueId*     srcActor);

msgGet(2K) enables the invoking thread to get the first message with the highest priority pending in the message queue queueIndex, within the message space whose local identifier is spaceLid. Messages with equal priority are posted and delivered in a first-in first-out order.

The address of the message delivered to the invoking thread is returned at the location defined by the msgAddr parameter. If no message is pending, the invoking thread is blocked until a message is sent to the message queue, or until the time-out (as defined by the expiration of the waitLimit parameter).

If the srcActor parameter is non-NULL, it points to a location where the unique identifier of the actor is to be stored. The actor that posted the message is referred to as the source actor).

No data copy is performed to deliver the message to the invoking thread. Multiple threads can be blocked, waiting in the same message queue. With the msgGetAny() call it is possible for one thread to wait for message arrival on all message queues, and to obtain the first posted message from any queue. It is not possible to define a subset of queues and to wait for a message to arrive on this subset. Consequently, msgGet() allows you to wait for a message on a queue described by an index held in its second argument, and msgGetAny() allows you to wait for a message from all queues.

The msgGetAny() call is defined as follows:

int msgGetAny(int           spaceLid,
              unsigned int* msgQueueId,
              KnTimeVal*    waitLimit,
              char**        msgAddr,
              KnUniqueId*   srcActor);

Figure 11-5 illustrates previously created actors receiving messages from queues.

Figure 11-5 Getting Messages From Queues

Graphic

A message that is of no further use to the application can be returned to its pool of messages and will be available for reallocation with the following call:

#include <mipc/chMipc.h>
int msgFree(int      spaceLid,
            char*    msg); 

To retrieve information about a specific message queue or message pool, use the following call:

#include <mipc/chMipc.h>
int msgPoolStat (int            spaceLid,
                 unsigned int   msgPoolId,
                 KnMsgPoolStat* stat);

int msgQueueStat(int             spaceLid,
                 unsigned int	    msgQueueId,
                 KnMsgQueueStat* stat);

Information about a message pool is stored in the KnMsgPoolStat structure which contains:

unsigned int msgSize

the sizes of the messages in the pool.

unsigned int msgNumber

the number of messages in the pool.

unsigned int msgFree

the number of free messages in the pool (messages that can be allocated using msgAllocate).

Information about a message queue is stored in the KnMsgQueueStat structure which contains:

unsigned int msgNumber

the number of messages currently waiting in the queue.


Caution - Caution -

msgFree and msgNumber may be invalid after the next time slice or thread schedule because other threads or applications have access to the message space.