diff -r 000000000000 -r a41df078684a kernel/eka/euser/cbase/ub_svr.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/eka/euser/cbase/ub_svr.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,611 @@ +// Copyright (c) 1995-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// e32\euser\cbase\ub_svr.cpp +// +// + +#include "ub_std.h" +//#define __DEBUG_IMAGE__ 1 +#if defined(__DEBUG_IMAGE__) && defined (__EPOC32__) +#include "e32svr.h" +#define __IF_DEBUG(t) {RDebug debug;debug.t;} +#else +#define __IF_DEBUG(t) +#endif + + + +_LIT(KSessionPanicCategory,"CSession"); +_LIT(KServerPanicCategory,"CServer"); + + + +/** +Default constructor. + +This constructor is empty. +*/ +EXPORT_C CSession2::CSession2() + {} + + + +/** +Destructor. + +It frees resources prior to destruction of the object. +Specifically, it removes this session object from the server +active object’s list of sessions. +*/ +EXPORT_C CSession2::~CSession2() + { + if (iLink.iNext) + iLink.Deque(); + } + + + +/** +Completes construction of this server-side client session object. + +The function is called by the server active object, the CServer2 derived +class instance, when a client makes a connection request. + +The connection request results in the creation of this session object, +followed by a call to this function. + +The default implementation is empty. + +@see CServer2::NewSessionL() +*/ +EXPORT_C void CSession2::CreateL() + { + } + +/** +Designates the (master or slave) server that is to service this session. + +This may be called from the NewSessionL() method of the CServer2-derived +class or from the CreateL() method of the CSession2-derived class. + +It must not be called after CreateL() has finished, as this is the point +at which the session binding become irrevocable. + +@see CServer::DoConnect() +*/ +EXPORT_C void CSession2::SetServer(const CServer2* aServer) + { + ASSERT(iLink.iNext == NULL); + ASSERT(aServer); + iServer = aServer; + } + +/** +Handles the situation when a call to CSession2::ServiceL(), which services +a client request, leaves. + +Servers are active objects, and the call to CSession2::ServiceL() to handle +a client request is executed as part of the server's active object RunL() +function. If the RunL() leaves, the active object framework calls the active +object's RunError() function. The server framework implements this as a call +to ServiceError() + +The default behaviour of this function is to complete the message, using +the leave value, if it has not already been completed. + +Servers can re-implement this as appropriate. + +@param aMessage The message containing the details of the client request that + caused the leave. +@param aError The leave code. + +@see CActive::RunL() +@see CActive::RunError() +*/ +EXPORT_C void CSession2::ServiceError(const RMessage2& aMessage,TInt aError) + { + if(!aMessage.IsNull()) + aMessage.Complete(aError); + } + + + +/** +Called by a server when it receives a disconnect message for the session. + +This message is sent by the kernel when all client handles to the session have +been closed. +This method deletes the session object and completes the disconnect message. + +A derived session implementation may overide this virtual method if it needs to +perform any asynchronous cleanup actions, these actions must end with a call to the +base class implementation of this method, which will delete the session object +and complete the disconnect message + +@param aMessage The disconnect message. + +@post 'this' session object has been deleted. +*/ +EXPORT_C void CSession2::Disconnect(const RMessage2& aMessage) + { + delete this; + aMessage.Complete(0); + } + + + +/** +Marks the start of resource count checking. + +It sets up a starting value for resource count checking. + +The function sets up a starting value for resource count checking by using +the value returned by a call to CSession2::CountResources(), and is the value +that will be used for comparison if CSession2::ResourceCountMarkEnd() is called +at some later time. + +The client/server framework does not call this function (nor +does it call CSession2::ResourceCountMarkEnd()), but is available for servers +to use, if appropriate. + +@see CSession2::CountResources() +@see CSession2::ResourceCountMarkEnd() +*/ +EXPORT_C void CSession2::ResourceCountMarkStart() + { + iResourceCountMark=CountResources(); + } + + + +/** +Marks the end of resource count checking. + +The function takes the current resource count by +calling CSession2::CountResources(), and compares it with the resource count +value saved when CSession2::ResourceCountMarkStart() was called. +If the resource counts differ, then the client thread is panicked (CSession 2)". + +The client/server framework does not call this function (nor does it call +CSession2::ResourceCountMarkStart()), but the function is available for +servers to use, if appropriate. + +@param aMessage Represents the details of the client request that is requesting + this resource check. + +@see CSession2::CountResources() +@see CSession2::ResourceCountMarkStart() +*/ +EXPORT_C void CSession2::ResourceCountMarkEnd(const RMessage2& aMessage) + { + if (iResourceCountMark!=CountResources()) + aMessage.Panic(KSessionPanicCategory,ESesFoundResCountHeaven); + } + + + +/** +Gets the number of resources currently in use. + +Derived classes must provide a suitable implementation. +The meaning of a resource depends on the design intent of the server. + +The default implementation panics the calling thread (CSession 1) +before returning KErrGeneral. + +@return The current number of resources in use. + +@see CSession2::ResourceCountmarkStart() +@see CSession2::ResourceCountmarkEnd() +*/ +EXPORT_C TInt CSession2::CountResources() + { + User::Panic(KSessionPanicCategory,ESesCountResourcesNotImplemented); + return KErrGeneral; + } + + + +/** +Extension function + + +*/ +EXPORT_C TInt CSession2::Extension_(TUint aExtensionId, TAny*& a0, TAny* a1) + { + return CBase::Extension_(aExtensionId, a0, a1); + } + + + + +/** +Constructs the server object, specifying the server type and the active +object priority. + +Derived classes must define and implement a constructor through which +the priority can be specified. A typical implementation calls this server +base class constructor through a constructor initialization list. + +@param aPriority The priority of this active object. +@param aType Indicates the type of session that the server creates. + If not explicitly stated, then the server creates + a session that is not sharable with other threads. +*/ +EXPORT_C CServer2::CServer2(TInt aPriority, TServerType aType) + : CActive(aPriority), + iSessionType((TUint8)aType), + iSessionQ(_FOFF(CSession2,iLink)), + iSessionIter(iSessionQ) + { + ASSERT(iSessionType == aType); + __ASSERT_COMPILE(EServerRole_Default == 0); + __ASSERT_COMPILE(EServerOpt_PinClientDescriptorsDefault == 0); + } + + + +/** +Frees resources prior to destruction. + +Specifically, it cancels any outstanding request for messages, and +deletes all server-side client session objects. +*/ +EXPORT_C CServer2::~CServer2() + { + Cancel(); + while (!iSessionQ.IsEmpty()) + { + CSession2 *pS=iSessionQ.First(); + pS->iLink.Deque(); + pS->iLink.iNext=0; + delete pS; + } + iServer.Close(); + } + + +/** +Sets whether the kernel will pin descriptors passed to this server in the context of the client +thread. + +Setting this is one way of ensuring that the server will not take page faults when accessing client +descriptors, which would otherwise happen if the data was paged out. + +This method overrides the default pinning policy of the server which is for the server +to pin its client's descriptors if the process creating the server is not data paged. +I.e. if CServer2::SetPinClientDescriptors() is not invoked on the server and +RProcess::DefaultDataPaged() of the process creating the server returns EFalse, +the server will pin its client's descriptors, otherwise the server will not pin its +client's descriptors. + +This method must be called prior to starting the server by calling the Start() method. + +@param aPin Set to ETrue for the server to pin its client's descriptors, set to + EFalse otherwise. +@panic E32USER-CBase 106 When this method is invoked after the server has been started. +@see CServer2::Start() + +@prototype +*/ +EXPORT_C void CServer2::SetPinClientDescriptors(TBool aPin) + { + if (iServer.Handle() != KNullHandle) + {// Server already started so too late to make it a pinning one. + Panic(ECServer2InvalidSetPin); + } + iServerOpts &= ~EServerOpt_PinClientDescriptorsMask; + if (aPin) + iServerOpts |= EServerOpt_PinClientDescriptorsEnable; + else + iServerOpts |= EServerOpt_PinClientDescriptorsDisable; + } + +/** +Assigns a role (master or slave) for this server. + +The master server is typically named, and receives all Connect messages +from clients. It can hand off some sessions to be processed by one or +more anonymous slave servers, each running in a separate thread. + +Both master and slave servers must call this function before calling +Start(), in order to define their roles. Once the server is started, +its role cannot be changed. + +@panic E32USER-CBase-? When this method is invoked after the server has been started. +@see CServer2::Start() + +@prototype +*/ +EXPORT_C void CServer2::SetMaster(const CServer2* aServer) + { + // Roles can only be assigned before the call to Start() + ASSERT(Server().Handle() == KNullHandle); + + if (aServer == NULL) + iServerRole = EServerRole_Standalone; + else if (aServer == this) + iServerRole = EServerRole_Master; + else + iServerRole = EServerRole_Slave; + } + + +/** +Adds the server with the specified name to the active scheduler, +and issues the first request for messages. + +If KNullDesC is specified for the name, then an anonymous server will be created. +To create a session to such a server, an overload of RSessionBase::CreateSession() +which takes RServer2 object as a parameter can be used. + +@param aName The name of the server. + KNullDesC, to create anonymous servers. +@return KErrNone, if successful, otherwise one of the other system wide error codes. + +@capability ProtServ if aName starts with a '!' character +*/ +EXPORT_C TInt CServer2::Start(const TDesC& aName) + { + TInt r = iServer.CreateGlobal(aName, iSessionType, iServerRole, iServerOpts); + if (r == KErrNone) + { + CActiveScheduler::Add(this); + ReStart(); + } + return r; + } + + + +/** +Adds the server with the specified name to the active scheduler, +and issues the first request for messages, and leaves if the operation fails. + +If KNullDesC is specified for the name, then an anonymous server will be created. +To create a session to such a server, the overload of RSessionBase::CreateSession() +which takes an RServer2 object as a parameter can be used. + +@param aName The name of the server. + KNullDesC, to create anonymous servers. +@capability ProtServ if aName starts with a '!' character +*/ +EXPORT_C void CServer2::StartL(const TDesC& aName) + { + User::LeaveIfError(Start(aName)); + } + + + +/** +Implements the cancellation of any outstanding request for messages. +*/ +EXPORT_C void CServer2::DoCancel() + { + + iServer.Cancel(); + } + + + +void CServer2::Connect(const RMessage2& aMessage) +// +// Handle a connect request. Ptr0()==Version. +// NOTE: We don't want this to leave as that may kill the server +// + { + + if (aMessage.Session()) + { + aMessage.Panic(KServerPanicCategory,ESessionAlreadyConnected); + return; + } + DoConnect(aMessage); + } + + + +// +//This is all of the Leaving code for connection creation. +//This is in a seperate function in an effort to force compilers to store aSession +//on the stack which enables cleanup to perform correctly when a Leave occurs +// +void CServer2::DoConnectL(const RMessage2& aMessage,CSession2* volatile& aSession) + { + TVersion v; + *(TInt*)&v = aMessage.Int0(); + aSession = NewSessionL(v, aMessage); + if (!aSession->iServer) + aSession->iServer = this; + aSession->CreateL(); + iSessionQ.AddLast(*aSession); + Exec::SetSessionPtr(aMessage.Handle(), aSession); + } + + + +/** +Handles the connect request from the client. We trap Leaves, to ensure +that existing sessions aren't affected by failure to create a new one. + +@param aMessage The Connect message sent by the client requesting the + connection. aMessage.Ptr0() is the required Version. +*/ +EXPORT_C void CServer2::DoConnect(const RMessage2& aMessage) + { + ASSERT(aMessage.Function() == RMessage2::EConnect); + ASSERT(aMessage.Session() == NULL); + ASSERT(!aMessage.IsNull()); + + CSession2* newSession = NULL; + TRAPD(err, DoConnectL(aMessage, newSession)); + if (err != KErrNone) + { + // Connect failed + delete newSession; + aMessage.Complete(err); + } + else + { + ASSERT(newSession != NULL); + CServer2* sessionServer = const_cast(newSession->Server()); + ASSERT(sessionServer != NULL); + + // The return value of Server() will be 'this', unless it was + // changed by a call to SetServer(). + if (sessionServer == this) + { + // no SetServer() call, so just complete the Connect message + aMessage.Complete(err); + } + else + { + // Transfer the new Csession to the specified slave Cserver + newSession->iLink.Deque(); + sessionServer->iSessionQ.AddLast(*newSession); + + // Ask the kernel to transfer the DSession to the slave DServer. + // Note: this Exec call also completes the Connect message. + TInt msgHandle = aMessage.iHandle; + const_cast(aMessage.iHandle) = 0; + ASSERT(msgHandle); + Exec::TransferSession(msgHandle, sessionServer->Server().Handle()); + } + } + + ASSERT(aMessage.IsNull()); + } + + + +/** +Handles the situation where a call to CServer2::RunL(), leaves. + +This is the server active object's implementation of the active object +framework's RunError() function. + +In practice, the leave can only be caused by a session's ServiceL() function, +which is called from this RunL(); this error is reflected back to that session +by calling its ServiceError() function. + +@param aError The leave code. + +@return KErrNone. + +@see CActive::RunL() +@see CActive::RunError() +@see CSession2::ServiceError() +*/ +EXPORT_C TInt CServer2::RunError(TInt aError) + { + Message().Session()->ServiceError(Message(),aError); + if (!IsActive()) + ReStart(); + return KErrNone; + } + + + +/** +Restarts the server. + +The function issues a request for messages. +*/ +EXPORT_C void CServer2::ReStart() + { + + iServer.Receive(iMessage,iStatus); + SetActive(); + } + + + +#ifndef __CSERVER_MACHINE_CODED__ +/** +Handles the receipt of a message. +*/ +EXPORT_C void CServer2::RunL() + { + TInt fn = Message().Function(); + + if(fn>=0) + { + // Service the message + CSession2* session=Message().Session(); + if(session) + session->ServiceL(Message()); + else + NotConnected(Message()); + } + else if(fn==RMessage2::EConnect) + { + Connect(Message()); + } + else if(fn==RMessage2::EDisConnect) + { + Disconnect(Message()); + } + else + { + BadMessage(Message()); + } + // Queue reception of next message if it hasn't already been done + if(!IsActive()) + ReStart(); + } + +#endif + + + +void CServer2::Disconnect(const RMessage2& aMessage) +// +// Process a disconnect message +// + { + CSession2* session=Message().Session(); + if(!session) + { + // Session not created yet, so just complete message. + aMessage.Complete(0); + return; + } + session->Disconnect(aMessage); + } + + + +void CServer2::BadMessage(const RMessage2& aMessage) + { + aMessage.Panic(KServerPanicCategory,EBadMessageNumber); + } + + + +void CServer2::NotConnected(const RMessage2& aMessage) + { + aMessage.Panic(KServerPanicCategory,ESessionNotConnected); + } + + + +/** +Extension function + + +*/ +EXPORT_C TInt CServer2::Extension_(TUint aExtensionId, TAny*& a0, TAny* a1) + { + return CActive::Extension_(aExtensionId, a0, a1); + }