Issott_Common Design Patterns for Symbian OS-The Foundations of Smartphone Software_0470516356 (779879), страница 23
Текст из файла (страница 23)
This service allows one thread to wait untilanother thread signals it has reached the same point; both threads thencontinue to execute as normal. We can use this to solve our problem asfollows:• Both the USB client thread and the USB Manager thread agree thatthe rendezvous point they will use is after the client has created theUSB Manager and the USB Manager has finished its initializing.• The USB client thread (the event consumer) calls the RThread::Rendezvous(TRequestStatus& aStatus) function passing ina TRequestStatus.
The client then waits on the Rendezvous()until it receives the event signal telling it the USB Manager has alsogot to the rendezvous point.• The USB Manager thread (the event generator) calls RThread::Rendezvous(TInt aReason) when it has finished initializing.This overload of Rendezvous() doesn’t take a TRequestStatus&because a thread can only be at one rendezvous point at a time and sothe kernel doesn’t need to be told which TRequestStatus it needsto complete.As you can see, the kernel thread’s Rendezvous service is implementedusing this pattern.
The USB Manager component supplies a client-side110EVENT-DRIVEN PROGRAMMINGlibrary containing the following code,15 which executes within the USBclient thread. It starts the transient server and uses the kernel threadRendezvous service to ensure synchronization between it and the newthread:EXPORT_C TInt RUsb::Connect(){TInt retry = 2;FOREVER{TInt err = CreateSession(KUsbServerName, Version(), 10);if ((err != KErrNotFound) && (err != KErrServerTerminated)){return err;}if (--retry == 0){return err;}err = StartServer();if ((err != KErrNone) && (err != KErrAlreadyExists)){return err;}}}// Start the server processstatic TInt StartServer(){const TUidType serverUid(KNullUid, KNullUid, KUsbmanSvrUid);RProcess server;TInt err = server.Create(KUsbmanImg, KNullDesC, serverUid);if (err != KErrNone){return err;}TRequestStatus stat;server.Rendezvous(stat);if (stat != KRequestPending){server.Kill(0);}else{server.Resume();}15 Sothat it doesn’t need to be re-written by each and every client.REQUEST COMPLETION111User::WaitForRequest(stat);err = (server.ExitType() == EExitPanic) ? KErrServerTerminated :stat.Int();server.Close();return err;}Here is the server initialization code that executes within the USBManager thread:GLDEF_C TInt E32Main(){__UHEAP_MARK;CTrapCleanup* cleanup = CTrapCleanup::New();TInt ret = KErrNoMemory;if (cleanup){TRAP(ret, RunServerL());delete cleanup;}__UHEAP_MARKEND;return ret;}// Perform all server initialization, in particular creation of the// scheduler and server, and then run the schedulerstatic void RunServerL(){// Various USB-specific initialization tasks here...// Initialization complete, now signal the clientRProcess::Rendezvous(KErrNone);// Ready to run// Server activities begin here...}Other Known UsesTRequestStatus and its associated functions are ubiquitous withinSymbian OS.
They form the basis of the following event-driven programming patterns:• Active Objects (see page 133)• Client–Server (see page 182)• Publish and Subscribe (see page 114).112EVENT-DRIVEN PROGRAMMINGVariants and Extensions• Using Multiple TRequestStatus Objects at OnceThe User class offers overloads of WaitForRequest() which canbe provided with references to multiple separate TRequestStatusinstances. These functions operate in much the same way as the singleTRequestStatus version, but wait until any of the TRequestStatus instances are completed. When the thread continues executing,it must check to see which of the TRequestStatus instances werecompleted and take action as appropriate.
This is often useful whereyou wish to use an asynchronous service that has an upper time limiton completion of the service. In this case, one TRequestStatusis passed to the asynchronous service and the other is passed to aninstance of RTimer or some similar class.• The Kernel Is the Event GeneratorA common degenerate form of this pattern is where there is no userside event generator thread and instead the kernel itself generatesevent signals. For example, the RTimer::After() function allowsan event consumer to request the kernel to send it an event signalafter a specified time.• The Kernel Doesn’t Pass on the TRequestStatusHere the kernel receives the TRequestStatus from an event consumer but doesn’t pass it on to the event generator directly.
Instead,the event generator uses a kernel service function to inform the kernelof the event signal. The context of the call, or the specific functionused, allows the kernel to match this up with the correct TRequestStatus and complete it on behalf of the event generator. An exampleof this is the RThread::Rendezvous() function.References• Asynchronous Completion Token [Schmidt, 1999] is a similar patternthat allows applications to associate state with the completion ofasynchronous operations.• Event Mixin (see page 93) is an alternative to this pattern for whenyou need to send event signals synchronously within a single thread.• Active Objects (see page 133) is an alternative to this pattern for whenyou need to send event signals asynchronously within a single thread.REQUEST COMPLETION113• Publish and Subscribe (see page 114) is an alternative to this patternfor when you need to send event signals asynchronously across threadand process boundaries.• Client–Server (see page 182) is a more heavyweight alternative to thispattern for when you need to send event signals synchronously orasynchronously across thread and process boundaries.114EVENT-DRIVEN PROGRAMMINGPublish and SubscribeIntentSecurely broadcast changes to other threads or processes listening forsuch notifications as a way of saving power and decoupling components.AKAP&SProblemContextAn event generator needs to broadcast the latest content of a data structureto zero or more event consumers located in other threads or processesthat do not need to track each intermediate state of the data structure.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.• You would like to perform security checks on either the event generators or the event consumers so that you can control who sends andreceives event signals.DescriptionThe problem that we wish to overcome here is an interesting one becauseof the large amount of flexibility that is required.
First of all, there needs tobe no restriction on the number of event generators or event consumers.Secondly, the event generators and the event consumers can be anywherein the system: in different threads or processes or even in the kernel itself.Clearly Event Mixin (see page 93) can’t be used to solve this problem byitself!A natural consequence of this flexibility is that it is not easy to guaranteethat each event consumer receives each and every event signal, which isknown as Reliable event signals.
The only completely reliable way to dothis is for the event generator to synchronously wait at the point it sendsan event signal until every event consumer has received the event signal.Only then should the event generator continue executing. If the eventgenerator doesn’t wait, it might find that it needs to send another eventsignal. So now it’s got two to send. Without a synchronization point, theevent generator could simply keep generating event signals and exhaustany queue of event signals.PUBLISH AND SUBSCRIBE115Reliable event signals can be dealt with in two ways:• Don’t even try to support them (the approach taken here).
Whilstthis restricts the scope of the problem we’re addressing, it has theadvantage of being significantly simpler to deal with. However, eventconsumers must only need to access the latest value of the eventsignal and must not need to know about every intermediate value.• Support them by generating them one at a time (not discussedhere).
However, Client–Server (see page 182) and Coordinator (seepage 211) can be combined to provide a solution to this problem.ExampleUSB Mass Storage is a technology that provides a standard interface toa variety of storage devices such as portable flash memory devices anddigital cameras. A full implementation of this technology requires theSymbian OS File Server to support mass storage drives and an applicationthat allows the end user to mount or un-mount them.A key requirement of the Mass Storage Application (MSA) is that itshould keep the end user informed about the status of the mass storagedrives, for example by showing an icon for each drive that is grayed outwhen the drive isn’t present and fully drawn when the drive is present.Rather than polling the File System for this information, the applicationshould register for notification of changes in the mass storage drive status.This example meets the requirements of this pattern because:• The MSA is in a different process from the File Server.• The MSA only needs to show the latest status of each drive to the usersince missing an event signal will not inconvenience the end user solong as the status icon is showing the correct state.• Only the File Server is a valid generator of mass storage events so allother potential event generators should not be allowed to publish anevent signal.
Conversely, there is no risk in anyone reading the drivestatus so there is no need to restrict the event consumers.SolutionThis pattern uses the kernel service RProperty 16 to allow software todefine properties. These properties are effectively data structures that canbe accessed by components running in any thread or process.17 One16 Implementedusing Request Completion (see page 104).speaking, this should be ’any process with sufficient platform security capabilities‘, but more on this later.17 Strictly116EVENT-DRIVEN PROGRAMMINGnotable aspect of properties is that components can register themselvesto receive notification when the property value changes, which is knownas subscribing.