diff -r 000000000000 -r dfb7c4ff071f serialserver/c32serialserver/SCOMM/cs_roles.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serialserver/c32serialserver/SCOMM/cs_roles.cpp Thu Dec 17 09:22:25 2009 +0200 @@ -0,0 +1,3221 @@ +// Copyright (c) 2005-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 +#include +#include +#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS +#include +#endif +#include "cs_roles.h" +#include "C32LOG.H" +#include "CS_STD.H" +#include "cs_msgs.h" +#include "cs_glob.h" + +using namespace RootServer; + +#define MSG_PRM(prmIndex) (prmIndex) // subsession is passed in message as parameter 3 + +const TInt KIniDataBufSizeIncrement = 60; // Initial buffer of 60 chars - after finding + // usual iniData section uses about 40 chars to describe workerId,Role and CSYlist + +// +// CC32WorkerDataGlobals +// + +CC32WorkerDataGlobals* CC32WorkerDataGlobals::NewL() + { + CC32WorkerDataGlobals* self = new(ELeave) CC32WorkerDataGlobals(); + CleanupStack::PushL(self); + self->ConstructL(sizeof(TC32WorkerThreadRegister), KMaxWorkerThreadId); + CleanupStack::Pop(self); + return self; + } + +// +// CCSYInfo +// + +CCSYInfo::CCSYInfo(TInt aThreadNum) +: iThreadNum(aThreadNum), +iLoaded(EFalse), +iLoadIndex(-1) + { + } + +CCSYInfo* CCSYInfo::NewL(const TDesC8& aCSYName, TInt aThreadNum) + { + CCSYInfo* self = new (ELeave) CCSYInfo(aThreadNum); + CleanupStack::PushL(self); + self->ConstructL(aCSYName); + CleanupStack::Pop(self); + return self; + } + + +void CCSYInfo::ConstructL(const TDesC8& aCSYName) + { + iCSYName=aCSYName.AllocL(); + } + +CCSYInfo::~CCSYInfo() + { + delete iCSYName; + iCSYPortPrefix.Close(); + } + + +TInt CCSYInfo::CompareCSYInfo(const CCSYInfo& aFirst,const CCSYInfo& aSecond) +// compares CSY records by comparing the csy filenames + { + //-ve if first is less than second + return aFirst.iCSYName->CompareF(*(aSecond.iCSYName)); + } + +TInt CCSYInfo::SetPortNameSizeToMaximum() + { + __ASSERT_DEBUG(iCSYPortPrefix.Length()==0,Fault(EBadState,_L8("CCSYInfo: attempt to grow portname for CSY for a non-null string (ie, module looks loaded)"))); + return iCSYPortPrefix.ReAlloc(KMaxFullName); + } + + +void CCSYInfo::SetPortName(const TDesC& aPortName) + { + // since this may be called at end of csy load process, we have no reasonable way to deal with OOM, + // so before the csy load process started, the portname + // was grown to max. After setting the string we resize the portname so that the remaining + // memory is freed. An RBuf allows us to resize in-place. + + iCSYPortPrefix = aPortName; + +#ifdef _DEBUG + TInt ret = iCSYPortPrefix.ReAlloc(aPortName.Length()); + __ASSERT_DEBUG(ret == KErrNone,Fault(EBadState,_L8("CCSYInfo::SetPortName: failed to shrink port prefix. Ret: %d"),ret)); +#else + (void)iCSYPortPrefix.ReAlloc(aPortName.Length()); // on failure, continue with current size. +#endif + + } + + + +void CCSYInfo::SetCSYHandle(CSerial* aSerialHandle) +//this handle is never actually used by CCSYInfo, just passed back to player later + { + iCSYHandle=aSerialHandle; + } + + + + + + +// +// CC32ThreadManager::CC32ParsedIniData +// + +CC32ThreadManager::CC32ParsedIniData::CC32ParsedIniData() + { + } + + +CC32ThreadManager::CC32ParsedIniData* CC32ThreadManager::CC32ParsedIniData::NewL(const TDesC8& aIniData, const TBool aIsDealer) + +// aIsDealer specifies whether this data was sent to us from the configurator as it spawned the +// dealer thread + { + CC32ParsedIniData* self = new(ELeave) CC32ParsedIniData(); + CleanupStack::PushL(self); + self->ConstructL(aIniData,aIsDealer); + CleanupStack::Pop(self); + return self; + } + + +CC32ThreadManager::CC32ParsedIniData::~CC32ParsedIniData() + { + iCSYs.Close(); + } + +void CC32ThreadManager::CC32ParsedIniData::ConstructL(const TDesC8& aIniData, const TBool aIsDealer) +// we could have had the NewL perform this function, but because that would men creating and destroying +// this object for each data read, we chose to save that by making this operation separate +// aIsDealer - true if this is the inidata for the dealer loading all the cmi files. This allows +// us to know this is a dealer even if nothing of the sort is in the [iniData] section and +// thus we can preserve backwards compat. + + +// on exit the data should be: +// workerID = always correct - if we are dealer it is automatically 0 regardless of what config says. If we +// are player we leave unless this is provided. We leave if it is zero and we are player. +// isPlayer - is false if we know we are dealer (aIsDealer) regardless of aIniData. Is true if we +// see Role=player. If role missing and we are not Dealer (!aIsDealer) then we don't try +// any recovery since a workerthread assumes it is a dealer if no role. +// isWildcard - true if a wildcard is provided in the csy list +// iCSYs - list of the csys in CSYList. It is valid that a dealer may not have a CSY list + { + TPtrC8 roleValue; + TPtrC8 workerIdValue; + TPtrC8 csyListValue; + + if (&aIniData==NULL || aIniData.Length()==0) + { + if (!aIsDealer) + { + C32LOG(KC32Warning,_L8("CC32ParsedIniData: Corrupt CMI file that references C32.DLL: Missing [IniData] section.")); + C32LOG(KC32Warning,_L8("CC32ParsedIniData: Please migrate all *.cmi files that reference C32.DLL to include Group token and [IniData] section.")); + // we don't panic in this scenario since this could be an old ini file we've found + // in future we will panic here once licencees have migrated any cmi files they need to + User::Leave(KErrCorrupt); + } + else + { + // dealers are allowed to not have CSYLists and we know what the role & workerId should be so continue + // in future this will be a config error but during migration we continue. + C32LOG(KC32Detail,_L8("CC32ParsedIniData: Supplied inidata for dealer is empty - must be old CMI file. Skipping.")); + return; + } + } + + + if (!GetVarFromIniData(aIniData, KNullDesC8, KWorkerIdLabel, workerIdValue) ) + { + C32LOG(KC32Warning,_L8("CC32ParsedIniData: Corrupt [IniData] section - missing workerId value.")); + + if (aIsDealer) + { + C32LOG(KC32Warning,_L8("CC32ParsedIniData: As we are dealer, ignoring corruption by setting workerId to 0 and continuing")); + iCMIWorkerId = KMainThreadId; + } + else + { +#ifdef _DEBUG + Fault(EBadIniData); // panic +#endif + User::Leave(KErrCorrupt); + } + } + else + { + // we accept whatever we get as the workerId + TLex8 lex(workerIdValue); + (void)lex.Val(iCMIWorkerId); + } + + + + + if (!GetVarFromIniData(aIniData, KNullDesC8, KRoleLabel, roleValue)) + { + C32LOG(KC32Warning,_L8("CC32ParsedIniData: Corrupt [IniData] section - missing Role label.")); + if (!aIsDealer) + { +#ifdef _DEBUG + Fault(EBadIniData); // panic +#endif + User::Leave(KErrCorrupt); + } + } + else if(roleValue.CompareF(KPlayerRole)==0) + { + iIsPlayer = ETrue; + } + else if(roleValue.CompareF(KDealerRole)!=0) + { + C32LOG2(KC32Warning,_L8("CC32ParsedIniData: Corrupt [IniData] section: Specified Role not recognised; must be Dealer or Player: %d "),&roleValue); + +#ifdef _DEBUG + Fault(EBadIniData); // panic +#endif + User::Leave(KErrCorrupt); + } + + + if (iIsPlayer && (iCMIWorkerId < 0 || iCMIWorkerId > KMaxWorkerThreadId)) + { + C32LOG2(KC32Warning,_L8("CC32ParsedIniData: Corrupt [IniData] section: Role=Player and WorkerId invalid: %d"),iCMIWorkerId); +#ifdef _DEBUG + Fault(EBadIniData); // panic +#endif + User::Leave(KErrCorrupt); + } + + + if (!GetVarFromIniData(aIniData, KNullDesC8, KCSYListLabel, csyListValue)) + { + if (iIsPlayer) + { + C32LOG(KC32Warning,_L8("CC32ParsedIniData: Corrupt [IniData] section - missing CSYList definition.")); +#ifdef _DEBUG + Fault(EBadIniData); // panic +#endif + User::Leave(KErrCorrupt); + } + } + else + { + + TInt sectionLen = 0; + TPtrC8 section(csyListValue); + + + while (section.Length() > 0) + { + // assume that a CSY name is at least 1 characters long + sectionLen = section.Find(TPtrC8(_S8(","))); + if (sectionLen > 0) + { + iCSYs.AppendL(section.Mid(0,sectionLen)); + + if (sectionLen+1 < section.Length()) + { + section.Set(section.Mid(sectionLen+1)); + } + else + { + section.Set(KNullDesC8); + } + } + else if ((sectionLen==0) && (section.Length() > 0)) + { + //possibly a double comma - we'll be nice and ignore it + section.Set(section.Mid(sectionLen+1)); + } + else if (section[0]=='*') + { + iIsDefault = ETrue; + if (section.Length() > 1) + { + section.Set(section.Mid(1)); + } + else + { + section.Set(KNullDesC8); + } + } + else + { + iCSYs.AppendL(section); + section.Set(KNullDesC8); + } + } + } + + + } + + + + + + + + + +// +// CC32ThreadManager::CC32ThreadInfo +// + +CC32ThreadManager::CC32ThreadInfo::CC32ThreadInfo() + { + } + + + +CC32ThreadManager::CC32ThreadInfo* CC32ThreadManager::CC32ThreadInfo::NewL(const TDesC8& aModuleName) + { + CC32ThreadInfo* self = new (ELeave) CC32ThreadInfo(); + CleanupStack::PushL(self); + self->ConstructL(aModuleName); + CleanupStack::Pop(self); + return self; + } + + +void CC32ThreadManager::CC32ThreadInfo::ConstructL(const TDesC8& aModuleName) + { + iModuleName = aModuleName.AllocL(); + } + +void CC32ThreadManager::CC32ThreadInfo::SetModuleNameL(const TDesC8& aModuleName) + { + delete iModuleName; + iModuleName = NULL; + iModuleName = aModuleName.AllocL(); + } + +CC32ThreadManager::CC32ThreadInfo::~CC32ThreadInfo() + { + delete iModuleName; + } + + + +TBool CC32ThreadManager::CC32ThreadInfo::CompareThreadInfo(const CC32ThreadInfo& aFirst,const CC32ThreadInfo& aSecond) +// aFirst should always be non-empty. aSecond may be an empty string if it is the dealer entry + { + return (aFirst.iModuleName==aSecond.iModuleName); + } + + + + + + + + +// +// CCPMLoader +// + + +CC32ThreadManager::CCPMLoader::CCPMLoader() + : CActive(EPriorityStandard) + { + // server is at a super-high priority, so whatever priority we choose is not going to make any difference + CActiveScheduler::Add(this); + + } + +CC32ThreadManager::CCPMLoader* CC32ThreadManager::CCPMLoader::NewL() + { + CCPMLoader* self = new(ELeave) CC32ThreadManager::CCPMLoader(); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +void CC32ThreadManager::CCPMLoader::ConstructL() + { + TInt ret = iCfgSvr.Connect(); + if (ret != KErrNone) + { + C32LOG2(KC32Dealer,_L8("CC32Dealer::LoadModule failed during connect to configurator. Code: %d"),ret); + User::Leave(ret); + } + } + + +void CC32ThreadManager::CCPMLoader::DoCancel() + { + C32LOG(KC32Detail,_L8("CC32Dealer::CCPMLoader: DoCancel called")); + + iCfgSvr.CancelLoadCpm(iName); + } + +CC32ThreadManager::CCPMLoader::~CCPMLoader() + { + Cancel(); + iCfgSvr.Close(); + // deques on delete of parent + } + +void CC32ThreadManager::CCPMLoader::RequestModuleLoad(CC32ThreadManager* aThreadMgr, CommsFW::TWorkerId aWorker, const TCFModuleName& aName) +// Begin the asynchronous process of loading a player + { + __ASSERT_DEBUG(!IsActive(),Fault(EBadState,_L8("CCPMLoader: Loader already busy with another request when loading module %S. Panicking"),&aName)); + + iName = TCFModuleName(aName); // store so that when we lose stack at this function exit, RRs server can still read data + iThreadManager = aThreadMgr; + iWorkerId = aWorker; + + iCfgSvr.LoadCpm(iName,iStatus); + + SetActive(); + + C32LOG2(KC32Dealer,_L8("CCPMLoader: Load request for module %S sent to Configurator"),&iName); + } + +void CC32ThreadManager::CCPMLoader::RunL() +// this normally never runs in the success case since new player's response beats it and dealer kills the loader. + { + TInt res = iStatus.Int(); + + C32LOG4(KC32Dealer,_L8("CC32Dealer: Configurator has completed LoadCPM (%S)(WorkId%d) request with %d"),&iName,iWorkerId,res); + + // assert_debug in here that status is never "already running" + __ASSERT_DEBUG(res != KErrRSModuleAlreadyExist, Fault(EBadState,_L8("CC32Dealer: LoadCPM returned KErrRSModuleAlreadyExists - this is unexpected so Panicking."))); + __ASSERT_DEBUG(res != KErrRSModuleUnknown, Fault(EBadState,_L8("CC32Dealer: LoadCPM returned KErrRSModuleUnknown - probably incorrect CMI file. Panicking."))); + + + // on success there is nothing we need to do with this response - just capture it and log it. + // on failure we clean up. + + if (res==KErrNone) + { + C32LOG2(KC32Detail,_L8("CC32Dealer::LoadCPM: Load for (%S) has been successful"),&iName); + } + else + { + __ASSERT_DEBUG(!iThreadManager->Dealer()->WorkerExists(iWorkerId),Fault(EBadState,_L8("CCPMLoader::RunL - WorkerExists(%d) but configurator returned a failure: %d"),iWorkerId,res)); + C32LOG2(KC32Dealer,_L8("CC32Dealer::LoadCPM (%S) has apparently failed so cleaning up pending requests and threadmanager"),&iName); + + // give threadmanager a chance to remove any memory we allocated before + // commencing the module load. + iThreadManager->ProcessModuleLoadFailed(iWorkerId); + + // we now leave ourselves inactive but still valid since we will be needed on the next attempt to load this cpm + // and there is no obvious place to delete ourselves + } + } + + + + +// +// CC32ThreadManager +// + +CC32ThreadManager::CC32ThreadManager(CC32Dealer* aDealer) + { + iDealer = aDealer; + } + + +CC32ThreadManager* CC32ThreadManager::NewL(CC32Dealer* aDealer) + { + CC32ThreadManager* self = new (ELeave) CC32ThreadManager(aDealer); + return self; + + } + +CC32ThreadManager::~CC32ThreadManager() + { + iCSYList.ResetAndDestroy(); + iC32ThreadList.ResetAndDestroy(); + + for (TInt i=0;i <= KMaxWorkerThreadId ; i++) + { + delete iCPMLoader[i]; + } + } + +CC32Dealer* CC32ThreadManager::Dealer() + { + __ASSERT_DEBUG(iDealer!=NULL,Fault(EBadState,_L8("CC32ThreadManager: Dealer() - iDealer is NULL. Panicking."))); + return iDealer; + } + + +TWorkerId CC32ThreadManager::DefaultThread() + { + return iDefaultThreadIndex; + } + + +TBool CC32ThreadManager::FindThreadByPortPrefix(const TDesC& aPortName, TWorkerId& aWorker) const +// returns EFalse if not found, ETrue otherwise +// unloaded csys or csys yet to be loaded have an empty port prefix so will not be found +// searching is done in folded way to avoid case problems. + { + TInt idx = 0; + if (&aPortName==NULL) + { + return KErrNotFound; + } + + if (aPortName.Length()==0) + { + return KErrNotFound; + } + + while (idx < iCSYList.Count()) + { + if (aPortName.CompareF(iCSYList[idx]->GetPortName())==0) + { + __ASSERT_DEBUG(iCSYList[idx]->IsLoaded(),Fault(EBadState,_L("Panicking due to finding CSY by PortName (%S) that is inactive at index: %d" ),&aPortName,idx)); + aWorker=iCSYList[idx]->WorkerId(); + return ETrue; + } + idx++; + } + + return EFalse; + } + + + +TInt CC32ThreadManager::MapPortprefixToCSYFileName(const TDesC& aPortName, TDes& aCSYFileName) +// aPortName is the port prefix format name to search on. +// unloaded csys or csys yet to be loaded have an empty port prefix so will not be found. +// aCSYFileName - on return contains the filename for the csy with the portprefix +// Return: KErrNone if the csy was found, KErrNotFound otherwise. +// Panics: +// if aCSYFileName is not long enough to take result. +// Returns KErrNotFound in the case of port prefix being null +// + { + TInt idx = 0; + if (&aPortName==NULL) + { + return KErrNotFound; + } + if (aPortName.Length()==0) + { + return KErrNotFound; + } + + while (idx < iCSYList.Count()) + { + if (aPortName.CompareF(iCSYList[idx]->GetPortName())==0) + { + __ASSERT_DEBUG(iCSYList[idx]->IsLoaded(),Fault(EBadState,_L("Panicking due to finding CSY by PortName (%S) that is inactive at index: %d" ),&aPortName,idx)); + aCSYFileName.Copy(*iCSYList[idx]->GetCSYName()); + return KErrNone; + } + idx++; + } + + return KErrNotFound; + } + + + +TBool CC32ThreadManager::FindThreadByFileName(const TDesC8& aCSYFileName8, TWorkerId &aWorkerId) const +// Find thread that would house this CSY, if no match found, return EFalse +// CSYFilename - CSY name with extension + { + TInt idx = 0; + while (idx < iCSYList.Count()) + { + if (aCSYFileName8.CompareF(*iCSYList[idx]->GetCSYName())==0) + { + aWorkerId = iCSYList[idx]->WorkerId(); + return ETrue; + } + idx++; + } + + return EFalse; + } + +TBool CC32ThreadManager::FindThreadOfActiveCSYByName(const TDesC& aName, TWorkerId& aWorkerId) const // GetPortInfo(aName) version, TWorkerId filled with worker +// find thread by filename of CSY, if not found try by portnam of CSY. +// only searches active CSYs +// aName - portprefix or filename of csy to find +// aWorkerId - on return contains workerId, unchanged if result is EFalse +// returns: ETrue if found, EFalse otherwise + { + TBuf8 nameWithExt8; + nameWithExt8.Copy(aName); + + TInt r=nameWithExt8.LocateReverse('.'); + if (r==KErrNotFound) + { + if (nameWithExt8.Length()>KMaxFileName - (TInt)KCSYExtension8.iTypeLength) // valid filename is checked here + { + return EFalse; + } + nameWithExt8.Append(KCSYExtension8); + } + + + TInt idx = 0; + while (idx < iCSYList.Count()) + { + if (iCSYList[idx]->IsLoaded() && ((aName.CompareF(iCSYList[idx]->GetPortName())==0) || (nameWithExt8.CompareF(*iCSYList[idx]->GetCSYName())==0))) + { + aWorkerId = iCSYList[idx]->WorkerId(); + return ETrue; + } + idx++; + } + + return EFalse; + } + +TInt CC32ThreadManager::FindThreadByGlobalIndex(const TInt aIndex) const +// aIndex is the array position the CSY would be in if it were in the old portmanager structure +// This function returns zero or a positive integer if the global index refers to a loaded +// csy. If not, then KErrNotFound is returned. + + { + __ASSERT_DEBUG(aIndex >= 0,Fault(EBadState)); + TInt absIdx = 0; + while (absIdx < iCSYList.Count()) + { + if (iCSYList[absIdx]->IsLoaded()) + { + if (iCSYList[absIdx]->LoadIndex()==aIndex) + { + return iCSYList[absIdx]->WorkerId(); + } + } + absIdx++; + } + + + return KErrNotFound; + } + +CSerial* CC32ThreadManager::GetSerialObjectFromCSYFileName(const TDesC& aCSYFileName) +// We might be asked to do an update even for a CSY that has been loaded since all CSY load requests are +// passed through to the player. This is because player needs to record each session that loads the module. + { + TBuf8 fileName8; // convert 16 to 8-bit string. huge stack usage, but no other way + fileName8.Copy(aCSYFileName); + TInt idx = 0; + + while (idx < iCSYList.Count()) + { + if (fileName8.CompareF(*iCSYList[idx]->GetCSYName())==0) + { + C32LOG1(KC32Detail, _L8("CC32ThreadManager::GetSerialObjectFromCSYFileName()")); + return iCSYList[idx]->GetCSYHandle(); + } + else + { + idx++; + } + } + return NULL; + } + +CSerial* CC32ThreadManager::GetSerialL(const TDesC& aPortName) + { + TInt idx; + User::LeaveIfError(GetIndexFromPortPrefix(aPortName, idx)); + return GetSerialObjectByIndex(idx); + } + +TInt CC32ThreadManager::GetIndexFromPortPrefix(const TDesC& aPortName, TInt& aIndex) + { + TInt idx = 0; + + while (idx < iCSYList.Count()) + { + if (aPortName.CompareF(iCSYList[idx]->GetPortName())==0) + { + C32LOG1(KC32Detail, _L8("CC32ThreadManager::GetIndexFromPortPrefix()")); + aIndex = idx; + return KErrNone; + } + else + { + idx++; + } + } + return KErrNotFound; + } + +CSerial* CC32ThreadManager::GetSerialObjectByIndex(TInt aIndex) + { + return iCSYList[aIndex]->GetCSYHandle(); + } + +TInt CC32ThreadManager::IncrementCountOnLoad(const TDesC& aCSYFileName) + { + TBuf8 fileName8; // convert 16 to 8-bit string. + fileName8.Copy(aCSYFileName); + TInt idx = 0; + + while (idx < iCSYList.Count()) + { + if (fileName8.CompareF(*iCSYList[idx]->GetCSYName())==0) + { + iCSYList[idx]->IncAccessCount(); + C32LOG3(KC32Detail, _L8("CC32ThreadManager::IncrementCountOnCSYLoad() AccessCount for %S CSY = %d"), &fileName8, iCSYList[idx]->AccessCount() ); + return KErrNone; + } + else + { + idx++; + } + } + return KErrNotFound; + } + +TInt CC32ThreadManager::DecrementCountOnCSYUnLoad(const TDesC& aCSYFileName) + { + TBuf8 fileName8; // convert 16 to 8-bit string. + fileName8.Copy(aCSYFileName); + TInt idx = 0; + + while (idx < iCSYList.Count()) + { + if (fileName8.CompareF(*iCSYList[idx]->GetCSYName())==0) + { + if(iCSYList[idx]->AccessCount()==1) + { + // unload CSY as all sesions have unloaded CSYs or closed now. + UpdateThreadManagerOnCSYUnload(fileName8); + } + iCSYList[idx]->DecAccessCount(); + C32LOG3(KC32Detail, _L8("CC32ThreadManager::DecrementCountOnCSYUnLoad() AccessCount for %S CSY = %d"), &fileName8, iCSYList[idx]->AccessCount() ); + return KErrNone; + } + else + { + idx++; + } + } + return KErrNotFound; + } + + +TInt CC32ThreadManager::GrowCSYPortNameToMaximumIfNotLoaded(const TDesC& aCSYFileName) + { + TBuf8 fileName8; // convert 16 to 8-bit string. + fileName8.Copy(aCSYFileName); + TInt idx = 0; + + while (idx < iCSYList.Count()) + { + if (fileName8.CompareF(*iCSYList[idx]->GetCSYName())==0) + { + if (!iCSYList[idx]->IsLoaded()) + { + return iCSYList[idx]->SetPortNameSizeToMaximum(); + } + else + { + return KErrNone; + } + } + else + { + idx++; + } + } + return KErrNotFound; + } + + +TInt CC32ThreadManager::RequestLoadModule(CommsFW::TWorkerId aWorker) + { + TInt ret = KErrNone; + + __ASSERT_DEBUG(!Dealer()->WorkerExists(aWorker),Fault(EBadState,_L8("CC32ThreadManager::RequestLoadModule: Worker already Exists! Panicking"))); + + + // loader may already exist but be inactive if a previous load attempt failed + if (iCPMLoader[aWorker] == NULL) + { + C32LOG3(KC32Detail,_L8("ThreadMgr::RequestLoadModule: Loader doesn't exist for worker %d (%S). Starting worker"),aWorker,GetModuleName(aWorker)); + TRAP(ret,iCPMLoader[aWorker] = CCPMLoader::NewL()); + if (ret != KErrNone) + { + C32LOG2(KC32Detail,_L8("CCPMLoader::NewL() leave occured (%d)"),ret); + return ret; + } + iCPMLoader[aWorker]->RequestModuleLoad(this,aWorker,*GetModuleName(aWorker)); + } + else if (!iCPMLoader[aWorker]->IsActive()) + { + C32LOG3(KC32Detail,_L8("CC32ThreadManager: Inactive Loader already exists - reusing to start worker %d (%S)."),aWorker,GetModuleName(aWorker)); + + iCPMLoader[aWorker]->RequestModuleLoad(this,aWorker,*GetModuleName(aWorker)); + } + else + { + C32LOG2(KC32Detail,_L8("ThreadMgr: RequestLoadModule: Attempt to start worker %d ignored due to already outstanding load- dealer should queue this req."),aWorker); + // if timer is already running, we assume load still in progress so we just exit + // and caller can queue request + ret=KErrInUse; + } + return ret; + } + + +void CC32ThreadManager::ProcessModuleLoadFailed(CommsFW::TWorkerId aWorker) +// called from LoadCPM when configurator reports that player has failed to start +// Gives thread manager a chance to do any cleanup. + { + C32LOG2(KC32Dealer,_L8("CC32ThreadManager:ProcessModuleLoadFailed: Failed worker was %d. Cleaning up pending requests."),aWorker); + + // give dealer a chance to remove any pending requests for the module which did not start + // and undo any memory allocations done as part of load + Dealer()->ProcessFailedPlayerLoad(aWorker); + + } + + +void CC32ThreadManager::ProcessModuleLoadSuccess(CommsFW::TWorkerId aWorker) +// called when dealer receives introduction msg from player, regardless of how player started + { + C32LOG2(KC32Detail, _L8("CC32ThreadManager::ProcessModuleLoadSuccess for worker %d. Deleting CPMLoader."), aWorker); + + // Delete the load Active object. It turns out that normally by this point the configurator has yet to complete the request, + // even though the player is already running, so by removing the loader here, we will be sending a Cancel to the configurator + // for an operation we know has already completed successfully. This is safe (view a request as consisting of two parts + // - 1) ask a server to make a request and 2) hope the server will let you know how it goes - we are + // now simply cancelling part 2 since we know how part 1 went) + + delete iCPMLoader[aWorker]; + iCPMLoader[aWorker] = NULL; + + } + + + +void CC32ThreadManager::UpdateThreadManagerOnCSYUnload(const TDesC8& aCSYFileName8) +// called when we predict Player will unload CSY, since the indexing of the CSYs and the auto-close on index 0 behaviour +// that the player uses (preserved legacy C32 behaviour) does not give an opportunity for this process to be intercepted +// on player side and a mesg sent to dealer. + { + TInt idx = 0; + + // NULL CSerial* and Portname + C32LOG2(KC32Detail, _L8("CC32ThreadManager::UpdateThreadManagerOnCSYUnLoad() for %S"), &aCSYFileName8); + while (idx < iCSYList.Count()) + { + if (aCSYFileName8.CompareF(*iCSYList[idx]->GetCSYName())==0) + { + __ASSERT_DEBUG(iCSYList[idx]->IsLoaded(),Fault(EBadState,_L8("UpdateThreadManagerOnCSYUnLoad: CSY marked as not loaded on call to update after unload. Panicking."))); + iCSYList[idx]->SetPortName(KNullDesC16); + iCSYList[idx]->SetCSYHandle(NULL); + iCSYList[idx]->SetLoadState(EFalse); + + // run thru list and adjust index numbers now that this csy has unloaded + for (TInt absIdx = 0; absIdx < iCSYList.Count(); ++absIdx) + { + if ((iCSYList[absIdx]->IsLoaded() && (iCSYList[absIdx]->LoadIndex() > iCSYList[idx]->LoadIndex()))) + { + iCSYList[absIdx]->SetLoadIndex(iCSYList[absIdx]->LoadIndex()-1); + } + } + + iCSYList[idx]->SetLoadIndex(-1); + --iNumLoadedModules; + + return; + } + else + { + idx++; + } + } + + C32LOG2(KC32Warning,_L8("UpdateThreadManagerOnCSYUnload: Could not find CSY supposedly unloaded: %S. Will Panic in debug."),&aCSYFileName8); + __ASSERT_DEBUG(0,Fault(EBadState)); + } + +void CC32ThreadManager::UpdateThreadManagerOnCSYLoad(const TDesC& aCSYFileName, const TDesC& aPortPrefix, CSerial* aSerial) +// We might be asked to do an update even for a CSY that has been loaded since all CSY load requests are +// passed through to the player. This is because player needs to record each session that loads the module. + { + TBuf8 fileName8; // convert 16 to 8-bit string. huge stack usage, but no other way + fileName8.Copy(aCSYFileName); + TInt idx = 0; + + + // we update the csyinfo with the new serial and port prefix and mark it active + + while (idx < iCSYList.Count()) + { + if (fileName8.CompareF(*iCSYList[idx]->GetCSYName())==0) + { + if (iCSYList[idx]->IsLoaded()==EFalse) + { + C32LOG5(KC32Detail, _L("C32ThrdMg::UpdateThreadManagerOnCSYLoad() for CSY[%d](%S).CSerial=%x. PortPrefix=%S"),idx,&aCSYFileName,aSerial,&aPortPrefix); + iCSYList[idx]->SetPortName(aPortPrefix); + iCSYList[idx]->SetCSYHandle(aSerial); + iCSYList[idx]->SetLoadState(ETrue); + + iCSYList[idx]->SetLoadIndex(iNumLoadedModules); + ++iNumLoadedModules; + } + else + { + C32LOG3(KC32Detail, _L8("CC32ThreadManager::UpdateThreadManagerOnCSYLoad(): CSY[%d] already updated. Access count is %d"),idx,iCSYList[idx]->AccessCount()); + } + return; + } + else + { + idx++; + } + } + + + C32LOG2(KC32Warning,_L8("UpdateThreadManagerOnCSYLoad: Could not find CSY supposedly loaded: %S. Will panic in debug."),&fileName8); + __ASSERT_DEBUG(0,Fault(EBadState)); + } + +void CC32ThreadManager::UpdateThreadManagerOnCSYLoadFailure(const TDesC& aCSYFilename) +// called to allow thread manager to clean up any allocations made prior to attempting CSY load. +// aCSYFilename - name with extension of CSY that player failed to load + { + CommsFW::TWorkerId worker; + + TBuf8 fileName8; // convert 16 to 8-bit string. huge stack usage, but no other way + fileName8.Copy(aCSYFilename); + + + TBool found = FindThreadByFileName(fileName8,worker); + + // it is possible we would not find it if two sessions had requested this csy load at same time and + // this is now the completion of the second sessions load attempt and the first session cleanup + // had destroyed the csyInfo. + if (found) + { + // if this was default thread we might have + // added an unknown CSY so in this case remove it. It does no harm if this CSY was specified in CMI + // file since on next attempt thread manager will place the CSY in same thread anyway. + + if (worker == iDefaultThreadIndex) + { + for (TInt i=0; i < iCSYList.Count(); i++) + { + // comparing the worker Ids first is a bit quicker + if (iCSYList[i]->WorkerId()==worker) + { + if (iCSYList[i]->GetCSYName()->Compare(fileName8)==0) + { + __ASSERT_DEBUG(!iCSYList[i]->IsLoaded(),Fault(EBadState,_L8("CC32ThreadManager::UpdateThreadManagerOnCSYLoadFailure: CSY[%d] is marked as loaded! Panicking."),i)); + C32LOG3(KC32Dealer,_L("CC32ThreadManager::UpdateThreadManagerOnCSYLoadFailure: Failed CSY (%S) was for default thread. Deleting CSY[%d]"),&aCSYFilename,i); + delete iCSYList[i]; + iCSYList.Remove(i); + return; + } + } + } + C32LOG2(KC32Dealer,_L("CC32ThreadManager::UpdateThreadManagerOnCSYLoadFailure: Failed CSY (%S) was for default thread but could not find. Will panic in debug."),&aCSYFilename); + __ASSERT_DEBUG(0,User::Invariant()); + } + } + else + { + C32LOG2(KC32Dealer,_L("CC32ThreadManager::UpdateThreadManagerOnCSYLoadFailure: Failed CSY record (%S) was not found so assuming another request deleted it"),&aCSYFilename); + } + } + + +CSerial* CC32ThreadManager::FindSerialObjectByGlobalIndex(const TInt aIndex) const +// aIndex is the array position the CSY would be in if it were in the old portmanager structure +// this is now the position of the csy in the active array +// index should be valid so panic if not + { + + TInt absIdx = 0; + while (absIdx < iCSYList.Count()) + { + if (iCSYList[absIdx]->IsLoaded()) + { + if (iCSYList[absIdx]->LoadIndex()==aIndex) + { + return iCSYList[absIdx]->GetCSYHandle(); + } + else + { + absIdx++; + } + } + else + { + absIdx++; + } + } + + C32LOG2(KC32Warning,_L8("FindSerialObjectByGlobalIndex - Panicking due to index out of range: %d"),aIndex); + __ASSERT_ALWAYS(0,Fault(EBadState)); + + return NULL; + } + +const HBufC8* CC32ThreadManager::GetModuleName(TWorkerId aWorkerId) +// aWorkerId assumed to be valid + { + return iC32ThreadList[aWorkerId]->ModuleName(); + } + + +#ifdef __FLOG_ACTIVE +void CC32ThreadManager::DumpThreadInfoAndCSYLists() + { + //log in Detail mode only + + C32LOG(KC32Detail,_L8("---- Dump of C32 Thread Manager ----")); + C32LOG2(KC32Detail,_L8("Default Thread: %d"),iDefaultThreadIndex); + TBuf8 shortName8; // TPortName is, rather annoyingly given it probably never needs to be, a 16-bit descriptor + + for (TInt i=0;i < iCSYList.Count();i++) + { + shortName8.Copy(iCSYList[i]->GetPortName()); + C32LOG8(KC32Detail,_L8("CSY List [%d].CSY Name:%S,ShrtNm:%S,ThrdID:%d,PlyrHdl:%x,Active:%d,Idx:%d"),i,iCSYList[i]->GetCSYName(),&shortName8,iCSYList[i]->WorkerId(),iCSYList[i]->GetCSYHandle(),iCSYList[i]->IsLoaded(),iCSYList[i]->LoadIndex()); + } + + TBool globalsExist = Dealer()->WorkerDataGlobalsExist(); + if (globalsExist) + { + C32LOG(KC32Detail,_L8("Thread List start.")); + } + else + { + C32LOG(KC32Detail,_L8("Thread List start. Still booting so don't know player thread status yet.")); + } + + for (TInt k=0;k < iC32ThreadList.Count(); k++) + { + if (iC32ThreadList[k]==NULL) + { + C32LOG2(KC32Warning,_L8("DumpThreadInfo: Internal check failure: Found NULL record at WorkerId: %d"),k); +#ifdef _DEBUG + Fault(EBadState); // panic +#endif + } + else if (iC32ThreadList[k]->ModuleName()->CompareF(KNullDesC8)==0) + { + if (k==0) + { + C32LOG(KC32Detail,_L8("Thread List[0]:Blank (This is normal)")); + } + else + { + C32LOG2(KC32Warning,_L8("DumpThreadInfo: Internal check failure: Found blank record for a player at WorkerId: %d"),k); + C32LOG1(KC32Warning,_L8("DumpThreadInfo: This indicates missing cmi file for this workerId. Continuing since this could be a test scenario.")); + // there is no panic here since we allow blanks in case user is mix-and-matching csys to threads + } + } + else + { + if (globalsExist) + { + C32LOG4(KC32Detail,_L8("Thread List[%d]:Name:%S. Running:%d"),k,iC32ThreadList[k]->ModuleName(),Dealer()->WorkerExists(k)); + } + else + { + C32LOG3(KC32Detail,_L8("Thread List[%d]:Name:%S. Dealer still starting so Running Status unknown."),k,iC32ThreadList[k]->ModuleName()); + } + } + } + + C32LOG(KC32Detail,_L8("---- End Dump of C32 Thread Manager ----")); + + + + } +#endif + + +void CC32ThreadManager::LoadCMIDataL(const TDesC8& aDealerIniData) +// load all the data from the CMI files. +// aCSYList provides us with the data for the dealer, but we have to query the Configurator for all +// the data for the player cmis. + { + + TInt ret; + TRSIter iter; + + + TCFModuleName moduleName; // as specified in CMI file + RArray numCSYsPerCMI; // used if we find two CSYs the same. Filled in as each + // cmi processed so that index of element is threadnum, + // and value is number of CSYs in that threadnum. + CleanupClosePushL(numCSYsPerCMI); + + TBool skipThisCMI; // the CMI is a dealer or somehow invalid beyond redemption + + CC32ParsedIniData* iniDataParser; + + RBuf8 iniData; // to accept data from the configurator + TInt iniBufSize; // tracks size of ini data buffer as we grow it + + RRsConfigurator cfgSvr; + + User::LeaveIfError(cfgSvr.Connect()); + CleanupClosePushL(cfgSvr); + + + iniBufSize=KIniDataBufSizeIncrement; + iniData.CreateL(iniBufSize); + iniData.CleanupClosePushL(); + + CC32ThreadInfo* newThreadRec; + CC32ThreadInfo* blankThreadRec; // we used to insert NULLs, but inserting this blank is a bit safer/easier to work with. + + // KMaxTUint16 magic value indicates no default thread has been identified - need to use unsigned value because + // TWorkerID is now an unsigned type. + iDefaultThreadIndex = KMaxTUint16; + + // process the dealer's own cmi data. Will leave if the cmi data is bad, unless + // we are the dealer. + iniDataParser = CC32ParsedIniData::NewL(aDealerIniData,ETrue); + CleanupStack::PushL(iniDataParser); + + + // add a record to the threadlist so that we have index 0 - it is irrelevant what we put in here + // so we will make the string an empty string since we won't ever use it - Dealer is always running! + // It has to be a valid string (so not null) otherwise the comparison (CompareThreadInfo) fails the type check. + blankThreadRec = CC32ThreadInfo::NewL(KNullDesC8); + ret = iC32ThreadList.Append(blankThreadRec); //transfer ownership + if (ret != KErrNone) + { + delete blankThreadRec; + User::Leave(ret); + } + blankThreadRec=NULL; + + __ASSERT_DEBUG(iC32ThreadList.Count()==1,Fault(EBadState)); + + if (iniDataParser->IsDefaultThread()) + { + iDefaultThreadIndex = KMainThreadId; + } + + + + for (TInt i=0 ; i < iniDataParser->NumCSYs(); i++) + { + TBuf8 csyFilename; + csyFilename.Copy(iniDataParser->iCSYs[i]); + TInt r=csyFilename.LocateReverse('.'); + if (r==KErrNotFound) + { + if (csyFilename.Length()>KMaxFileName - (TInt)KCSYExtension8.iTypeLength) + { + C32LOG2(KC32Warning,_L("LoadCMIDataL: Corrupt [IniData] section: Dealer has a CSY that is too big: %S. Will panic in debug."),&iniDataParser->iCSYs[i]); + __ASSERT_DEBUG(0,Fault(EBadIniData)); + _LIT8(KCSYDudName, "BadCsy" ); + csyFilename.Copy(KCSYDudName()); // continue with a dud record + } + csyFilename.Append(KCSYExtension8); + } + + CCSYInfo* csyInfoRec = CCSYInfo::NewL(csyFilename, KMainThreadId); + CleanupStack::PushL(csyInfoRec); + + ret = iCSYList.InsertInOrder(csyInfoRec, TLinearOrder(&CCSYInfo::CompareCSYInfo)); + + if (ret == KErrAlreadyExists) + { + C32LOG2(KC32Warning,_L8("LoadCMIDataL: Corrupt [IniData] section: Dealer has the same CSY listed twice: %S"),&iniDataParser->iCSYs[i]); +#ifdef _DEBUG + Fault(EBadIniData); // panic +#endif + + // ignore and carry on + CleanupStack::PopAndDestroy(csyInfoRec); // dont need duplicate further + } + else if (ret != KErrNone) + { + User::Leave(ret); + } + else + { + // ownership transferred + CleanupStack::Pop(csyInfoRec); + } + } + + CleanupStack::PopAndDestroy(iniDataParser); + + + // iterate through all the CMI files + // Any CMI data sections that don't have "Role=Player" are ignored. Dealer CMIs can be ignored + // since we processed our dealer's cmi above. + + // If we find two CMI files with the same module name we fault. + // The order we find them is the order that Configurator supplies them to us - it is probably alphabetical. + // If we see the wildcard twice, we process it only the first time. + // If we do not see the wildcard we make the first player the wildcard thread, or the dealer if no players. + // If a player has no CSYs, we ignore it. + + // We do this in one pass: + // 1. Read records from each CMI file as they come and add them to the structure. If the thread has no number, + // or a duplicate number, or "0" (which should be the dealer) we fault. + // After each + // CMI file is complete we record the total number of CSYs added for that thread. Before adding + // each CSY we scan the list to ensure that CSY has not already been added. If so, then we + // move it to whichever of the two threads it is allocated to that has the least number of CSYs. + // + // As a small efficiency gain, since we may be searching the CSY names many times we order the + // CSY list by CSY name. This order is not preserved once we've finished parsing the CMI files - ie, + // further discovered csys through loading are simply appended to the end. + // An efficincy improvement would be to parse the ini data sequentially. At the moment we reuse the commsfw ini + // file parsing routine but this would re-read the data each time. + // Also, a bit of string copying goes on so this could potentially be reduced. + + + while ( cfgSvr.EnumerateModules(_L8("C32SerComms"),iter,moduleName) == KErrNone) + { + + //keep enlarging inidata buffer until we get it big enough + ret=cfgSvr.GetModuleIniData(moduleName, iniData, iniBufSize); + if (ret==KErrOverflow) + { + iniData.ReAllocL(iniBufSize); + ret=cfgSvr.GetModuleIniData(moduleName, iniData, iniBufSize); + } + User::LeaveIfError(ret); + + skipThisCMI=EFalse; + + // if it leaves we check it was due to corrupt data and continue since we can cope + // with all corrupt data cases + C32LOG2(KC32Bootup,_L8("LoadCMIDataL: Parsing inidata for module %S"),&moduleName); + TRAP(ret,iniDataParser = CC32ParsedIniData::NewL(iniData,EFalse)); + CleanupStack::PushL(iniDataParser); + + + if ((ret != KErrNone) && (ret != KErrCorrupt)) + { + User::Leave(ret); + } + else + { + __ASSERT_DEBUG(iniDataParser,Fault(ESvrStartServer)); + } + + if (iniDataParser->IsPlayer()) + { + newThreadRec = CC32ThreadInfo::NewL(moduleName); + CleanupStack::PushL(newThreadRec); + + if (iC32ThreadList.Find(newThreadRec, TIdentityRelation(&CC32ThreadInfo::CompareThreadInfo)) != KErrNotFound) + { + // unfortunately there is a rare circumstance where we may miss a duplicate. If the dealer + // is not part of the group, and then a member in the group has + // the same name, we won't spot this. only configurator can detect this. + C32LOG(KC32Warning,_L8("LoadCMIDataL: Corrupt IniData found - duplicate module name.")); +#ifdef _DEBUG + Fault(EBadIniData); // panic +#endif + User::Leave(KErrCorrupt); + } + + if ((iniDataParser->NumCSYs() < 1) && !iniDataParser->IsDefaultThread()) + { + C32LOG(KC32Warning,_L8("LoadCMIDataL: Corrupt [IniData] section: Player has no CSY list.")); +#ifdef _DEBUG + Fault(EBadIniData); // panic +#endif + skipThisCMI=ETrue; + C32LOG(KC32Bootup,_L8("Skipping CMI file due to no CSYs found")); + } + + + + if (!skipThisCMI) + { + // workerID range is checked in parser so we don't re-check here + + + if ((iDefaultThreadIndex <= KMainThreadId) && (iniDataParser->IsDefaultThread())) + { + // if dealer was default, we override here since we now have a default player + iDefaultThreadIndex = iniDataParser->WorkerId(); + } + + if (iniDataParser->WorkerId() >= iC32ThreadList.Count()) + { + // expand the array if necessary + // these elements may be filled in later + while (iniDataParser->WorkerId() > iC32ThreadList.Count()) + { + blankThreadRec = CC32ThreadInfo::NewL(KNullDesC8); + ret = iC32ThreadList.Append(blankThreadRec); //transfer ownership + if (ret != KErrNone) + { + delete blankThreadRec; + User::Leave(ret); + } + blankThreadRec=NULL; + } + iC32ThreadList.AppendL(newThreadRec); + CleanupStack::Pop(newThreadRec); //ownership transferred + } + else + { + // we are using up an existing element + if (iC32ThreadList[iniDataParser->WorkerId()]->ModuleName()->CompareF(KNullDesC8)!=0) + { + C32LOG2(KC32Warning,_L8("LoadCMIDataL: Corrupt IniData found - player workerId is a duplicate: %d."),iniDataParser->WorkerId()); +#ifdef _DEBUG + Fault(EBadIniData); // panic +#endif + User::Leave(KErrCorrupt); + } + else + { + delete iC32ThreadList[iniDataParser->WorkerId()]; //blank record + iC32ThreadList[iniDataParser->WorkerId()] = newThreadRec; + CleanupStack::Pop(newThreadRec); //ownership transferred + } + } + + if (iniDataParser->WorkerId() >= numCSYsPerCMI.Count()) + { + // expand the array if neceesary + while (iniDataParser->WorkerId() > numCSYsPerCMI.Count()) + { + numCSYsPerCMI.AppendL(-1); + } + numCSYsPerCMI.AppendL(iniDataParser->NumCSYs()); + } + else + { + if (numCSYsPerCMI[iniDataParser->WorkerId()]>0) + { + C32LOG4(KC32Warning,_L8("LoadCMIDataL: Internal check failure: attempted to add CSYs for workerID twice. WorkerId: %d, CurrNumCSYs: %d, NewNumCSYs: %d"),iniDataParser->WorkerId(),numCSYsPerCMI[iniDataParser->WorkerId()],iniDataParser->NumCSYs()); +#ifdef _DEBUG + Fault(EBadState); // panic +#endif + User::Leave(KErrCorrupt); + } + else + { + numCSYsPerCMI[iniDataParser->WorkerId()]=iniDataParser->NumCSYs(); + } + } + + for (TInt i=0 ; i < iniDataParser->NumCSYs(); i++) + { + TBuf8 csyFilename; + csyFilename.Copy(iniDataParser->iCSYs[i]); + TInt r=csyFilename.LocateReverse('.'); + if (r==KErrNotFound) + { + if (csyFilename.Length()>KMaxFileName - (TInt)KCSYExtension8.iTypeLength) + { + C32LOG2(KC32Warning,_L("LoadCMIDataL: Corrupt [IniData] section: Player has a CSY that is too big: %S. Will panic in debug."),&iniDataParser->iCSYs[i]); + __ASSERT_DEBUG(0,Fault(EBadIniData)); + _LIT8(KCSYDudName, "BadCsy" ); + csyFilename.Copy(KCSYDudName()); // continue with a dud record + } + csyFilename.Append(KCSYExtension8); + } + + CCSYInfo* csyInfoRec = CCSYInfo::NewL(csyFilename, iniDataParser->WorkerId()); + CleanupStack::PushL(csyInfoRec); + + ret = iCSYList.InsertInOrder(csyInfoRec, TLinearOrder(&CCSYInfo::CompareCSYInfo)); + if (ret == KErrAlreadyExists) + { + // duplicate discovered + ret = iCSYList.FindInOrder(csyInfoRec, TLinearOrder(&CCSYInfo::CompareCSYInfo)); + if (numCSYsPerCMI[iniDataParser->WorkerId()] > numCSYsPerCMI[iCSYList[ret]->WorkerId()]) + { + C32LOG4(KC32Bootup,_L8("Reallocating ThreadId for CSY %S [%d -> %d] due to duplicate appearance in CMI files"),iCSYList[ret]->GetCSYName(),iCSYList[ret]->WorkerId(),iniDataParser->WorkerId()); + numCSYsPerCMI[iCSYList[ret]->WorkerId()]--; + iCSYList[ret]->SetWorkerId(iniDataParser->WorkerId()); + } + else + { + C32LOG3(KC32Bootup,_L8("CSY %S appears in two CMI files. Leaving in current thread (%d) due to equal CSY balance across threads."),iCSYList[ret]->GetCSYName(),iCSYList[ret]->WorkerId()); + numCSYsPerCMI[iniDataParser->WorkerId()]--; + } + CleanupStack::PopAndDestroy(csyInfoRec); // dont need duplicate further + } + else if (ret != KErrNone) + { + User::Leave(ret); + } + else + { + // ownership transferred + CleanupStack::Pop(csyInfoRec); + } + + } + } // if skip + else + { + CleanupStack::PopAndDestroy(&newThreadRec); + } + + } //if is player + CleanupStack::PopAndDestroy(iniDataParser); + + }//while enumerate modules + + + if (iDefaultThreadIndex == KMaxTUint16) + { + // try to find any usable players for the default. otherwise, we use dealer. + TInt k=TC32WorkerThreadPublicInfo::EFirstPlayerThread; // + while (k < iC32ThreadList.Count()) + { + if (iC32ThreadList[k]!=blankThreadRec) + { + iDefaultThreadIndex = k; + } + k++; + } + if (iDefaultThreadIndex == KMaxTUint16) + { + iDefaultThreadIndex = KMainThreadId; + } + } + + CleanupStack::PopAndDestroy(&iniData); + CleanupStack::PopAndDestroy(&cfgSvr); + CleanupStack::PopAndDestroy(); // numCSYsPerCMI + + + +#ifdef __FLOG_ACTIVE + DumpThreadInfoAndCSYLists(); +#endif + + } + +void CC32ThreadManager::SetDefaults() +// called if the initialisation of the threadmanager goes wrong (LoadCMIDataL leaves), which is probably when +// there is no memory. So this function sets up the thread manager into a state so that it is still +// internally consistent and able to load modules so C32 can keep booting. If memory is available +// by the time a module load request is received, it can be loaded successfully. + { + iDefaultThreadIndex = KMainThreadId; + + // by destroying whatever is in the csylist, all CSYs will default to the main thread + iCSYList.ResetAndDestroy(); + + // we don't need the thread list anymore - all csy load attempts will go to default thread + // so this list will never be checked. If a player is loading at boot as well, + // the bind at that stage will see the list is destroyed and the player will just sit there unused. + iC32ThreadList.ResetAndDestroy(); + + // iCPMLoader will be empty at this point, so we leave it null. + } + + +// +// CC32Dealer +// + + +CC32Dealer* CC32Dealer::NewL(CC32WorkerThread* aOwnerThread, const TDesC8& aDealerIniData) + { + CC32Dealer* self = new(ELeave) CC32Dealer(aOwnerThread); + CleanupStack::PushL(self); + self->ConstructL(aDealerIniData); + CleanupStack::Pop(self); + return self; + } + +CC32Dealer::CC32Dealer(CC32WorkerThread* aOwnerThread) +: CC32Server(aOwnerThread, EPriority), +iOwnerThread(aOwnerThread) + { + } + +// aCSYList is the CSY list as extracted from the ini data that the CWorkerThread receives from the +// configurator. +void CC32Dealer::ConstructL(const TDesC8& aDealerIniData) + { + // set up the thread manager, which will load the cmi data + // if this fails we have two possible methods of recovery. + // The first is to attempt to set some defaults, which at least allows c32 to provide service + // If this fails too (possibly low memory) then we mark the Dealer as having failed to start + // which then means it will not start its server. + + TRAPD(ret,iThreadManager = CC32ThreadManager::NewL(this)); + if (ret!=KErrNone) + { + C32LOG2(KC32Bootup,_L8("CC32Dealer::ConstructL: Could not create thread manager so setting iFailedStartup TRUE. Ret=%d"),ret); + iFailedStartup=ETrue; + } + else + { + + TRAP(ret,iThreadManager->LoadCMIDataL(aDealerIniData)); + if (ret != KErrNone) + { + C32LOG2(KC32Bootup,_L8("CC32Dealer: CMI data load left with %d. Ignoring CMI files and defaulting to loading all CSYs into main thread."),ret); + iThreadManager->SetDefaults(); + } + + } + + + if(!iFailedStartup) + { + iWorkerDataGlobals = CC32WorkerDataGlobals::NewL(); + + TC32WorkerThreadRegister& mainProperties = *WorkerDataGlobals().GetWorkerGlobals(TC32WorkerThreadPublicInfo::EMainThread); + // players set their heap when they get introduction from main thread, but + // as this does not happen for main thread we set main thread's heap here separately + mainProperties.iHeap = &User::Heap(); + mainProperties.iDealer = this; + if(!iOwnerThread->Player()) + { + mainProperties.iPlayer = iOwnerThread->Player(); + } + +#ifdef _DEBUG + if(iOwnerThread->AllocFailType() != RAllocator::ENone) + { + WorkerDataGlobals().GetWorkerGlobals(TC32WorkerThreadPublicInfo::EMainThread)->iHeap->__DbgSetAllocFail(iOwnerThread->AllocFailType(), iOwnerThread->AllocFailRate()); + } +#endif + + C32LOG1(KC32Dealer,_L8("calling inherited ConstructL()")); + inherited::ConstructL(iThreadManager); + } + } + + +/** +When deleting a session it will be in response to a disconnect from the client, so we +signal that to the session. If this thread is in the process of shutting down we need to +check whether it was just waiting for this session (if this was the last one) and if this is +the case we can initiate the asynchronous process of shutting down the worker thread. +*/ +void CC32Dealer::DeleteSession(CCommSession* aSession) + { + // If we ever see evidence of shutdowns nested inside active calls (ie "this" deleted prematurely then consider + // switching handling to use a CAsyncOneShot) + aSession->CompleteDisconnect(); + if(WorkerThread().ShuttingDown() && CanShutdown()) + { + WorkerThread().SetDealerShutdownComplete(ETrue); + WorkerThread().MaybeTriggerThreadShutdownCallback(); + } + } + + +/** +Iterate through all sessions and make sure they deploy the SubSessionProcessor on each owned sub-session. +It is not known here what the SubSessionProcessor actually does as it is implemented by the caller. +*/ +void CC32Dealer::ProcessSubSessions(TWorkerId aPeerId, CCommSession::TSubSessionProcessor aSubSessionProcessor, TAny* aPtr) + { + iSessionIter.SetToFirst(); + CCommSession* sess; + while((sess = static_cast(iSessionIter++)) != NULL) + { + sess->ProcessSubSessions(aPeerId, aSubSessionProcessor, aPtr); + } + } + +TInt CC32Dealer::SubsessionCountInPlayer(TWorkerId aPeerId) + { + TInt numSubSessions = 0; + ProcessSubSessions(aPeerId, CCommSession::CountSubSessions, &numSubSessions); + return numSubSessions; + } + +CC32Dealer::~CC32Dealer() + { + C32LOG1(KC32Shutdown,_L8("CC32Dealer::~CC32Dealer().")); + +#ifdef __FLOG_ACTIVE + iSessionIter.SetToFirst(); + CCommSession* pSession; + // we log any sessions remaining + while((pSession=static_cast(iSessionIter++))!=NULL) + { + C32LOG2(KC32Shutdown, _L8("CC32Dealer::~CC32Dealer(): Session(%08x): Remaining. WARNING: this will cause objects to leak."), pSession); + } +#endif + + iParkedRequests.Close(); + delete iThreadManager; + iThreadManager = NULL; + + if (WorkerDataGlobalsExist()) //won't exist if we've OOM'd during construction and reached here during cleanup. + { + for(TWorkerId id = TC32WorkerThreadPublicInfo::EMaxWorkerThreadId; id >= TC32WorkerThreadPublicInfo::EFirstPlayerThread; --id) + { + if(WorkerExists(id)) + { + FreeWorkerReferences(id); + } + } + } + + + delete iWorkerDataGlobals; + iWorkerDataGlobals = NULL; + C32LOG(KC32Shutdown,_L8("CC32Dealer::~CC32Dealer() complete.")); + + } + +void CC32Dealer::ShutdownIfReady() + { + ASSERT(iOwnerThread->WorkerId() == TC32WorkerThreadPublicInfo::EMainThread); // protect against this being called for a dealerPlayer + C32LOG2(KC32Shutdown, _L("CC32Dealer::ShutdownIfReady() - iSessionShutdownComplete = %d"), iSessionShutdownComplete ); + if(iSessionShutdownComplete) + { + iOwnerThread->TriggerThreadShutdownCallback(); + } + } + + +/** +Used during binding when the Dealer receives a introduction response message from a worker. +The Dealer will set-up housekeeping datastructures for the worker +@see TC32WorkerMsg::EMainIntroductionResp +*/ + +void CC32Dealer::ProcessWorkerIntroductionResponse(const TC32WorkerMainIntroductionRespMsg& aMsg) + { + const TC32WorkerThreadPublicInfo& msgInfo = aMsg.WorkerInfo(); + C32LOG2(KC32Dealer, _L("Process Worker Introduction Response for worker: %d"),msgInfo.iWorkerId); + ASSERT(msgInfo.iWorkerId > TC32WorkerThreadPublicInfo::EMainThread && msgInfo.iWorkerId <= TC32WorkerThreadPublicInfo::EMaxWorkerThreadId); + ASSERT(!WorkerDataGlobals().WorkerPresent(msgInfo.iWorkerId)); + TC32WorkerThreadRegister& workerReg = *WorkerDataGlobals().GetWorkerGlobals(msgInfo.iWorkerId); + static_cast(workerReg) = msgInfo; + workerReg.iDealer = workerReg.iWorker->Dealer(); + workerReg.iPlayer = workerReg.iWorker->Player(); + RemovePendingIntroductionResponse(); + + // process response message. Process parked request here + ProcessParkedRequest(); + +#ifdef _DEBUG + // We only switch on the configured simulated allocation failures once the bindings are complete, because + // it's too hard to recover from them earlier. This is a regrettable but hopefully harmless limitation in + // practice, ie if we're OOM during boot then recovery strategies aren't obvious. + workerReg.iHasGlobalAllocFails = aMsg.FailType() != RAllocator::ENone; + if(workerReg.iHasGlobalAllocFails) + { + workerReg.iHeap->__DbgSetAllocFail(aMsg.FailType(), aMsg.FailRate()); + } +#endif + + } + +void CC32Dealer::DeleteCPMLoader(CommsFW::TWorkerId aWorkerId) + { + // remove waiting LoadCPM object + iThreadManager->ProcessModuleLoadSuccess(aWorkerId); + } + +void CC32Dealer::RemoveParkedRequestsOnSessionClose(CCommSession* aSession) + { + const TInt parkedCount = iParkedRequests.Count(); + for(TInt i = parkedCount - 1; i >= 0; --i) + { + const TSessionMessagePair& pair = iParkedRequests[i]; + if(pair.iSession == aSession) + { + C32LOG2(KC32Dealer, _L8("CC32Dealer::RemoveParkedRequest() - found parked for closing session %08x "), aSession); + SafeComplete(pair.iMessage, KErrSessionClosed); // session is being closed + iParkedRequests.Remove(i); + } + } + } + +void CC32Dealer::ProcessParkedRequest() + { + const TInt parkedCount = iParkedRequests.Count(); + C32LOG2(KC32Bootup, _L8("CC32Dealer::ProcessParkedRequestL() - %d parked messages to process"), parkedCount); + for(TInt i = parkedCount - 1; i >= 0; --i) + { + const TSessionMessagePair& pair = iParkedRequests[i]; + // Check that the session still exists + iSessionIter.SetToFirst(); + CSession2* ss; + while((ss = iSessionIter++) != NULL && ss != pair.iSession) + { + } + if(ss && !pair.iMessage.IsNull()) + { + C32LOG2(KC32Bootup, _L8("CC32Dealer::ProcessParkedRequestL() - parked messages for sess %08x"), ss); + RMessage2 tempMsg = pair.iMessage; + iParkedRequests.Remove(i); // remove parked request + TRAPD(res, ss->ServiceL(tempMsg)); + if(res != KErrNone) + { + SafeComplete(tempMsg, res); + } + } + } + } + +TInt CC32Dealer::ParkRequest(CCommSession* aSession, const RMessage2& aMessage) + { + __ASSERT_DEBUG(aMessage.Function()==ECommLoadCommModule,Fault(EBadState,_L8("CC32Dealer::ParkRequest - Attempt to park a non-moduleLoad request. Panicking. Session(%08x) Message(%08x)"), aSession, aMessage.Handle())); + + C32LOG3(KC32Bootup, _L8("CC32Dealer::ParkRequest Session(%08x) Message(%08x)"), aSession, aMessage.Handle()); + return iParkedRequests.Append(TSessionMessagePair(aSession, aMessage)); + } + + +TInt CC32Dealer::LoadCPMOnLoadCommModule(CommsFW::TWorkerId aWorker) + { + return iThreadManager->RequestLoadModule(aWorker); + } + + +void CC32Dealer::ProcessFailedPlayerLoad(CommsFW::TWorkerId aWorker) +// called from CPMLoader if configurator reported load failed. +// gives dealer a chance to do any cleanup. + { + // sweep parked requests array and remove any CSY load requests that were hoping to be loaded into the + // failed player + // They will be completed with appropriate KErrNoMemory (most likely reason for player load to fail) + // error code. Transversing the array is somewhat expensive since we're reprocessing all queued messages. + TFileName csyfilename; + C32LOG2(KC32Dealer,_L8("CC32Dealer:ProcessFailedPlayerLoad: Sweeping parked requests array to complete(w/KErrNoMemory) and remove any waiting on failed player %d"),aWorker); + const TInt parkedCount = iParkedRequests.Count(); + for (TInt i = parkedCount -1 ;i >= 0; --i) + { + const TSessionMessagePair& pair = iParkedRequests[i]; + TInt res = pair.iMessage.Read(0,csyfilename); + + TBuf8 fileName8; + fileName8.Copy(csyfilename); + + + if (res==KErrNone) + { + TInt r=csyfilename.LocateReverse('.'); + if (r==KErrNotFound) + { + // length was checked when message first processed before queuing + __ASSERT_DEBUG(csyfilename.Length() <= KMaxFileName - (TInt)KCSYExtension.iTypeLength, Fault(EBadState,_L("CC32Dealer::ProcessFailedPlayerLoad: Message(%08x) filename is too long. Panicking."),&pair.iMessage)); + csyfilename.Append(KCSYExtension); + } + + CommsFW::TWorkerId worker; + TBool found = iThreadManager->FindThreadByFileName(fileName8,worker); + + + + if (!found) + { + // CSY must have been destined for the default thread + worker = iThreadManager->DefaultThread(); + } + + if (worker == aWorker) + { + C32LOG3(KC32Detail,_L8("CC32Dealer:ProcessFailedPlayerLoad: Completing and removing message(%08x) at index %d"),&pair.iMessage,i); + SafeComplete(pair.iMessage,KErrNoMemory); + iParkedRequests.Remove(i); + } + + } + else + { + __ASSERT_DEBUG(res==KErrNone,Fault(EBadState,_L8("CC32Dealer::ProcessFailedPlayerLoad: Could not read filename from Message at iParkedRequests[%d]. Res=%d Panicking."),i,res)); + // in release mode just ignore mesg and keep going + } + + } + + } + + +/** ThreadManager is updated with CSerial* and PortPrefix. PortPrefix is mapped to CSYFileName +* and this mapping then provides CSYFileName, which is searched in CSYcontainer in session to +* test whether the CSY in question is loaded in this session or not - this mapping is used while +* closing Comm Module +*/ +void CC32Dealer::ProcessLoadCommModuleSuccessResponse(const RMessage2& aMessage, CSerial* aSerial) +// called from workerthread dispatchMsg, or directly if load is for co-resident player + { + /** Map find CSYFileName in ThreadManager and update PortPrefix and CSerial* field against it. In case of duplication, + * the CSerial* will be updated again though it'll be point to the same physical address + */ + + TFileName csyFilename; + Read(0, aMessage, csyFilename, 0); + TInt r=csyFilename.LocateReverse('.'); + if (r==KErrNotFound) + { + csyFilename.Append(KCSYExtension); + } + C32LOG2(KC32Dealer, _L("CC32Dealer::ProcessLoadCommModuleSuccessResponse, insert CSerial* and PortPrefix for: %S"),&csyFilename); + + iThreadManager->UpdateThreadManagerOnCSYLoad(csyFilename, aSerial->Name(), aSerial); + + // complete message in dealer after update is done in ThreadManager + SafeComplete(aMessage, KErrNone); + } + +void CC32Dealer::ProcessLoadCommModuleFailureResponse(const RMessage2& aMessage, TInt aFailReason) + { + TBool dummy; + TFileName csyFilename; + Read(0, aMessage, csyFilename, 0); + + TInt ret = AddCSYExtension(csyFilename,aMessage); + __ASSERT_DEBUG(ret == KErrNone, Fault(EBadState,_L("CC32Dealer::ProcessLoadCommModuleFailureResponse: Could not add extension to CSY filename. This is inconsistent since should not get here in such case. Panicking."))); + + + C32LOG1(KC32Dealer, _L("CC32Dealer::ProcessLoadCommModuleFailureResponse, remove CSY from Session csy container and de-allocate memory for this csy in ThreadManager")); + static_cast(aMessage.Session())->RemoveCSYFromSession(csyFilename,dummy); + // de-allocate memory on this call + iThreadManager->UpdateThreadManagerOnCSYLoadFailure(csyFilename); + // complete message after removing csy from session CSY container and de-allocating memory + SafeComplete(aMessage, aFailReason); + } + + +TInt CC32Dealer::Read(TInt aPos, const RMessagePtr2& aMessage , TDes16& aDes, TInt aOffset) + { + C32LOG3(KC32Dealer, _L8("CC32Dealer::Read(), Pos (%d), Offset (%d)"), aPos, aOffset); + + TInt ret = aMessage.Read(aPos, aDes, aOffset); + if (ret!=KErrNone) + { + C32LOG1(KC32Dealer, _L8("Error at the time of reading data from client")); + PanicClient(EBadDescriptor,aMessage); + } + return ret; + } + +void CC32Dealer::FreeWorkerReferences(TWorkerId aWorkerId) + { + C32LOG2(KC32Shutdown, _L("CC32Dealer::FreeWorkerReferences(%d)"), aWorkerId ); + TC32WorkerThreadRegister& properties(*WorkerDataGlobals().GetWorkerGlobals(aWorkerId)); +#ifdef _DEBUG + // The RootServer normally checks the heap for leaks when the module unloads + // but for C32 CSY modules this is commonly too early, since the Dealer (acting as PitBoss) holds + // its reference open until the cleanup completes. Hence here we check for + // remaining allocations if we hold the last reference and if no thread + // which used it died involuntarily + RCFSharedHeap* heap = static_cast(properties.iHeap); + C32LOG4(KC32Warning, _L8("~~~CC32Dealer::FreeWorkerReferences heap(%08x).AccessCount()==%d, cell count=%d"), (TUint) heap, heap->AccessCount(), heap->Count()); + if(heap->AccessCount() <= 2) + { + if(heap->Count() > 0) + { + //C32LOG2(KRSModule,KRSModuleLeak,_L8("~~~SerComms-CC32Dealer::FreeWorkerReferences.Following leaks are from Ser-Comms C32 (more info logged under: C32SerComms *)")); + C32LOG1(KRSModule,KRSModuleLeak); + heap->LogAllocatedCells(RootServer::KLogSubSysRSModule, RootServer::KLogRSLeakTag); + RProperty pubsub; + TInt res = pubsub.Attach(RootServer::KUidCommsProcess, RootServer::KUidCommsModuleLeakCounter); + //No nead for cleanup stack, cannot leave before Close + if (res == KErrNone) + { + TInt count; + res =pubsub.Get(count); + if (res == KErrNone) + { + count += heap->Count(); + res =pubsub.Set(count); + } + } + pubsub.Close(); + if (res != KErrNone) + { + __CFLOG_1(RootServer::KLogSubSysRSModule, RootServer::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 + C32LOG1(KC32Warning, _L8("--- end of leaked cell log. If leaks not shown enable logging for: RSModule *")); + } + } +#endif + properties.Clear(); + } + +/** +Check if we can unbind from a worker. This is only possible if the local Dealer doesn't +have any outstanding sub-sessions terminating in the peer and it doesn't have any sessions +with outstanding closes against the peer. Otherwise the channel is still needed. +*/ +TBool CC32Dealer::CanUnbindFromWorker(TWorkerId aWorker) + { + if (!WorkerThread().DealerByRef().Player(aWorker)) + { + return ETrue; + } + if(SubsessionCountInPlayer(aWorker) == 0) + { + // Check for any sessions which have outstanding session closes against the Worker + iSessionIter.SetToFirst(); + CCommSession* sess; + while((sess = static_cast(iSessionIter++)) != NULL) + { + if(sess->IsPlayerInDisconnectList(aWorker)) + { + return EFalse; + } + } + return ETrue; + } + return EFalse; + } + + +/** +Check whether a worker Id is legal and a worker with that Id is installed. +*/ +TBool CC32Dealer::WorkerExists(TWorkerId aId) const + { + return WorkerDataGlobals().WorkerPresent(aId); + } + +void CC32Dealer::SessionShutdownComplete() + { + iSessionShutdownComplete = ETrue; + ShutdownIfReady(); + } + + +void CC32Dealer::PostMessage(TWorkerId aWorkerId, TCFMessage& aMessage) + { + iOwnerThread->PostMessage(aWorkerId, aMessage); + } + +#ifdef _DEBUG + // If a heap has been configured from boot to have a failure mode then we don't override this here as + // the lifetime failure testing is more important than the specific test case doing a SetFailNext + void CC32Dealer::SetFailNextForAllHeaps(TInt aFailNext) + { + for(TWorkerId workerId = TC32WorkerThreadPublicInfo::EMainThread; workerId <= TC32WorkerThreadPublicInfo::EMaxWorkerThreadId; ++workerId) + { + const TC32WorkerThreadRegister& worker = *WorkerDataGlobals().GetWorkerGlobals(workerId); + if(WorkerExists(workerId) && !worker.iHasGlobalAllocFails) + { + worker.iHeap->__DbgSetAllocFail((aFailNext < 0)? RAllocator::EReset: RAllocator::EFailNext, aFailNext); + } + } + } +#endif + +/** +Given the "local" worker thread's id (normally but not necessarily the current worker +thread and that of another "foreign" worker thread, if they have different heaps then +switch heaps and return the previous one, otherwise return NULL. +*/ +RAllocator* CC32Dealer::MaybeSwitchHeap(TWorkerId aForeignWorkerId) + { + + const TC32WorkerThreadPublicInfo& foreignInfo = *WorkerDataGlobals().GetWorkerGlobals(aForeignWorkerId); + ASSERT(foreignInfo.iHeap); + RHeap* heap = &User::Heap(); + if(heap != foreignInfo.iHeap) + { + C32LOG2(KC32Detail, _L8("CC32Dealer::MaybeSwitchHeap - Switching heap to %08x."),foreignInfo.iHeap); + return User::SwitchAllocator(foreignInfo.iHeap); + } + else + { + C32LOG2(KC32Detail, _L8("CC32Dealer::MaybeSwitchHeap - No heap switch happened - Heap %08x."),heap); + } + + return NULL; + } + +/** +Called by any Player/thread to add a sub-session to a session. It will switch the local heap to that +of the peer before performing operations on the session pointer. It is essential that the session lock is used +around this call. +@see CC32SubSessionIx::Lock +@see CC32SubSessionIx::UnLock +*/ +TInt CC32Dealer::AddSubSession(CCommSubSession* aSubSession, CCommSession* aSession, TInt& aHandle) + { + RAllocator* prevAllocator = MaybeSwitchHeap(aSession->WorkerId()); + TInt err = aSession->iSubSessions.Add(aSubSession, aHandle); + C32LOG4(KC32Detail, _L8("CC32Dealer::AddSubSession(%08x, %08x, => %08x)"), aSubSession, aSession, aHandle); + if(prevAllocator) + { + User::SwitchAllocator(prevAllocator); + } + return err; + } + +/** +Called by any Player/thread to remove a sub-session from a session. It will switch the local heap to that +of the peer before performing operations on the session pointer. It is essential that the session lock is used +around this call. +@see CC32SubSessionIx::Lock +@see CC32SubSessionIx::UnLock +*/ +void CC32Dealer::RemoveSubSession(TInt aHandle, CCommSession* aSession) + { + RAllocator* prevAllocator = MaybeSwitchHeap(aSession->WorkerId()); + C32LOG3(KC32Detail, _L8("CC32Dealer::RemoveSubSession(%08x, %08x"), aHandle, aSession); + VERIFY(aSession->iSubSessions.Remove(aHandle) != NULL); + if(prevAllocator) + { + User::SwitchAllocator(prevAllocator); + } + } + +/** +Called by any Player/thread to find handle for a sub-session. It will switch the local heap to that +of the peer before performing operations on the session pointer. It is essential that the session lock is used +around this call. +@see CC32SubSessionIx::Lock +@see CC32SubSessionIx::UnLock +*/ +TInt CC32Dealer::FindSubSession(CCommSubSession* aSubSession, CCommSession* aSession, TInt& aHandle) + { + RAllocator* prevAllocator = MaybeSwitchHeap(aSession->WorkerId()); + TInt err = aSession->iSubSessions.Find(aSubSession, aHandle); + C32LOG4(KC32Detail, _L8("CC32Dealer::FindSubSession(%08x, %08x, => %08x)"), aSubSession, aSession, aHandle); + if(prevAllocator) + { + User::SwitchAllocator(prevAllocator); + } + return err; + } + +CC32Dealer* CC32Dealer::Dealer(TWorkerId aWorkerId) const + { + return WorkerDataGlobals().GetWorkerGlobals(aWorkerId)->iDealer; + } + +CC32Player* CC32Dealer::Player(TWorkerId aWorkerId) const + { + return WorkerDataGlobals().GetWorkerGlobals(aWorkerId)->iPlayer; + } + +void CC32Dealer::SendIntroductionToWorker(CommsFW::TWorkerId aWorkerId) + { + // Send the worker peer the Dealer introduction message + TC32WorkerMainIntroductionMsg msg(this); + PostMessage(aWorkerId, msg); + AddPendingIntroductionResponse(); + } + +void CC32Dealer::AddPendingIntroductionResponse() + { + ++iPendingIntroResponses; + } + +void CC32Dealer::RemovePendingIntroductionResponse() + { + --iPendingIntroResponses; + } + +/** +This is a simple check shutting down is not possible if there are still open sessions, unless +shutdown type is EImmediate but then this method is not called. +*/ +TBool CC32Dealer::CanShutdown() + { + CC32Data* globals = CC32DataInTls(); + C32LOG2(KC32Detail, _L8("CC32Dealer::CanShutdown() \t iNumSessions = %d "), globals->iNumSessions); + return globals->iNumSessions <= 0; + } + + +void CC32Dealer::ProcessShutdownRequest(CommsFW::TCFShutdownType aType) +// Since the c32 framework mimmicks esock's but does not yet contain a DealerPlayer concept, +// this function will be called by whichever thread sees a shutdown first. + { + TBool commenceShutdown = CanShutdown(); + + // If have to do now and there are still sessions then we exit anyway but suppress the heap check and log a rude message. We + // used to delete the sessions but that isn't safe + if(EImmediate==aType) + { + commenceShutdown = ETrue; + +#ifdef __FLOG_ACTIVE + iSessionIter.SetToFirst(); + if(iSessionIter++ != NULL) + { + TInt cnt = 0; + iSessionIter.SetToFirst(); + CSession2* ss; + while((ss = iSessionIter++) != NULL) + { + C32LOG2(KC32Detail, _L8("<==Session(%08x): remaining"), ss); + ++cnt; + } + C32LOG2(KC32Detail, _L8("NB! Immediate shutdown commanded but #%d client sessions remaining (bad test code?)"), cnt); + } +#endif + } + + if(WorkerThread().IsMainThread()) + { + // this section will always be called by each thread since no dealerPlayer yet + + // Even the Dealer (acting as pitboss) yields to the immediate shutdown + if(commenceShutdown) + { + WorkerThread().DealerByRef().SessionShutdownComplete(); + } + } + WorkerThread().SetDealerShutdownComplete(commenceShutdown); + } + +// +// TC32WorkerThreadRegister +// + +TC32WorkerThreadRegister::TC32WorkerThreadRegister() + { +#ifdef _DEBUG + iHasGlobalAllocFails = EFalse; +#endif + } + +void TC32WorkerThreadRegister::Clear() + { + inherited::Clear(); + iDealer = NULL; + iPlayer = NULL; + } + +// +// CC32SubSessionIx +// + +void CC32SubSessionIx::InitialiseL() + { + User::LeaveIfError(iLock.CreateLocal()); + } + +CC32SubSessionIx::~CC32SubSessionIx() + { + iLock.Close(); + User::Free(iIx); + } + +TInt CC32SubSessionIx::Find(CCommSubSession* aSubSession, TSubSessionHandle& aHandle) const + { + iLock.AssertLockHeld(); + for(TInt ix = 0; ix < iSize; ++ix) + { + TEntry& e = iIx[ix]; + if(e.iType != CCommSubSession::ENull && e.iObject == aSubSession) + { + aHandle = MakeHandle(ix, e.iMagic, e.iType); + return KErrNone; + } + } + return KErrNotFound; + } + +TInt CC32SubSessionIx::Add(CCommSubSession* aSubSession, TSubSessionHandle& aHandle) + { + iLock.AssertLockHeld(); + if(TUint(iFreeListHead) >= TUint(iSize)) + { + TInt err = ExpandArray(); + if(err != KErrNone) + { + return err; + } + } + TInt ix = iFreeListHead; + TEntry& e = iIx[ix]; + ASSERT(e.iType == CCommSubSession::ENull); + iFreeListHead = e.iNextFree; + ASSERT(TUint(iFreeListHead) <= TUint(iSize)); + e.iObject = aSubSession; + e.iType = TUint8(aSubSession->Type()); + TInt magic = (e.iMagic + 1) & KMagicMask; + e.iMagic = TUint16(magic); + aHandle = MakeHandle(ix, magic, e.iType); + ++iActiveCount; + return KErrNone; + } + +CCommSubSession* CC32SubSessionIx::Remove(TSubSessionHandle aHandle) + { + iLock.AssertLockHeld(); + ASSERT(TUint(iFreeListHead) <= TUint(iSize)); + TEntry* e = At(aHandle); + ASSERT(e); + CCommSubSession* subSess = e->iObject; + e->iType = TUint8(CCommSubSession::ENull); + e->iNextFree = iFreeListHead; + iFreeListHead = e - &iIx[0]; + ASSERT(TUint(iFreeListHead) < TUint(iSize)); + --iActiveCount; + return subSess; + } + +CCommSubSession* CC32SubSessionIx::At(TInt aHandle, CCommSubSession::TSubSessionType aType) const + { + iLock.AssertLockHeld(); + TEntry* e = At(aHandle); + if(e && (e->iType == aType || aType == CCommSubSession::EAny)) + { + return e->iObject; + } + return NULL; + } + +CC32SubSessionIx::TEntry* CC32SubSessionIx::At(TSubSessionHandle aHandle) const + { + TEntry* e = 0; + TInt ix = IndexFromHandle(aHandle); + if(TUint(ix) < TUint(iSize)) + { + e = &iIx[ix]; + if(MakeHandle(ix, e->iMagic, e->iType) != aHandle) + { + e = 0; + } + } + return e; + } + +TInt CC32SubSessionIx::ExpandArray() + { + ASSERT(TUint(iFreeListHead) <= TUint(iSize)); + TInt size = iSize + EIndexGranularity; + if(size > KIndexLimit) + { + return KErrNoMemory; + } + TEntry* ix = reinterpret_cast(User::ReAlloc(iIx, size * sizeof(TEntry))); + if(!ix) + { + return KErrNoMemory; + } + iIx = ix; + iSize = size; + for(TInt ii = iFreeListHead; ii < size;) + { + TEntry& e = ix[ii]; + e.iNextFree = ++ii; + e.iType = TUint8(CCommSubSession::ENull); + } + return KErrNone; + } + +CC32SubSessionIx::TIter::TIter(CC32SubSessionIx& aContainer) +: iContainer(aContainer) + { + SetToFirst(); + } + +void CC32SubSessionIx::TIter::SetToFirst() + { + iPos = 0; + } + +CCommSubSession* CC32SubSessionIx::TIter::operator++(TInt) + { + TSubSessionHandle dummyHandle; + return Next(dummyHandle); + } + +CCommSubSession* CC32SubSessionIx::TIter::Next(TSubSessionHandle& aHandle) + { + iContainer.iLock.AssertLockHeld(); + TEntry* e = &iContainer.iIx[iPos]; + while(iPos < iContainer.iSize) + { + TInt oldPos = iPos++; + if(e->iType != CCommSubSession::ENull) + { + aHandle = iContainer.MakeHandle(oldPos, e->iMagic, e->iType); + return e->iObject; + } + ++e; + } + return NULL; + } + + +// +// CC32Player class definitions +// + + +CC32Player* CC32Player::NewL(CC32WorkerThread* aOwnerThread) + { + CC32Player* self = new(ELeave) CC32Player(aOwnerThread); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +CC32Player::CC32Player(CC32WorkerThread* aOwnerThread) +: iOwnerThread(aOwnerThread) + { + } + +void CC32Player::ConstructL() + { + iPortManager = CPortManager::NewL(); + } + +/** +The Player destructor doesn't have much to do as a lot of the cleanup is done during the +normal shutdown routines. Here the Player merely deletes all sub-sessions it owns. +*/ +CC32Player::~CC32Player() + { + // The object container is stored as a packed array, so working backwards through it avoids invalidating + // the iterator when removing entries (and as a bonus is more efficient) + C32LOG1(KC32Shutdown, _L8("CC32Player::~CC32Player()")); + for(TInt i = iSubSessions.Count() - 1; i >= 0; --i) + { + CCommSubSession* subSession = iSubSessions[i]; + C32LOG2(KC32Shutdown, _L8("-- destroying -- %08x"), subSession ); + // This subsession needs deletion, which we do by closing it until it disappears + // (generally this should only require a single close, but it's possible that the + // Dealer died with it additionally open) + ASSERT(subSession); + ASSERT(subSession->AccessCount() > 0); + // Following logic presumes that each Close() decrements AccessCount() by one; + // anything else is too broken for words + for(TInt cnt = subSession->AccessCount(); cnt > 0; --cnt) + { + CPort* p = CPortFromSubSession(subSession); // subSession->iPort + p->Close(); // close CPort + subSession->Close(); // close CCommSubSession + } + } + iSubSessions.ResetAndDestroy(); + delete iPortManager; + iPortManager = NULL; + } + +/** +The Player can unbind from another worker thread if it doesn't have any sub-sessions +belonging to a session in the peer. +*/ +TBool CC32Player::CanUnbindFromWorker(TWorkerId aWorker) + { + C32LOG3(KC32Bootup, _L8("CC32Player::CanUnbindFromWorker(%d): %d subsess"), aWorker, iSubSessions.Count() ); + for(TInt idx = iSubSessions.Count() - 1; idx >= 0; --idx) + { + CCommSubSession* ss = iSubSessions[idx]; + C32LOG3(KC32Bootup, _L8("-- subsess %08x worker=%d"), ss, ss->Session()->WorkerId()); + if(iSubSessions[idx]->Session()->WorkerId() == aWorker) + { + return EFalse; + } + } + return ETrue; + } + +/** +Check whether the Player is ready to shut down and if so, tells the Worker Thread who owns it. +*/ +void CC32Player::MaybeSetPlayerShutdownComplete(TBool aForceShutdownNow) + { + TBool shutdownNow = aForceShutdownNow || (iSubSessions.Count() == 0); + C32LOG4(KC32Bootup, _L8("CC32Player::MaybeSetPlayerShutdownComplete(), shutdownNow = %d [forced=%d, #subSess=%d]"), + shutdownNow, aForceShutdownNow, iSubSessions.Count() ); + WorkerThread().SetPlayerShutdownComplete(shutdownNow); + } + +/** +If an incoming shutdown request is of type EImmediate, informs the +Worker Thread that Player shutdown is complete, otherwise do nothing here. +*/ +void CC32Player::ProcessShutdownRequest(CommsFW::TCFShutdownType aType) + { + C32LOG2(KC32Bootup, _L8("CC32Player::ProcessShutdownRequest(%d)"), aType ); + WorkerThread().SetPlayerShutdownComplete(aType == EImmediate); + } + +void CC32Player::ProcessUnLoadCommModuleMsg(CSerial* aSerial) + { + __ASSERT_DEBUG(aSerial != NULL, Fault(EBadState, _L8("NULL CSerial* passed to player !"))); + __ASSERT_DEBUG(aSerial->AccessCount() > 0, Fault(EBadState, _L8("CC32Player::ProcessUnLoadCommModuleMsg - AccessCount < 0 before Close()"))); + if(aSerial->AccessCount() > 0) + { + aSerial->Close(); + } + else + { + C32LOG2(KC32Warning, _L8("CC32Player::ProcessUnLoadCommModuleMsg - AccessCount negative %d, Cannot call close()"), aSerial->AccessCount() ); + } + return; + } + +/** +Write a handle back to Ptr3 of the current message +*/ +TInt CC32Player::WriteSubSessionHandle(TInt aHandle) + { + TPckgC pH(aHandle); + return iSession->Write(MSG_PRM(3), Message(),pH); + } + +// CC32Player::CloseSubSession closes a CPort so CommCancel is not used. +void CC32Player::CloseSubSession(const RMessage2& aMessage, CCommSubSession* aSubSession) + { + ASSERT(aSubSession); + CPort* p = CPortFromSubSession(aSubSession); + C32LOG1(KC32Player, _L8("CC32Player::CloseSubSession")); + p->FreeSession(aSubSession->Session()); // check if CPort can be freed in this session + p->Close(); // close CPort + if(aSubSession->AccessCount()==1) + // the access count == 1 and subsession is being closed. Remove subsession from session's CC32SubSessionIx + { + aSubSession->Session()->SubSessions().Lock(); + TInt handle; + TInt res = CC32DealerByRef().FindSubSession(aSubSession, aSubSession->Session(), handle); + if(res == KErrNone) + { + CC32DealerByRef().RemoveSubSession(handle, aSubSession->Session()); + } + else + { + __ASSERT_DEBUG(EFalse, Fault(EFindSubSessionFailed, _L8("Could not find subsession in CC32SubSessionIx while subsession count is 1"))); + } + aSubSession->Session()->SubSessions().Unlock(); + } + aSubSession->Close(); // close CCommSubSession, removes from TSubSessionContainer if access count drops to zero + SafeComplete(aMessage, KErrNone); + } + +/** +This method is uses for example when the session is closed by the client whilst still having +active sub-sessions. All outstanding requests on CPort are cancelled using CPort::CommCancel +@see CC32Player::CloseSession +*/ +void CC32Player::CloseAllOwnedSubSessions(CCommSession* aSession) + { + // The object container is stored as a packed array, so working backwards through it avoids invalidating + // the iterator when removing entries (and as a bonus is more efficient) + C32LOG2(KC32Player, _L8("CC32Player::CloseAllOwnedSubSessions() subsessions in TSubSessionContainer = %d"), iSubSessions.Count() ); + for(TInt i = iSubSessions.Count() - 1; i >= 0; --i) + { + CCommSubSession* subSession = iSubSessions[i]; + if(subSession->Session() == aSession) // close subsessions belonging to the closing session + { + C32LOG2(KC32Player, _L8("CC32Player::CloseAllOwnedSubSessions()-closing subsession %x"), iSubSessions[i] ); + // close all subSessions in 'this' aSession as session is being closed. + // may not lead to ~CPort being called if CPort is being shared across sessions + // AccessCount of all subsession = AccessCount of CPort + while(subSession->AccessCount() >= 1) + { + CPort* p = CPortFromSubSession(subSession); // subSession->iPort + C32LOG3(KC32Player, _L8("CC32Player::CloseAllOwnedSubSessions()-Closing CPort %x for subsession %x"),p, iSubSessions[i] ); + p->CommCancel(0, aSession); // cancel all outstanding requests + p->Close(); // close all CPorts in this session + if (subSession->AccessCount() == 1) + { + subSession->Close(); // close CCommSubSession. This also deletes object + break; //avoid accessing deleted subSession object + } + else + { + subSession->Close(); // close CCommSubSession + } + } + } + } + } + +/** +Used when the dealer signals it is closing a session and the Player needs to cleanup +all resources related to/owned by the session. +@see TPlayerMsg::ESessionClose +*/ +void CC32Player::CloseSession(CCommSession* aSession) + { + // Remove all subsessions in this aSession + CloseAllOwnedSubSessions(aSession); + } + +#ifdef _DEBUG +// I'm worried that we'll have dangling session usage, hence this debug-build deliberate +// corruption of it +static void CorruptSessionPointer(TAny* aSessionPtr) + { + CCommSession** sessPtr = reinterpret_cast(aSessionPtr); + *sessPtr = reinterpret_cast(-1); + } +#endif + +CPort* CC32Player::CPortFromSubSession(CCommSubSession* aSubSession) + { + return aSubSession->iPort; + } + +/** +Process the client message forwarded from the Dealer. For subsession-Close commands +the Dealer provides the subsession pointer explicitly as it has already been removed from +the index. +*/ +void CC32Player::ProcessMessageL(const RMessage2& aMessage, CCommSubSession* aSubSession) + { + iSession = static_cast(aMessage.Session()); + iCurrentMessage = &aMessage; + C32LOG4(KC32Player, _L8("CC32Player:\tProcessMessageL: session=%08x, subsess=%08x, Message(%08x) "), iSession, aSubSession, aMessage.Handle()); + iComplete = ETrue; +#ifdef _DEBUG + CleanupStack::PushL(TCleanupItem(CorruptSessionPointer, &iSession)); +#endif + + switch (aMessage.Function()) + { + case ECommLoadCommModule: + LoadCommModule(aMessage); +#ifdef _DEBUG + CleanupStack::PopAndDestroy(); +#endif + return; + case ECommCloseCommModule: + CloseCommModule(aMessage); +#ifdef _DEBUG + CleanupStack::PopAndDestroy(); +#endif + return; + case ECommPortInfoByName: + { + TPortName name; + Read(1,aMessage,name); + PortInfo(aMessage,name); +#ifdef _DEBUG + CleanupStack::PopAndDestroy(); +#endif + return; + } + //case ECommPortInfoByNumber: as the global index is not valid in player ! + // instead CSerial* is forwarded to player to obtain PortInfo through TC32PlayerGetPortInfoMsg msg + } + + if((aMessage.Function()==ECommOpen) + || + (aMessage.Function()==ECommOpenWhenAvailable)) + { + NewPortL(aMessage); +#ifdef _DEBUG + CleanupStack::PopAndDestroy(); +#endif + return; + } + + if (aMessage.Function()==ECommClose) + { + CloseSubSession(aMessage, aSubSession); +#ifdef _DEBUG + CleanupStack::PopAndDestroy(); +#endif + return; + } + + CPort *p = CPortFromSubSession(aSubSession); + + if(p->SessionHasBeenPreempted(iSession)) + { + SafeComplete(aMessage, KErrCancel); +#ifdef _DEBUG + CleanupStack::PopAndDestroy(); +#endif + return; + } + + // These functions need to be dispatched before the check below is made + if(aMessage.Function()==ECommSetAccess) + { + p->CommSetAccess(aMessage, *iSession); +#ifdef _DEBUG + CleanupStack::PopAndDestroy(); +#endif + return; + } + else if(aMessage.Function()==ECommOpenWhenAvailableCancel) + { + if (p->IsBlockedSetAccessWaiting(*iSession)) + { + p->CommSetAccessCancel(0, iSession); + SafeComplete(aMessage, KErrNone); + } + else + { + SafeComplete(aMessage, KErrNotFound); + } + +#ifdef _DEBUG + CleanupStack::PopAndDestroy(); +#endif + return; + } + + // Any other requests will be denied until the port is fully open + if(p->SessionIsAwaitingOpen(iSession)) + { + SafeComplete(aMessage, KErrNotReady); +#ifdef _DEBUG + CleanupStack::PopAndDestroy(); +#endif + return; + } + + switch (aMessage.Function()) + { + case ECommRead: + p->CommRead(aMessage, iSession); + break; + case ECommReadCancel: + p->CommReadCancel(aMessage.Int3(), iSession); + SafeComplete(aMessage, KErrNone); + break; + case ECommQueryReceiveBuffer: + p->CommQueryReceiveBuffer(aMessage, *iSession); + break; + case ECommResetBuffers: + p->CommResetBuffers(aMessage, *iSession); + break; + case ECommWrite: + p->CommWrite(aMessage, iSession); + break; + case ECommWriteCancel: + p->CommWriteCancel(aMessage.Int3(), iSession); + SafeComplete(aMessage, KErrNone); + break; + case ECommBreak: + p->CommBreak(aMessage, iSession); + break; + case ECommBreakCancel: + p->CommBreakCancel(aMessage.Int3(), iSession); + SafeComplete(aMessage, KErrNone); + break; + case ECommCancel: + p->CommCancel(aMessage.Int3(), iSession); + SafeComplete(aMessage, KErrNone); + break; + case ECommConfig: + p->CommConfig(aMessage, *iSession); + break; + case ECommSetConfig: + p->CommSetConfig(aMessage, *iSession); + break; + case ECommCaps: + p->CommCaps(aMessage, *iSession); + break; + case ECommSetMode: + p->CommSetServerConfig(aMessage, *iSession); + break; + case ECommGetMode: + p->CommGetServerConfig(aMessage, *iSession); + break; + case ECommSignals: + p->CommSignals(aMessage, *iSession); + break; + case ECommSetSignalsToMark: + p->CommSetSignalsToMark(aMessage, *iSession); + break; + case ECommSetSignalsToSpace: + p->CommSetSignalsToSpace(aMessage, *iSession); + break; + case ECommReceiveBufferLength: + p->CommReceiveBufferLength(aMessage, *iSession); + break; + case ECommSetReceiveBufferLength: + p->CommSetReceiveBufferLength(aMessage, *iSession); + break; + case ECommSetAccess: + p->CommSetAccess(aMessage, *iSession); + break; + +#ifdef _DEBUG + case ECommDebugState: + p->CommDebugState( aMessage, *iSession); + break; +#endif + +#ifdef _DEBUG_DEVCOMM + case ECommDbgDoDumpDebugInfo: + p->CommDumpDebugInfo(aMessage); + break; +#endif + } + + // Extensions to the CCommSession starts from here + switch (aMessage.Function()) + { + case ECommNotifySignals: + p->CommNotifySignalChange(aMessage, iSession); + break; + case ECommNotifySignalsCancel: + p->CommNotifySignalChangeCancel(aMessage.Int3(), iSession); + SafeComplete(aMessage, KErrNone); + break; + case ECommNotifyFlowControl: + p->CommNotifyFlowControlChange(aMessage, iSession); + break; + case ECommNotifyFlowControlCancel: + p->CommNotifyFlowControlChangeCancel(aMessage.Int3(), iSession); + SafeComplete(aMessage, KErrNone); + break; + case ECommGetFlowControl: + p->CommGetFlowControlStatus(aMessage, iSession); + break; + case ECommNotifyConfigChange: + p->CommNotifyConfigChange(aMessage, iSession); + break; + case ECommNotifyConfigChangeCancel: + p->CommNotifyConfigChangeCancel(aMessage.Int3(), iSession); + SafeComplete(aMessage, KErrNone); + break; + case ECommNotifyBreak: + p->CommNotifyBreak(aMessage, iSession); + break; + case ECommNotifyBreakCancel: + p->CommNotifyBreakCancel(aMessage.Int3(), iSession); + SafeComplete(aMessage, KErrNone); + break; + case ECommGetRole: + p->CommGetRole(aMessage, iSession); + break; + case ECommNotifyDataAvailable: + p->CommNotifyDataAvailable(aMessage, iSession); + break; + case ECommNotifyDataAvailableCancel: + p->CommNotifyDataAvailableCancel(aMessage.Int3(), iSession); + SafeComplete(aMessage, KErrNone); + break; + case ECommNotifyOutputEmpty: + p->CommNotifyOutputEmpty(aMessage, iSession); + break; + case ECommNotifyOutputEmptyCancel: + p->CommNotifyOutputEmptyCancel(aMessage.Int3(), iSession); + SafeComplete(aMessage, KErrNone); + break; + // case default: // no default case here as it is dealt with in CCommSession::ServiceL + } + // Extensions to the CCommSession ends to here +#ifdef _DEBUG + CleanupStack::PopAndDestroy(); // TCleanupItem(CorruptSessionPointer, &iSession)); +#endif + } + +void CC32Player::SafeComplete(const RMessagePtr2& aMessage, TInt aCompletionCode) + { + if(iComplete) + { + ::SafeComplete(aMessage, aCompletionCode); + } + } + +void CC32Player::DontCompleteCurrentRequest() + { + iComplete = EFalse; + } + +void CC32Player::LoadCommModule(const RMessage2& aMessage) +/** + * Load a comm module + */ + { + TFileName csyfilename; + Read(0,aMessage,csyfilename); + + TInt r=csyfilename.LocateReverse('.'); + if (r==KErrNotFound) + { + csyfilename.Append(KCSYExtension); + } + + C32LOG3(KC32Player, _L("CC32Player::LoadCommModule : %S. aMessage:%08x"), &csyfilename,aMessage.Handle()); + CSerial *s=NULL; + TRAPD(res,s=iPortManager->LoadCommModuleL(csyfilename)); // if you see a crash with (iPortManager == null) here, you might have mis-configured cmi files + if(res == KErrNone) + { + // no leave occurred, library loaded successfully ! Send pointers to message, CsyFilename, and portPrefix + // back to dealer, dealer updates the information in ThreadManager and completes the message + CC32WorkerThread& owner = WorkerThread(); + if(owner.WorkerId()==TC32WorkerThreadPublicInfo::EMainThread) + { + // co-resident player, make direct function call + owner.Dealer()->ProcessLoadCommModuleSuccessResponse(aMessage, s); + DontCompleteCurrentRequest(); + } + else if(owner.PeerReachable(TC32WorkerThreadPublicInfo::EMainThread)) + { + // different thread, send via transports + TC32PlayerLoadCommModuleSuccessResp respMsg(aMessage, s); + owner.PostMessage(TC32WorkerThreadPublicInfo::EMainThread, respMsg); + DontCompleteCurrentRequest(); + const RNullableMessage& forwardedMsg = static_cast(aMessage); + forwardedMsg.NullHandle(); + } + else + { + C32LOG1(KC32Warning, _L8("CC32Player::LoadCommModule() Peer Thread Unreachable")); + DontCompleteCurrentRequest(); + __ASSERT_DEBUG(0, User::Panic(KCFTransportPanic, ECFTransAbsentWorker)); + } + C32LOG1(KC32Player, _L("CC32Player::LoadCommModule : Load completed successfully - message sent to Dealer")); + + } + else + { + // library load failure ! Mesg sent back to dealer to inform of failed load attempt, dealer + // removes CSY from session CSY container and de-allocates memory allocated to this to-be-loaded + // CSY, and completes message with reason of load failure + CC32WorkerThread& owner = WorkerThread(); + if(owner.WorkerId()==TC32WorkerThreadPublicInfo::EMainThread) + { + // co-resident player, make direct function call + owner.Dealer()->ProcessLoadCommModuleFailureResponse(aMessage, res); + DontCompleteCurrentRequest(); + } + else if(owner.PeerReachable(TC32WorkerThreadPublicInfo::EMainThread)) + { + // different thread, send via transports + TC32PlayerLoadCommModuleFailureResp respMsg(aMessage, res); + owner.PostMessage(TC32WorkerThreadPublicInfo::EMainThread, respMsg); + DontCompleteCurrentRequest(); + const RNullableMessage& forwardedMsg = static_cast(aMessage); + forwardedMsg.NullHandle(); + } + else + { + C32LOG1(KC32Warning, _L8("CC32Player::LoadCommModule() Peer Thread Unreachable")); + DontCompleteCurrentRequest(); + __ASSERT_DEBUG(0, User::Panic(KCFTransportPanic, ECFTransAbsentWorker)); + } + } + } + +void CC32Player::CloseCommModule(const RMessage2& aMessage) +/** + * Close a comm module + */ + { + TFullName name; // not a TPortName for backwards compat reasons + Read(0,aMessage,name); + C32LOG2(KC32Player, _L("CC32Player::CloseCommModule() called, CommModule : %S"), &name); + CSerial* s=NULL; + TRAPD(res,s=iPortManager->GetSerialL(name)); + if (res==KErrNone) + { + s->Close(); // call Close() on CSerial object + } + SafeComplete(aMessage, res); + } + +void CC32Player::NewPortL(const RMessage2& aMessage) // do we need to pass CCommSession* here ? +/** + * Ask the port manager to open a port in the CSY which is then added + * to this session's port list (CC32SubSessionIx). If another session + * has already opened the same port, the port manager will still give + * us a reference if the port is not being used exclusively. + * + * @param aMessage handle to the IPC message from the client + */ + { + C32LOG1(KC32Player, _L8("CC32Player::NewPortL()")); + iSession = static_cast(aMessage.Session()); + C32LOG3(KC32Player, _L8("CC32Player:NewPort: session=%08x, Message(%08x) "), iSession, aMessage.Handle()); + CPort *p=NULL; + CSerial *s=NULL; + TFullName name; + TUint port; + TInt len; + TInt handle; + + // Extract port name and number + VERIFY_RESULT(ExtractPortNameAndNumber(aMessage, name, port, len), KErrNone); // must be valid as it is already checked in dealer + TRAPD(res,s=iPortManager->GetSerialL(name.Left(len))); + /** + We can't ASSERT here as the client should have loaded the CSY before opening the port. + This will fail only in the circumstance when the other session unloads the Comm Module and + by the time message is received on player, the GetSerial fails due to module not present. + */ + if (res!=KErrNone) + { + SafeComplete(aMessage, res); + return; + } + if(aMessage.Function()==ECommOpenWhenAvailable) + { + // This default role, as specified in aMessage.Int2(), will only be used + // if the port is being opened for the first time + TRAP(res,p=iPortManager->GetPortL(name,port,s,(TUint)EIntCommWaitUntilAvailable, + (TUint)aMessage.Int2(), iSession)); + } + else + { + TRAP(res,p=iPortManager->GetPortL(name,port,s,aMessage.Int1(),aMessage.Int2(), iSession)); + } + + if(res!=KErrNone) + { + SafeComplete(aMessage, res); + return; + } + else + { + /** no leaves occurred, either new or existing CPort opened successfully + * Iterate through TSubSessionContainer if (p==CPortFromSubSession() && subSess->Session() == iSession) + * call subSess->Open() so that its AccessCount is incremented else + * create a "new" subsession. Iterating the TSubSessionContainer won't + * be a performance bottleneck as RComm::Open() call is made only once + */ + TBool existingSubSess = EFalse; // Set ETrue when existing subsession found + TInt idxFound = 0; // stores the index of existing sub-session from TSubSessionContainer + for(TInt i = iSubSessions.Count() - 1; i >= 0; --i) + { + if(CPortFromSubSession(iSubSessions[i]) == p && iSubSessions[i]->Session() == iSession) // check for existing CPort* && check for session + { + existingSubSess=ETrue; // existing CPort* found which is this session + idxFound = i; // store index + break; + } + } + + if(existingSubSess == EFalse) + { + /** new SubSession + * create "new" subsession + * Add CPort* (p) to this newly created subsession + * Add CCommSubSession to TSubSessionContainer + * Add CCommSubSession* to CC32SubSessionIx to obtain subsession handle + * Write the handle back to client + * if writing handle back to client fails, remove subsession from CC32SubSessionIx + */ + + C32LOG1(KC32Player, _L8("CC32Player::NewPortL() - NEW SUBSESSION")); + CCommSubSession* css = CCommSubSession::NewL(iSession, p, this); // create CCommSubSession + res = iSubSessions.Append(css); // add to TSubSessionContainer + if(res != KErrNone) + { + css->Close(); // Close() newly created sub-session (which will delete it as accesscount = 1) this could not be appended to TSubSessionContainer + SafeComplete(aMessage, res); // complete message with error returned from Append + return; + } + // lock SubSessionIx container in dealer and obtain "new" TSubSessionHandle - TInt + iSession->SubSessions().Lock(); + res = CC32DealerByRef().AddSubSession(css, iSession, handle); + if(res == KErrNone) + { + // write subsession handle to client + res = WriteSubSessionHandle(handle); + if(res != KErrNone) // if cannot write handle to client + { + // we can remove the index from CC32SubSessionIx for this session as its a new subSess + CC32DealerByRef().RemoveSubSession(handle, iSession); + } + } + iSession->SubSessions().Unlock(); + + if(res != KErrNone) // if cannot write handle to client + { + p->Close(); // close CPort + css->Close(); // close CCommSubSession, removes from TSubSessionContainer if access count drops to zero. + } + + SafeComplete(aMessage, res); + return; + } + else + { + /** existing SubSession, + * call Open() on subSess to increment AccessCount + * CC32Dealer::FindSubSession() to find subsession aHandle + * write the aHandle back to client + * if writing handle back to client, call Close() on subsession and CPort to decrement reference counts + */ + C32LOG1(KC32Player, _L8("CC32Player::NewPortL() - EXISTING SUBSESSION")); + iSubSessions[idxFound]->Open(); // increment AccessCount + + // lock SubSessionIx container in dealer + iSession->SubSessions().Lock(); + // find existing sub-session from this session's CC32SubSessionIx + res = CC32DealerByRef().FindSubSession(iSubSessions[idxFound], iSession, handle); // should not fail + if(res != KErrNone) + { + C32LOG1(KC32Warning, _L8("CC32Player::NewPortL() - Failed to find existing sub-session in this session ")); + } + __ASSERT_DEBUG(res == KErrNone, Fault(EFindSubSessionFailed)); + // write existing or new subsession handle back to client + res = WriteSubSessionHandle(handle); + + iSession->SubSessions().Unlock(); + + if(res != KErrNone) // if cannot write handle to client + { + p->Close(); // close CPort + iSubSessions[idxFound]->Close(); // close CCommSubSession + } + + SafeComplete(aMessage, res); + return; + } + } + } + +TInt CC32Player::ExtractPortNameAndNumber(const RMessagePtr2& aMessage, TDes& aPortName, TUint& aPortNumber, TInt& aLength) +/** +Extract the port name and number from RMessage +*/ + { + Read(0,aMessage, aPortName); + + _LIT(KDoubleColon, "::"); + aLength = aPortName.Find(KDoubleColon); + if (aLength == KErrNotFound) + { + return KErrNotFound; + } + // extract the numeric value after :: + TInt numPos = aLength + KDoubleColon.iTypeLength; + TPtrC numPtr(&aPortName[numPos], aPortName.Length() - numPos); + TLex lexer(numPtr); + TInt ret = lexer.Val(aPortNumber); + + return ret; + } + +void CC32Player::PortInfo(const RMessage2& aMessage,const TPortName& aPortName) +/** + * Write back the port info to the client for a specified port + * + * @param aPortName name of the port to get information about + */ + { + TSerialInfo port; + TInt ret=0; + if((ret=iPortManager->PortInfo(aPortName,port))==KErrNone) + { + TPckgC p(port); + Write(0,aMessage,p); + } + SafeComplete(aMessage, ret); + } + +void CC32Player::PortInfo(const RMessage2& aMessage, CSerial* aSerialPtr) +/** + * Write back the port info to the client for a specified port + * + * @param aSerial CSerial Pointer obtained by lookup in CC32ThreadManager + */ + { + iComplete = ETrue; + TFileName name; + TSerialInfo port; + TInt ret=0; + ASSERT(aSerialPtr); + aSerialPtr->Info(port); + aSerialPtr->ModuleName(name); + TPckgC p(port); + ret = Write(0,aMessage,p); + if(ret == KErrNone) //if not KErrNone then client will already be panic'd so do not atttempt further write as this will panic the server + { + Write(1,aMessage,name); + } + SafeComplete(aMessage, ret); + } + +TInt CC32Player::Write(TInt aPos, const RMessagePtr2& aMessage , const TDesC8& aDes, TInt aOffset) +/** + * Write and kill the client if it leaves. + * + * Copies data from an 8 bit descriptor in the server address space to the client + * thread's address space. The target location must be a valid modifiable descriptor. + * Data is copied from the source descriptor to the specified offset position within + * the target descriptor data area. The length of data copied is the length of the + * source descriptor. The length of the target descriptor is set to the length of + * the source descriptor plus the value of the offset. + * + * @param aPtr A pointer to a valid address within the client thread's address space. + * The data type at this location must be a modifiable descriptor, i.e. aTDes8 type. + * @param aDes An 8 bit descriptor in the server address space. This is the source of the copy operation. + * @param aOffset The offset from the start of the target descriptor data area where copying is to begin. + * This value must be greater than or equal to zero. + * + * @panic This function will panic the client if the WriteL() leaves + */ + { + //C32LOG4(KC32Player, _L8("CC32Player::Write() Data = (%s), Pos (%d) Offset (%d)"), aDes.Ptr(), aPos, aOffset); + + TInt ret = aMessage.Write(aPos, aDes, aOffset); + if (ret!=KErrNone) + { + C32LOG1(KC32Player, _L8("CC32Player::Write \t Error at the time of writing data to client")); + PanicClient(EBadDescriptor,aMessage); + } + return ret; + } + + +TInt CC32Player::Read(TInt aPos, const RMessagePtr2& aMessage , TDes8& aDes, TInt aOffset) +/** + * Read and kill the client if it leaves. + * + * Copies data from the client thread's address space into an 8 bit descriptor + * in the server address space. The source data must be a valid descriptor. + * Data is copied from the specified offset position within the source descriptor + * data area. The length of data copied is the length of source descriptor data + * minus the offset value. If the offset value is greater than the length of the + * source descriptor, then no data is copied. The length of data copied is limited + * to the maximum length of the target descriptor. + * + * @param aPtr A pointer to a valid address within the client thread's address space. + * The data at this pointer must be a descriptor, i.e. a TDesC8 type. + * @param aDes An 8 bit descriptor in the server address space. This is the target + * of the copy operation. + * @param aOffset The offset from the start of the source descriptor data area from where + * copying is to begin. This value must be greater than or equal to zero. + * + * @panic This function will panic the client if the ReadL() leaves + */ + { + C32LOG3(KC32Player, _L8("CC32Player::Read(), Pos (%d), Offset (%d)"), aPos, aOffset); + + TInt ret = aMessage.Read(aPos, aDes, aOffset); + if (ret!=KErrNone) + { + C32LOG1(KC32Player, _L8("CC32Player::Read \t Error at the time of reading data from client")); + PanicClient(EBadDescriptor,aMessage); + } + return ret; + } + + +TInt CC32Player::Write(TInt aPos, const RMessagePtr2& aMessage , const TDesC16& aDes, TInt aOffset) +/** + * Write and kill the client if it leaves. + * + * (see CC32Player::Write() with 8-bit descriptor) + * + * @param aPtr A pointer to a valid address within the client thread's address space. + * The data type at this location must be a modifiable descriptor, i.e. aTDes16 type. + * @param aDes A 16 bit descriptor in the server address space. This is the source of the copy operation. + * @param aOffset The offset from the start of the target descriptor data area where copying is to begin. + * This value must be greater than or equal to zero. + * + * @panic This function will panic the client if the WriteL() leaves + */ + { + //C32LOG4(KC32Player, _L8("CC32Player::Write(), Data = (%s), Pos (%d), Offset (%d)"), aDes.Ptr(), aPos, aOffset); + + TInt ret = aMessage.Write(aPos, aDes, aOffset); + if (ret!=KErrNone) + { + C32LOG1(KC32Player, _L8("CC32Player::Write \t Error at the time of writing data to client")); + PanicClient(EBadDescriptor,aMessage); + } + return ret; + } + + +TInt CC32Player::Read(TInt aPos, const RMessagePtr2& aMessage , TDes16& aDes, TInt aOffset) +/** + * Read and kill the client if it leaves. + * + * (see CC32Player::Write() with 8-bit descriptor) + * + * @param aPtr A pointer to a valid address within the client thread's address space. + * The data at this pointer must be a descriptor, i.e. a TDesC16 type. + * @param aDes A 16 bit descriptor in the server address space. This is the target + * of the copy operation. + * @param aOffset The offset from the start of the source descriptor data area from where + * copying is to begin. This value must be greater than or equal to zero. + * + * @panic This function will panic the client if the ReadL() leaves + */ + { + C32LOG3(KC32Player, _L8("CC32Player::Read(), Pos (%d), Offset (%d)"), aPos, aOffset); + + TInt ret = aMessage.Read(aPos, aDes, aOffset); + if (ret!=KErrNone) + { + C32LOG1(KC32Player, _L8("CC32Player::Read \t Error at the time of reading data from client")); + PanicClient(EBadDescriptor,aMessage); + } + return ret; + } + + +// EOF - CS_ROLES.CPP + +