Message Handling

This document describes message queues and message handling.

Message queues

The request handling kernel side DFC is managed by a message queue object of the TMessageQueue type. The message queue consists of a DFC and a doubly linked list of received messages. The messages are represented by TThreadMessage objects and are owned by each user thread. Requests are queued as message objects on the message queue.

The driver framework requires that the driver sets a DFC queue to use with the message queue. This is done by calling DLogicalChannel::SetDfcQ(). The message queue must also be enabled to receive incoming messages by calling TMessageQue::Receive().

// Logical Channel Second stage constructor
TInt DExDriverLogicalChannel::DoCreate(TInt /*aUnit*/, const TDesC8*
        /*anInfo*/, const TVersion& aVer)
    {
    ...
    // Set up the DFC queue for this driver. Here, the DFC 
    // queue is created by the PDD dedicated for this driver.
    SetDfcQ(Pdd()->DfcQ());

    // Start receiving the incoming requests on the message queue
    iMsgQ.Receive();
    ...
    }

The Kernel provides a standard DFC queue, which runs on a dedicated kernel thread called DFCThread0, for general use by drivers. However, it is recommended that the driver creates its own DFC thread to process its requests. The following example shows how a PDD can implement the DfcQ() function to return a newly created DFC thread:

// DfcQ - Creates a DFC queue dedicated for the tutorial driver
TDynamicDfcQue* DExUartPhysicalChannelH4::DfcQ()
    {
    // Create a DFC queue dedicated to the driver with a specified 
    // priority
    TInt r = Kern::DynamicDfcQCreate(pDfcQ,KExUartDfcPriority,
    KExUartDfcName);
    if (r!=KErrNone)
        {
        // DfcQ failed, return NULL
        return NULL;
        }
    return iDfcQueue;
    }

The DFC thread that is created for the driver must be destroyed after use. To do this, the driver can create an Exit or Kill DFC request and queue it to the thread to be destroyed in the logical channel destructor. This exit DFC function cancels any other requests pending using TDfc::Cancel() and calls Kern::Exit() to terminate the thread.

The TDynamicDfcQue has a destroy method that can be run on the channel destructor. The destroy method destroys the DFC queue, kills the DFC thread and deletes the TDynamicDfcQue object itself. This avoids the possibilities of memory leaks in the DFC.

Message handling

All synchronous and asynchronous requests are passed to the HandleMsg() function by the framework, with the message as an argument. A driver should implement the function to identify the message type and handle the messages accordingly.

The client thread is blocked until the message is completed, as the request uses the thread's message object. If the client thread was left free, it would corrupt the message if another request was issued.

When the driver has completed handling the message, it notifies the framework by calling TThreadMessage::Complete(). The client thread is then unblocked, and can either block on the thread semaphore or issue further requests before blocking.

For synchronous requests, the message is not completed until the request itself is complete, and the driver calls the TThreadMessage::Complete() function after the actual completion of the request. However, for asynchronous requests, the message is completed after the driver has accepted the request, but not necessarily after the actual completion of the request, which can happen later. This means that the driver calls TThreadMessage::Complete() as soon as it has received the message and initiated the required processing to complete the request.

void DExDriverLogicalChannel::HandleMsg(TMessageBase* aMsg)
    {            
    TThreadMessage& m = *(TThreadMessage*)aMsg;        
    // obtain the function id value to determine the request nature
    TInt id = m.iValue;        
    ...
    if (id>=0)    // Synchronous messages
        {
        // call synchronous message handler function, DoControl()    
        TInt r = DoControl(id,m.Ptr0(),m.Ptr1());
        m.Complete(r,ETrue);
        return;
        }    
    }