Symbian OS Explained - Effective C++ Programming For Smartphones (2005) (779885), страница 34
Текст из файла (страница 34)
This means you should guard against:• writing lengthy RunL() or DoCancel() methods• repeatedly resubmitting requests• assigning your active objects a higher priority than is necessary.Stray signals can arise if:9.15•the active object is not added to the active scheduler•SetActive() is not called following the submission of arequest•the active object is completed more than once for any givenrequest.SummaryWhile the previous chapter gave a high-level overview of active objectson Symbian OS, this chapter focused on active objects in detail, walkingthrough the roles, responsibilities and interactions between the activescheduler, active objects and the asynchronous service providers theyencapsulate.It contained the detail necessary to write good active object or asynchronous service provider code and to extend the active scheduler.Example code illustrated the use of active objects for state machines andfor implementing background step-wise tasks, either using active objectsdirectly or through the CIdle wrapper class.At this level of detail, active objects can seem quite complex andthis chapter should mostly be used for reference until you start to workdirectly with complex active object code.10Symbian OS Threads and ProcessesDon’t disturb my circles!Said to be the last words of Archimedes who was drawing geometricfigures in the dust and became so absorbed that he snapped at a Romansoldier.
The soldier became enraged, pulled out his sword and killed himChapters 8 and 9 discussed the role of active objects in multitasking codeon Symbian OS. Active objects are preferred to threads for this rolebecause they were designed specifically to suit the resource-limited hardware upon which Symbian OS runs.
Multithreaded code has significantlyhigher run-time requirements compared to active objects: for example,a context switch between threads may be an order of magnitude slowerthan a similar switch between active objects running in the same thread.1Threads tend to have a larger size overhead too, typically requiring 4 KBkernel-side and 8 KB user-side for the program stack, compared to activeobjects, which generally occupy only the size of the C++ object (oftenless than 1 KB).One of the main differences between multitasking with threads andactive objects is the way in which they are scheduled.
Active objectsmultitask cooperatively within a thread and, once handling an event,an active object cannot be pre-empted by the active scheduler inwhich it runs. Threads on Symbian OS are scheduled pre-emptivelyby the kernel.Pre-emptive scheduling of threads means that data shared by threadsmust be protected with access synchronization primitives such as mutexesor semaphores. However, despite this additional complexity, pre-emptivescheduling is sometimes necessary. Consider the case of two activeobjects, one assigned a high priority because it handles user-input events1A context switch between threads running in the same process requires the processorregisters of the running thread to be stored, and the state of the thread replacing it to berestored. If a reschedule occurs between threads running in two separate processes, theaddress space accessible to the thread, the process context, must additionally be stored andrestored.152SYMBIAN OS THREADS AND PROCESSESand another with a lower priority, which performs increments of a longrunning task when no other active object’s event handler is runningon the thread.
If the event handler of the lower-priority active objecthappens to be running when an event for the higher-priority active objectcompletes, it will continue to run to completion. No pre-emption willoccur and the low-priority active object effectively ”holds up” the handlerof the high-priority object. This can make the user interface sluggish andunresponsive.Typically, on Symbian OS, problems of this nature are avoided bycareful analysis of each active object’s event handler, to ensure that eventprocessing is kept as short as possible (described in Chapters 8 and 9).It would generally not be regarded as reason enough to multithread thecode.
However, there are occasions where the use of several threadsmay be necessary, say to perform a task which cannot be split intoshort-running increments. By implementing it in a separate thread, it canrun asynchronously without impacting an application’s response to userinterface events.On Symbian OS, active objects multitask cooperatively within athread and cannot be pre-empted by the active scheduler; threadsare scheduled pre-emptively by the kernel.10.1Class RThreadOn Symbian OS, the class used to manipulate threads is RThread (you’llnotice that it’s an R class, the characteristics of which are described inChapter 1). An object of type RThread represents a handle to a thread,because the thread itself is a kernel object.
An RThread object can beused to create or refer to another thread in order to manipulate it (e.g.suspend, resume, panic or kill it) and to transfer data to or from it.The RThread class has been modified quite significantly as part ofthe changes made for the new hard real-time kernel delivered in releasesof Symbian OS v8.0. I’ll identify the main differences as I come tothem. Most notably, a number of the functions are now restricted foruse on threads in the current process only, whereas previous versions ofSymbian OS allowed one thread to manipulate any other thread in thesystem, even those in other processes.
The changes to Symbian OS v8.0have been introduced to protect threads against abuse from potentiallymalicious code.At the time of going to press, Symbian identifies the hard real-timekernel as ”EKA2” – which is a historical reference standing for ”EPOC22Symbian OS was previously known as EPOC, and earlier still, EPOC32.CLASS RThread153Kernel Architecture 2” – and refers to the kernel of previous releasesas EKA1. Throughout this chapter, and the rest of the book, I’ll usethis nomenclature to distinguish between versions of Symbian OS v8.0containing the new kernel and previous versions when discussing anyAPI differences.On Symbian OS v7.0, class RThread is defined as follows ine32std.h.
I’ve included the entire class definition because I’ll mentionmany of its methods throughout this chapter.class RThread : public RHandleBase{public:inline RThread();IMPORT_C TInt Create(const TDesC& aName, TThreadFunction aFunction,TInt aStackSize,TAny* aPtr,RLibrary* aLibrary,RHeap* aHeap,TInt aHeapMinSize,TInt aHeapMaxSize,TOwnerType aType);IMPORT_C TInt Create(const TDesC& aName, TThreadFunction aFunction,TInt aStackSize,TInt aHeapMinSize,TInt aHeapMaxSize,TAny* aPtr,TOwnerType aType=EOwnerProcess);IMPORT_C TInt Create(const TDesC& aName,TThreadFunction aFunction,TInt aStackSize,RHeap* aHeap,TAny* aPtr,TOwnerType aType=EOwnerProcess);IMPORT_C TInt SetInitialParameter(TAny* aPtr);IMPORT_C TInt Open(const TDesC& aFullName,TOwnerType aType=EOwnerProcess);IMPORT_C TInt Open(TThreadId aID,TOwnerType aType=EOwnerProcess);IMPORT_C TThreadId Id() const;IMPORT_C void Resume() const;IMPORT_C void Suspend() const;IMPORT_C TInt Rename(const TDesC& aName) const;IMPORT_C void Kill(TInt aReason);IMPORT_C void Terminate(TInt aReason);IMPORT_C void Panic(const TDesC& aCategory,TInt aReason);IMPORT_C TInt Process(RProcess& aProcess) const;IMPORT_C TThreadPriority Priority() const;IMPORT_C void SetPriority(TThreadPriority aPriority) const;IMPORT_C TProcessPriority ProcessPriority() const;IMPORT_C void SetProcessPriority(TProcessPriority aPriority) const;IMPORT_C TBool System() const;IMPORT_C void SetSystem(TBool aState) const;IMPORT_C TBool Protected() const;IMPORT_C void SetProtected(TBool aState) const;IMPORT_C TInt RequestCount() const;IMPORT_C TExitType ExitType() const;IMPORT_C TInt ExitReason() const;IMPORT_C TExitCategoryName ExitCategory() const;IMPORT_C void RequestComplete(TRequestStatus*& aStatus,TInt aReason) const;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;154SYMBIAN OS THREADS AND PROCESSESIMPORT_C void WriteL(const TAny* aPtr,const TDesC16& aDes,TInt anOffset) const;IMPORT_C void Logon(TRequestStatus& aStatus) const;IMPORT_C TInt LogonCancel(TRequestStatus& aStatus) const;IMPORT_C RHeap* Heap();IMPORT_C void HandleCount(TInt& aProcessHandleCount,TInt& aThreadHandleCount) const;IMPORT_C TExceptionHandler* ExceptionHandler() const;IMPORT_C TInt SetExceptionHandler(TExceptionHandler* aHandler,TUint32 aMask);IMPORT_C void ModifyExceptionMask(TUint32 aClearMask,TUint32 aSetMask);IMPORT_C TInt RaiseException(TExcType aType);IMPORT_C TBool IsExceptionHandled(TExcType aType);IMPORT_C void Context(TDes8& aDes) const;IMPORT_C TInt GetRamSizes(TInt& aHeapSize,TInt& aStackSize);IMPORT_C TInt GetCpuTime(TTimeIntervalMicroSeconds& aCpuTime)const;inline TInt Open(const TFindThread& aFind,TOwnerType aType=EOwnerProcess);};The base class of RThread is RHandleBase, which encapsulatesthe behavior of a generic handle and is used as a base class throughoutSymbian OS to identify a handle to another object, often a kernel object.As Chapter 1 discussed, CBase must always be the base class of a C class(if only indirectly), but RHandleBase is not necessarily always the baseof an R class, although you’ll find that a number of Symbian OS R classesdo derive from it (e.g.
RThread, RProcess, RMutex and RSessionBase). Neither does RHandleBase share the characteristics of classCBase (such as a virtual destructor and zero-initialization through anoverloaded operator new). Instead, class RHandleBase encapsulatesa 32-bit handle to the object its derived class represents and exports alimited number of public API methods to manipulate that handle, namelyHandle(), SetHandle(), Duplicate() and Close().You can use the default constructor of RThread to acquire a handleto the thread your code is currently running in, which can be usedas follows:RHeap* myHeap = RThread().Heap(); // Handle to the current thread's heap// ...or..._LIT(KClangerPanic, "ClangerThread");// Panic the current thread with KErrNotFoundRThread().Panic(KClangerPanic, KErrNotFound);The handle created by the default constructor is actually a pseudohandle set to the constant KCurrentThreadHandle, which is treatedspecially by the kernel.
If you want a ”proper” handle to the current threadTHREAD PRIORITIES155(that is, a handle that is in the thread handle list) to pass between threadsin a process, you need to duplicate it using RThread::Duplicate():RThread properhandle.Duplicate(RThread());You can acquire a handle to a different thread either by creating it or byopening a handle on a thread which currently exists in the system. As youcan see from its definition, class RThread defines several functions forthread creation. Each function takes a descriptor representing a uniquename for the new thread, a pointer to a function in which executionstarts, a pointer to data to be passed to that function and a value for thestack size of the thread, which defaults to 8 KB. The Create() functionis overloaded to allow you to set various options associated with thethread’s heap, such as its maximum and minimum size and whetherit shares the creating thread’s heap or uses a specific heap.