diff -r 000000000000 -r dfb7c4ff071f serialserver/c32serialserver/SCOMM/cs_thread.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/serialserver/c32serialserver/SCOMM/cs_thread.cpp Thu Dec 17 09:22:25 2009 +0200 @@ -0,0 +1,983 @@ +// 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 "cs_thread.h" +#include "C32LOG.H" +#include "CS_STD.H" +#include "cs_roles.h" +#include "cs_msgs.h" +#include "cs_glob.h" +#include + +#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS +#include +#endif + +#include + +using namespace CommsFW; + +#ifndef KLogSubSysSerComms +__CFLOG_STMT(_LIT8(KLogSubSysSerComms, "SerComms");) +#endif + +// +// CC32WorkerThread class definitions +// + +CC32WorkerThread* CC32WorkerThread::NewL(TCFModuleInfo* aModuleInfo) + { + CleanupStack::PushL(TCleanupItem(DeleteHBufC8, aModuleInfo->iIniData)); + CC32WorkerThread* self = new (ELeave) CC32WorkerThread; + CleanupStack::PushL(self); + self->ConstructL(aModuleInfo); + CleanupStack::Pop(self); + CleanupStack::PopAndDestroy(); // aModuleInfo->iIniData + return self; + } + +CC32WorkerThread::CC32WorkerThread() +#ifdef _DEBUG + : iFailType(RAllocator::ENone) +#endif + { + } + +/** +The worker thread secondary construction will create the relevant Player/Dealer +instances needed as well as the channel handler to the Root Server. If and only if it +is the main thread it will also create the PitBoss. +@note If it has a Dealer and is not the main thread it is a WorkerDealer. +*/ +void CC32WorkerThread::ConstructL(TCFModuleInfo* aModuleInfo) + { + TBool isDealer; + TBool isPlayer; + __CFLOG_OPEN; + C32LOG1(KC32Bootup,_L8("CC32WorkerThread::ConstructL Determining roles")); + DetermineRoleL(*aModuleInfo->iIniData, isDealer, isPlayer); + + iWorkerRegister = CC32WorkerRegister::NewL(WorkerId(), NULL); + iTransport = CCommsTransport::NewL(*iWorkerRegister, NULL, NULL); + iTransport->RegisterLegacyInterface(this); + + // initglobals allocates on heap and stores pointer in TLS, so + // requires special cleanup if we leave beyond here - handled in RunC32Thread + C32GlobalUtil::InitC32GlobalsL(this); + +#ifdef _DEBUG + if(aModuleInfo->iIniData!=NULL) + { + // Support for simulated heap allocation failures: place "AllocFail= type rate" in .CMI file + // where: + // type == ERandom, ETrueRandom, EDeterministic, EFailNext + // rate == rate/chance of failure + // See RAllocator documentation for details. Best to set this for the heap owner only, since the + // last one processed will determine function for all heap users & it could get confusing. + _LIT8(KAllocFailLabel, "AllocFail"); + TPtrC8 allocFail; + if(GetVarFromIniData(*(aModuleInfo->iIniData), KNullDesC8, KAllocFailLabel, allocFail)) + { + TLex8 lex(allocFail); + TPtrC8 failTypeTok = lex.NextToken(); + const struct + { + const TText8* iLabel; + RAllocator::TAllocFail iType; + } failModes[] = + { + { _S8("ERandom"), RAllocator::ERandom }, + { _S8("ETrueRandom"), RAllocator::ETrueRandom }, + { _S8("EDeterministic"), RAllocator::EDeterministic }, + { _S8("EFailNext"), RAllocator::EFailNext } + }; + TInt i; + for(i = sizeof(failModes) / sizeof(failModes[0]) - 1; i >= 0; --i) + { + if(TPtrC8(failModes[i].iLabel).CompareF(failTypeTok) == 0) + { + break; + } + } + TInt rate = 0; + lex.SkipSpace(); + if(i < 0 || lex.Val(rate) != KErrNone) + { + // Already in Debug mode so just log and panic + C32LOG1(KC32Bootup, _L("Panic - Corrupt Ini data - AllocFail param")); + Fault(EBadIniData); + } + iFailType = failModes[i].iType; + iFailRate = rate; + } + + /* + For debug builds, ensure that the array inside the cleanup stack will never + need to allocate any memory. This aids checking for leaked cells across a + sequence of calls that is heap-balanced. + */ + { + const TInt KStretchExtent = 10; + TRAP_IGNORE( + for(TInt i = 0; i < KStretchExtent; i++) + { + CleanupStack::PushL((TAny*)1); + } + CleanupStack::Pop(KStretchExtent); + ) + } + + } +#endif // _DEBUG + + if(isDealer) + { + if(IsMainThread()) + { + C32LOG(KC32Bootup, _L8("I am the Main Thread. Creating Dealer.")); + iDealer = CC32Dealer::NewL(this,*(aModuleInfo->iIniData)); + if(iDealer->ThreadManager()) + { + if(iDealer->ThreadManager()->DefaultThread() == KMainThreadId) + { + /* + The default thread which loads unlisted CSYs is the dealer/main thread (workerId=0), + this isn't normal in a multi-threaded configuration, but can happen due to + 1. BAD configuration (would fault in debug builds to signal a corrupt configuration, but not in release) + 2. CMI files with no C32SerComms group field in it. + 3. CSYList=* field present in IniData section of dealer/main thread (workerId=0) [valid, but discouraged in multi-threaded conf] + */ + C32LOG(KC32Warning,_L8("Dealer is default thread for loading unlisted CSYs! A BAD configuration perhaps ?")); + isPlayer=ETrue; + } + } + } + } + else + { + SetDealerShutdownComplete(ETrue); + } + + if(isPlayer) + { + C32LOG1(KC32Bootup, _L8("I am a Player, creating instance.")); + iPlayer = CC32Player::NewL(this); + } + else + { + SetPlayerShutdownComplete(ETrue); + } + + + // Start listening for binds, etc, from the RS + C32LOG1(KC32Bootup, _L8("CC32WorkerThread::ConstructL Init RS ChannelHandler")); + iChannelHandler = CCommChannelHandler::NewL(aModuleInfo->iRxQueues, aModuleInfo->iTxQueues, this); + C32LOG1(KC32Bootup, _L8("CC32WorkerThread::ConstructL Done")); + } + +CC32WorkerThread::~CC32WorkerThread() + { + C32LOG2(KC32Shutdown, _L8("CC32WorkerThread(%08x)::~CC32WorkerThread()"), this); + delete iTransport; + delete iChannelHandler; + delete iPlayer; + if(IsMainThread()) + { + delete iDealer; + } + delete iWorkerRegister; + C32LOG2(KC32Shutdown, _L8("CC32WorkerThread(%08x)::~CC32WorkerThread() complete"), this); + __CFLOG_CLOSE; + } + +/** Determine from inidata whether this worker is Dealer, Player or both. */ +void CC32WorkerThread::DetermineRoleL(const TDesC8& aIniData, TBool &aIsDealer, TBool &aIsPlayer) + { + aIsDealer = EFalse; + aIsPlayer = EFalse; + // BC when No IniData present, this CPM is both Dealer and Player + if(&aIniData==NULL) + { + aIsDealer = ETrue; + aIsPlayer = ETrue; + } + else + { + TPtrC8 roleValue; + // Role missing - Dealer + if (!GetVarFromIniData(aIniData, KNullDesC8, KRoleLabel, roleValue)) + { + aIsDealer = ETrue; + } + else + { + // Role present - player + if (roleValue.CompareF(KPlayerRole)==0) + { + aIsPlayer = ETrue; + } + // Role present - dealer + else if (roleValue.CompareF(KDealerRole)==0) + { + aIsDealer = ETrue; + } + // Role present - invalid arg + else + { + C32LOG2(KC32Warning, _L8("Invalid Role in [IniData] section: %S"),&roleValue); + __ASSERT_DEBUG(0,Fault(EBadIniData)); + } + } + + // WorkerId missing + TPtrC8 workerIdValue; + if(!GetVarFromIniData(aIniData, KNullDesC8, KWorkerIdLabel, workerIdValue)) + { + C32LOG1(KC32Warning, _L8("Corrupt [IniData]: WorkerId missing.")); + __ASSERT_DEBUG(0,Fault(EBadIniData)); + // leave if ini data corrupt rather than panic unless debug + User::Leave(KErrCorrupt); + } + + User::LeaveIfError(ConvertVal(workerIdValue, iWorkerId)); + // check for boundary values for WorkerId + if(iWorkerId > TC32WorkerThreadPublicInfo::EMaxWorkerThreadId) + { + C32LOG2(KC32Warning, _L8("Corrupt [IniData]: Invalid WorkerId: %d"),iWorkerId); + __ASSERT_DEBUG(0,Fault(EBadIniData)); + // leave if ini data corrup rather than panic if not debug + User::Leave(KErrCorrupt); + } + + if(iWorkerId == TC32WorkerThreadPublicInfo::EMainThread && aIsDealer == EFalse) + { + C32LOG1(KC32Warning, _L8("Worker Id zero with no dealer role. overriding")); + aIsDealer = ETrue; + } + + // CSYList present - Player + TPtrC8 csyListValue; + if(GetVarFromIniData(aIniData, KNullDesC8, KCSYListLabel, csyListValue)) + { + aIsPlayer = ETrue; + } + } // End of else block [if(aIniData==NULL)] + } + + + +/** +Check that our end sub-module address is correctly named: the sub-module name must be numeric and match our worker id. +*/ +TInt CC32WorkerThread::DecodePeerId(const TCFSubModuleAddress* aSubModule1, const TCFSubModuleAddress* aSubModule2, TWorkerId& aPeerId) + { + TInt err = KErrNone; + if(ConvertVal(aSubModule1->SubModule(), aPeerId) != KErrNone || aPeerId != WorkerId()) + { + err = KErrCorrupt; + } + else + { + if(ConvertVal(aSubModule2->SubModule(), aPeerId) != KErrNone || + aPeerId > TC32WorkerThreadPublicInfo::EMaxWorkerThreadId) + { + err = KErrCorrupt; + } + } + return err; + } + + +/** +Deals with binding requests from the Root Server. Note that although the Root Server allows +multiple identical bindings C32 does not allow this and will panic if the binding already exists. +Bindings are expressed in C32 by CCommsTransport. Since all sub-module +names are unique across all C32 instances (they are the individual owning worker ID converted to text) +they can be used here. I.e. the remote end sub-module is converted back to int and used to insert the +CCommsTransport into an array in the position corresponding to the remote end sub-module name/number. +As for responding to the bind request there are two cases: + -# This worker is a "dumb" Player: Send bind response immediately. + -# This worker is the Main thread: Send introduction messages to remote end and + postpone bind response until introduction response messages have arrived back. +@see CCommsTransport +*/ +void CC32WorkerThread::CFBindMessageReceived(const TCFBindMsg& aMsg) + { + __CFLOG_SMADDR2(( KLogSubSysSerComms, KLogSubSysSerComms, _L8("W%d: CFBindMessageReceived %S <=> %S"), + WorkerId(), + &aMsg.SubModule1()->Printable(__FullModName1), + &aMsg.SubModule2()->Printable(__FullModName2) )); + TWorkerId bindId; + TInt err = DecodePeerId(aMsg.SubModule1(), aMsg.SubModule2(), bindId); + ASSERT(bindId <= TC32WorkerThreadPublicInfo::EMaxWorkerThreadId); + if(err == KErrNone) + { + if(iTransport->PeerReachable(bindId)) + { + C32LOG2(KC32Bootup, _L("%d Already exists. Error in configuration of cmi files"),bindId); + //Must panic b/c the new module will be left in a half bound state and will tend to get out of hand + __ASSERT_DEBUG(0, User::Panic(KCFTransportPanic, ECFTransInvalidWorkerId)); + } + else + { + err = iTransport->EstablishTransportToPeer(bindId, aMsg.ChannelInput(), aMsg.ChannelOutput()); + // Main thread introduces itself; workers wait passively for this + if(err == KErrNone) + { + iTransport->SetLastRequestIdConcerningPeer(bindId, aMsg.Identifier()); + if(IsMainThread()) + { + C32LOG1(KC32Bootup, _L("Sending Introduction Message to Worker")); + DealerByRef().SendIntroductionToWorker(bindId); + } + } + } + } + /* Main dealer only completes when it has received the introduction response messages from peers, + dumb players respond immediately. */ + if(err!=KErrNone || !IsMainThread()) + { + TCFBindCompleteMsg respMsg(aMsg.Identifier(), err); + VERIFY_RESULT(iChannelHandler->Send(respMsg), KErrNone); + } + } + +void CC32WorkerThread::SetDealerShutdownComplete(TBool aComplete) + { + // Backsliding not permitted; cannot step back to !complete + if(aComplete) + { + iDealerShutdownComplete = aComplete; + MaybeCompleteUnbindings(); + } + } + +void CC32WorkerThread::SetPlayerShutdownComplete(TBool aComplete) + { + // Backsliding not permitted; cannot step back to !complete + if(aComplete) + { + iPlayerShutdownComplete = aComplete; + MaybeCompleteUnbindings(); + } + } + +/** +The unbind requests are only responded to once the channel is presumed to be idle: + - for Dealers this means no sessions remain with subsessions on that Player + - for Players this means no subsessions remain for that Dealer +So the peer handler is marked as pending unbind but the unbind response is not sent back +until both Dealer and Player (if any) confirm idleness, which has to be checked whenever +closing sessions or subsessions. +Perhaps this could/should switch to using ref counts? +Once unbind is pending the Dealer refrains from creating any new subsessions which use it. +@see CC32WorkerThread::MaybeCompleteUnbinding +*/ +void CC32WorkerThread::CFUnbindMessageReceived(const TCFUnbindMsg& aMsg) + { + // Mark the peer handler as unbinding + __CFLOG_SMADDR2(( KLogSubSysSerComms, KLogSubSysSerComms, _L8("W%d: CFUnbindMessageReceived %S <=> %S"), + WorkerId(), + &aMsg.SubModule1()->Printable(__FullModName1), + &aMsg.SubModule2()->Printable(__FullModName2) )); + + TWorkerId unbindId; + TInt err = DecodePeerId(aMsg.SubModule1(), aMsg.SubModule2(), unbindId); + if(err == KErrNone && !iTransport->PeerReachable(unbindId)) + { + __ASSERT_DEBUG(0, User::Panic(KCFTransportPanic, ECFTransAbsentWorker)); // shouldn't happen, but could possibly by race with thread panicked in shutdown? + err = KErrNotFound; + } + if(err == KErrNone) + { + iTransport->SetLastRequestIdConcerningPeer(unbindId, aMsg.Identifier()); + iTransport->SetDropTransportPending(unbindId, ETrue); + MaybeCompleteUnbinding(unbindId); + } + else + { + TCFUnbindCompleteMsg respMsg(aMsg.Identifier(), err); + VERIFY_RESULT(iChannelHandler->Send(respMsg), KErrNone); + } + } + +/** +Can be called at any time, will poll all peer handlers for unbind "readiness" and make sure +they complete the unbind if possible. +@see CC32WorkerThread::MaybeCompleteUnbinding +*/ +void CC32WorkerThread::MaybeCompleteUnbindings() + { + if(iProlongBindingLife == 0) + { + for(TWorkerId player = TC32WorkerThreadPublicInfo::EMainThread; player <= TC32WorkerThreadPublicInfo::EMaxWorkerThreadId; ++player) + { + MaybeCompleteUnbinding(player); + } + } + } + +/** +If the peer handler have been marked for unbind check whether local Dealer/Player is ready to +unbind from this particular worker. If so, deletes handler and send unbind response to Root Server. +@see CC32Dealer::CanUnbindFromWorker +@see CC32Player::CanUnbindFromWorker +@see TCFUnbindCompleteMsg +*/ +void CC32WorkerThread::MaybeCompleteUnbinding(TWorkerId aWorker) + { + if(iTransport->PeerReachable(aWorker) && iTransport->IsDropTransportPending(aWorker)) + { + if(!Dealer() || Dealer()->CanUnbindFromWorker(aWorker)) + { + if(!Player() || Player()->CanUnbindFromWorker(aWorker)) + { + C32LOG2(KC32Bootup, _L("CC32WorkerThread::MaybeCompleteUnbinding(%d) - dropping transport & unbinding"), aWorker); + TCFUnbindCompleteMsg respMsg(iTransport->LastRequestIdConcerningPeer(aWorker), KErrNone); + iTransport->DropTransportToPeer(aWorker); + VERIFY_RESULT(iChannelHandler->Send(respMsg), KErrNone); + } + } + } + } + +/** +A shutdown message can be of several types: + -# EImmediate: We shutdown immediately even if leaking resources, although doing a best effort to cleanup. However, SymbianOS doesnt allow the server to gracefully terminate sessions so certain things cant be cleaned up. + -# EOptional: Ignored. + -# EGraceful: The module only unloads once no resources remain, which means for the Dealer no sessions and for the Player no subsessions. The shutdown request arrives after the unbind requests. +@see CommsFW::TCFShutdownMsg +@see CommsFW::TCFShutdownType +@see CC32Dealer::ProcessShutdownRequest +@see CC32Player::ProcessShutdownRequest +@see CC32WorkerThread::SetShuttingDown +@see CC32WorkerThread::MaybeTriggerThreadShutdownCallback +*/ +void CC32WorkerThread::CFShutdownMessageReceived(const CommsFW::TCFShutdownMsg& aMsg) + { + const CommsFW::TCFShutdownType type = aMsg.Type(); + C32LOG2(KC32Shutdown, _L("CFShutdownMessageReceived(%d)"), type); + + if(EOptional == type) + { + C32LOG1(KC32Shutdown, _L("Type is EOptional: ignoring")); + return; + } + else if(EImmediate == type) + { + C32LOG1(KC32Shutdown, _L("Type is EImmediate")); + DealerByRef().SetImmediateShutdownPresent(); + } + + SetShuttingDown(); + + if(Dealer()) + { + Dealer()->ProcessShutdownRequest(type); + } + if(Player()) + { + Player()->ProcessShutdownRequest(type); + } + + MaybeTriggerThreadShutdownCallback(); + } + +void CC32WorkerThread::PostMessage(TWorkerId aWorkerId, TCFMessage& aMessage) + { + TCFLegacyMessagePacker::PackForPostage(aWorkerId, aMessage); + iTransport->PostMessage(aMessage); + } + +/** +Deal with incoming messages from other workers. +*/ +void CC32WorkerThread::DispatchL(const TCFMessage& aMessage, TWorkerId aSenderId) + { + switch(aMessage.Code()) + { + case TC32WorkerMsg::EMainIntroduction: + { + C32LOG2(KC32CommsTrspMsg, _L("DispatchL(%d). EMainIntroduction"), aSenderId); + ASSERT(!IsMainThread()); + const TC32WorkerMainIntroductionMsg& msg = reinterpret_cast(aMessage); + iDealer = msg.Dealer(); + iWorkerRegister->SetGlobalThreadRegister(&iDealer->WorkerDataGlobals()); + TC32WorkerThreadPublicInfo info; + info.Init(WorkerId(), aSenderId == TC32WorkerThreadPublicInfo::EMainThread); + info.iWorker = this; + TC32WorkerMainIntroductionRespMsg respMsg(info); +#ifdef _DEBUG + respMsg.SetFailType(iFailType); + respMsg.SetFailRate(iFailRate); +#endif + PostMessage(aSenderId, respMsg); + break; + } + case TC32WorkerMsg::EMainIntroductionResp: + { + C32LOG2(KC32CommsTrspMsg, _L("DispatchL(%d). EMainIntroductionResp"), aSenderId); + ASSERT(IsMainThread()); + const TC32WorkerMainIntroductionRespMsg& msg = reinterpret_cast(aMessage); + DealerByRef().ProcessWorkerIntroductionResponse(msg); + + /* In contrast with the players, who send their bind response immediately to the RootServer, + the Main dealer has deferred sending the bind response until it has initialised the protocols + signalled in the introduction response message. This ultimately ensures that the configurator + only increments the sequence level when all protocols are actually initialised. */ + TCFBindCompleteMsg bindresp(iTransport->LastRequestIdConcerningPeer(aSenderId), KErrNone); + VERIFY_RESULT(iChannelHandler->Send(bindresp), KErrNone); + iTransport->SetLastRequestIdConcerningPeer(aSenderId, TCFCommsMessageId::KNullMessageId); + // delete cpmLoader + DealerByRef().DeleteCPMLoader(aSenderId); + break; + } + + case TC32PlayerMsg::EForwardRequest: + { + ASSERT(Player()); + TC32PlayerForwardRequestMsg fwdReqMsg(reinterpret_cast(aMessage)); + C32LOG2(KC32CommsTrspMsg, _L8("DispatchL(%d). EForwardRequest "), aSenderId); + Player()->ProcessMessageL(fwdReqMsg.Message(), fwdReqMsg.SubSession()); + break; + } + case TC32PlayerMsg::ESessionClose: + { + C32LOG2(KC32CommsTrspMsg, _L("DispatchL(%d). ESessionClose"), aSenderId); + CC32Player* player = Player(); + ASSERT(player); + const TC32PlayerSessionCloseMsg& sessionCloseMsg = reinterpret_cast(aMessage); + /* Only do something if the message is within the deadline and we're sure + the session pointer is safe to use */ + TTime time; + time.HomeTime(); + if(time.Int64()<=sessionCloseMsg.SessionCloseDeadline()) + { + IncProlongBindingLife(); + player->CloseSession(sessionCloseMsg.Session()); + TC32PlayerSessionCloseRespMsg sessionCloseRespMsg(WorkerId(), sessionCloseMsg); + PostMessage(aSenderId, sessionCloseRespMsg); + DecProlongBindingLife(); + if(ShuttingDown()) + { + player->MaybeSetPlayerShutdownComplete(EFalse); + MaybeTriggerThreadShutdownCallback(); + } + } + break; + } + case TC32PlayerMsg::ESessionCloseResp: + { + C32LOG2(KC32CommsTrspMsg, _L("DispatchL(%d). ESessionCloseResp"), aSenderId); + ASSERT(IsMainThread()); + const TC32PlayerSessionCloseRespMsg& msg = reinterpret_cast(aMessage); + ASSERT(msg.Session()); + + /* Only do something if the message is within the deadline and we're sure + the session pointer is safe to use */ + TTime time; + time.HomeTime(); + if(time.Int64()<=msg.SessionCloseDeadline()) + { + CCommSession* session = msg.Session(); + session->SessionCloseResp(msg.WorkerId()); + if(session->IsDisconnectListEmpty()) + { + // Deleting the session may allow the binding to be removed, which will + // cause the channel handler to be deleted + Dealer()->DeleteSession(session); + } + } + break; + } + case TC32PlayerMsg::ELoadCommModuleMsg: + { + C32LOG2(KC32CommsTrspMsg, _L("DispatchL(%d). ELoadCommModuleMsg"), aSenderId); + ASSERT(Player()); + TC32PlayerLoadCommModuleMsg msg(reinterpret_cast(aMessage)); + // make function call to player here + Player()->LoadCommModule(msg.Message()); + break; + } + case TC32PlayerMsg::ELoadCommModuleSuccessResp: + { + C32LOG2(KC32CommsTrspMsg, _L("DispatchL(%d). ELoadCommModuleResp"), aSenderId); + ASSERT(IsMainThread()); + TC32PlayerLoadCommModuleSuccessResp msg(reinterpret_cast(aMessage)); + // update CC32ThreadManager with CSerial*, portprefix and csyfilename for the csy loaded successfully + // portprefix is retrieved using CSerial* (by switching heap if necessary) and csyfilename is obtained + // from message, after dealer after updating ThreadManager complete message with KErrNone, to emilinate + // possible race condition of player completing message and subsequent port open rerquest reaching + // ThreadManager before dealer gets a chance to update ThreadManager + Dealer()->ProcessLoadCommModuleSuccessResponse(msg.Message(), msg.SerialPtr()); + break; + } + case TC32PlayerMsg::ELoadCommModuleFailureResp: + { + C32LOG2(KC32CommsTrspMsg, _L("DispatchL(%d). ELoadCommModuleFailureResp"), aSenderId); + ASSERT(IsMainThread()); + const TC32PlayerLoadCommModuleFailureResp msg(reinterpret_cast(aMessage)); + // remove CSY from session CSY container and de-allocate memory for this csy in ThreadManager as load failed + Dealer()->ProcessLoadCommModuleFailureResponse(msg.Message(), msg.FailureReason()); + break; + } + case TC32PlayerMsg::EUnLoadCommModuleMsg: + { + C32LOG2(KC32Detail, _L("DispatchL(%d). EUnLoadCommModuleMsg"), aSenderId); + ASSERT(Player()); + TC32PlayerUnLoadCommModuleMsg msg(reinterpret_cast(aMessage)); + Player()->ProcessUnLoadCommModuleMsg(msg.SerialPtr()); + break; + } + case TC32PlayerMsg::EGetPortInfoMsg: + { + C32LOG2(KC32CommsTrspMsg, _L("DispatchL(%d). EGetPortInfoMsg"), aSenderId); + ASSERT(Player()); + TC32PlayerGetPortInfoMsg msg(reinterpret_cast(aMessage)); + // make function call to player here + Player()->PortInfo(msg.Message(), msg.SerialPtr()); + break; + } + default: + { + C32LOG1(KC32Warning, _L("Unknown Transport message received. Do Nothing")); + break; + } + + } + } + +/** +The ProcessMessageL function which takes care of incoming messages doesnt TRAP anything, +instead we count on dealing with failures here in the RunError. This simplifies the +ProcessMessageL function and avoids the cost of another TRAP +@see CC32Player::ProcessMessageL +*/ +void CC32WorkerThread::OnDispatchLeave(const TCFMessage& aMessage, TWorkerId /*aSenderId*/, TInt aError) + { + C32LOG3(KC32Warning,_L8("CC32WorkerThread::OnDispatchLeave- Dispatch Left!! for message %x with error %d"),&aMessage,aError); + switch(aMessage.Code()) + { + case TC32PlayerMsg::EForwardRequest: + { + TC32PlayerForwardRequestMsg fwdReqMsg(reinterpret_cast(aMessage)); + if(aError != KErrNone && aError != KErrBadDescriptor) + { + fwdReqMsg.Message().Complete(aError); + C32LOG1(KC32Warning,_L8("CC32WorkerThread::OnDispatchLeave, EForwardRequest - completing message.")); + } + break; + } + case TC32PlayerMsg::ELoadCommModuleMsg: + { + TC32PlayerLoadCommModuleMsg loadMsg(reinterpret_cast(aMessage)); + if(aError != KErrNone && aError != KErrBadDescriptor) + { + loadMsg.Message().Complete(aError); + C32LOG1(KC32Warning,_L8("CC32WorkerThread::OnDispatchLeave, ELoadCommModuleMsg - completing message.")); + } + break; + } + case TC32PlayerMsg::ELoadCommModuleSuccessResp: + { + TC32PlayerLoadCommModuleSuccessResp loadSuccessMsg(reinterpret_cast(aMessage)); + if(aError != KErrNone && aError != KErrBadDescriptor) + { + loadSuccessMsg.Message().Complete(aError); + C32LOG1(KC32Warning,_L8("CC32WorkerThread::OnDispatchLeave, ELoadCommModuleSuccessResp - completing message.")); + } + break; + } + case TC32PlayerMsg::ELoadCommModuleFailureResp: + { + TC32PlayerLoadCommModuleFailureResp loadFailMsg(reinterpret_cast(aMessage)); + if(aError != KErrNone && aError != KErrBadDescriptor) + { + loadFailMsg.Message().Complete(aError); + C32LOG1(KC32Warning,_L8("CC32WorkerThread::OnDispatchLeave, ELoadCommModuleFailureResp - completing message.")); + } + break; + } + case TC32PlayerMsg::EGetPortInfoMsg: + { + TC32PlayerGetPortInfoMsg portInfoMsg(reinterpret_cast(aMessage)); + if(aError != KErrNone && aError != KErrBadDescriptor) + { + portInfoMsg.Message().Complete(aError); + C32LOG1(KC32Warning,_L8("CC32WorkerThread::OnDispatchLeave, EGetPortInfoMsg - completing message.")); + } + break; + } + } + } + + +TBool CC32WorkerThread::ShuttingDown() const + { + return iWorkerShuttingDown; + } + +void CC32WorkerThread::SetShuttingDown() + { + iWorkerShuttingDown = ETrue; + } + +void CC32WorkerThread::SessionShutdownComplete() + { + if(IsMainThread()) + { + DealerByRef().SessionShutdownComplete(); + } + else + { + TriggerThreadShutdownCallback(); + } + } + +void CC32WorkerThread::TriggerThreadShutdownCallback() + { + C32LOG1(KC32Shutdown, _L("CC32WorkerThread::TriggerThreadShutdownCallback()") ); + CC32DataInTls()->iShutdownWatchDog->Shutdown(); + } + +void CC32WorkerThread::MaybeTriggerThreadShutdownCallback() + { + if(!ShuttingDown()) + { + return; + } + TBool doTrigger = DealerByRef().TestImmediateShutdownPresent(); + C32LOG4(KC32Shutdown, _L("MaybeTriggerThreadShutdownCallback() - immediate=%d, dealerComplete=%d, playerComplete=%d"), + doTrigger, DealerShutdownComplete(), PlayerShutdownComplete()); + if(!doTrigger && DealerShutdownComplete()) + { + if(!PlayerShutdownComplete() && Player()) + { + Player()->MaybeSetPlayerShutdownComplete(EFalse); + } + if(PlayerShutdownComplete()) + { + // Check if all bindings have gone + doTrigger = ETrue; + for(TWorkerId player = TC32WorkerThreadPublicInfo::EMainThread; player <= TC32WorkerThreadPublicInfo::EMaxWorkerThreadId; ++player) + { + if(player != WorkerId() && iTransport->PeerReachable(player)) + { + C32LOG2(KC32Bootup, _L("MaybeTriggerThreadShutdownCallback() - peer %d binding remains"), player); + doTrigger = EFalse; + break; + } + } + } + } + if(doTrigger) + { + if(WorkerId() == TC32WorkerThreadPublicInfo::EMainThread) + { + DealerByRef().ShutdownIfReady(); + } + else + { + TriggerThreadShutdownCallback(); + } + } + } + + + +LOCAL_C void LoadDeviceDrivers(void) +/** + * On the target, load all the EUART LDDs and PDDs that can be found + */ + { +#if defined (__EPOC32__) + //Load Pdd's + _LIT(CommPddName, "EUART"); + _LIT(CommPddName0, "EUART0"); + _LIT(CommPddName1, "EUART1"); + _LIT(CommPddName2, "EUART2"); + _LIT(CommPddName3, "EUART3"); + _LIT(CommPddName4, "EUART4"); + _LIT(CommPddName5, "EUART5"); + _LIT(CommPddName6, "EUART6"); + _LIT(CommPddName7, "EUART7"); + _LIT(CommPddName8, "EUART8"); + _LIT(CommPddName9, "EUART9"); + User::LoadPhysicalDevice(CommPddName); + User::LoadPhysicalDevice(CommPddName0); + User::LoadPhysicalDevice(CommPddName1); + User::LoadPhysicalDevice(CommPddName2); + User::LoadPhysicalDevice(CommPddName3); + User::LoadPhysicalDevice(CommPddName4); + User::LoadPhysicalDevice(CommPddName5); + User::LoadPhysicalDevice(CommPddName6); + User::LoadPhysicalDevice(CommPddName7); + User::LoadPhysicalDevice(CommPddName8); + User::LoadPhysicalDevice(CommPddName9); + + // Load logical device driver + _LIT(CommLddName, "ECOMM.LDD"); + User::LoadLogicalDevice(CommLddName); +#endif // #ifdef __EPOC32__ + } + + +void CC32WorkerThread::DeleteHBufC8(TAny* aHBufC) + { + delete static_cast(aHBufC); + } + + +/** +The C32 thread. This is where control will resume when the RootServer starts a C32 instance. +This function creates the worker thread object and starts the active scheduler. +*/ +EXPORT_C TInt CC32WorkerThread::ThreadEntryPoint(TAny* aArg) + { + return RunC32Thread(static_cast(aArg)); + } + +EXPORT_C TInt CC32WorkerThread::RunC32Thread(TCFModuleInfo* aArg) +/** + * The comm server thread. + */ + { + CTrapCleanup* tc=CTrapCleanup::New(); + if (!tc) + { + return KErrNoMemory; + } + + LoadDeviceDrivers(); + + CCommScheduler* cs = CCommScheduler::New(); + + TCFModuleInfo* moduleInfo = aArg; + + TRAPD(res, CC32WorkerThread::NewL(moduleInfo)); // takes ownership of aArg and eventually deletes + + if(res != KErrNone) + { + // memory added to the TLS won't have been deleted since it isn't referred to be the cleanupstack + // due to it being deemed to be "owned" by the thread via the TLS. So we delete it here + CTLSRedirector* tls = CRedirectorInTls(); + if (tls != NULL) + { + // The CC32Data destructor does not own any data that it points to, so does not delete the data, + // So we have to delete watchdog directly + + CC32Data* globals = CC32DataInTls(); + if (globals != NULL) + { + delete globals->iShutdownWatchDog; + } + delete tls; + Dll::SetTls(NULL); // so that logger sees it is gone + } + + delete cs; + delete tc; + + return res; + } + + RThread::Rendezvous(KErrNone); + + CActiveScheduler::Start(); + + delete cs; + delete tc; + + return KErrNone; + } + + +// +// CCommChannelHandler class definitions +// +CCommChannelHandler::CCommChannelHandler(CC32WorkerThread* aWorkerThread) +: CCFModuleChannelHandler(CActive::EPriorityStandard), + iWorkerThread(aWorkerThread) + { + } + +CCommChannelHandler* CCommChannelHandler::NewL(RCFChannel::TMsgQueues aRxQueues, + RCFChannel::TMsgQueues aTxQueues, + CC32WorkerThread* aWorkerThread) + { + CCommChannelHandler* pHandler = new (ELeave) CCommChannelHandler(aWorkerThread); + CleanupStack::PushL(pHandler); + pHandler->ConstructL(aRxQueues, aTxQueues); + CleanupStack::Pop(pHandler); + return pHandler; + } + +TInt CCommChannelHandler::Send(const CommsFW::TCFMessage &aMessage) + { + return inherited::Send(aMessage); + } + + +/** +This will be called by the handler when a shutdown message have been received from +the Root Server. Delegating the call to the Worker Thread. +*/ +void CCommChannelHandler::CFMessageShutdown(const TCFShutdownMsg& aMessage) + { + iWorkerThread->CFShutdownMessageReceived(aMessage); + } + +/** +This will be called by the handler when a Discovery message have been received from +the Root Server. It will send a response, telling the Root Server that it has a single +sub-module/binding point which is named after the WorkerID. +As each Worker has a unique ID each worker thus also has a unique sub-module name to bind to, +which potentially allows for simpler handling code and easy to interpret logs. +*/ +void CCommChannelHandler::CFMessageDiscover(const TCFDiscoverMsg& aMessage) + { + ASSERT(aMessage.Size() > 0); + TBuf8<4> subname; + _LIT8(subconst, "%d"); + subname.Format(subconst, iWorkerThread->WorkerId()); + aMessage.SubModuleNames()->Copy(subname); + TCFDiscoverRespMsg respMsg(aMessage.Identifier(), 1, EFalse); + VERIFY_RESULT(Send(respMsg), KErrNone); + } + +/** +This will be called by the handler when a Bind message have been received from +the Root Server. Delegating the call to the Worker Thread. +*/ +void CCommChannelHandler::CFMessageBind(const TCFBindMsg& aMessage) + { + iWorkerThread->CFBindMessageReceived(aMessage); + } + +/** +This will be called by the handler when a Unbind message have has received from +the Root Server. Delegating the call to the Worker Thread. +*/ +void CCommChannelHandler::CFMessageUnbind(const TCFUnbindMsg& aMessage) + { + iWorkerThread->CFUnbindMessageReceived(aMessage); + } + +