Home > Message-Passing, Projects, RTOS, SKC++ > SKC++: Receiving messages

SKC++: Receiving messages

Thursday, November 19, 2009 Leave a comment Go to comments

Although I separated the ideas of posting an untrammelled event, using post, and sending a message (which also posts an event), using send, this is not such a good idea for receiving messages. One of the key things about an SKC++ task is that it should be able to wait at a single point in its processing in order to get something to process and that it should deal with only one event or message at a time. Accordingly, the wait function has been enhanced to deal with message reception, as well as with raw events. I’ve also taken the opportunity to rearrange the overloads:

// For efficient use if no messages are ever sent here.
Event wait(const unsigned short eventFilter = EVENT_ALL);

// For general use.
Event wait(MessagePtr& pMessage, const unsigned short eventFilter = EVENT_ALL);

But before we can receive any messages, we must create one or more message queues and  and attach each of them to a specific event. This means that the event used in sending a message determines the particular, prioritised queue to which it will be directed. The technique is best described by example:

void activity()
{
    MessageQueue queue1(8);
    attachMessageQueue(queue1, EVENT_1);
    MessageQueue queue2(10);
    attachMessageQueue(queue2, EVENT_2);
    while(true)
    {
        MessagePtr pMsg;
        Event event = wait(pMsg);

        // Other stuff...
    }
}

The argument given to the MessageQueue constructor is the maximum number of messages the queue can hold. This limit is artificial because a linked list is used of theoretically infinite length. It is still important, though, to put a sensible limit on queue length. If a task cannot always keep up with the rate of arrival of messages, it is better to discover this during early testing than to risk some obscure bug at a customer’s site in Greenland on New Year’s Day.

The pMsg argument passed to wait is a MessagePtr, created locally. It is initially invalid (i.e. it points to no message) but it is passed by reference to wait, so that wait can point it to any incoming message.

When wait returns, it tells us the event which caused it to do so. If that event is EVENT_1 or EVENT_2, we can expect that pMsg now points to a message received from the appropriate queue. Any other event indicates the occurrence of a raw post, without a message, and pMsg then remains invalid and can be ignored in subsequent processing.

A subtle but important point is that when an event is attached to a queue, that event remains set while there are still messages in the queue. By contrast, an event unattached to a queue is reset automatically each time it is serviced by wait.

Processing the message

So far, so straightforward. We’ve picked up a message and pMsg points to it. What does that message look like? Here is a picture:

Message

Brilliant! We gave wait a pointer to the base-class message (wait, being generic, uses one of these), so that’s all we can see. How do we fix this so that we can deal with the rest of the message – the interesting part – which was sent to us? There are about four ways, three of which come recommended:

Just knowing the message type

Maybe your task accepts only one type of message. In this case, the simplest way of dealing with the problem is to sidestep it. Instead of creating a MessagePtr (to the base class), create one of the exact type you need and pass that to wait. Of course, wait will treat it as the base type but you can still use the original to work with the message when it arrives.

Or maybe there is one message type per event. In that case, it is easy to establish what type of message you have; let’s assume its class is MyMsg. But to get at it, you need to use the proper type of pointer. That’s easy, too, because SKC++ allows you to do this:

MsgPtr<MyMsg> pMyMsg = pMsg;

Sounds dangerous. This is the kind of thing we should be using dynamic_cast for. Well, effectively, that’s what’s happening. But dynamic_cast works only on raw pointers, not on smart ones. And it doesn’t work at all if RTTI is turned off, which it often is for compilation of embedded software. SKC++ has its own mini-RTTI for messages. When attempting this kind of assignment, it checks the value of the typeId in the message (see above) against the value of a static constant created when MsgPtr<MyMsg> was first instantiated. I could go into more detail, but I won’t. The effect is that the assignment either succeeds, in which case ownership of the message is tranferred to pMyMsg, or fails, in which case pMsg still points to the message and pMyMsg does not. Failure of the assignment is silent but attempted use of an invalid message pointer is treated as an error, so it pays, as always, to check the pointer’s validity before attempting to use it.

[Note that typeId() is meant only for internal use by SKC++; I may even make it private. The value returned should not be used directly by application code, for reasons I don’t want to bore you with here.]

This approach is recommended for its simplicity but it is not suitable for all situations.

RTTI

If this is turned on for your compiler, it can be used. Bear in mind that you still cannot use dynamic_cast on a message pointer, though the type of assignment referred to above still works, of course.

It could be tempting to use RTTI where messages cannot be mapped one-to-one with events (which is a simpler form of RTTI itself). However, I have already forced, more-or-less, the inclusion of a switch block in most activity functions because I chose an efficient but non-OO way to represent events. Adding a (further) level of RTTI raises the spectre of nested switch blocks. Truly horrible!

This approach is not recommended. There are better ways…

Polymorphic action

There is a virtual function called action in the Message base class. This has its own default implementation (more on that later) but what you need to do, in order to follow the OO way, is:

  • Override action in your own message class, to do whatever you want it to do.
  • When the message arrives, call its action function via the base class in the time-honoured manner.

Although this would get you points in a C++ exam, it is not, by itself, a complete solution. The action is part of the message, not part of your task. Probably, action will need to call various task-defined functions in order to process the message data appropriately. It may even be necessary for task code to call back to various other functions which may be defined for the message, or even to access its data directly. It would be unwise to make too much public in order for this to happen; the inherently intimate relationship between a message and a task accepting it makes it justifiable, in my view, to make the message a friend of the task and possibly vice versa.

So this approach avoids any RTTI nonsense but it implies close cooperation, in design, between the message and the task. For situations where that can easily be achieved, it is an approach I recommend. But for ultimate flexibility, we have…

Use of agents

In the form of message-passing which I previously referred to as “general”, messages are designed for some system-related purpose and are passed from task to task, maybe, in order to have a sequence of operations performed on the message data. Obviously, all those tasks expected to process a message must know what it looks like but it is inappropriate for the message to carry some arbitrary fixed behaviour around in its action function. What needs to be done by each task is something to be organised by the task itself. The key characteristics of the “agent” approach are:

  • The message is designed as required but its action function is not overridden.
  • A task receives the message via a public interface function (the message must not be sent from afar directly to the task’s activity function, using send).
  • This function installs a task-related agent object into the message. This object is simply another message with its own action function but with little or no data.
  • The interface function uses send to dispatch the message to the activity function in the usual way.
  • On receipt of the message, activity calls the message’s action function. Because action has not been overridden, the base-class default version is invoked.
  • The base class’s default action function calls the agent’s action function, which does the required work.

There are still “friendship” issues but this time they are largely between the task and its agent(s). Given the nature of the general message-passing model, it is reasonable, I think, to equip the message itself with public access functions, or even to make its actual data public (for a communications packet, for example).

This approach, though requiring a level of indirection, is recommended for its generality.

Of course, agents could contain agents, and so on, like Russian dolls. That’s something I wouldn’t recommend!

Advertisements
Categories: Message-Passing, Projects, RTOS, SKC++ Tags:
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: