Symbian OS Explained - Effective C++ Programming For Smartphones (2005) (779885), страница 49
Текст из файла (страница 49)
If writable globaldata is used inadvertently, it returns an error at build time for ARM targets,emitting a message from the PETRAN tool similar to the following:ERROR: Dll 'TASKMANAGER[1000C001].DLL' has uninitialised dataThe only global data you can use with DLLs is constant global data ofthe built-in types, or of a class with no constructor. Thus while you mayhave constant global data such as this in a DLL:static const TUid KUidClangerDll = { 0x1000C001 };static const TInt KMinimumPasswordLength = 6;You cannot use these:static const TPoint KGlobalStartingPoint(50, 50);// This literal type is deprecated (see Chapter 5)static const TPtrC KDefaultInput =_L("");static const TChar KExclamation('!');The reason for this is the presence of a non-trivial class constructor,which requires the objects to be constructed at runtime.
This means that,although the memory for the object is pre-allocated in code, it doesn’tactually become initialized and const until after the constructor has run.Thus, at build time, each constitutes a non-constant global object andcauses the DLL build to fail for target hardware.Note that the following object is also non-constant because, althoughthe data pointed to by pClanger is constant, the pointer itself isnot constant:// Writable static data!static const TText* pClanger = (const TText*)"clanger";This can be corrected as follows:// pClanger is constantstatic const TText* const pClanger = (const TText*)"clanger";THREAD-LOCAL STORAGE223Incidentally, the issue of not allowing non-constant global data inDLLs highlights another difference between the behavior of Windowsemulator builds and builds for target hardware.
The emulator can useunderlying Windows DLL mechanisms to provide per-process DLL data.If you do inadvertently use non-constant global data in your code, it willgo undetected on emulator builds and will only fail when building fortarget hardware.Symbian OS DLLs must not contain writable global or static data.The only global data which may be used are constants, either of thebuilt-in types or of classes with no constructor.13.4 Thread-Local StorageAs I mentioned, the lack of writable global data in DLLs can be difficultwhen you are porting code to run on Symbian OS. However, the operatingsystem does provide a mechanism whereby a DLL can manage writablestatic data on a per-thread basis using thread-local storage, commonlyknown as ”TLS”.
This allocates a single machine word of writable staticdata per thread for every DLL, regardless of whether the DLL uses it.Obviously, the memory overhead is far less significant than allocatinga 4 KB chunk for each DLL which uses static data.
However, the priceof using TLS instead of direct memory access is performance; data isretrieved from TLS about 30 times slower than direct access, because thelookup involves a context switch to the kernel in order to access the data.The use of TLS for per-thread access to global static data is safe becauseit avoids complications when the DLL is loaded into multiple processes.However, for writable static data to be used by multiple threads, thisapproach must be extended. One technique uses a server to store thedata, which has the benefit of being able to use static data without theneed for TLS, because it is a process.
The server can make this dataavailable to its clients, which may run in multiple threads.3 Of course,the inter-process context switch required to access the server also hasperformance implications, as I discuss in Chapter 11.The TLS slot can be used directly if you have only one machine wordof data to store. For extensibility, it is more likely that you’ll use it to storea pointer to a struct or simple T Class which encapsulates all the datayou would otherwise have declared as static.Thread-local storage is usually initialized when the DLL is attachedto a thread within the DLL entry point, E32Dll(). Typically, code is3You can find an example of this technique in the EpocStat product released by Peroon(www.peroon.com/Downloads.html), for which full source code is available for download.224BINARY TYPESadded to construct the struct containing the global data and store it inthread-local storage using the static function Dll::SetTLS().
(Whenthe DLL is detached from the thread, the TLS slot should be reset and thestatic data deleted.) To access the data, you should use the static functionDll::Tls(). This will return a TAny* which can be cast and used toaccess the data. For simplicity, you may wish to provide a utility function,or set of functions, to access the data from a single point.Here’s some example code to illustrate the use of thread-local storagewhen implementing a task manager which runs as a single instance.
Thecode is a modification of the previous version above, and can now beused within a DLL:// TaskManager.hclass CTask; // Defined elsewhereclass CTaskManager : public CBase{public:IMPORT_C static CTaskManager* New();∼CTaskManager();public:IMPORT_C void AddTaskL(CTask* aTask);// ... omitted for clarityprivate:CTaskManager();void ConstructL();private:CTaskManager(const CTaskManager&);// Not implementedCTaskManager& operator =(const CTaskManager&); // prevents copyingprivate:RPointerArray<CTask>* iTasks; // Single instance};// Accesses the task manager transparently through TLSinline CTaskManager* GetTaskManager(){ return (static_cast<CTaskManager*>(Dll::Tls())); }// TaskManager.cppGLDEF_C TInt E32Dll(TDllReason aReason){TInt r =KErrNone;CTaskManager* taskManager = NULL;switch (aReason){#ifdef __WINS__//On Windows, DLL attaches to the current process, not a threadcase EDllProcessAttach:#elsecase EDllThreadAttach:#endif// Initialize TLStaskManager = CTaskManager::New();if (taskManager){Dll::SetTls(taskManager);THREAD-LOCAL STORAGE225}break;#ifdef __WINS__case EDllProcessDetach:#elsecase EDllThreadDetach:#endif// Release TLStaskManager = static_cast<CTaskManager*>(Dll::Tls());if (taskManager){delete taskManager;Dll::SetTls(NULL);}break;default:break;}return(r);}// Non-leaving because it is called by E32Dll() which cannot leaveEXPORT_C CTaskManager* CTaskManager::New(){CTaskManager* me = new CTaskManager();if (me){me->iTasks = new RPointerArray<CTask>(4);if (!me->iTasks){delete me;me = NULL;}}return (me);}If you look at the documentation for class DLL in your SDK, you mayfind that it directs you to link against EUser.lib to use the Tls()and SetTls() functions.
You’ll find this works for emulator builds, butfails to link for ARM. This is because the methods are not implementedin EUser.dll – you should now link against edllstub.lib, beforelinking to EUser.lib.Thread-local storage (TLS) can be used to work around the prohibition of writable global data. However, the use of TLS affectsperformance; data is retrieved from TLS about 30 times slowerthan direct access because the lookup involves a context switch tothe kernel.22613.5BINARY TYPESThe DLL LoaderAn interesting scenario can arise when you attempt to replace a DLLwith a version you have rebuilt, perhaps a debug version or one thatincludes tracing statements to help you track down a problem.
You mayfind that it is not ”picked up” by the loader, which continues to run theoriginal version.As I described earlier, a DLL running from RAM is only loaded once,even if multiple processes use it. The DLL loader uses reference counting,and only unloads the DLL when none of its clients are running. So, if theDLL is loaded by other processes, it will not be unloaded and cannot bereplaced by a newer version until the original is unloaded. This is alsorelevant if you are attempting to replace a ROM-based4 DLL. If a ROMversion of the DLL you wish to replace is used before your componentloads, your component will also end up using the ROM-based version. Ineffect, this means that you can never replace a DLL which is used by theapplication launcher shell.The DLL is loaded by name lookup (with additional UID checkingwhich I’ll describe shortly).
If a DLL is not already loaded, the DLL loaderuses a particular lookup order to find it, as follows:1.The same directory as the process wishing to load the DLL (oftenc:\system\libs).2.The directory associated with the filesystem’s default session path(which is usually the root of the C: drive).3.The \system\libs directories on all available drives, in the following order: C: (the internal storage drive), A:, B:, D:, E:, ..., Y:and finally, Z: (the ROM).If you wish to replace a DLL, you should ensure that you put thereplacement where it will be loaded before the original version.13.6UIDsA UID is a signed 32-bit value which is used as a globally uniqueidentifier.
Symbian OS uses a combination of up to three UIDs to create aTUidType compound identifier. UIDs are used extensively, for example,to identify the type of a component and to verify that it is compatible andsupports a particular interface. Thus, a DLL can register a type to reflectthe interface it is implementing. The DLL loader can check the type of4In case you were wondering, you can never replace a DLL used by a component onROM, because the ROM binaries are linked together when the ROM is built. However, youcan replace a ROM DLL if the component that is using it isn’t running from ROM.UIDs227a DLL (using RLibrary::Type()) to determine whether a componentis of the correct type, and prevent other files which may share the samename from being loaded.The three UIDs are identified as UID1, UID2 and UID3 and aregenerally used as follows:• UID1 is a system-level identifier to distinguish between EXEs(KExecutableImageUid = 0x1000007a) and DLLs (KDynamicLibraryUid = 0x10000079)• UID2 distinguishes between components having the same UID1,for example between shared libraries (KSharedLibraryUid =0x1000008d) or polymorphic DLLs such as applications (KUidApp= 0x100039CE), recognizers (0x10003A19) or front-end processors(0x10005e32)• UID3 identifies a component uniquely.