Issott_Common Design Patterns for Symbian OS-The Foundations of Smartphone Software_0470516356 (779879), страница 68
Текст из файла (страница 68)
However, to be sure that the singleton has beencreated, each thread must call the CSingleton::CreateL() methodonce, unless the thread is guaranteed to start after one or more otherthreads have already called CSingleton::CreateL().362MAPPING WELL-KNOWN PATTERNS ONTO SYMBIAN OSPositive Consequences• Prevents race conditions and provides a leave-safe solution for usewhere WSD is available and multiple threads must be able to accessa singleton.• Use of double-checked locking optimizes performance by locking themutex on singleton creation only and not on every access.Negative Consequences• 4 KB of RAM is used for the WSD in the one process sharing thesingleton.• There is additional complexity of implementation and use compared to Implementation A.
If it’s possible to ensure access tothe singleton by a single thread only, Implementation A is preferable.• Safe use of DCLP cannot currently be guaranteed. There is no wayto prevent a compiler from reordering the code in such a waythat the singleton pointer becomes globally visible before all thesingleton is fully constructed. In effect, one thread could access asingleton that is only partially constructed.
This is a particular hazardon multiprocessor systems featuring a ‘relaxed’ memory model tocommit writes to main memory in bursts in order of address, ratherthan sequentially in the order they occur.24 Symmetric multiprocessingis likely to be found in future versions of Symbian OS. However, itis also a problem on single processor systems. The ‘volatile’ keywordcan potentially be used to write a safe DCLP implementation butcareful examination of the compiler documentation and a detailedunderstanding of the ISO C++ specification is required. Even then theresulting code is compiler-dependent and non-portable.• The example given is insecure because it uses a globally accessiblemutex.
Global objects are inherently prone to security and robustnessissues since a thread running in any process in the system can openthem by name and, in this case, block legitimate threads attempting to create the singleton by calling Wait() without ever callingSignal().• You cannot share a singleton across multiple threads executing indifferent processes. This is because each process has its own address24 [Alexandrescu, 2001] suggests that you should perform triple-checked locking by firstchecking your compiler documentation before going on to implement the DCLP! If youplan to use the DCLP, you should certainly consult [Myers and Alexandrescu, 2004] fora detailed discussion of the potential hazards.
The final section of that paper suggestsalternative ways to add thread-safety to Singleton that avoid the use of the DCLP.SINGLETON363space and a thread running in one process cannot access the addressspace of another process to get at the singleton object.Implementation D: Thread-Managed Singleton within a ProcessConstraints on the Singleton• Must be used within a single process but may be used across multiplethreads.• May be used within an EXE or a DLL on any version of Symbian OS.• WSD may not be used.DetailsAs its name suggests, the storage word used by TLS is local to a threadand each thread in a process has its own storage location. Where TLS isused to access a singleton in a multi-threaded environment, the pointerthat references the location of the singleton must be passed to eachthread.
This is not a problem since all threads are in the same processand they can all access the memory address. This can be done usingthe appropriate parameter of RThread::Create(). If this is not done,when the new thread calls Dll::Tls(), it receives a NULL pointer.This implementation does not need to use DCLP because, for thethreads to share the singleton, its instantiation must be controlled and thelocation of an existing singleton passed to threads as they are created.The main thread creates the singleton before the other threads exist,thus the code is single-threaded at the point of instantiation and doesnot need to be thread-safe.
Once the singleton is available, its locationmay be passed to other threads, which can then access it. Thus thisimplementation is described as ‘thread managed’ because the singletoncannot be lazily allocated, but must be managed by the parent thread.Let’s look at some code to clarify how this works. Firstly, the CSingleton class exports two additional methods that are used by callingcode to retrieve the singleton instance pointer from the main thread, SingletonPtr(), and set it in the created thread, InitSingleton().These methods are added so that code in a process that loads and usesthe singleton-providing DLL can initialize and use the singleton withoutneeding to be aware of how the singleton class is implemented.class CSingleton : public CBase{public:// To create the singleton instanceIMPORT_C static void CreateL();// To access the singleton instance364MAPPING WELL-KNOWN PATTERNS ONTO SYMBIAN OSIMPORT_C static CSingleton& Instance();// To get the location of the singleton so that it can be passed to a// new threadIMPORT_C static TAny* SingletonPtr();// To initialize access to the singleton in a new threadIMPORT_C static TInt InitSingleton(TAny* aLocation);private:CSingleton();∼CSingleton();void ConstructL();};EXPORT_C /*static*/ CSingleton& CSingleton::Instance(){CSingleton* singleton = static_cast<CSingleton*>(Dll::Tls());return (*singleton);}EXPORT_C /*static*/ void CSingleton::CreateL(){ASSERT(!Dll::Tls());if (!Dll::Tls()) // No singleton instance exists yet so create one{CSingleton* singleton = new(ELeave) CSingleton();CleanupStack::PushL(singleton);singleton->ConstructL();User::LeaveIfError(Dll::SetTls(singleton));CleanupStack::Pop(singleton);}}EXPORT_C TAny* CSingleton::SingletonPtr(){return (Dll::Tls());}EXPORT_C TInt CSingleton::InitSingleton(TAny* aLocation){return (Dll::SetTls(aLocation));}The code for the main thread of a process creates the singleton instanceand, as an illustration, creates a secondary thread, in much the same wayas we’ve seen in previous snippets:// Main (parent) thread must create the singletonCSingleton::CreateL();//Create a secondary threadRThread thread1;User::LeaveIfError(thread1.Create(_L("Thread1"), Thread1EntryPoint,KDefaultStackSize, KMinHeapSize,KMaxHeapSize,CSingleton::SingletonPtr()));CleanupClosePushL(thread1);// Resume thread1, etc...SINGLETON365Note that the thread creation function now takes the return value ofCSingleton::SingletonPtr() as a parameter value (the value isthe return value of Dll::Tls() – giving access to the TLS data of themain thread).
The parameter value must then be passed to CSingleton::InitSingleton() in the secondary thread:TInt Thread1EntryPoint(TAny* aParam){TInt err = CSingleton::InitSingleton(aParam);if (err == KErrNone ){ // This thread can now access the singleton in the parent thread...}return err;}Positive Consequences• Singleton can be implemented in a DLL and accessed by multiplethreads in the process into which the DLL is loaded.Negative Consequences• Accidental complexity of implementation and use. For example, codethat executes in a secondary thread must initialize its own TLS databy explicitly acquiring and setting the location of the singleton in theparent thread.• The main thread must control the creation of every secondarythread.• This solution works for multiple threads executing in a single process,but cannot be used to share a singleton across multiple threadsexecuting in different processes. This is because each process has itsown address space and a thread running in one process cannot accessthe address space of another process.Implementation E: System-wide SingletonConstraints on the Singleton• Can be used anywhere in the system across multiple processes orthreads.• May be used within an EXE or a DLL on any version of Symbian OS.• WSD may not be used.366MAPPING WELL-KNOWN PATTERNS ONTO SYMBIAN OSDetailsAs described in the consequences sections of Implementations C andD, those implementations allow a singleton to be accessed by multiplethreads within a single Symbian OS process, but not by multiple processes.The singleton implementation is only a single instance within a process.If several processes load a DLL which provides Implementation D, eachprocess would have its own copy of a singleton to preserve an importantquality of processes – that their memory is entirely isolated from any otherprocess.Where a system-wide singleton is required on Symbian OS, oneprocess must be assigned ownership of the singleton object itself.
Eachtime another process, or client, wishes to use the singleton it has to makean IPC request to get a copy of the singleton. To enable this, the providerof the singleton should make a client-side DLL available that exposes thesingleton interface to these other processes but is implemented to use IPCtransparently to obtain the copy of the actual singleton.Having hidden the IPC details from clients behind what is effectivelya Proxy [Gamma et al., 1994] based on the singleton interface, you havea choice of which IPC mechanism to use to transmit the copy of thesingleton data. Here are some mechanisms provided by Symbian OS thatyou can use to perform thread-safe IPC without the need for WSD or theDCLP:• Client–Server (see page 182)This has been the most commonly used mechanism to implement thispattern as it has existed in Symbian OS for many versions now.