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 EPOC.
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 initialisation 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 initialise 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 !
}
}
}
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.