Symbian OS Explained - Effective C++ Programming For Smartphones (2005) (779885), страница 36
Текст из файла (страница 36)
Additionally, DoAsyncTask() creates the thread in which the synchronousfunction will run, calls Logon() upon the thread to receive notificationwhen it terminates, then sets itself active before resuming the thread.When the thread terminates, the Logon() request completes (theiStatus of the active object receives the exit reason). The activescheduler calls RunL() on the active object which, in turn, notifies thecaller that the function call has completed by calling User::RequestComplete() on iCaller, the stored TRequestStatus object.The class implements the RunError() and DoCancel() functionsof the CActive base class.
In DoCancel(), the code checks whether thethread is still running because a case could arise where the long-runningfunction has completed and the thread has ended, but the resulting eventnotification has not yet been handled by the active scheduler. If thethread is outstanding, DoCancel() calls Kill() on it, closes the threadhandle and completes the caller with KErrCancel.CAsyncTask::CAsyncTask(): CActive(EPriorityStandard) // Standard priority unless good reason{// Add to the active schedulerCActiveScheduler::Add(this);}160SYMBIAN OS THREADS AND PROCESSES// Two-phase construction code omitted for clarityCAsyncTask::∼CAsyncTask(){// Cancel any outstanding request before cleanupCancel(); // Calls DoCancel()// The following is called by DoCancel() or RunL()// so is unnecessary here too// iThread.Close(); // Closes the handle on the thread}void CAsyncTask::DoAsyncTask(TRequestStatus& aStatus){if (IsActive()){TRequestStatus* status = &aStatus;User::RequestComplete(status, KErrAlreadyExists);return;}// Save the caller’s TRequestStatus to notify them lateriCaller = &aStatus;// Create a new thread, passing the thread function and stack sizes// No extra parameters are required in this example, so pass in NULLTInt res = iThread.Create(KThreadName, ThreadEntryPoint,KDefaultStackSize, NULL, NULL);if (KErrNone!=res){// Complete the caller immediatelyUser::RequestComplete(iCaller, res);}else{// Set active; resume new thread to make the synchronous call// (Change the priority of the thread here if required)// Set the caller and ourselves to KRequestPending// so the active scheduler notifies on completion*iCaller = KRequestPending;iStatus = KRequestPending;SetActive();iThread.Logon(iStatus); // Request notification when thread diesiThread.Resume();// Start the thread}}TInt CAsyncTask::ThreadEntryPoint(TAny* /*aParameters*/){// Perform a long synchronous task e.g.
a lengthy calculationTInt res = SynchronousTask();// Task is complete so end this thread with returned error codeRThread().Kill(res);return (KErrNone);// This value is discarded}void CAsyncTask::DoCancel(){// Kill the thread and complete with KErrCancel// ONLY if it is still runningTExitType threadExitType = iThread.ExitType();if (EExitPending==threadExitType)STOPPING A RUNNING THREAD161{// Thread is still runningiThread.LogonCancel();iThread.Kill(KErrCancel);iThread.Close();// Complete the callerUser::RequestComplete(iCaller, KErrCancel);}}void CAsyncTask::RunL(){// Check in case thread is still running e.g. if Logon() failedTExitType threadExitType = iThread.ExitType();if (EExitPending==threadExitType) // Thread is still running, kill itiThread.Kill(KErrNone);// Complete the caller, passing iStatus value to RThread::Kill()User::RequestComplete(iCaller, iStatus.Int());iThread.Close(); // Close the thread handle, no need to LogonCancel()}TInt CAsyncTask::RunError(TInt anError){if (iCaller){User::RequestComplete(iCaller, anError);}return (KErrNone);}For more sophisticated thread death notification, you can alternativelyuse the RUndertaker thread-death notifier class, which is described indetail in the SDK.
By creating an RUndertaker, you receive notificationwhen any thread is about to die. The RUndertaker passes back anotification for each thread death, including the exit reason and theTThreadId. It creates a thread-relative handle to the dying thread,which effectively keeps it open. This means that you must close thethread handle to release the thread finally into the abyss – hence thename of the class. This notification is useful if you want to track thedeath of every thread in the system, but if you’re interested in thedeath of one specific thread, it’s easier to use RThread::Logon(), asdescribed above.It is possible to receive notification when a thread dies by making acall to RThread::Logon() on a valid thread handle.
The mannerand reason for thread termination can also be determined fromthe RThread handle of an expired thread by calling ExitType(),ExitReason() and ExitCategory().16210.4SYMBIAN OS THREADS AND PROCESSESInter-Thread Data TransferOn Symbian OS, you can’t transfer data pointers directly between threadsrunning in separate processes, because process address spaces are protected from each other, as I described in Chapter 8.5On EKA1 versions of Symbian OS, RThread provides a set of functionsthat enable inter-thread data transfer regardless of whether the threadsare in the same or different processes:IMPORT_C TInt GetDesLength(const TAny* aPtr) const;IMPORT_C TInt GetDesMaxLength(const TAny* aPtr) const;IMPORT_C void ReadL(const TAny* aPtr,TDes8& aDes,TInt anOffset) const;IMPORT_C void ReadL(const TAny* aPtr,TDes16 &aDes,TInt anOffset) const;IMPORT_C void WriteL(const TAny* aPtr,const TDesC8& aDes,TInt anOffset)const;IMPORT_C void WriteL(const TAny* aPtr,const TDesC16& aDes,TInt anOffset)const;On EKA2 these methods have been withdrawn from class RThread,but are implemented by RMessagePtr, which can be used by a serverto transfer data to and from a client thread.
The functions take slightlydifferent parameters but perform a similar role. I’ll discuss inter-threaddata transfer here using the RThread API, which is applicable to EKA1releases. In EKA2 the RMessagePtr API is more intuitive, using aparameter value rather than TAny* to identify the source or targetdescriptor in the ”other” thread. You should consult an appropriate v8.0SDK for complete documentation.On EKA1, RThread::WriteL() can be used to transfer data from thecurrently running thread to the thread represented by a valid RThreadhandle.
RThread::WriteL() takes a descriptor (8- or 16-bit) containing the data in the current thread and writes it to the thread on whosehandle the function is called. The destination of the data in that otherthread is also a descriptor, which should be modifiable (derived fromTDes). It is identified as a const TAny pointer into the other thread’saddress space. The function leaves with KErrBadDescriptor if theTAny pointer does not appear to point to a valid descriptor.
The maximum length of the target descriptor can be determined before writing bya call to RThread::GetDesMaxLength().5This is true for EKA2 and previous releases of Symbian OS running on target hardware.However, the Symbian OS Windows emulator for releases prior to EKA2 does not protectthe address spaces of Symbian OS processes. The threads share writable memory, whichmeans that each emulated process can be accessed by other processes. Code which usesdirect pointer access to transfer data between threads in different processes will appear towork on the Windows emulator, then fail spectacularly when deployed on a real SymbianOS handset.EXCEPTION HANDLING163RThread::ReadL() can be used to transfer data into the currentlyrunning thread from the thread represented by a valid RThread handle.ReadL() transfers data from a descriptor in the ”other” thread, identifiedas a const TAny pointer, into a modifiable descriptor (8- or 16-bit) inthe current thread.
The function leaves with KErrBadDescriptor ifthe TAny pointer does not point to what appears to be a valid descriptor.The length of the target descriptor can be determined before reading fromit by a call to RThread::GetDesLength().Inter-thread data transfer uses descriptors because they fully describetheir length and maximum allowable length, which means that no extraparameters need to be transferred to indicate this information. If you wantto transfer non-descriptor data, you can use the package classes TPckgCand TPckg, described in Chapter 6, which effectively ”descriptorize” thedata. Package classes are only valid for transferring a flat data structuresuch as that found in a T class object; their use in inter-thread data transferbetween client and server threads is discussed in Chapter 12.10.5 Exception HandlingOn EKA1, the RThread API supports thread exception management.
Onthe more secure EKA2 platform, this has moved into class User andapplies only to the current thread. The signatures of the five functions arethe same, however, regardless of the class that implements them.TExceptionHandler* ExceptionHandler() const;TInt SetExceptionHandler(TExceptionHandler* aHandler, TUint32 aMask);void ModifyExceptionMask(TUint32 aClearMask, TUint32 aSetMask);TInt RaiseException(TExcType aType);TBool IsExceptionHandled(TExcType aType);SetExceptionHandler() allows you to define an exception handler function for the thread for which the handle is valid. It takes anexception-handling function and a bitmask that allows you to specify thecategory of exception which will be handled (see e32std.h).constconstconstconstconstconstconstTUintTUintTUintTUintTUintTUintTUintKExceptionAbort=0x01;KExceptionKill=0x02;KExceptionUserInterrupt=0x04;KExceptionFpe=0x08;KExceptionFault=0x10;KExceptionInteger=0x20;KExceptionDebug=0x40;If an exception is raised on the thread that falls into the category handled, it will be passed to the specified exception handler to be dealt with.164SYMBIAN OS THREADS AND PROCESSESThe exception handler function, TExceptionHandler, is a typedeffor a function which takes a value of TExcType (an enumeration whichfurther identifies the type of exception) and returns void.