diff -r 000000000000 -r dfb7c4ff071f commsfwsupport/commselements/serverden/src/sd_dealer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/commsfwsupport/commselements/serverden/src/sd_dealer.cpp Thu Dec 17 09:22:25 2009 +0200 @@ -0,0 +1,510 @@ +// Copyright (c) 2008-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 "sd_log.h" +#include "sd_roles.h" + + +#ifdef _DEBUG +// Panic category for "absolutely impossible!" vanilla ASSERT()-type panics from this module +// (if it could happen through user error then you should give it an explicit, documented, category + code) +_LIT(KSpecAssert_ElemSvrDenDealrC, "ElemSvrDenDealrC"); +#endif + +using namespace Den; +using namespace CommsFW; + +// +// CCommonDealer +// + +/** +This function will first do the ConstructL and if that succeeds it will +start the server using the server name discovered during ConstructL. So when this +function returns the new dealer object, its server is in effect running. +*/ +EXPORT_C CCommonDealer* CCommonDealer::NewL(CCommonServer* aServer) + { + CCommonDealer* self = new(ELeave) CCommonDealer(aServer); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +EXPORT_C void CCommonDealer::ConstructL() + { + iServer->StartL(iServer->ServerName()); + } + +EXPORT_C CCommonDealer::CCommonDealer(CCommonServer* aServer) +: iServer(aServer) + { + } + +EXPORT_C CCommonDealer::~CCommonDealer() + { + COMMONLOG((WorkerId(), KECommonServerTag, _L8("CCommonDealer::~CCommonDealer()."))); + iParkedRequests.Close(); + delete iServer; + } + +EXPORT_C const TDesC& CCommonDealer::ServerName() const + { + return iServer->ServerName(); + } + +/** +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. +*/ +EXPORT_C void CCommonDealer::DeleteSession(CWorkerSession* 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(iServer->WorkerThread().ShuttingDown() && CanShutdown()) + { + iServer->WorkerThread().SetDealerShutdownComplete(ETrue); + iServer->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 CCommonDealer::ProcessSubSessions(TWorkerId aPeerId, CWorkerSession::TSubSessionProcessor aSubSessionProcessor, TAny* aArg) + { + iServer->SessionIterator().SetToFirst(); + CWorkerSession* sess; + while((sess = static_cast(iServer->SessionIterator()++)) != NULL) + { + sess->SubSessions().Lock(); + + static_cast(sess)->ProcessSubSessions(aPeerId, aSubSessionProcessor, aArg); + + sess->SubSessions().Unlock(); + } + } + +/** +Forget all subsessions that belonged to a Player in the dead peer. The post-mortem cleanup mechanism will +separately complete any client requests against these. +*/ +EXPORT_C void CCommonDealer::CleanupDeadWorker(TWorkerId aPeerId) + { + iServer->SessionIterator().SetToFirst(); + CSession2* sess; + while((sess = iServer->SessionIterator()++) != NULL) + { + static_cast(sess)->CleanupDeadWorker(aPeerId); + } + } + +TInt CCommonDealer::SubsessionCountInPlayer(TWorkerId aPeerId) + { + TInt numSubSessions = 0; + ProcessSubSessions(aPeerId, CWorkerSession::CountSubSessions, &numSubSessions); + return numSubSessions; + } + +/** +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 CCommonDealer::CanUnbindFromWorker(TWorkerId aWorker) + { + //TBDAA ASSERT_HOME_THREAD; + if (!iServer->WorkerThread().PitBoss().Player(aWorker)) + { + return ETrue; + } + if(SubsessionCountInPlayer(aWorker) == 0) + { + // Check for any sessions which have outstanding session closes against the Worker + iServer->SessionIterator().SetToFirst(); + CSession2* sess; + while((sess = iServer->SessionIterator()++) != NULL) + { + if(static_cast(sess)->IsPlayerInDisconnectList(aWorker)) + { + return EFalse; + } + } + return ETrue; + } + return EFalse; + } + +/** Add to the queue of indeterminate requests parked for re-evaluation once configuration has completed +*/ +EXPORT_C TInt CCommonDealer::ParkRequest(CWorkerSession* aSession, const RMessage2& aMessage, TParkReason aReason) const + { + COMMONLOG((WorkerId(), KECommonBootingTag, _L8("CCommonDealer::ParkRequest Session(%08x) Message(%08x) - search back through the logs for the message handle to see info about the message when it was decoded in ServiceL"), aSession, aMessage.Handle())); + TInt err = iParkedRequests.Append(TParkedRequest(aSession, aMessage, aReason)); + +#if defined _DEBUG || defined SYMBIAN_TRACE_ENABLE + if( ( err == KErrNone ) && ( iParkedRequests.Count() == 1 ) ) + { + // Log hint to anyone who runs into a CPM deadlock due to a dependency on load order. + // We log at the first ParkRequest to avoid unnecessary log messages as the risk only + // exists if a module depends on a module with a parked request. + // We log in C32Start as well as ESock and RDebug so that users are directed from the + // CStart32 log to the ESock documentation in case they fail to realise the implications + // of a park request. + _LIT8(KParkRequestPotentialDeadlockWarning, "ESockSvr CCommonDealer::ParkRequest(reason=%d): If the log hangs before the \"ESockSvr startup modules loaded!\" log entry then there might be a deadlock involving a client calling too early during bootup before a component is ready. See Esock How To."); + #ifdef _DEBUG + RDebug::Printf(reinterpret_cast(TPtrC8(KParkRequestPotentialDeadlockWarning).Ptr()), aReason); + #endif + COMMONLOG((WorkerId(), KECommonBootingTag, KParkRequestPotentialDeadlockWarning)); + } +#endif + + return err; +} + +CCommonDealer::TParkedRequest::TParkedRequest(CWorkerSession* aSession, const RMessage2& aMessage, TParkReason aReason) +: iSession(aSession), + iMessage(aMessage), + iReason(aReason) + { + } + +/** Complete all requests parked for the given reason +*/ +void CCommonDealer::ReleaseParkedRequests(TParkReason aReason) + { + COMMONLOG((WorkerId(),KECommonBootingTag, _L8("CCommonDealer::ReleaseParkedRequests(aReason = %d) - %d total parked messages"), aReason, iParkedRequests.Count())); + TInt i = 0; + while(i < iParkedRequests.Count()) // traverse in order of arrival to minimise odd effects for clients (they shouldn't have such dependencies really, but could) + { + const TParkedRequest& request = iParkedRequests[i]; + if(request.iReason == aReason) + { + // Check that the session still exists + iServer->SessionIterator().SetToFirst(); + CWorkerSession* ss; + while((ss = static_cast(iServer->SessionIterator()++)) != NULL && ss != request.iSession) + { + } + + if(ss) + { + COMMONLOG((WorkerId(),KECommonBootingTag, _L8("CCommonDealer::ReleaseParkedRequests() - parked message %08x for sess %08x"), request.iMessage.Handle(), ss)); + TRAPD(err, ss->ServiceL(request.iMessage)); + if(err != KErrNone && !request.iMessage.IsNull()) + { + request.iMessage.Complete(err); + } + } + iParkedRequests.Remove(i); + } + else + { + ++i; + } + } + } + +#if defined(__ELEMENTS_MESSAGE_INTERCEPT_ACTIVE) + +/** Complete a selection of requests parked by debug requests +*/ +void CCommonDealer::ReleaseDebugParkedRequests(CWorkerSession* aSess, TInt aSubSessHandle) + { + COMMONLOG((WorkerId(),KECommonBootingTag, _L8("CCommonDealer::ReleaseDebugParkedRequests(aSess=%08x, aSubSessHandle=%08x) - %d total parked messages"), aSess, aSubSessHandle, iParkedRequests.Count())); + TInt i = 0; + while(i < iParkedRequests.Count()) // traverse in order of arrival to minimise odd effects for clients (they shouldn't have such dependencies really, but could) + { + const TParkedRequest& request = iParkedRequests[i]; + if(request.iReason == EDebugParking && + (!aSess || aSess == request.iSession) && + (!aSubSessHandle || aSubSessHandle == request.iMessage.Int3())) + { + // Check that the session still exists + iServer->SessionIterator().SetToFirst(); + CWorkerSession* ss; + while((ss = static_cast(iServer->SessionIterator()++)) != NULL && ss != request.iSession) + { + } + if(ss) + { + COMMONLOG((WorkerId(),KECommonBootingTag, _L8("CCommonDealer::ReleaseDebugParkedRequests() - parked message %08x for sess %08x"), request.iMessage.Handle(), ss)); + TRAPD(err, ss->ServiceL(request.iMessage)); + if(err != KErrNone && !request.iMessage.IsNull() && !ss->Disconnecting()) + { + request.iMessage.Complete(err); + } + } + iParkedRequests.Remove(i); + } + else + { + ++i; + } + } + } + +#endif // __ELEMENTS_MESSAGE_INTERCEPT_ACTIVE + + +/** Once server configuration is complete any requests parked because they required the full environment can be +re-evaluated +*/ +EXPORT_C void CCommonDealer::ProcessConfigurationComplete(TConfigurationCompletionType aType) + { + // The enums are numerically equivalent (at time of writing), so have to hope compiler does the obvious here + CCommonDealer::TParkReason reason(EIndeterminateDuringBoot); + switch(aType) + { + case EModuleInitialisation: + reason = EIndeterminateDuringBoot; + break; + case ETierMapping: + reason = EAwaitingTierToWorkerMapping; + break; + default: + __ASSERT_DEBUG(FALSE, User::Panic(KSpecAssert_ElemSvrDenDealrC, 1)); + } + ReleaseParkedRequests(reason); + } + +void CCommonDealer::ProcessShutdownRequest(TCFShutdownType aType) + { + TBool shutdownImmediately = 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(CommsFW::EImmediate==aType) + { + shutdownImmediately = ETrue; + +#ifdef SYMBIAN_TRACE_ENABLE + iServer->SessionIterator().SetToFirst(); + if(iServer->SessionIterator()++ != NULL) + { + TInt cnt = 0; + iServer->SessionIterator().SetToFirst(); + CSession2* ss; + while((ss = iServer->SessionIterator()++) != NULL) + { + COMMONLOG((WorkerId(), KECommonServerTag, _L8("<==Session(%08x): remaining"), ss)); + ++cnt; + } + COMMONLOG((WorkerId(), KECommonServerTag, _L8("NB! Immediate shutdown commanded but #%d client sessions remaining (bad test code?)"), cnt)); + } +#endif + } + + if(WorkerThread().IsMainThread()) + { + if(CommsFW::EImmediate != aType) + { + WorkerThread().PitBoss().StartShutdown(); + } + // Even the PitBoss yields to the immediate shutdown + if(shutdownImmediately) + { + WorkerThread().PitBoss().SessionShutdownComplete(); + } + } + WorkerThread().SetDealerShutdownComplete(shutdownImmediately); + } + + + +// +// CCommonWorkerDealer +// + +EXPORT_C CCommonWorkerDealer* CCommonWorkerDealer::NewL(CCommonServer* aServer) + { + CCommonWorkerDealer* self = new(ELeave) CCommonWorkerDealer(aServer); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +EXPORT_C CCommonWorkerDealer::CCommonWorkerDealer(CCommonServer* aServer) +: CCommonDealer(aServer) + { + } + +/** +The name of the server will be postfixed with thread ID and Worker ID. If this worker is revived in +case of a crash, it will have a different thread ID and not cause a name clash with the dead server. +It is also useful for debugging/logging purposes. +*/ +EXPORT_C void CCommonWorkerDealer::ConstructL() + { + iEligibleClients.ConstructL(); // Slightly unusual construction pattern, but does it reallly need to be CBase.. + + // Replace server name + _LIT(KServerNameFormat, "-%d-%x"); + TUint threadId = RThread().Id(); + iServer->ServerName().AppendFormat(KServerNameFormat, WorkerId(), threadId); + + CCommonDealer::ConstructL(); //Start the server etc + } + +EXPORT_C CCommonWorkerDealer::~CCommonWorkerDealer() + { + //CloseSessionSustainer(); + } + +/** +This method is called directly from the PitBoss thread, never from the local worker thread. +*/ +EXPORT_C void CCommonWorkerDealer::AddEligiblePidL(TProcessId aId) + { + iEligibleClients.AddL(aId); + } + +/** +This method is called directly from the PitBoss thread, never from the local worker thread. +*/ +EXPORT_C void CCommonWorkerDealer::RemoveEligiblePid(TProcessId aId) + { + iEligibleClients.Remove(aId); + } + +/** +The WorkerDealer is using this when a client does a Connect, to check whether the process has +been allowed access by the PitBoss. +*/ +EXPORT_C TBool CCommonWorkerDealer::IsEligible(TProcessId aId) + { + return iEligibleClients.IsEligible(aId); + } + +/* +void CCommonWorkerDealer::CloseSessionSustainer() + { + //iSessionSustainer.Close(); + } +*/ + +// +// EligibleClients +// + +CCommonWorkerDealer::XEligibleClients::XEligibleClients() + { + // Initialize the list of eligible client PIDs + for(TInt i=0; i