diff -r 000000000000 -r dfb7c4ff071f commsfwsupport/commselements/rootserver/rootsrv/rootutil.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/commsfwsupport/commselements/rootserver/rootsrv/rootutil.cpp Thu Dec 17 09:22:25 2009 +0200 @@ -0,0 +1,2071 @@ +// Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "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: +// + +/** + @file + @internalComponent +*/ + +#include "rsstd.h" +#include +#include +#include +#include +#include +#include "bindmgr.h" + + +#ifdef _DEBUG +// Panic category for "absolutely impossible!" vanilla ASSERT()-type panics from this module +// (if it could happen through user error then you should give it an explicit, documented, category + code) +_LIT(KSpecAssert_ElemRootServerrt, "ElemRootServerrt"); +#endif + +__CFLOG_STMT(_LIT8(KLogSubSysRS, "RootServer");) // subsystem name + +CNotifySuddenDeathContainer::CNotifySuddenDeathContainer(MNotifySuddenDeath* aNotifier): + iNotifier(aNotifier) + /** Constructor for Sudden Death notifier container + + @param aNotifier The object to be notified of death + */ + { + } + +void CNotifySuddenDeathContainer::Dequeue() + /** Dequeue + + */ + { + iLink.Deque(); + } + +void CNotifySuddenDeathContainer::Notify(TInt aReason) + /** Notify of SuddenDeath + + @param aReason The reason to associate with the Sudden Death event + */ + { + iNotifier->SuddenDeath(aReason); + } + +CModuleUndertaker* CModuleUndertaker::NewL(MNotifySuddenDeath& aNotifier, RThread& aThread) + /** Creates new CModuleUndertaker object. + @param aNotifier The object to be notified of death. + @param aThread The thread to be monitored. + @return Pointer to the undertaker object. + */ + { + CModuleUndertaker* pUndertaker = new (ELeave) CModuleUndertaker(aNotifier, aThread); + CActiveScheduler::Add(pUndertaker); + return pUndertaker; + } + +void CModuleUndertaker::Logon() + /** Logs on to thread. + Thread logon to monitor for death + */ + { + iThread.Logon(iStatus); + SetActive(); + } + +void CModuleUndertaker::DoCancel() + { + (void)iThread.LogonCancel(iStatus); + } + +void CModuleUndertaker::RunL() + /** Calls sudden death function on object registered for notification + */ + { + LogThreadDeath(); + + TExitType type = iThread.ExitType(); + TInt reason = iThread.ExitReason(); + + // Decide if the thread exit was expected or caused by an error. + if((iThread.ExitType() != EExitKill) || (reason != KErrNone)) + { + // Looks like a panic or a forced kill/terminate happened to it. + reason = KErrRSSuddenViolentDeath; + } + else + { + reason = KErrRSSuddenDeath; + } + + // Call the notifier. + iNotifier.SuddenDeath(reason); + } + +void CModuleUndertaker::LogThreadDeath() + { + #if defined _DEBUG || defined __CFLOG_ACTIVE || defined SYMBIAN_TRACE_ENABLE + + RThread& deadThread = iThread; + + TExitType type = deadThread.ExitType(); + TInt reason = deadThread.ExitReason(); + + TName processName = RProcess().Name(); + TPtrC processDisplayName = processName; + TInt pos = processName.Locate(L'['); + if(pos > 0) + { + // Strip off the UID and instance number. + processDisplayName.Set(processName.Left(pos)); + } + TBuf8 processName8; + processName8.Copy(processDisplayName); + + TName threadName = deadThread.Name(); + TBuf8 threadName8; + threadName8.Copy(threadName); + + TBuf8 cat8; + cat8.Copy(deadThread.ExitCategory()); + + if(type == EExitPending) + { + __CFLOG_1(KLogSubSysRS, KLogCode, + _L8("ERROR: CModuleUndertaker::RunL - the death of thread %S was reported but the exit type indicates that the thread is still running. The rootserver is probably in an unstable state."), + &threadName8); + + __CF_BREAK_IF_DEBUGGER(); + } + else + { + _LIT8(KExitNormalStr, "(Normal)"); + _LIT8(KExitKillStr, "(Kill)"); + _LIT8(KExitTerminateStr, "(Terminate)"); + _LIT8(KExitPanicStr, "Panic:"); + + _LIT8(KKillNormalInfoFormatStr, "%S::%S Thread Died %S - Category: \"%S\", Reason: %d, Thread ID: %x, BTrace ID: 0x%x"); + _LIT8(KKillCancelInfoFormatStr, "%S::%S Thread Died %S - Category: \"%S\", Reason: %d, Thread ID: %x, BTrace ID: 0x%x"); + _LIT8(KKillErrorInfoFormatStr, "ERROR: %S::%S Thread Died %S - Category: \"%S\", Reason: %d, Thread ID: %x, BTrace ID: 0x%x"); + _LIT8(KPanicInfoFormatStr, "ERROR: %S::%S Thread Died %S %S %d - Thread ID: %x, BTrace ID: 0x%x"); + + TPtrC8 type8; + TPtrC8 formatStr; + + // Determine the reason why the thread exited and try to log as much information + // as possible if the exit was unexpected. + switch(type) + { + case EExitKill: + { + switch(reason) + { + case KErrNone: + type8.Set(KExitNormalStr); + formatStr.Set(KKillNormalInfoFormatStr); + break; + case KErrCancel: + type8.Set(KExitKillStr); + formatStr.Set(KKillCancelInfoFormatStr); + break; + default: + type8.Set(KExitKillStr); + formatStr.Set(KKillErrorInfoFormatStr); + break; + } + } + break; + case EExitTerminate: + type8.Set(KExitTerminateStr); + formatStr.Set(KKillErrorInfoFormatStr); + break; + case EExitPanic: + type8.Set(KExitPanicStr); + formatStr.Set(KPanicInfoFormatStr); + break; + default: + __ASSERT_DEBUG(false, User::Panic(KSpecAssert_ElemRootServerrt, 1)); // should never get here + break; + } + + // Decide if the thread exit was expected or caused by an error. + #ifdef _DEBUG + if((deadThread.ExitType() != EExitKill) || ((reason != KErrNone) && (reason != KErrCancel))) + { + // Log unexpected thread exits to the debug port. + RDebug::Printf(reinterpret_cast(formatStr.Ptr()), &processName8, &threadName8, &type8, &cat8, reason, TUint(deadThread.Id().Id()), deadThread.BTraceId()); + } + #endif + + // Log the thread's death to UTrace if available. + #ifdef SYMBIAN_TRACE_ENABLE + enum + { + KPrimaryFilter = 194, // server den + KMaxLogTextLength = 250 + }; + + class TLogIgnoreOverflow8 : public TDes8Overflow + { + public: + void Overflow(TDes8& /*aDes*/) { } + }; + + // Format the log text into a buffer for UTrace. + TBuf8 buf; + TLogIgnoreOverflow8 overflowHandler; + buf.AppendFormat(formatStr, &overflowHandler, &processName8, &threadName8, &type8, &cat8, reason, TUint(deadThread.Id().Id()), deadThread.BTraceId()); + + UTracePfAny(KPrimaryFilter, KText, ETrue, EFalse, buf.Length(), buf.Ptr(), buf.Length()); + #endif + + // Log the thread's death to CFlog if available. + __CFLOG_VAR((KLogSubSysRS, KLogCode, formatStr, &processName8, &threadName8, &type8, &cat8, reason, TUint(deadThread.Id().Id()), deadThread.BTraceId())); + } + + #endif + } + +CModuleUndertaker::CModuleUndertaker(MNotifySuddenDeath& aNotifier, RThread& aThread) +: CActive(EPriorityLow), // NB! lower priority than that of the session handlers to avoid race between undertaker & load handler + iNotifier(aNotifier), + iThread(aThread) + /** Constructor for ModuleUndertaker + */ + { + } + +CModuleUndertaker::~CModuleUndertaker() + /** Destructor for ModuleUndertaker + */ + { + Cancel(); + } + + +EXPORT_C CCommsProviderModule::CCommsProviderModule( RThread& aThread ) + : CActive(EPriorityStandard) + , iState(EIdle) + , iThread(aThread) + , iNotifiers(_FOFF(CNotifySuddenDeathContainer, iLink)) +// Surplus initialisers left as documentation of intent +//iUndertaker(NULL), + {} + +EXPORT_C CCommsProviderModule::~CCommsProviderModule() + { + __CFLOG_1(KLogSubSysRS, KLogCode, + _L8("CCommsProviderModule::~CCommsProviderModule: %S"), &iName); + + iSend.Close(); + iRecv.Close(); + + if(iHeapPtr) + { +#if defined(SYMBIAN_C32ROOT_API_V3) +#if !defined(ESOCKV3_TEMPORARY_PAIN_RELIEF) +#ifdef _DEBUG + // If modules sharing a heap are unloaded in anything but LIFO order then + // they can't easily check for leaks (ie A marks heap, B loads, A unloads but + // doesn't check heap because it sees that someone else is using it, B unloads + // and doesn't check heap either. The real owner of the heap is the RootServer, + // so here we check the count and log leaked cells if any remain after the last + // user has unloaded. + // In _DEBUG builds we also execute a debugger breakpoint when cells remain, + // unless the module has been flagged as "unstable". A stable module sharing a + // heap with an unstable one might see this breakpoint but in general a leaky + // module should have a private heap to ensure cleanup. + RCFSharedHeap& heap = static_cast(*iHeapPtr); + if(heap.AccessCount() <= 2) //@todo worry more about why it's two + { + if(heap.Count() > 0) + { + __CFLOG_VAR( (KLogSubSysRSModule, KLogCode, _L8("~CCommsProviderModule: module '%S' heap(%08x) has %d cells remaining:"), &iName, &heap, heap.Count()) ); + heap.LogAllocatedCells(KLogSubSysRSModule, KLogRSLeakTag); + + // The configuration flags suppressing the panic: modules known to be unstable are not checked. These + // include all modules that have panicked (if they never had the chance to cleanup it isn't helpful + // to list "leaks") + const TUint32 noCheckCondition = TRSStartModuleParamContainer::KCF_UnstableModule; + + // If not waivered by optional flags, call __DEBUGGER() + if( (iControlFlags & noCheckCondition)==0) + { + RProperty pubsub; + TInt res = pubsub.Attach(iRootServer->iProcessKey, iRootServer->iLeakKey.iUid); + //Does not leave before close therefore no need for cleanup stack + if (res == KErrNone) + { + TInt count; + res =pubsub.Get(count); + if (res == KErrNone) + { + ++count; + res =pubsub.Set(count); + } + } + pubsub.Close(); + if (res != KErrNone) + { + __CFLOG_1(KLogSubSysRSModule, KLogRSLeakTag, _L8("Unable to report leaks. Error: %d"), res); + } + } + // As much as anything this log line is here to make it apparent that the breakpoint above was hit + // See the comment block above for more explanations. + __CFLOG_VAR( (KLogSubSysRSModule, KLogRSLeakTag, _L8("--- end of leaked cell log")) ); + } + } +#endif +#endif +#endif + iHeapPtr->Close(); + } + + if(iRootServer->FindCpm(iName)) + { + iLink.Deque(); // Remove from list in Root Server + __CFLOG_1(KLogSubSysRS, KLogCode, + _L8("CCommsProviderModule::~CCommsProviderModule: removing %S from RS List"), &iName); + } + + delete iUndertaker; + + // Create a timer in case some other entity holds an open handle on the + // thread which prevents the kernel from destroying it. We timeout + // after one second. + TAutoClose timer; + if(timer.iObj.CreateLocal() == KErrNone) + { + // Ensure the thread has been destroyed by using a notification so that its unique name is available for + // reuse in case the module is reloaded soon afterwards. + TRequestStatus destructionStatus; + iThread.NotifyDestruction(destructionStatus); + iThread.Close(); + + enum{ KThreadDestructionTimeout = 1000000 }; + + TRequestStatus timerStatus; + timer.iObj.After(timerStatus, KThreadDestructionTimeout); + + // Wait for the thread to be destroyed or for the timeout. + User::WaitForRequest(destructionStatus, timerStatus); + if(timerStatus.Int() == KRequestPending) + { + timer.iObj.Cancel(); + User::WaitForRequest(timerStatus); + } + else + { + User::CancelMiscNotifier(destructionStatus); + User::WaitForRequest(destructionStatus); + + __CFLOG_2(KLogSubSysRS, KLogCode, + _L8("CCommsProviderModule::~CCommsProviderModule: %S thread has still not been destroyed after %fs"), &iName, KThreadDestructionTimeout / 1000.0); + } + } + else + { + iThread.Close(); + } + + // If a starting thread has been given the pointer then it's too dangerous to delete it here as might crash + // them - better to risk a small leak + if(iState != EStarting) + { + delete iThreadStartupInfo; + } +#ifdef SYMBIAN_C32ROOT_API_V3 + delete iIniData; +#endif + + // Remove from CActiveScheduler + if(IsAdded()) + { + Deque(); + } + // Remove from RootServer list of modules + if(iLink.iNext != NULL) + { + iLink.Deque(); + } + } + +TInt CCommsProviderModule::CancelLoad() + /** Cancel Load. Marks whether an attempt to cancel load has happened + * + * @return KErrCancel if cancelation of the load module operation was initiated + * or KErrRSModuleAlreadyExist if the module's thread has started + * running and can no longer be canceled. + */ + { + __CFLOG_1(KLogSubSysRS, KLogCode, + _L8("CCommsProviderModule - CancelLoad %S"), &iName); + + TInt result = KErrCancel; + + // Check to see if the load module operation can be canceled or if the thread + // is already in the process of starting. + if((iState == EStarting) || (iState == ERunning)) + { + __CFLOG_1(KLogSubSysRS, KLogCode, + _L8("CCommsProviderModule - CancelLoad: %S module has already started running and can no longer be canceled"), &iName); + + result = KErrRSModuleAlreadyExist; + } + + return result; + } + +TInt CCommsProviderModule::CancelUnload(TRSUnLoadType aType) + /** Cancel unload. Marks whether an attempt to cancel an unload has happened + * + * @param aType The type of unload that was in progress + * @return KErrCancel if cancelation of the unload module operation was initiated + * or KErrRSModuleNotLoaded if the module's thread has started dying and + * can no longer be canceled. + */ + { + #ifdef __CFLOG_ACTIVE + TPtrC8 unloadType = GetUnloadTypeName(aType); + __CFLOG_2(KLogSubSysRS, KLogCode, + _L8("CCommsProviderModule - CancelUnload: %S module has started an %S unload"), &iName, &unloadType); + #endif + + TInt result = KErrCancel; + + // Check to see if the unload module operation can be canceled or if the thread + // is already in the process of dying. + if(aType == EUnGraceful) + { + __CFLOG_1(KLogSubSysRS, KLogCode, + _L8("CCommsProviderModule - CancelUnload: %S module has already started an EUnGraceful unload and can no longer be canceled"), &iName); + + result = KErrRSModuleNotLoaded; + } + else + { + if(iState==EStopping) + { + iState=ERunning; + } + } + + return result; + } + +void CCommsProviderModule::CreateThreadL() + /** Creates and sets up thread ready for initialisation + */ + { + iUndertaker = CModuleUndertaker::NewL(*this, iThread); + + TInt res = iLibrary.Load(iDll); + __CFLOG_STMT(TBuf8 fnam;) + __CFLOG_STMT(fnam.Copy(iDll);) + + + if (res!=KErrNone && res!=KErrAlreadyExists) + { + __CFLOG_2(KLogSubSysRS, KLogCode, + _L8("CModuleBase::CreateThreadL: Unable to load DLL %S: %d."), + &fnam, res); + + User::Leave(res); + } + + __CFLOG_1( KLogSubSysRS, KLogCode, + _L8("CModuleBase::CreateThreadL: Loaded module library %S."), + &fnam); + + TUidType type = iLibrary.Type(); + if ( ! UidMatch(type[1]) ) + { + __CFLOG_1(KLogSubSysRS, KLogCode,_L8("CModuleBase - UID mismatch for %S"), &fnam); + User::Leave(KErrRSInvalidUidType); + } + + // If ordinal is set to 0, default to 1 (first) + if(iThreadFunctionOrdinal==0) + { + __CFLOG(KLogSubSysRS, KLogCode, _L8("CModuleBase - Using default ordinal: 1")); + iThreadFunctionOrdinal = 1; + } + __CFLOG_STMT( + else + { + __CFLOG_1(KLogSubSysRS, KLogCode,_L8("CModuleBase - Using configured ordinal: %d"), iThreadFunctionOrdinal); + } + ); // __CFLOG_STMT + + TThreadFunction libEntry=(TThreadFunction)iLibrary.Lookup(iThreadFunctionOrdinal); + + if (libEntry==NULL) + { + User::Leave(KErrBadLibraryEntryPoint); + } + + RCFChannel::TMsgQueues inputQueue; + RCFChannel::TMsgQueues outputQueue; + + iSend.GetMsgQueues(inputQueue); + iRecv.GetMsgQueues(outputQueue); + + iThreadStartupInfo = new(ELeave) CRSTransientModuleInfo(outputQueue, inputQueue, iHeapType, libEntry, iIniData, iPriority, iControlFlags); +#ifdef SYMBIAN_C32ROOT_API_V3 + iIniData = NULL; // iThreadStartupInfo now has ownership +#endif + + switch(iHeapType) + { + case EShareHeap: + iHeapPtr = iRootServer->FindHeap(iShareHeapWith); + if(iHeapPtr) + { + if(KErrNone!=iHeapPtr->Open()) + { + __CFLOG(KLogSubSysRS, KLogWarning, + _L8("CCommsProviderModule::CreateThreadL - Couldn't open shared heap!")); + User::Leave(KErrRSUnableToOpenHeap); // Found the heap but couldn't open it + } + __CFLOG_1(KLogSubSysRS, KLogCode, + _L8("CCommsProviderModule::CreateThreadL: Shared heap address %x"), iHeapPtr); + } + else + { + __CFLOG(KLogSubSysRS, KLogWarning, + _L8("CCommsProviderModule - Couldn't find correct heap!")); + User::Leave(KErrRSUnableToFindHeap); // Couldn't find the required heap! + } + break; + case ENewHeap: + iHeapPtr = User::ChunkHeap(NULL,iMinHeapSize,iMaxHeapSize); + if(!iHeapPtr) + { + __CFLOG(KLogSubSysRS, KLogWarning, + _L8("CCommsProviderModule::CreateThreadL:Unable to create new heap")); + User::Leave(KErrRSNoNewHeapsAvailable); + } + else + { + __CFLOG_1(KLogSubSysRS, KLogCode, _L8("CCommsProviderModule::CreateThreadL: New heap address %x"), iHeapPtr); + } + break; + case EDefaultHeap: + __CFLOG(KLogSubSysRS, KLogCode, _L8("CCommsProviderModule::CreateThreadL: Using Default Heap.")); + break; // iHeapPtr is left NULL + default: + __ASSERT_DEBUG(0, User::Panic(KSpecAssert_ElemRootServerrt, 2)); + } + + TName threadName; + threadName.Copy(iName); + res=CreateModuleThread(threadName, CRSTransientModuleInfo::ModuleThreadStartup, + iStackSize, iHeapPtr, iThreadStartupInfo); +#if defined(SYMBIAN_C32ROOT_API_V3) +#if !defined(ESOCKV3_TEMPORARY_PAIN_RELIEF) +#if defined(C32ROOT_ALLOW_SUBSTITUTE_THREAD_NAMES) + // Reloading a module that has leaked a named resource will cause an already-exists + // error because the old thread hasn't been fully cleaned up. It can be helpful to get past + // this point in a debug build since if the resource in question is named it may later fail + // in a more specific fashion + if (res == KErrAlreadyExists) + { + // If not waivered by optional flags, call __DEBUGGER() + if( (iControlFlags & TRSStartModuleParamContainer::KCF_UnstableModule)==0) + { + __DEBUGGER(); + } + TInt suffix = 0; + RProperty::Get(KUidSystemCategory, iRootServer->iDeathKey.iUid, suffix); + do + { + threadName.Copy(iName); // expand to Unicode + _LIT(KSubstituteNameFormat, "_%d"); + threadName.AppendFormat(KSubstituteNameFormat, ++suffix); + __CFLOG_VAR( (KLogSubSysRS, KLogCode, + _L8("CCommsProviderModule::CreateThreadL - KErrAlreadyExists; trying with %S"), &threadName) ); + res=CreateModuleThread(threadName, CRSTransientModuleInfo::ModuleThreadStartup, + iStackSize, iHeapPtr, iThreadStartupInfo); + } while(res == KErrAlreadyExists); + } +#endif +#endif +#endif + if (res!=KErrNone) + { + __CFLOG_2(KLogSubSysRS, KLogCode, + _L8("CCommsProviderModule::CreateThreadL:Unable to create thread %S error %d.."), + &iName, res); + User::Leave(res); + } + + __CFLOG_1(KLogSubSysRS, KLogCode, + _L8("CCommsProviderModule::CreateThreadL: Created thread for %S."), &iName); + + CActiveScheduler::Add(this); + } + +EXPORT_C void CCommsProviderModule::ConstructL(CRootServer* aRootServer, + const TRSStartModuleParams& aParams, HBufC8* aIniData) + /** + * Contruct module. Sets up module, creating comms channels and thread. + @param aRootServer The root server object + @param aParams The data needed to load module + @param aIniData The initialisation data for the module + */ + { + __CFLOG_STMT(TBuf8 fnam;) + __CFLOG_STMT(fnam.Copy(aParams.iParams.iFilename);) + __CFLOG_4(KLogSubSysRS, KLogCode, + _L8("CCommsProviderModule::ConstructL:%S,%S Pri:%d Stack:%d."), + &aParams.iParams.iName, &fnam, aParams.iParams.iPriority, aParams.iParams.iStackSize); + + // Copy the essential information + iName.Copy(aParams.iParams.iName); + iDll.Copy(aParams.iParams.iFilename); + iPriority = aParams.iParams.iPriority; + iRootServer = aRootServer; + iIniData = aIniData; + iStackSize = aParams.iParams.iStackSize; + iHeapType = aParams.iParams.iHeapType; + iMinHeapSize = aParams.iParams.iMinHeapSize; + iMaxHeapSize = aParams.iParams.iMaxHeapSize; + iShareHeapWith = static_cast(aParams.iParams.iShareHeapWith); + iThreadFunctionOrdinal = aParams.iParams.iThreadFunctionOrdinal; + iIsSticky = aParams.iParams.iIsSticky; + iIsServer = aParams.iParams.iIsServer; + iControlFlags = aParams.iParams.iControlFlags; + + if(KErrNone!=iSend.Create(KCFMaxChannelQueues)) + { + User::Leave(KErrRSUnableToCreateQueues); + } + + if(KErrNone!=iRecv.Create(KCFMaxChannelQueues)) + { + User::Leave(KErrRSUnableToCreateQueues); + } + } + +TInt CCommsProviderModule::Load(TRequestStatus& aStatus) + /** Initiates the loading of a module. Sets states and resumes thread + @param aStatus The status of the active object which will be completed on load + */ + { + iState = EStarting; + iReqStatus = &aStatus; + aStatus = KRequestPending; + + iThread.Rendezvous(iStatus); + iThread.Resume(); + SetActive(); + + __CFLOG_1(KLogSubSysRS, KLogCode, + _L8("CCommsProviderModule::Load: Resumed thread %S."), &iName); + + return KErrNone; + } + +TInt CCommsProviderModule::Unload() + /** Initiates the unloading of a module. Simply sets internal state + */ + { + + // We now attempt to send shutdowns to zombies - even though they failed to respond to + // some earlier request they may notice a shutdown, and it's much better that they shutdown + // politely than that an UnGraceful thread kill happens + if ( iState != ERunning && iState != EZombie ) + { + return KErrRSModuleNotRunning; + } + + __CFLOG_1(KLogSubSysRS, KLogCode, + _L8("CCommsProviderModule::UnLoading %S."), &iName); + + iState = EStopping; + + return KErrNone; + } + +TBool CCommsProviderModule::UidMatch(const TUid& aUid) const + /** Matches the Uid for a CPM + @param aUid The Uid to be checked + */ + { + return ((aUid.iUid==KUidCommsServerModule)||(aUid.iUid==KUidCommsProviderModule)); + } + +RThread CCommsProviderModule::GetThread() const + /** Return pointer to thread for a CPM + */ + { + return iThread; + } + +#ifdef __CFLOG_ACTIVE +const TText8* CCommsProviderModule::GetStateName() const + /** Returns a pointer to a text string identifying the module's current state + */ + { + switch(iState) + { + case EIdle: + return _S8("Idle"); + case EStarting: + return _S8("Starting"); + case ERunning: + return _S8("Running"); + case EStopping: + return _S8("Stopping"); + case EZombie: + return _S8("Zombie"); + case EDead: + return _S8("Dead"); + default: + return _S8(""); + } + } +#endif + +#if defined _DEBUG || defined __CFLOG_ACTIVE || defined SYMBIAN_TRACE_ENABLE +const TText8* CCommsProviderModule::GetUnloadTypeName(RootServer::TRSUnLoadType aType) + /** Returns a pointer to a text string identifying the requested module unload type + */ + { + // Determine the unload type. + switch(aType) + { + case EOptional: + return _S8("EOptional"); + case EGraceful: + return _S8("EGraceful"); + case EImmediate: + return _S8("EImmediate"); + case EUnGraceful: + return _S8("EUnGraceful"); + default: + return _S8(""); + } + } +#endif + +void CCommsProviderModule::Inhume() + { + iState = EZombie; + } + + +void CCommsProviderModule::RegisterSuddenDeathNotifierL(MNotifySuddenDeath* aNotifier) + /** Register a sudden death notifier. + Instantiates a container with the pointer to the notifier and + puts it in the TblQue list. + @param aNotifier The notifier to be registered. + */ + { + __CFLOG(KLogSubSysRS, KLogCode, _L8("CCommsProviderModule - RegisterSuddenDeathNotifier")); + CNotifySuddenDeathContainer* pContainer = new (ELeave) CNotifySuddenDeathContainer(aNotifier); + iNotifiers.AddLast(*pContainer); + } //lint !e429 // custody taken of pContainer + +void CCommsProviderModule::UnregisterSuddenDeathNotifier(MNotifySuddenDeath* aNotifier) + /** Unregister a sudden death notifier. + Searches for container with a pointer to notifier being unregistered, and if found deletes + unlinks the container and deletes it. Failure to find such a notifier is not an error as this + is typically a clean-up behaviour. + @param aNotifier The notifier to be unregistered. + */ + { + TDblQueIter notifierIter(iNotifiers); + CNotifySuddenDeathContainer *pContainer; + + while((pContainer = notifierIter++)!=NULL) + { + if(pContainer->Match(aNotifier)) + { + pContainer->Dequeue(); + delete pContainer; + } + } + } + +EXPORT_C void CCommsProviderModule::SuddenDeath(TInt aDeathTypeErr) + /** Controls events on death of a module + Notifies Bind Manager of death event, so that it can clean up + aReason The reason associated with the death event + @param aDeathTypeErr xxx + */ + { + #ifdef __CFLOG_ACTIVE + TPtrC8 stateName(GetStateName()); + __CFLOG_2(KLogSubSysRS, KLogCode, + _L8("CCommsProviderModule::SuddenDeath - %S module's thread died while module was in state: %S."), &iName, &stateName); + #endif + + iExitReason = iThread.ExitReason(); + if(aDeathTypeErr == KErrRSSuddenViolentDeath) + { + // Looks like a panic or a kill happened to it. Flag it as unstable to suppress the leak detection, since + // it never had a chance to clean-up + SetControlFlags(ControlFlags() | TRSStartModuleParamContainer::KCF_UnstableModule); + } + TInt result(KErrNone); + __CFLOG(KLogSubSysRS, KLogCode, + _L8("CCommsProviderModule::SuddenDeath - BM::UnbindAlreadyDeadL called")); + TRAP(result, iRootServer->BindManager()->UnbindAlreadyDeadL(iStatus, iName)); + SetActive(); + if( result != KErrNone ) + { + TRequestStatus *status = &iStatus; + User::RequestComplete(status, result); + } + if ( iState != EStopping ) + { + iState = EZombie; + } + } + + +void CCommsProviderModule::NotifyOfDeath(TInt aReason) + /** Controls notifications on death of a module + @param aReason The reason associated with the death event + */ + { + + TDblQueIter notifierIter(iNotifiers); + CNotifySuddenDeathContainer *pContainer; + + while((pContainer = notifierIter++)!=NULL) + { + pContainer->Notify(aReason); + pContainer->Dequeue(); + delete pContainer; + } + iNotifiers.Reset(); + } + +EXPORT_C void CCommsProviderModule::RunL() + /** Handles each active object completion event differently + depending on the state of the module + */ + { + #ifdef __CFLOG_ACTIVE + TPtrC8 stateName(GetStateName()); + __CFLOG_3(KLogSubSysRS, KLogCode, + _L8("CCommsProviderModule::RunL name: %S, state: %S, status: %d"), + &iName, &stateName, iStatus.Int()); + #endif + + TBool dieNow = EFalse; + switch(iState) + { + case EZombie: + dieNow = ETrue; + iRootServer->SuddenDeath(KErrRSSuddenDeath); // Notify Root Server + __CFLOG_1(KLogSubSysRS, KLogCode, + _L8("CCommsProviderModule::ModuleEndedL(%S) called"), &iName); + TRAPD(res, iRootServer->BindManager()->ModuleEndedL(iName)); // no use checking if this left...module is dead + iLink.Deque(); // Remove from list in Root Server + NotifyOfDeath(KErrRSSuddenDeath); + break; + case EIdle: + break; + case EStarting: + delete iThreadStartupInfo; // clean up startupinfo now, seeing as thread has rendezvoused + iThreadStartupInfo = NULL; + if (iStatus.Int()==KErrNone) + { + __CFLOG_1(KLogSubSysRS, KLogCode, + _L8("CCommsProviderModule::RunL - %S moving from Starting state to Running state"), &iName); + iState = ERunning; + iUndertaker->Logon(); // Monitor for thread death + } + else + { + __CFLOG_2(KLogSubSysRS, KLogCode, + _L8("CCommsProviderModule::RunL - %S dying now due to error %d"), &iName, iStatus.Int()); + dieNow = ETrue; + NotifyOfDeath(KErrRSSuddenDeath); + } + + User::RequestComplete(iReqStatus, iStatus.Int()); // trigger session handler + break; + case ERunning: + break; + case EStopping: + __CFLOG_1(KLogSubSysRS, KLogCode, + _L8("CCommsProviderModule::RunL - %S dying now"), &iName); + dieNow = ETrue; + __CFLOG_1(KLogSubSysRS, KLogCode, + _L8("CCommsProviderModule::RunL - ModuleEndedL(%S)"),&iName); + TRAP(res, iRootServer->BindManager()->ModuleEndedL(iName)); + __ASSERT_DEBUG(res==KErrNone, User::Panic(KSpecAssert_ElemRootServerrt, 3)); + iLink.Deque(); // Remove from list in Root Server + iRootServer->SuddenDeath(KErrNone); + NotifyOfDeath(iExitReason); + break; + case EDead: + dieNow = ETrue; + break; + default: + __CFLOG_1(KLogSubSysRS, KLogCode, _L8("CCommsProviderModule::RunL() unexpected case <%d>!"), iState); + } + + if (dieNow) + { + __CFLOG_1(KLogSubSysRS, KLogCode, + _L8("CCommsProviderModule::RunL - %S deleting self"), &iName); + if (IsAdded()) + { + Deque(); // Remove from active scheduler + } + iLibrary.Close(); // + delete this; // Disappear + } + } + + +CSessionHandler::CSessionHandler(CRootServerSession* aSession, CBindManager* aBindMgr, const RMessage2& aMessage): + CActive(EPriorityStandard), iRequestCompleted(EFalse), + iSession(aSession), iBindMgr(aBindMgr), iState(ESHIdle), iMessage(aMessage) + /** Abstract base class to handle session requests + @param aSession The root server session + @param aBindMgr Pointer to the bind manager + @param aMessage The message which will be completed + */ + { + RS_DETLOG((KLogSubSysRS, KLogCode, _L8("CSessionHandler(%X) - msgHdl = %x"), this, iMessage.Handle())); + CActiveScheduler::Add(this); + } + +CSessionHandler::~CSessionHandler() + { +// Although sometimes useful this logging statement has to be left suppressed because sessions can be +// deleted by the base CServer2 dtor, ie after CFLOG has been deleted by the RootServer dtor. +// RS_DETLOG((KLogSubSysRS, KLogCode, _L8("~CSessionHandler(%X)"), this)); + Cancel(); + if (IsAdded()) + { + Deque(); + } + iLink.Deque(); + } + +void CSessionHandler::SuddenDeath(TInt aError) + /** Only called if not over-ridden by derived class, but this + should never be the case. + @param aError The reason associated with the death event + */ + { + (void) aError; // suppress error when not logging + __CFLOG_VAR((KLogSubSysRS, KLogCode, _L8("CSessionHandler::SuddenDeath(%d)"), aError)); + User::Panic(RThread().Name(), KRSBadState); // derived class should override this for cases where it can legiarise + } + +CCommsProviderModule* CSessionHandler::GetModulePtr(const TCFModuleNameF& aName) const + /** Retrieves module pointer + @param aName The name to retrieve module pointer for + @return Pointer to module + */ + { + CRootServer* rootServer = iSession->RootServer(); + return rootServer->FindCpm( aName ); + } + +void CSessionHandler::CompleteClientRequest(TInt aRequest) + /** Complete Request + @param aRequest The reason associated with the completion event + */ + { + if (!iRequestCompleted) + { + RS_DETLOG((KLogSubSysRS, KLogCode, _L8("CSessionHandler(%X)::CompleteClientRequest(hdl %x = %d)"), this, iMessage.Handle(), aRequest)); + iMessage.Complete(aRequest); + iRequestCompleted = ETrue; + } + } + +CSessionHandlerLoadCPM::CSessionHandlerLoadCPM(CRootServerSession* aSession, CBindManager* aBindMgr, const RMessage2& aMessage) + : CSessionHandler(aSession, aBindMgr, aMessage) + /** Constructor for Derived session handler + @param aSession The root server session + @param aBindMgr Pointer to Bind Manager + */ + { + } + +CSessionHandlerLoadCPM::~CSessionHandlerLoadCPM() + /** Destructor for Derived session handler + */ + { + delete iLoadCompletionNotificationWaiter; + } + +CSessionHandlerLoadCPM* CSessionHandlerLoadCPM::NewL(CRootServerSession* aSession, CBindManager* aBindMgr, const RMessage2& aMessage) + /** Create New instance of this session handler + @param aSession The root server session + @param aBindMgr Pointer to Bind Manager + */ + { + CSessionHandlerLoadCPM* pSession = new(ELeave) CSessionHandlerLoadCPM(aSession, aBindMgr, aMessage); + CleanupStack::PushL(pSession); + pSession->ConstructL(); + CleanupStack::Pop(); + return pSession; + } + +CSessionHandler::TSHType CSessionHandlerLoadCPM::HandlerType() + /** Identifies the subclass type + */ + { + return ESHTypeLoadCPM; + } + +TInt CSessionHandlerLoadCPM::Start() + /** Starts the load process + */ + { + iState = ESHStarting; + TInt res = KErrNotFound; + CCommsProviderModule* modulePtr = GetModulePtr(iName); + if(modulePtr) + { + res = modulePtr->Load(iStatus); + SetActive(); + } + return res; + } + + +void CSessionHandlerLoadCPM::SetHandler(const TCFModuleNameF& aModule) + /** Set up this handler + @param aModule The name of the module handled + */ + { + RS_DETLOG((KLogSubSysRS, KLogCode, _L8("CSessionHandlerLoadCPM - Setting up handler"))); + iName = aModule; + } + +TBool CSessionHandlerLoadCPM::CancelHandler(TBool aCompleteRequest, TBool aWaitForCompletionIfUncancelable) + /** Cancel Handler attempts to cancel request + @param aCompleteRequest Whether to complete request or not + @param aWaitForCompletionIfUncancelable Whether to wait for completion in a nested active scheduler (option used during session cleanup) + @return Returns ETrue if the handler should be deleted by the caller or EFalse if the handler will delete itself + */ + { + // Pretend message has already been completed if + // we are asked not to and it hasn't already + if ((!aCompleteRequest) && (!iRequestCompleted)) + { + iRequestCompleted = ETrue; + } + + TBool prevIsActive = IsActive(); + TInt prevStatus = iStatus.Int(); + + Cancel(); + + // If the module's thread is already starting, reactivate the load handler + // because it is no longer possible to cancel the load operation and we + // need to let the load handler finish. + if(iStatus.Int() == KErrRSModuleAlreadyExist) + { + // We should not complete the request now if the cancel did not succeed + // regardless of the value of the aCompleteRequest parameter: the + // operation is still in progress. + aCompleteRequest = EFalse; + + // Restore the previous state of our active object before cancellation was + // attempted. + iStatus = prevStatus; + if(prevIsActive) + { + SetActive(); + } + + // Check to see if we should wait for the uncancelable operation to + // complete. This is necessary if the session that initiated the operation + // is closing or else the handler will be orphaned and the completion signal + // will go astray. + if(aWaitForCompletionIfUncancelable) + { + __ASSERT_DEBUG(iLoadCompletionNotificationWaiter, User::Panic(KSpecAssert_ElemRootServerrt, 4)); + + // Start a nested scheduler to process the signals required to complete + // the operation. Not the neatest solution, but the least invasive. + // Otherwise CloseSession would be have to be broken into steps and + // restructured around an active object. + // CSessionHandlerLoadCPM::Cancel's semantics are arguably broken anyway + // since it is not guaranteed to work and doesn't wait for completion of + // the load module operation if it fails. + CActiveSchedulerWait* waiter = iLoadCompletionNotificationWaiter; + waiter->Start(); + + delete waiter; + } + + return EFalse; + } + + // Must complete the client AFTER we have canceled all unload operations. + if (aCompleteRequest) + { + CompleteClientRequest(iStatus.Int()); + } + + return ETrue; + } + + +CSessionHandler* CSessionHandlerLoadCPM::MatchPending(const TCFModuleNameF& aModule) + /** Match a pending load + @param aModule The name of the loading module to be found + */ + { + if ( aModule != iName ) + { + return NULL; + } + else + { + __CFLOG(KLogSubSysRS, KLogCode, _L8("CSessionHandlerLoadCPM - Found match")); + return this; + } + } + +void CSessionHandlerLoadCPM::ConstructL() + { + // We preallocate this in case we need it since it is used in + // certain session cleanup situations. + iLoadCompletionNotificationWaiter = new(ELeave) CActiveSchedulerWait; + } + +void CSessionHandlerLoadCPM::CompleteClientRequest(TInt aRequest) + /** Complete Request + @param aRequest The reason associated with the completion event + */ + { + if (!iRequestCompleted) + { + RS_DETLOG((KLogSubSysRS, KLogCode, _L8("CSessionHandlerLoadCPM(%X)::CompleteClientRequest(hdl %x = %d)"), this, iMessage.Handle(), aRequest)); + iMessage.Complete(aRequest); + iRequestCompleted = ETrue; + } + + // Complete a caller waiting in a nested scheduler for us to complete. + // This is necessary to handle the corner case where a load module + // operation was not cancelled in time and a session needs to wait for + // the handler to complete before the session is closed. + if(iLoadCompletionNotificationWaiter && iLoadCompletionNotificationWaiter->IsStarted()) + { + iLoadCompletionNotificationWaiter->AsyncStop(); + + // Waiter is now responsible for deletion for safety - we will + // probably delete ourselves directly after this function call. + iLoadCompletionNotificationWaiter = NULL; + } + } + +void CSessionHandlerLoadCPM::RunL() + /** Handles different states of handler as load progresses + */ + { + __CFLOG_1(KLogSubSysRS, KLogCode, + _L8("CSessionHandlerLoadCPM::RunL status %d"), iStatus.Int()); + CCommsProviderModule* modulePtr = GetModulePtr(iName); + + switch(iState) + { + case ESHStarting: + // We get here when the module's thread Rendezvous()s + if (iStatus.Int() == KErrNone) + { + iState = ESHLoading; + if(modulePtr) + { + RCFChannel::TMsgQueues inputQueue; + RCFChannel::TMsgQueues outputQueue; + modulePtr->GetSendChannel().GetMsgQueues(inputQueue); + modulePtr->GetRecvChannel().GetMsgQueues(outputQueue); + __CFLOG_1( KLogSubSysRS, KLogCode, + _L8("CSessionHandlerLoadCPM called BM::CreateModuleL(%S)" ),&iName); + TRAPD(res, iBindMgr->CreateModuleL(iStatus, modulePtr->GetName(), + inputQueue, outputQueue)); + SetActive(); + if (res != KErrNone) + { + __CFLOG_2(KLogSubSysRS, KLogCode, + _L8("CSessionHandlerLoadCPM::Load: Unable to register %S thread with BindMgr - will kill it: %d.."), + &iName, res); + // This is invidious - the thread lives but the bindmgr can't register it + TRequestStatus* status = &iStatus; + User::RequestComplete(status, res); + delete modulePtr; + } + } + } + else + { + // if rendezvous with !KErrNone, just complete, cos object is deleted + __CFLOG(KLogSubSysRS, KLogCode,_L8("CSessionHandlerLoadCPM::RunL bad rendezvous")); + CompleteClientRequest(iStatus.Int()); + delete this; + } + break; + case ESHLoading: + if ( iStatus.Int() == KErrNone ) + { + __CFLOG_1(KLogSubSysRS, KLogCode, + _L8("CSessionHandlerLoadCPM::RunL %S loaded sucessfully"), + &iName); + CompleteClientRequest( iStatus.Int()); + delete this; + } + else + { + // load failed so we want to kill our thread! + __CFLOG_1(KLogSubSysRS, KLogCode, + _L8("CSessionHandlerLoadCPM::RunL %S load failed"), + &iName); + if(modulePtr) + { + // the thread has failed discovery so the client must + // decide whether it wants to kill the module as the rootserver + // now has a case of the living dead... + modulePtr->Inhume(); + CompleteClientRequest(KErrRSZombie); + delete this; + } + else + { + // there is no valid module representation in the rootserver + // so this module must have had a sudden death after rendezvous + // and did not complete loading fully + CompleteClientRequest(KErrServerTerminated); + delete this; + } + } + break; + case ESHCompleting: + case ESHKilling: + case ESHIdle: + case ESHBinding: + case ESHUnBinding: + case ESHUnLoading: + default: + User::Panic(RThread().Name(), KRSBadState); + } + } + +void CSessionHandlerLoadCPM::DoCancel() + /** Attempt to cancel request + */ + { + CCommsProviderModule* modulePtr = GetModulePtr(iName); + __ASSERT_DEBUG(modulePtr!=NULL, User::Panic(RThread().Name(), KRSModuleRefMissing)); + + TInt cancelLoadResult = KErrRSModuleUnknown; + + if (modulePtr) + { + cancelLoadResult = modulePtr->CancelLoad(); + } + + if((cancelLoadResult == KErrCancel) || (cancelLoadResult == KErrRSModuleUnknown)) + { + if(iBindMgr->Cancel(iStatus) != KErrNone) + { + TRequestStatus* pStatus = &iStatus; + User::RequestComplete(pStatus, cancelLoadResult); + } + + // Must complete the client AFTER we have canceled all load operations. + CompleteClientRequest(KErrCancel); + } + else + { + TRequestStatus* pStatus = &iStatus; + User::RequestComplete(pStatus, cancelLoadResult); + } + } + +void CSessionHandlerLoadCPM::SuddenDeath(TInt aError) + /** Completes message on death of a module + @param aError The reason associated with the death event + */ + { + __CFLOG(KLogSubSysRS, KLogCode, + _L8("CSessionHandlerLoadCPM - Notified of SuddenDeath")); + CompleteClientRequest(aError); + delete this; + + } + +CSessionHandlerUnLoadCPM::CSessionHandlerUnLoadCPM(CRootServerSession* aSession, CBindManager* aBindMgr, const RMessage2& aMessage) + :CSessionHandler(aSession,aBindMgr,aMessage) + /** Constructor for Derived session handler + @param aSession The root server session + @param aBindMgr Pointer to Bind Manager + */ + { + } + +CSessionHandlerUnLoadCPM::~CSessionHandlerUnLoadCPM() + /** Destructor for Derived session handler + */ + { + delete iLoadCompletionNotificationWaiter; + } + +CSessionHandlerUnLoadCPM* CSessionHandlerUnLoadCPM::NewL(CRootServerSession* aSession, CBindManager* aBindMgr, const RMessage2& aMessage) + /** Create New instance of this session handler + @param aSession The root server session + @param aBindMgr Pointer to Bind Manager + */ + { + CSessionHandlerUnLoadCPM* pSession = new(ELeave) CSessionHandlerUnLoadCPM(aSession, aBindMgr,aMessage); + CleanupStack::PushL(pSession); + pSession->ConstructL(); + CleanupStack::Pop(); + return pSession; + } + +CSessionHandler::TSHType CSessionHandlerUnLoadCPM::HandlerType() + /** Identifies the subclass type + */ + { + return ESHTypeUnLoadCPM; + } + +TInt CSessionHandlerUnLoadCPM::Start() + /** Starts unload procedure + We have 2 types of unload: Graceful and Ungraceful. + Each unload works differently, but ultimately ends in the module + being removed + */ + { + RS_DETLOG((KLogSubSysRS, KLogCode, _L8("CSessionHandlerUnLoadCPM(%X)::Start(%S)"), this, &iName)); + + CCommsProviderModule* modulePtr = GetModulePtr(iName); + // we have already checked it is running, so if we can't find it now... + __ASSERT_ALWAYS( modulePtr!=NULL, User::Panic(RThread().Name(), KRSModuleRefMissing)); + + TInt result; + switch(modulePtr->GetState()) + { + case EStarting: + case EStopping: + // If it's an optional or graceful request then module needs to be in the running state + if(iType == EUnGraceful) + break; + delete this; + return KErrRSModuleNotRunning; + case EDead: + return KErrRSModuleNotRunning; + default: + // Reassure the tools that we're ignoring other values + break; + } + + switch(iType) + { + case EOptional: + case EGraceful: + case EImmediate: + __CFLOG_1(KLogSubSysRS, KLogCode, + _L8( "CSessionHandlerUnLoadCPM - BM::UnbindAllAndShutDownL(%S)"), + &iName ); + // fire off the asynchronous request + TRAP(result, iBindMgr->UnbindAllAndShutDownL(iStatus, iName, iType)); + SetActive(); + if (result == KErrNone) + { + result = modulePtr->Unload(); + // we have already checked module is running so... + __CFLOG_1(KLogSubSysRS, KLogCode, + _L8( "CSessionHandlerUnLoadCPM - error %d"), result); + __ASSERT_DEBUG(result==KErrNone, User::Panic(RThread().Name(), KRSBindMgrError)); + iState = ESHUnLoading; + } + else + { + __CFLOG_1(KLogSubSysRS, KLogCode, + _L8("CSessionHandlerUnLoadCPM - BM unable to unload %S gracefully"), + &iName); + TRequestStatus* status = &iStatus; + User::RequestComplete(status, result); + } + break; + case EUnGraceful: + iState = ESHKilling; + TRAPD( res, modulePtr->RegisterSuddenDeathNotifierL(this)); + if (res!=KErrNone) + { + CompleteClientRequest(res); + delete this; + } + else + { + __CFLOG_1(KLogSubSysRS, KLogCode, + _L8("CSessionHandlerUnLoadCPM - Unloading %S ungracefully"), + &iName); + modulePtr->GetThread().Kill(KErrCancel); +#ifdef SYMBIAN_C32ROOT_API_V3 + // Any module whose thread is terminated must be regarded as unstable: no + // point in checking their cleanup when they had no chance to do any + modulePtr->SetControlFlags(modulePtr->ControlFlags() | TRSStartModuleParamContainer::KCF_UnstableModule); +#endif + } + // this is all, cos we have already set in motion the events which + // will call our RunL and complete the message and clean up + break; + default: + User::Panic(RThread().Name(), KRSBadState); + } + return KErrNone; + } + +void CSessionHandlerUnLoadCPM::SetHandler(const TCFModuleNameF& aModule, TRSUnLoadType aType) + /** + @param aModule The name of the module handled + @param aType The type of unload - graceful/ungraceful + */ + { + #ifdef __CFLOG_ACTIVE + TPtrC8 unloadType = CCommsProviderModule::GetUnloadTypeName(aType); + __CFLOG_1(KLogSubSysRS, KLogCode, _L8("CSessionHandlerUnLoadCPM - Setting up handler for an %S unload"), &unloadType); + #endif + + iName = aModule; + iType = aType; + } + +TBool CSessionHandlerUnLoadCPM::CancelHandler(TBool aCompleteRequest, TBool aWaitForCompletionIfUncancelable) + /** Cancel handler attempts to cancel request + @param aCompleteRequest Whether to complete request or not + @param aWaitForCompletionIfUncancelable Whether to wait for completion in a nested active scheduler (option used during session cleanup) + @return Returns ETrue if the handler should be deleted by the caller or EFalse if the handler will delete itself + */ + { + // Although sometimes useful this logging statement has to be left suppressed because sessions can be + // deleted by the base CServer2 dtor, ie after CFLOG has been deleted by the RootServer dtor. + //__CFLOG(KLogSubSysRS, KLogCode, _L8("CSessionHandlerUnLoadCPM::CancelHandler")); + + // Pretend message has already been completed if + // we are asked not to and it hasn't already + if ((!aCompleteRequest) && (!iRequestCompleted)) + { + iRequestCompleted = ETrue; + } + + TBool prevIsActive = IsActive(); + TInt prevStatus = iStatus.Int(); + + // We cannot place this code inside DoCancel because the handler may not be waiting for any + // operation to complete so DoCancel will not be called. + TInt cancelUnloadResult = KErrRSModuleUnknown; + CCommsProviderModule* module = GetModulePtr(iName); + if (module) + { + cancelUnloadResult = module->CancelUnload(iType); + + if(cancelUnloadResult == KErrCancel) + { + module->UnregisterSuddenDeathNotifier(this); + } + } + + // If the module unload is still cancelable, cancel any bind operations and + // complete the client. + if((cancelUnloadResult == KErrCancel) || (cancelUnloadResult == KErrRSModuleUnknown)) + { + // Cancel any bind operations. + Cancel(); + + // Must complete the client request AFTER canceling any bind operations. + CompleteClientRequest(KErrCancel); + } + + // Else, if the module's thread is already dying, reactivate the unload handler + // because it is no longer possible to cancel the unload operation and we need + // to let the unload handler finish. + else if(cancelUnloadResult == KErrRSModuleNotLoaded) + { + // We should not complete the request now if the cancel did not succeed + // regardless of the value of the aCompleteRequest parameter: the + // operation is still in progress. + aCompleteRequest = EFalse; + + // Restore the previous state of our active object before cancellation was + // attempted. + iStatus = prevStatus; + if(prevIsActive) + { + SetActive(); + } + + // Check to see if we should wait for the uncancelable operation to + // complete. This is necessary if the session that initiated the operation + // is closing or else the handler will be orphaned and the completion signal + // will go astray. + if(aWaitForCompletionIfUncancelable) + { + __ASSERT_DEBUG(iLoadCompletionNotificationWaiter, User::Panic(KSpecAssert_ElemRootServerrt, 5)); + + // Start a nested scheduler to process the signals required to complete + // the operation. Not the neatest solution, but the least invasive. + // Otherwise CloseSession would be have to be broken into steps and + // restructured around an active object. + // CSessionHandlerUnLoadCPM::Cancel's semantics are arguably broken anyway + // since it is not guaranteed to work and doesn't wait for completion of + // the load module operation if it fails. + CActiveSchedulerWait* waiter = iLoadCompletionNotificationWaiter; + waiter->Start(); + + delete waiter; + } + + return EFalse; + } + + // Must complete the client AFTER we have canceled all unload operations. + if (aCompleteRequest) + { + CompleteClientRequest(iStatus.Int()); + } + + return ETrue; + } + + +CSessionHandler* CSessionHandlerUnLoadCPM::MatchPending(const TCFModuleNameF& aModule) + /** Match a pending load + @param aModule The name of the loading module to be found + */ + { + if (aModule != iName) + { + return NULL; + } + else + { + __CFLOG(KLogSubSysRS, KLogCode,_L8("CSessionHandlerUnLoadCPM - Found match")); + return this; + } + } + +void CSessionHandlerUnLoadCPM::ConstructL() + { + // We preallocate this in case we need it since it is used in + // certain session cleanup situations. + iLoadCompletionNotificationWaiter = new(ELeave) CActiveSchedulerWait; + } + +void CSessionHandlerUnLoadCPM::CompleteClientRequest(TInt aRequest) + /** Complete Request + @param aRequest The reason associated with the completion event + */ + { + if (!iRequestCompleted) + { + RS_DETLOG((KLogSubSysRS, KLogCode, _L8("CSessionHandlerUnLoadCPM(%X)::CompleteClientRequest(hdl %x = %d)"), this, iMessage.Handle(), aRequest)); + iMessage.Complete(aRequest); + iRequestCompleted = ETrue; + } + + // Complete a caller waiting in a nested scheduler for us to complete. + // This is necessary to handle the corner case where an unload module + // operation was not cancelled in time and a session needs to wait for + // the handler to complete before the session is closed. + if(iLoadCompletionNotificationWaiter && iLoadCompletionNotificationWaiter->IsStarted()) + { + iLoadCompletionNotificationWaiter->AsyncStop(); + + // Waiter is now responsible for deletion for safety - we will + // probably delete ourselves directly after this function call. + iLoadCompletionNotificationWaiter = NULL; + } + } + +void CSessionHandlerUnLoadCPM::RunL() + /** Deal with unloading as progresses through different states + */ + { + // first time we come in here will be on a successful + // bindmgr request. we only ignore as we will be back in here + // after triggered from sudden death event + __CFLOG_1(KLogSubSysRS, KLogCode, + _L8("CSessionHandlerUnloadCPM::RunL status %d"), iStatus.Int()); + + CCommsProviderModule* module = GetModulePtr(iName); + switch(iState) + { + case ESHUnLoading: + if(iStatus.Int()==KErrNone) + { + iState = ESHKilling; + // if we get a valid pointer then we are not too late to get a notification + // of death from module. If we are too late, then module is already dead + // so we are happy and can complete message + if (module) + { + TRAPD(res, module->RegisterSuddenDeathNotifierL(this)); + if(res!=KErrNone) + { + CompleteClientRequest(res); + delete this; + } + else + { + __CFLOG(KLogSubSysRS, KLogCode, + _L8("CSessionHandlerUnloadCPM::RunL now waiting for Sudden Death")); + } + } + else + { + // module already dead + CompleteClientRequest(iStatus.Int()); + delete this; + } + } + else + { + // BM could not unload cleanly so return to client and let them decide + if (module) + { + module->CancelUnload(iType); + } + CompleteClientRequest(iStatus.Int()); + delete this; + } + break; + case ESHKilling: + __CFLOG(KLogSubSysRS, KLogCode,_L8("CSessionHandlerUnloadCPM::RunL Successfully unloaded")); + CompleteClientRequest(iStatus.Int()); + delete this; + break; + case ESHIdle: + __CFLOG_1(KLogSubSysRS, KLogCode,_L8("CSessionHandlerUnloadCPM::RunL Unbind request failed (%d)"), iStatus.Int()); + CompleteClientRequest(iStatus.Int()); + delete this; + break; + default: + User::Panic(RThread().Name(), KRSBadState); + } + } + +void CSessionHandlerUnLoadCPM::DoCancel() + /** Attempt to cancel request + */ + { + if(iBindMgr->Cancel(iStatus) != KErrNone) + { + TRequestStatus* pStatus = &iStatus; + User::RequestComplete(pStatus, KErrCancel); + } + } + +void CSessionHandlerUnLoadCPM::SuddenDeath(TInt aError) + /** Completes message on death of a module + @param aError The reason associated with the death event + */ + { + __CFLOG(KLogSubSysRS, KLogCode, _L8("CSessionHandlerUnLoadCPM - Notified of SuddenDeath")); + if(iType == EUnGraceful) + { + aError = KErrNone; // client requested this sudden death, so make it look less surprising + } + CompleteClientRequest(aError); + delete this; + + } + +CSessionHandlerBinding::CSessionHandlerBinding(CRootServerSession* aSession, CBindManager* aBindMgr, const RMessage2& aMessage) + : CSessionHandler(aSession, aBindMgr,aMessage) + /** Constructor for Derived session handler + @param aSession The root server session + @param aBindMgr Pointer to Bind Manager + @param aMessage The message which will be completed + */ + { + } + +CSessionHandlerBinding* CSessionHandlerBinding::NewL(CRootServerSession* aSession, CBindManager* aBindMgr, const RMessage2& aMessage) + /** Create New instance of this session handler + @param aSession The root server session + @param aBindMgr Pointer to Bind Manager + @param aMessage The message which will be completed + */ + { + CSessionHandlerBinding* pSession = new(ELeave) CSessionHandlerBinding(aSession, aBindMgr,aMessage); + return pSession; + } + +CSessionHandler::TSHType CSessionHandlerBinding::HandlerType() + /** Identifies the subclass type + */ + { + return ESHTypeBinding; + } + +TInt CSessionHandlerBinding::Start() + /** Starts Bind request + */ + { + iState = ESHBinding; + __CFLOG(KLogSubSysRS, KLogCode,_L8("CSessionHandlerBinding - Call BM::BindSubModulesL")); + TRAPD(result, iBindMgr->BindSubmodulesL(iStatus, iBindType, iNameFrom, iNameTo, iForwardQLength, iReverseQLength)); + SetActive(); + if (result != KErrNone) + { + __CFLOG_1(KLogSubSysRS, KLogCode, + _L8( "CSessionHandlerBinding - BM::BindSubmodulesL left with %d"), result); + // trigger our runL to deal with aftermath of leave + TRequestStatus* status = &iStatus; + User::RequestComplete(status, result); + } + return KErrNone; + } + +CSessionHandler* CSessionHandlerBinding::MatchPending(const TCFSubModuleAddress& aFrom, + const TCFSubModuleAddress& aTo) + /** Match a pending load + @param aFrom The name of one module involved + @param aTo The name of the other module involved + */ + { + + if ((iNameFrom == aFrom) && (iNameTo == aTo)) + { + return this; + } + else + { + return NULL; + } + } + +void CSessionHandlerBinding::SetHandler(const TCFSubModuleAddress& aFrom, + const TCFSubModuleAddress& aTo, TRSBindType& aBindType, + TInt aForwardQLength, TInt aReverseQLength) + /** Set up this handler + @param aFrom The name of one module handled + @param aTo The name of the other module handled + @param aBindType The type of bind + @param aForwardQLength Number of slots in queue from->to + @param aReverseQLength Number of slots in queue to->from + */ + { + iNameFrom = aFrom; + iNameTo = aTo; + iBindType = aBindType; + iForwardQLength = aForwardQLength; + iReverseQLength = aReverseQLength; + } + +TBool CSessionHandlerBinding::CancelHandler(TBool aCompleteRequest, const TBool /*aWaitForCompletionIfUncancelable*/) + /** Cancel Handler attempts to cancel request + @param aCompleteRequest Whether to complete request or not + @param aWaitForCompletionIfUncancelable Whether to wait for completion in a nested active scheduler (option used during session cleanup) + @return Returns ETrue if the handler should be deleted by the caller or EFalse if the handler will delete itself + */ + { + // Pretend message has already been completed if + // we are asked not to complete it, and it hasn't already + iState = ESHCompleting; + + if ((!aCompleteRequest) && (!iRequestCompleted)) + { + iRequestCompleted = ETrue; + } + +// Although sometimes useful this logging statement has to be left suppressed because sessions can be +// deleted by the base CServer2 dtor, ie after CFLOG has been deleted by the RootServer dtor. +// __CFLOG(KLogSubSysRS, KLogCode,_L8("CSessionHandlerBinding - Cancel Handler")); + + Cancel(); + + return ETrue; + } + +void CSessionHandlerBinding::RunL() + /** Handles the aftermath of a bind request. Fills TRSBindingInfo struct + with bind state for each sub-module involved + */ + { + __CFLOG_1( KLogSubSysRS, KLogCode,_L8("CSessionHandlerBinding::RunL status %d"), + iStatus.Int() ); + + // Check completion code + TInt res=iStatus.Int() ; + if (res!=KErrNone) + { + CompleteClientRequest(res); + delete this; + return; + } + + // Get BindInfo structure from client, we might need to put stuff in it + // and write it back before completing the client + TRSBindingInfo bindInfo; + res = iMessage.Read(0, bindInfo); + if (res!=KErrNone) + { + CompleteClientRequest(res); + delete this; + return; + } + + CBindManager::TBindingInfo info; + TBool found=EFalse; + switch(iState) + { + case ESHBinding: + // Do while more bindings and match not found + res=iBindMgr->EnumerateBindings(iNameFrom, ETrue, info); + while( (KErrNone==res) && (!found)) + { + if(EHierarchical==bindInfo.iParams.iType) + { + found = ((info.iSubModuleAddr2==iNameTo)&&(info.iSubModuleAddr1==iNameFrom)); + } + else // if type == ECustom + { + found = (((info.iSubModuleAddr2==iNameTo)&&(info.iSubModuleAddr1==iNameFrom)) || + ((info.iSubModuleAddr1==iNameTo)&&(info.iSubModuleAddr2==iNameFrom)) ); + } + + if(found) + { + __CFLOG(KLogSubSysRS, KLogCode, + _L8("CSessionHandlerBinding found a match in bind info")); + bindInfo.iParams.iState1 = info.iSubModuleState1; + bindInfo.iParams.iState2 = info.iSubModuleState2; + } + else // if not found + { + res=iBindMgr->EnumerateBindings(iNameFrom, EFalse, info); + } + } + + // We succeeded bindingm but have no knowledge of the binding? ASSERT + __ASSERT_DEBUG(found, User::Panic(RThread().Name(), KRSBadState)); + + // Write the info struct back to client, complete the RMessage and delete this + res = iMessage.Write(0, bindInfo); + if (KErrNone==res) + { + res=iStatus.Int(); + } + + CompleteClientRequest(res); + delete this; + break; + + + case ESHCompleting: + CompleteClientRequest(iStatus.Int()); + delete this; + break; + default: + User::Panic(RThread().Name(), KRSBadState); + } + } + +void CSessionHandlerBinding::DoCancel() + /** Attempt to cancel request + */ + { + TInt res = iBindMgr->Cancel(iStatus); + if (res!=KErrNone) + { + TRequestStatus *status = &iStatus; + User::RequestComplete(status, KErrCancel); + } + + // Must complete the client request AFTER canceling any bind operations. + CompleteClientRequest(KErrCancel); + } + +CSessionHandlerUnBinding::CSessionHandlerUnBinding(CRootServerSession* aSession, CBindManager* aBindMgr, const RMessage2& aMessage) + : CSessionHandler(aSession, aBindMgr,aMessage) + /** Constructor for Derived session handler + @param aSession The root server session + @param aBindMgr Pointer to Bind Manager + */ + { + } + +CSessionHandlerUnBinding* CSessionHandlerUnBinding::NewL(CRootServerSession* aSession, CBindManager* aBindMgr, const RMessage2& aMessage) + /** Create New instance of this session handler + @param aSession The root server session + @param aBindMgr Pointer to Bind Manager + @param aMessage The message which will be completed + */ + { + CSessionHandlerUnBinding* pSession = new(ELeave) CSessionHandlerUnBinding(aSession, aBindMgr,aMessage); + return pSession; + } + +CSessionHandler::TSHType CSessionHandlerUnBinding::HandlerType() + /** Identifies the subclass type + */ + { + return ESHTypeUnBinding; + } + +TInt CSessionHandlerUnBinding::Start() + /** Starts the unbinding process + */ + { + iState = ESHUnBinding; + __CFLOG(KLogSubSysRS, KLogCode,_L8("SessionHandlerUnBind - BM::UnbindSubmodulesL")); + TRAPD(result, iBindMgr->UnbindSubmodulesL(iStatus, iNameFrom, iNameTo)); + SetActive(); + if (result != KErrNone) + { + // trigger our runL to deal with aftermath of leave + TRequestStatus* status = &iStatus; + User::RequestComplete(status, result); + } + return KErrNone; + } + +CSessionHandler* CSessionHandlerUnBinding::MatchPending(const TCFSubModuleAddress& aFrom, + const TCFSubModuleAddress& aTo) + /** Match a pending load + @param aFrom The name of one module involved + @param aTo The name of the other module involved + */ + { + + if ((iNameFrom == aFrom) && (iNameTo == aTo)) + { + return this; + } + else return NULL; + + } + +void CSessionHandlerUnBinding::SetHandler(const TCFSubModuleAddress& aFrom, + const TCFSubModuleAddress& aTo) + /** Set up this handler + @param aFrom The name of one module handled + @param aTo The name of the other module handled + */ + { + iNameFrom = aFrom; + iNameTo = aTo; + } + +TBool CSessionHandlerUnBinding::CancelHandler(TBool aCompleteRequest, const TBool /*aWaitForCompletionIfUncancelable*/) + /** Cancel Handler attempts to cancel request + @param aCompleteRequest Whether to complete request or not + @param aWaitForCompletionIfUncancelable Whether to wait for completion in a nested active scheduler (option used during session cleanup) + @return Returns ETrue if the handler should be deleted by the caller or EFalse if the handler will delete itself + */ + { + iState = ESHCompleting; + // Pretend message has already been completed if + // we are asked not to and it hasn't already + if ((!aCompleteRequest) && (!iRequestCompleted)) + { + iRequestCompleted = ETrue; + } + +// Although sometimes useful this logging statement has to be left suppressed because sessions can be +// deleted by the base CServer2 dtor, ie after CFLOG has been deleted by the RootServer dtor. +// __CFLOG( KLogSubSysRS, KLogCode,_L8("CSessionHandlerUnBinding - Cancel Handler")); + + Cancel(); + + return ETrue; + } + +void CSessionHandlerUnBinding::RunL() + /** Handles aftermath of unbind, by completing the message + */ + { + __CFLOG_1(KLogSubSysRS, KLogCode,_L8("CSessionHandlerUnBinding::RunL status %d"), + iStatus.Int()); + CompleteClientRequest(iStatus.Int()); + delete this; + } + +void CSessionHandlerUnBinding::DoCancel() + /** Attempt to cancel request + */ + { + CompleteClientRequest(KErrCancel); + TInt res = iBindMgr->Cancel(iStatus); + if (res!=KErrNone) + { + TRequestStatus *status = &iStatus; + User::RequestComplete(status, KErrCancel); + } + } + +CRSTransientModuleInfo::CRSTransientModuleInfo(const RCFChannel::TMsgQueues& aTxQueues, + const RCFChannel::TMsgQueues& aRxQueues, + TRSHeapType aThreadHeapType, + TThreadFunction aLibEntry, + HBufC8* aIniDataOnRSHeap, + TThreadPriority aPriority, + TUint32 aControlFlags) +: iThreadHeapType(aThreadHeapType), + iLibEntry(aLibEntry), + iIniDataOnRSHeap(aIniDataOnRSHeap), + iPriority(aPriority), + iControlFlags(aControlFlags) + { + iCFModuleInfo.iTxQueues=aTxQueues; + iCFModuleInfo.iRxQueues=aRxQueues; + } + +CRSTransientModuleInfo::~CRSTransientModuleInfo() + { + delete iIniDataOnRSHeap; + } + +TInt CRSTransientModuleInfo::ModuleThreadStartup(TAny* aArg) + { + CRSTransientModuleInfo* startupInfo = (CRSTransientModuleInfo*) aArg; + + if (startupInfo->iControlFlags & TRSStartModuleParamContainer::KCF_ThreadAsSystem) + { + User::SetCritical( User::ESystemCritical); + } + else + { + User::SetCritical( User::ENotCritical); + } + + RThread().SetPriority(startupInfo->iPriority); + + // Duplicate the ini data into the new thread's heap. + // ** NOTE FOR CPM IMPLEMENTORS ** + // It is an explicit responsibility to take ownership of the iIniData and to delete it + if(startupInfo->iIniDataOnRSHeap) + { + startupInfo->iCFModuleInfo.iIniData = startupInfo->iIniDataOnRSHeap->Alloc(); + if(!startupInfo->iCFModuleInfo.iIniData ) + { + return KErrNoMemory; + } + } + else + { + startupInfo->iCFModuleInfo.iIniData = startupInfo->iIniDataOnRSHeap; + } + return startupInfo->iLibEntry(&startupInfo->iCFModuleInfo); + } + +