Wiley.Symbian.OS.C.plus.plus.for.Mobile.Phones.Aug.2007 (779890), страница 36
Текст из файла (страница 36)
This effectively accomplishes thesame result, since adding an object to the scheduler inserts it after anyother objects of the same priority. However, whilst this is perhaps simplerto write, it relies on an implementation detail of the active scheduler andtherefore is less transparent than explicit priority adjustment.It is worth pointing out that active objects and the adjustment of theirpriorities cannot guarantee a specific response time. The overall idea isthat of co-operative event handling – where it is possible that one objectmay take an unbounded amount of time before it finishes its RunL()processing. If your code must have a response within a certain period oftime of an event occurring then you need to use the Symbian OS preemptive thread system in conjunction with suitable thread and processpriorities.176ACTIVE OBJECTS6.6 Active Object CancellationAll asynchronous service providers must implement cancel functionscorresponding to their request functions.
All active objects that issuerequest functions must also provide a DoCancel() function to cancelthe request that they originally issued.A cancel is actually a request for early completion. Every asynchronousrequest that is issued must be completed precisely once – whether normally or by a cancel method. The contractual obligation of a cancelfunction is to:• execute synchronously• return quickly• complete the original asynchronous request, usually with a status ofKErrCancel.Whilst active objects must implement the DoCancel() method, itshould never be called directly.
Instead you call the Cancel() methodthat is inherited from CActive.EXPORT_C void CActive::Cancel(){if (iStatus.iFlags&TRequestStatus::EActive){DoCancel();User::WaitForRequest(iStatus);iStatus.iFlags&=∼(TRequestStatus::EActive |TRequestStatus::ERequestPending);}}The method behaves as follows:• It checks whether the object is active by inspecting the underlyingrequest status flags and checking for the EActive bit. If an object isnot active, then there is no request to cancel and hence no call toDoCancel() is needed.• If the object is active, a call is made to the pure virtual DoCancel() function, which must ask for early completion of the originalasynchronous request.• Then, User::WaitForRequest() is called but unlike the activescheduler, which waits for any request to complete, this time acall is made to the overload that takes a TRequestStatus& argument: the iStatus of the active object.
This means, ‘Wait untilthis specific request is completed.’ As long as iStatus is set toACTIVE OBJECT CANCELLATION177KRequestPending then the request has not yet been completed.Once the request semaphore for the thread is signaled and iStatus is no longer KRequestPending, this line returns. It is the callto User::WaitForRequest() that prevents RunL() from beingcalled when an active object is cancelled. This line ‘eats’ the eventassociated with cancellation so that when the active scheduler waitsfor the next event, it is oblivious to the cancellation of this activeobject.If your call to Cancel() appears to hang, then the reason is mostprobably because your call to DoCancel() call didn’t result iniStatus being completed.• Finally, the last line updates the underlying request status flags toindicate that the object is no longer active and doesn’t have anoutstanding asynchronous request.If you accidentally call DoCancel() instead of Cancel(), yourRunL() method may be called unexpectedly.In normal circumstances, most asynchronous requests complete normally and only the occasional request needs to be canceled.
Despitetheir relative infrequency, it is important to ensure that cancellationswork cleanly and successfully under all possible conditions. In fact, whencreating an active object, you need to consider all the possible ways thata request can complete. The remainder of this section examines, in turn,each of the principal ways that completion may occur. It is not altogethersurprising that the majority of them relate to cancellation.Handling a Request that Cannot RunIn some cases it is possible to make a request that does not actually start.For example, there might be insufficient memory or resources to executethe request, or the client might supply incorrect parameters to the request.In such a situation, the implementer of the asynchronous service shouldinform the requester that the request could not be carried out.
There areseveral possible ways to achieve this, such as returning an error code orleaving. However, these techniques can lead to complex client code thatneeds to TRAP calls to the request function or check for any returnederrors before calling SetActive(). This is often error prone.It is preferable to complete the client’s request by setting the status toan error code (see Figure 6.7). In this way, RunL() is called normally and178ACTIVE OBJECTSClientRequest interfaceRequest issued to asynch.service interfaceRequest()Request interface detects error inclient's request and immediatelycompletes it with KErrGeneralClient's request is completedbefore control returns to clientRequest is nowcompleted!Figure 6.7Request unable to startthe client’s implementation of RunL() should check for error conditionsby inspecting the value of iStatus.Let’s consider a trivial example of a very simple asynchronous requestto divide one number by the other.
The function treats a request todivide by zero as an error and so checks for this prior to initiating theasynchronous request. The request function might look something likethis:void RAsyncDivider::Divide(TRequestStatus& aStatus, TInt aDividend,TInt aDivisor){aStatus = KRequestPending;if (aDivisor == 0){TRequestStatus* status = &aStatus;User::RequestComplete(status, KErrArgument);}else{// Calculate result etc.}}The service provider sets the client’s request status to KRequestPending. It then checks the request arguments to make sure that theyare valid. If the client attempts to use a divisor of zero then the client’srequest status is immediately completed with an error prior to carryingout any asynchronous operation.
In this way, the client receives an eventnotification and, if active objects are involved, the client active object’sRunL() method is called with iStatus equal to the KErrArgumenterror code.ACTIVE OBJECT CANCELLATIONClientRequest interfaceRequest issued to asynch.service interface179Request providerRequest()Start asynchronous processingof client requestTRequestStatus set toKRequestPendingTime passes forclient...Processing of client'srequest...Processing complete!Request is nowcompleted!Client request is completed withKErrNone and thread is signalledFigure 6.8 Normal request processingCompleting a Request after Normal ProcessingAn asynchronous request is issued, is processed, and an event is signaledsome time after the processing is complete.In this common scenario (see Figure 6.8), the request service providerhas time to process the client’s request.
When the service provider isClientRequest interfaceRequest issued to asynch.service interfaceRequest providerRequest()Start asynchronous processing ofclient requestTRequestStatus set toKRequestPendingTime passes forclient...Processing of client'srequest...Client decides to cancel originalasynchronous operationCancelRequest()Cancel (early completion) directivereceived by request providerClient's request cancelled!Client's original request completed withKErrCancel and thread signalledRequest is nowcancelled!Figure 6.9Early request cancellation180ACTIVE OBJECTSready, it completes the client’s request with a completion code, typicallyKErrNone.
This also signals the client’s thread that an event occurred.Canceling a Request EarlyThe request is cancelled before the service provider has finished processing the original request.The request provider receives the cancellation request whilst it is stillcarrying out the task. It must immediately complete the client’s requeststatus with a suitable value (usually KErrCancel).
Figure 6.9 illustratesthe standard cancellation of an asynchronous request.Canceling a Request LateAn attempt is made to cancel the request, but the request completesnormally before the service provider processes the cancellation.In Figure 6.10, the client asked to cancel the request, but the requesthad already been completed normally by the service provider. In thiscase, the service provider must ignore the cancellation request. WhenCActive::Cancel() calls User::WaitForRequest(iStatus),ClientRequest interfaceRequest issued to asynch.service interfaceRequest providerRequest()Start asynchronous processing ofclient requestTRequestStatus set toKRequestPendingTime passes forclient...Processing of client'srequest...Processing complete!Request is nowcompleted!Client request is completed withKErrNone and thread is signalledClient attempts to cancel originalasynchronous operationCancelRequest()Cancel (early completion) directivereceived by request providerNothing to service provider todo as request was alreadycompletedFigure 6.10Late request cancellationSTARTING AND STOPPING THE SCHEDULER181its iStatus value is already something other than KRequestPendingand the thread’s semaphore count is already more than zero as a result ofthe normal completion, hence this call returns immediately.When implementing a Symbian OS server, be sure that it can copewith late cancellation.Canceling a Request when the Service Provider TerminatesUnexpectedlyIt is entirely normal to use the client–server framework to make asynchronous requests from client to server.
If the server unexpectedlyterminates whilst a client’s request is pending, the kernel ensures that theclient’s request is completed with the error code KErrServerTerminated. In this way, the client is not left waiting for a request to completeeven though the server has died.Canceling a Request when Asynchronous Server Resourcesare ExhaustedWhen using the client–server framework, each server may support a fixednumber of simultaneous asynchronous requests per connection. If thenumber of asynchronous requests for a session have been used by otherasynchronous operations, then the kernel cannot set up the asynchronousrequest and completes the request status with KErrServerBusy.6.7 Starting and Stopping the SchedulerApplication programmers rarely have to call the Start() and Stop()functions of the active scheduler.