Symbian OS Explained - Effective C++ Programming For Smartphones (2005) (779885), страница 45
Текст из файла (страница 45)
This means that the client thread can terminate but the server threadwill continue to run, allowing other client threads to access the serverwithout it dying unexpectedly.On a real Symbian OS phone, the server is launched as a process,the code for which is significantly more straightforward than for emulatorthread startup. Once the server thread or process has started, the clientthread calls Logon() in case a failure occurs before server initializationfunction is complete. The Logon() call ensures that the client thread isnotified if the server dies before the server startup TRequestStatus issignaled.
This prevents the client thread from hanging indefinitely if serverinitialization fails. Once the server entry point signals the client and setsthe value of start to something other than KRequestPending, theLogon() notification is canceled. I’ll discuss how the server-side codemanages initialization later in this chapter.The client thread passes a TRequestStatus to the server for notification when its initialization is complete.
A TServerStart object(”starter”) is used to transfer the client’s TRequestStatus object(”start”) to the server. The TServerStart class is defined and implemented as follows:class TServerStart{public:TServerStart() {};TServerStart(TRequestStatus& aStatus);TPtrC AsCommand() const;TInt GetCommand();void SignalL();private:TThreadId iId;TRequestStatus* iStatus;};inline TServerStart::TServerStart(TRequestStatus& aStatus):iId(RThread().Id()),iStatus(&aStatus){aStatus=KRequestPending;}SERVER STARTUP CODE203// Descriptorizes ’this’ to pass it from client to serverinline TPtrC TServerStart::AsCommand() const{return TPtrC(reinterpret_cast<const TText*>(this),sizeof(TServerStart)/sizeof(TText));}// Called server-side to ’reconstitute’ an object of this typeTInt TServerStart::GetCommand(){RProcess serverProcess;if (serverProcess.CommandLineLength()! =sizeof(TServerStart)/sizeof(TText))return (KErrGeneral);TPtr ptr(reinterpret_cast<TText*>(this),0,sizeof(TServerStart)/sizeof(TText));serverProcess.CommandLine(ptr);return (KErrNone);}// Called server-side to notify the client that server initialization// completed successfullyvoid TServerStart::SignalL(){RThread client;User::LeaveIfError(client.Open(iId));client.RequestComplete(iStatus, KErrNone);client.Close();}A TServerStart object encapsulates the client’s TThreadId and apointer to a TRequestStatus object.
For hardware builds, the objectis passed between the client and server processes by wrapping it in adescriptor, using the AsCommand() method and passing it as a commandline argument. Server-side, a TServerStart object can retrieve thesevalues using GetCommand(), which reads the command line withwhich the process was created. On the emulator, the TServerStartobject is passed as a parameter to the thread function. The server usesTServerStart::SignalL() to notify the client that initialization hascompleted successfully.The code to start a server is quite complex because it differsdepending on whether the server will run on a phone or theemulator. This complexity has been removed from Symbian OSv8.0 because Symbian OS process emulation on Win32 has beenimproved (see Chapter 10 for more details).12.4 Server Startup CodeHaving considered the client-side code to start the server, I will nowmove on to discuss the server-side startup code:204THE CLIENT–SERVER FRAMEWORK IN PRACTICE// Initialize and run the serverstatic void RunTheServerL(TServerStart& aStart){// First create and install the active schedulerCActiveScheduler* scheduler = new (ELeave) CActiveScheduler;CleanupStack::PushL(scheduler);CActiveScheduler::Install(scheduler);CHerculeanServer::NewLC();// creates the serverUser::LeaveIfError(RThread().Rename(KServerName));aStart.SignalL();// Signal the client that initialization is completeCActiveScheduler::Start();// Enter the wait loop// Exited – cleanup the server and schedulerCleanupStack::PopAndDestroy(2, scheduler);}// Main entry-point for the server thread/processstatic TInt RunTheServer(TServerStart& aStart){CTrapCleanup* cleanup=CTrapCleanup::New();TInt r=KErrNoMemory;if (cleanup){TRAP(r,RunTheServerL(aStart));delete cleanup;}return (r);}#ifdef __WINS__ // Different startup code for emulator and hardware// Thread entry-point functionstatic TInt ThreadFunction(TAny* aParameters){// The TServerStart object is passed as the thread parameterreturn RunTheServer(*static_cast<TServerStart*>(aParameters));}// WINS DLL entry-pointIMPORT_C TInt WinsMain();EXPORT_C TInt WinsMain(){// Returns the real thread function cast to TIntreturn reinterpret_cast<TInt>(&ThreadFunction);}TInt E32Dll(TDllReason){return (KErrNone);}#else // Phone hardware// Process entry-pointTInt E32Main(){// Reads the startup parameters and runs the serverTServerStart start;TInt r=start.GetCommand();if (KErrNone==r)r=RunTheServer(start);return (r);}#endifSERVER CLASSES205Again, as you can see, the startup code differs for hardware process andemulator DLL builds.
On the emulator, the server DLL has an entry pointcalled WinsMain(),which takes no parameters and returns a TInt.However, the Symbian OS thread function entry point function musttake a TAny* parameter, so WinsMain() is used to return the actualthread function (ThreadFunction()) by casting the function pointer toa TInt. The TAny* parameter passed to the thread function refers to theclient’s TServerStart object. This is passed directly to the main serverstartup function, called RunTheServer().
It sounds quite complex, butas you can see, it only takes a few lines of code.On hardware, the server is a process with E32Main() as its entry point.It simply instantiates a TServerStart object and uses GetCommand()to retrieve the client’s TThreadId and TRequestStatus. Having doneso, it calls RunTheServer(). At this point, the divergence between theemulator and hardware code is at an end.RunTheServer() first creates a cleanup stack for the server.
Havingdone so, it calls RunTheServerL() within a TRAP harness (recall fromChapter 2 that there must be at least one top-level TRAP in the systembefore the cleanup stack can be used). RunTheServerL() creates andinstalls an active scheduler to allow it to service incoming client requests.It then creates the CServer-derived object (and any objects that uses)and calls CServer::Start() or CServer::StartL().
Once theserver object is created and ready to receive connection requests, theserver thread is renamed with the name of the server. This is an optionalstep, but it is useful when debugging panics to be able to see thename of the server thread. At this point, server startup is complete andTServerStart::SignalL() is called to notify the client that theserver is fully initialized.Having signaled the client, the Start() method is called on theactive scheduler to make it ready to receive and process client requests.Within this function, the server thread enters the active schedulerwait loop, receiving and servicing client requests until CActiveScheduler::Stop() is called.12.5 Server ClassesThe fundamental server-side classes, deriving from CServer andCSharableSession, are defined as follows:class CHerculeanServer : public CServer // Receives client requests{public:static CServer* NewLC();void AddSession();void RemoveSession();206THE CLIENT–SERVER FRAMEWORK IN PRACTICEprotected:virtual TInt RunError(TInt aError); // Overrides CActive::RunError()private:CHerculeanServer();void ConstructL();virtual CSharableSession* NewSessionL(const TVersion& aVersion)const; // From CServerprivate:TInt iSessionCount;CShutdown iShutdown;};inline CHerculeanServer::CHerculeanServer():CServer(0, ESharableSessions){}class CAsyncHandler; // Active object class for asynchronous requests// Services client requestsclass CHerculeanSession : public CSharableSession{public:CHerculeanSession(){};virtual void CreateL(const CServer& aServer);private:void SlayNemeanLionL(const RMessage& aMessage);void SlayHydraL(const RMessage& aMessage);void CaptureCeryneianHindL(const RMessage& aMessage);void SlayErymanthianBoarL(const RMessage& aMessage);void CleanAugeanStablesL(const RMessage& aMessage);void SlayStymphalianBirdsL(const RMessage& aMessage);private:∼CHerculeanSession();inline CHerculeanServer& Server();void ServiceL(const RMessage& aMessage); // From CSharableSessionprivate:CAsyncHandler* iAsyncRequestHandler;HBufC8* iClientBuf;};inline CHerculeanServer& CHerculeanSession::Server(){return *static_cast<CHerculeanServer*>(const_cast<CServer*>(CSharableSession::Server()));}The CServer-derived class CHerculeanServer is the main serverclass; it coordinates server startup and shutdown and, as I described in theprevious chapter, receives incoming requests.