This document describes how to handle multiple asynchronous requests with a wait loop.
A user thread which requires asynchronous services from more than one asynchronous service provider uses a wait loop.
The allows a thread to issue as many request as are necessary and to handle their completion. This construction forms the heart of almost all programs that run under Symbian platform.
A typical program:
waits for any request to complete by waiting on the thread’s request semaphore.
scans in turn through the asynchronous service providers for which a request is known to have been issued and tests the relevant TRequestStatus for completion; this is identified by a value other than KRequestPending.
when the first such completed request has been identified, handles the completed request, possibly by issuing a further request to an asynchronous service provider.
goes back to the top of the wait loop and waits again.
As an example, a terminal emulator must work with multiple requests simultaneously, to deal with:
key presses and user input in general, to control the action of the program and put characters on the emulated terminal screen and/or send them to the remote host
received data and to put characters on the emulated terminal screen or perform some control function.
the completion of any transmit operation that was in progress; if successful, there may be more data to transmit; if unsuccessful, some recovery action may be necessary
The typical protocol of a wait loop is:
The initialization phase which issues the first asynchronous requests.
Entry into the main loop which waits for any of these requests to complete and then handles the completion of one of them.
Detection of conditions which imply termination of the program. Typically, one of the request completion handlers sets appropriate flags and the wait loop terminates.
Cleanup and final termination.
initialize; while (!finished()) { wait for one or more requests to complete decide which request completed, and handle it } terminate;
Note that only one request should be handled per iteration of the loop. As part of the request completion handling process, the request may be re-issued, or new requests issued or existing ones cancelled. If the request cannot be identified, then the thread’s request semaphore has been signalled invalidly; this is indicative of a programming error, a condition is known as a stray signal - the wait loop should raise a panic.
In the case of a terminal emulator which works with multiple outstanding requests, it must be able to service whichever request completes and must be able to re-issue a request, if necessary. For example, when one key has been processed, a request for the next key must be issued.
In the following example:
Call Initialize() to initialize the emulator.
Implement while() to handle events.
Call the function User::WaitForAnyRequest() to wait for any request to complete instead of waiting for a specific request to complete.
Identify the particular request that completed by checking the request status objects for a value not equal to KRequestPending.
class TerminalEmulator { public: void MainLoop(); // main loop private: void Initialize(); // initialize void Terminate(); // clean up and terminate TBool IsFinished(); // whether finished private: // request status objects TRequestStatus iKeyPressed; // whether key pressed ... // ...etc for other request status // handler functions void HandleKeyPressed(); // handle key pressed ... // ... etc other handlers ... };
void TerminalEmulator::MainLoop() { Initialize(); // Initialize emulator while (!IsFinished()) // Handle events until finished { // wait for any request to complete User::WaitForAnyRequest(); // identify and handle the completed request if (iKeyPressed!=KRequestPending) { HandleKeyPressed(); } else if (iReceiveCompleted!=KRequestPending) { HandleReceiveCompleted(); } else if (iTransmitCompleted!=KRequestPending) { HandleTransmitCompleted(); } else { // something we weren’t expecting // panic ! } } }
Note
The request status is set by the service provider to KRequestPending when a request is issued. When the request is complete, the service provider uses an operating system call to set the request status value to some other value — usually a standard error code — and then to wake up the thread that requested the service, causing its WaitForAnyRequest() to complete.