Issott_Common Design Patterns for Symbian OS-The Foundations of Smartphone Software_0470516356 (779879), страница 22
Текст из файла (страница 22)
It also allows securitychecks to be applied to both event generators and event consumers.104EVENT-DRIVEN PROGRAMMINGRequest CompletionIntentUse TRequestStatus as the basis for event signals sent across threadand process boundaries.AKANone knownProblemContextYou wish to propagate warm event signals between an event generatorand an event consumer operating within different threads or processes.Summary• You wish to reduce the power usage of your component.• You want to promote encapsulation of and loose coupling betweenyour event generators and event consumers.DescriptionAn event generator wishes to propagate event signals to an event consumer that is running in a separate thread, possibly in a different process.The fact that a thread boundary is being crossed means Event Mixin (seepage 93) can’t be used to solve the problem.In addition, the need to support event signals crossing a processboundary means that a number of other possible solutions, such asfunction pointers, simply won’t work.
This is because the Symbian OSkernel enforces memory isolation between processes and so an object inone process cannot be directly accessed from another process. In fact,the only way to send event signals between processes is to involve thekernel in some way.ExampleThe USB Manager component in Symbian OS uses the transient variant ofClient–Server (see page 182) to co-ordinate the support for USB. Being atransient server, it is initialized when a client first creates a session to it.When the first session has been created, there is a risk that the client startsto issue requests immediately the session is in place, before the serverthread is able to service them, when the server thread is still reading in itsinitialization data.
The client thread must wait until it receives an eventREQUEST COMPLETION105signal to tell it that the server thread is fully initialized and the client canstart making requests to it.SolutionSymbian OS provides low-level, asynchronous, service-handling primitives based around the following two concepts:• Asynchronous request status – this is encapsulated by the TRequestStatus class which is essentially a 32-bit value. Many Symbian OSAPIs have functions that take a TRequestStatus& as a parameterwhich is an indication that the function provides some asynchronousservice. Use of such a function puts the calling code into the roleof event consumer and the component that implements the functioninto the role of event generator. The TRequestStatus itself is usedby the event consumer to request an event signal whilst the eventconsumer completes it to signal that an event has occurred and hencecan be viewed as the representation of the event signal.• Thread request semaphore – this is the means by which an event generator tells an event consumer about an event signal.
Event consumerscan then determine which TRequestStatus has completed andhence which event signal it has received. A thread that is waitingfor a request to be completed waits on the thread request semaphore(i.e. it does not proceed with any further processing) until the requesthas been completed and so requires no processor resource, thus saving power until it next needs to do something. The thread does notcontinue executing until the thread request semaphore is signaled.Since the completion of a request contains very little contextualinformation on the nature of the event being signaled,12 this patternis best used when implementing higher-level services, such as ActiveObjects (see page 133) or Client–Server (see page 182).
It is not oftenused well when applied directly.An important point to note is that whilst this pattern does work withina thread it is bad practice to use it for intra-thread event signals. This isbecause better alternatives exist, such as Event Mixin (see page 93) andActive Objects (see page 133).StructureThe event consumer (see Figure 4.3) has an instance of TRequestStatus (either as a member variable or held on the stack, as appropriate).12 Thatis, a TRequestStatus is a warm event signal.106EVENT-DRIVEN PROGRAMMINGEvent Generator ThreadEvent Consumer ThreadEvent GeneratorEvent ConsumerRequestsCompletesTRequestStatusThe passing of theTRequestStatus& and theeventual TRequest StatusCompletion is mediated bythe TCBTrusted Computing Base+++++RThread::RequestComplete(TRequestStatus*&, TInt)User::WaitForRequest(TRequestStatus&)User::WaitForRequest(TRequestStatus&, TRequestStatus&)User::WaitForNRequest(TRequestStatus*, TInt)User::WaitForAnyRequest()Figure 4.3Structure of the Request Completion patternA reference to this TRequestStatus is passed to the event generatorvia the kernel.
It then uses one of the User functions to wait on thethread request semaphore until one or more event signals have beencompleted.The event generator completes the event consumer’s request whenan appropriate event has occurred by calling the RequestComplete()function on the RThread object representing the event consumer’s threadwhich signals the appropriate thread request semaphore.13Note that both the event generator and event consumer must be inuser threads; they cannot be part of the kernel itself.DynamicsFigure 4.4 shows how this pattern operates. Since we are talking abouttwo different threads of execution, it is only really possible to talk aboutthem separately except at synchronization points.13Other functions can be used to complete a TRequestStatus but they do so eitheronly within the same thread or only for a specific type of event signal, such as RProcess::Rendezvous(), so aren’t mentioned here.REQUEST COMPLETIONEvent Consumer ThreadEvent Consumer107Kernel ThreadEvent Generator ThreadTrustedComputing BaseEvent GeneratorSomeRequestFunction(aStatus)SomeRequestFunction(TRequestStatus&)The event generatorthread continues itsexecution and performswhatever processing itneeds to prior tocompleting the requestback to the eventconsumerWaitForRequest(aStatus)The eventconsumer threadwaits at this pointProcessing()RequestComplete(TRequestStatus&, aReason)WaitForRequestreturns when therequest has beencompletedFigure 4.4 Dynamics of the Request Completion patternWithin the Event Consumer ThreadThe event consumer passes a reference to its TRequestStatus instanceto a request function.
This function has the responsibility of setting thevalue of the TRequestStatus to KRequestPending, to indicate thatit has not yet completed, and then passing it to the appropriate kernelservice API. The kernel service API synchronously returns to allow thethread to continue execution.The event consumer may then use one of the User::WaitForRequest() functions that take one or more TRequestStatus& as aparameter.
This causes the thread to stop any further execution until therequest has been completed (i.e. this is a synchronization point). It canthen continue executing some other task, however, at some point theevent consumer thread needs to wait on the TRequestStatus.When the request has been completed the thread continues executingby returning from the User::WaitForRequest() function.Within the Event Generator ThreadThe kernel passes to the event generator thread what is effectivelya reference14 to the event consumer’s TRequestStatus. The eventgenerator does not need to respond immediately and can continue toexecute until it is ready to issue the event signal.This is done by using the function RThread::RequestComplete().This function is called on an instance of RThread that acts as a handle14 It’snot a direct reference, to preserve the memory isolation of different processes.108EVENT-DRIVEN PROGRAMMINGto the event consumer thread.
It takes a TRequestStatus*& thatindicates the request to be completed and a TInt which becomes thenew value of the TRequestStatus in the event consumer thread, andso provides an indication of the outcome of the event. The completion ofthe TRequestStatus causes the event consumer thread to recommenceexecution.ImplementationAny given implementation of this pattern generally relies on selecting theappropriate service routine (provided by the kernel) by which the eventconsumer can pass its TRequestStatus to the event generator.The use of the functions themselves is very simple.In the Event Consumer ThreadTRequestStatus status;SomeAsynchronousServiceFunction(status); // Sets status to// KRequestPendingUser::WaitForRequest(status); // Thread doesn’t continue until status is// completedIn the Event Generator ThreadIn the case where the event generator thread receives a TRequestStatus& to complete directly, the following code completes the request:TRequestStatus* status = &iStatus;consumerThread.RequestComplete(status, result);where:• consumerThread is an RThread handle to the event consumer’sthread• iStatus is the TRequestStatus& to be completed and obtainedfrom the kernel service• result is a TInt containing the outcome of the event, such asKErrNone for successful completion.ConsequencesPositives• This pattern is simple to implement.• The power usage of the components involved is reduced.REQUEST COMPLETION109• The event generator knows when an event consumer is listening forevents.Negatives• The kernel must provide a service routine that passes the TRequestStatus from the event consumer to the event generator.• Since the event consumer thread is not executing while waiting for theevent, there is no easy way to cancel its interest in the event withoutthe thread being killed outright.• Only warm event signals can be sent.• The event consumer must re-register with the event generator following every event signal and hence may miss an event in the time takento re-register.• Only a single event consumer per event signal is supported.• There is no simple way for security checks to be performed on eitherthe event generators or the event consumers.Example ResolvedThe USB example problem can be solved by the use of the kernelthread Rendezvous service.