Kernel-side Messages

Kernel-side messages are a means of communication between Symbian platform threads executing kernel-side code.

Typically, they are used by device drivers to communicate between a client thread, usually a user thread, and a kernel thread running the actual device driver code.

The mechanism consists of a message, containing data, and a queue that is associated with a DFC. The DFC runs in order to deal with each message.

A kernel-side message is represented by a TMessageBase object, that allows a single 32-bit argument to be passed, and a single 32-bit return value. In general, more arguments can be passed by deriving classes from TMessageBase. In practice, each Symbian platform thread has a TThreadMessage object embedded within it. TThreadMessage is derived from TMessageBase, and contains space for 10 extra 32-bit arguments. These objects are used for communication with device driver threads.

Both TMessageBase and TThreadMessage are defined in ...\kernel\kernel.h, which is exported to epoc32\include\kernel.

Figure 1. Message threads and queues

SDblQueLink is simply an object that allows a message to be linked to another in the form of a doubly-linked list.

The message queue is represented by a TMessageQue object, which consists of a DFC plus a doubly-linked list of received messages. The DFC is attached to the receiving thread’s DFC queue and it runs whenever a message becomes available for processing.

TMessageQue is defined in ...include\kernel\kernel.h, which is exported to epoc32\include\kernel; SDblQueLink is defined in ...\nkern\nklib.h, which is exported to epoc32\include\nkern.

The following shows, in simple terms, the relationship between messages and the message queues:

Figure 2. Relationship between messages and message queues

When a message is sent to the queue, either:

  • the message is accepted immediately, and the receiving thread’s DFC runs. This will happen if the message queue is ready to receive, which will be the case if the message queue is empty and the receiving thread has requested the next message.

or

  • the message is placed on the delivered message queue, and the DFC does not run. This will happen if there are other messages queued ahead of this one or if the receiving thread has not (yet) requested another message.

A kernel-side message may be in one of three states at any time:

  • FREE - represented by the TMessageBase::EFree enum value. This indicates that the message is not currently in use.

  • DELIVERED - represented by the TMessageBase::EDelivered enum value. This indicates that the message is attached to a message queue but is not currently in use by the receiving thread. It may be removed from the queue and discarded with no ill effects on the receiving thread. The client thread pointer may not be used while the message is in this state.

  • ACCEPTED - represented by the TMessageBase::EAccepted enum value. This indicates that the message is not attached to a message queue but is currently in use by the receiving thread. The message may not be discarded.

Transitions between these states, including adding the message to, and removing the message from a message queue, occur under the protection of the global TMessageQue::MsgLock fast mutex. The use of a mutex is necessary to avoid queue corruption, for example, when multiple threads are sending messages to the same message queue at the same time. The use of a fast mutex means that message passing operations may only be invoked from a thread context.

Kernel-side messages may be sent either synchronously or asynchronously. Each TMessageBase object contains an NFastSemaphore on which the sending thread will wait after sending a synchronous message. The semaphore is signalled by the receiving thread after the message has been processed and the completion code has been written in. The sending thread is then released and picks up the return code. The NFastSemaphore also contains a pointer to the sending NThread; this serves to identify the sending thread and is therefore set up for both synchronous and asynchronous message send. Note that this pointer is reference counted - the access count of the originating DThread is incremented when the message is sent. This prevents the sending DThread object disappearing if the thread terminates unexpectedly. When the message is completed the extra access is removed asynchronously - the thread completing the message will not need to delete the DThread itself. This is done to avoid unpredictable execution times for message completion. Also note that even messages sent asynchronously must be completed; this is required to set the message state back to FREE and to remove the access count from the sending thread.

The TThreadMessage objects embedded in Symbian platform thread control blocks, DThread objects, are always sent synchronously - this ensures that one message per thread will always suffice. In addition, these messages are cancelled if the corresponding thread terminates. Cancelling an ACCEPTED message has no effect, but cancelling a DELIVERED message will remove the message from the queue and also remove the access count held by the message on the sending thread. Hence the sending thread pointer should only be used by the receiving thread if the message is in the ACCEPTED state.