Wiley.Symbian.OS.C.plus.plus.for.Mobile.Phones.Aug.2007 (779890), страница 37
Текст из файла (страница 37)
The Control Framework, CONE, doesthat for you.The active scheduler’s wait loop is started by issuing CActiveScheduler::Start(). Clearly, before this happens, at least one request function should be issued, so that the first User::WaitForAnyRequest()completes. From that point on, any completed event causes the RunL()function of one of your active objects to be called.A RunL() function can stop the active scheduler by issuingCActiveScheduler::Stop(). When that RunL() returns, the function call to CActiveScheduler::Start() completes.Stopping the active scheduler brings down the thread’s event-handlingframework, which is not something you should do lightly.
Only do it fromthe main active object that controls the thread.182ACTIVE OBJECTS6.8 Understanding a Stray SignalIf the active scheduler cannot find the active object associated with anevent, this indicates a programming error known as a stray signal and theactive scheduler panics the thread with the code E32USER-CBase 46. 1In this situation, the active scheduler is told that a request was completed by the act of the thread’s request semaphore being incremented,however, the scheduler cannot find the associated active object on whichto call RunL().There are several programming errors which can lead to a stray signalbeing reported. Stray signals can be quite common when you first startworking with active objects.
The guidelines in the following sections canhelp you to understand why you see a stray signal panic and what youcan do to solve it.Forgetting to Set an Object as ActiveIf you make an asynchronous request but forget to indicate that the activeobject is now active then the scheduler cannot locate the active objectwhen the request is completed. When the scheduler iterates through allthe active objects in its queue, it can see that the request status valueis not KRequestPending but, because the object wasn’t ‘tagged’ asactive, it doesn’t know that a request was ever issued.
The schedulertreats this as a programming error and therefore panics.You should normally call SetActive() in your code immediatelyafter making your asynchronous request. In particular, you must ensurethat you do not call any potentially leaving functions between makingthe request and setting your object active.
In this way, the request phasecontractual obligations become atomic.Commonly, the failure to set an object as active is seen when an objectis written so that it always makes an asynchronous request but only callsSetActive() when the object is not already active:RequestService(iStatus);if (!IsActive())SetActive();With the above implementation, the asynchronous request is alwaysissued but SetActive() is only called if the object has no outstanding request already being processed.
This can lead to the case whereiStatus is used for two requests simultaneously.1E32USER-CBase is the category of panic for user-originated programming errors whenworking with CBase-derived objects from the euser.dll library. The panic number, 46,is specifically used to indicate that a stray signal has occurred.UNDERSTANDING A STRAY SIGNAL183Forgetting to Set a Request as PendingWhen creating an asynchronous service, the service provider has acontractual obligation to ensure that the client’s TRequestStatus iscorrectly initialized to KRequestPending prior to its completion.When the scheduler is searching for an active object upon whichto call RunL(), it explicitly checks the active object’s request statusflags to ensure that the object has a request pending. If you have calledSetActive() but the request status was not set to KRequestPendingduring the request-initialization phase, then the scheduler is unableto identify it and treats it as a stray-signal panic.
For asynchronousclient–server requests, the Symbian OS framework ensures this takesplace automatically.Making a Second RequestAs discussed earlier, there is a one-to-one relationship between activeobjects and asynchronous request functions. Since each active objectcontains precisely one TRequestStatus data member, it can makeonly one asynchronous request at any given time.
If an existing requestis still pending at the time a new request is issued then effectively twoservice providers are competing for the same request status. This causesno problem to the service provider, but it results in the request semaphorefor the thread being signaled twice, once for each event completion. Forthe first request that is completed, a RunL() dispatch is made withouterror and the object is set as being inactive.
However, when the schedulernext calls User::WaitForAnyRequest(), it returns immediately andattempts to locate the relevant active object. This time the schedulerpanics with a stray signal since it fails to find an appropriate active objectupon which to call RunL().Completing a Request TwiceIf your code creates an asynchronous service within its own thread, thencare must be taken to ensure that you complete the CActive::iStatusmember only once.Let’s look at a simple example in which a RunL() method completes the object’s request status immediately, thereby requesting anotherRunL() call as soon as possible. The core implementation of such amethod might look something like this: 2void CRunAsSoonAsPossible::RunL(){// do some processing to handle original event2This code is an oversimplification, for illustrative purposes only.
See Section 6.11 fora description of the potential dangers of such code and how to use it safely.184ACTIVE OBJECTS...// Now request RunL() be called again as soon as possibleTRequestStatus* myStatus = &iStatus;User::RequestComplete(myStatus, KErrNone);SetActive();}The precise action of User::RequestComplete() is explained inSection 6.10. For now, it is sufficient to know that the effect of this callis to simulate the making of an asynchronous request and its successfulcompletion, thereby allowing the scheduler to call the object’s RunL()again.A common question in such a situation is, ‘How do I implementDoCancel() for this kind of object?’ There is usually confusion herebecause it is not always clear what asynchronous request was issued.This often leads to creating incorrect code as follows:void CRunAsSoonAsPossible::DoCancel(){// INCORRECT: Try to cancel my requestTRequestStatus* myStatus = &iStatus;User::RequestComplete(myStatus, KErrCancel);}A possible problem would occur if the object were cancelled.
In this situation, iStatus has been completed twice: once from the RunL() callto User::RequestComplete() and again from within DoCancel().Since a call to User::RequestComplete() simulates a request completion, the active scheduler is confused into believing that two activeobjects are ready for dispatch.In this particular case, the correct implementation for DoCancel() issimply to leave the method body empty:void CRunAsSoonAsPossible::DoCancel(){// CORRECT: No asynchronous request to cancel since iStatus// was already completed immediately by the RunL() function.}The active object’s request status was completed by RunL() so nofurther processing is needed here.Completing a Request with KRequestPendingThe KRequestPending constant has special meaning for the activeobject framework and should not be used outside it.
If a request isUNDERSTANDING A STRAY SIGNAL185completed with KRequestPending then the thread’s request semaphoreis incremented, indicating that an event is ready for processing. However,the active scheduler only calls RunL() on an active object when itsrequest status value (completion code) is not KRequestPending. Thiscan mean that the scheduler reaches the end of its active object queuewithout locating a suitable object to dispatch.Setting the Status of a Completed Request to KRequestPendingNormally in Symbian OS, asynchronous requests are made within thecontext of calls between a client and a server.
In this situation, theservice provider’s client API should provide an asynchronous requestfunction that the client can call. The implementation of the serviceprovider’s client-side asynchronous request function should ultimatelyend up calling either:void RSessionBase::SendReceive(TInt aFunction,TRequestStatus& aStatus) const;orvoid RSessionBase::SendReceive(TInt aFunction, const TIpcArgs& aArgs,TRequestStatus& aStatus) const;In either case, the implementation of these methods automatically setsthe client’s request status to KRequestPending before dispatching therequest to the server.In some situations, the server may process the client’s request beforecontrol returns to the client’s request function.
Alternatively, there may beinsufficient resources to deliver the request asynchronously to the server.In either of these situations, the client’s request status may be completedalready by the time the SendReceive() function call returns andtherefore aStatus already contains a valid completion code.If your code attempts to set aStatus to KRequestPending aftercalling the service provider’s client API, then you risk overwriting therequest completion code. From the active scheduler’s perspective, thiswould make your active object appear as though it has made a request,but it has not yet been completed.
This ultimately creates a stray signalpanic within your thread.If you are creating asynchronous APIs outside the client–serverframework, you may need to assign a request status the value ofKRequestPending in order to satisfy the active scheduler that a requesthas been initiated. Take care to do this immediately before initiating therequest to the underlying service provider.186ACTIVE OBJECTSUsing RThread::RequestSignal() and the Active ObjectFrameworkCalls to RThread::RequestSignal() increment the request semaphore count for your thread. If used in conjunction with the activescheduler framework, it may confuse the scheduler into believing it hasan active object that is ready to run when no such object exists.Using Active Objects and Calling User::WaitForRequest()A common mistake is to use TRequestStatus objects outside theactive object framework to initiate multiple simultaneous asynchronousrequests and then wait for one to complete synchronously.
For example,starting a thread and a 5-second timer in parallel. If the thread does notexit within five seconds, then something must have gone wrong.The code might look like this:TRequestStatus timerStatus;TRequestStatus threadStatus;// start thread// start timerUser::WaitForRequest(timerStatus, threadStatus);Symbian OS provides an overload of User::WaitForRequest()that allows you to achieve exactly this. The above pseudo-code says, ‘Iwant to wait for either my timer request or my thread request to completebut I do not care which one completes first.’Until at least one of the requests has been completed, the threadis suspended.