commsfwsupport/commselements/serverden/src/sd_player.cpp
changeset 0 dfb7c4ff071f
child 9 77effd21b2c9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/commsfwsupport/commselements/serverden/src/sd_player.cpp	Thu Dec 17 09:22:25 2009 +0200
@@ -0,0 +1,340 @@
+// 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 <e32base.h>
+#include <cfshared.h>
+#include "sd_log.h"
+#include "sd_roles.h"
+#include "sd_msgs.h"
+#include "sd_std.h"
+#include "sd_errors.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_ElemSvrDenPlayrC, "ElemSvrDenPlayrC");
+#endif
+
+using namespace Den;
+using namespace CommsFW;
+
+//
+// CSockSessionProxy
+//
+EXPORT_C CCommonSessionProxy::CCommonSessionProxy(CWorkerSession* aSession, CCommonPlayer& aPlayer)
+:	iSession(aSession),
+	iPlayer(aPlayer),
+	iNumSubSessClosing(ELivingSession)
+	{
+	//COMMONLOG((WorkerId(),KECommonBootingTag, _L8("CSockSessionProxy %08x:\tCSockSessionProxy(), iSockSession %08x"), this, iSession) );
+	}
+
+EXPORT_C CCommonSessionProxy::~CCommonSessionProxy()
+	{
+	//LOG(ESockLog::Printf(KESockSessDetailTag, _L8("CSockSessionProxy %08x:\t~CSockSessionProxy(), iSockSession %08x"), this, iSockSession) );
+	if(iLink.iNext)
+		{
+		iLink.Deque();
+		}
+	}
+
+void CCommonSessionProxy::BeginSessionClose()
+	{
+	//COMMONLOG((WorkerId(),KECommonBootingTag, _L8("CSockSessionProxy %08x:\tBeginSessionClose(), iSockSession %08x"), this, iSession) );
+	/* Only do something if the message is within the deadline and we're sure
+	   the session pointer is safe to use */
+	CCommonWorkerThread& worker = iPlayer.WorkerThread();
+	worker.IncProlongBindingLife();
+	__ASSERT_DEBUG(!IsClosing(), User::Panic(KSpecAssert_ElemSvrDenPlayrC, 1));
+	iNumSubSessClosing = 1;	// dummy subsession to prevent premature suicide during this close loop
+
+	// 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)
+	CCommonPlayer::TSubSessionContainer& subSessions(iPlayer.SubSessions());
+	for(TInt i = subSessions.Count() - 1; i >= 0; --i)
+		{
+		CWorkerSubSession* subSession = subSessions[i];
+		if(subSession->Session() == iSession)
+			{
+			++iNumSubSessClosing;
+			if(!subSession->IsClosing())
+				{
+				subSession->DeleteMe();
+				}
+			}
+		}
+
+	NotifySubSessionDestroyed();	// remove the dummy subsession
+	}
+
+EXPORT_C void CCommonSessionProxy::NotifySubSessionDestroyed()
+	{
+	//COMMONLOG((WorkerId(),KECommonBootingTag, _L8("CSockSessionProxy %08x:\tNotifySubSessionDestroyed(), iSockSession %08x"), this, iSession) );
+	if(IsClosing() && --iNumSubSessClosing <= 0)
+		{
+		__ASSERT_DEBUG(iNumSubSessClosing == 0, User::Panic(KSpecAssert_ElemSvrDenPlayrC, 2));
+		CCommonWorkerThread& worker = iPlayer.WorkerThread();
+		worker.CompleteSessionClose(iSession);
+		delete this;
+		}
+	}
+
+
+//
+// CCommonPlayer
+//
+
+/**
+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.
+*/
+EXPORT_C CCommonPlayer::~CCommonPlayer()
+	{
+	// 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)
+	COMMONLOG((WorkerId(),KECommonBootingTag, _L8("CCommonPlayer::~CCommonPlayer()")));
+
+	for(TInt i = iSubSessions.Count() - 1; i >= 0; --i)
+		{
+		CWorkerSubSession* subSession = iSubSessions[i];
+		COMMONLOG((WorkerId(),KECommonBootingTag, _L8("-- destroying %08x"), subSession));
+		subSession->DeleteMe();
+		}
+
+	// Subsessions belong to sessions and get removed when those close. The only time a Player should be going down
+	// with leftover subsessions is an immediate shutdown, else either a session wasn't destroyed properly or a subsession
+	// got leaked off it. We do this after the cleanup loop above so the ptrs are helpfully listed
+	__ASSERT_DEBUG((iSubSessions.Count() == 0 || PitBoss().TestImmediateShutdownPresent()), User::Panic(KSpecAssert_ElemSvrDenPlayrC, 3));
+
+	iSubSessions.ResetAndDestroy();
+	}
+
+EXPORT_C CCommonPlayer::CCommonPlayer(CCommonWorkerThread* aOwnerThread, TPlayerRole aPlayerRole)
+:	iOwnerThread(aOwnerThread),
+	iPlayerRole(aPlayerRole)
+	{
+	COMMONLOG((WorkerId(),KECommonBootingTag, _L8("CCommonPlayer::CCommonPlayer()")));
+	iSessionProxies.SetOffset(CCommonSessionProxy::GetLinkOffset());
+	}
+
+/**
+MultiTool function to return the current session proxy for a session, but also to create it
+if it doesn't exist. This avoids having the same pattern in multiple places where we first check
+to see if it exists then creates it if not.
+*/
+EXPORT_C CCommonSessionProxy* CCommonPlayer::CurrentSessionProxyL()
+	{
+	if(!iCurrentSessionProxy)
+		{
+		iCurrentSessionProxy = FindOrCreateSessionProxyL(Session());
+		}
+	return iCurrentSessionProxy;
+	}
+
+CCommonSessionProxy* CCommonPlayer::FindOrCreateSessionProxyL(CWorkerSession* aSession)
+	{
+	CCommonSessionProxy* proxy = FindSessionProxy(aSession);
+	if(proxy==NULL)
+		{
+		// No existing proxy; create a new one and append to list
+		proxy = DoCreateSessionProxyL(aSession);
+		iSessionProxies.AddLast(*proxy);
+		}
+	return proxy;
+	}
+
+CCommonSessionProxy* CCommonPlayer::FindSessionProxy(CWorkerSession* aSession)
+	{
+	TDblQueIter<CCommonSessionProxy> iter(iSessionProxies);
+	CCommonSessionProxy* proxy;
+	while((proxy = iter++) != NULL)
+		{
+		if(proxy->Session() == aSession)
+			{
+			break;
+			}
+		}
+	return proxy;
+	}
+
+/**
+The Player can unbind from another worker thread if it doesn't have any sub-sessions
+belonging to a session in the peer's Dealer and there are no interface cookies left registered
+at all
+*/
+TBool CCommonPlayer::CanUnbindFromWorker(TWorkerId aWorker)
+	{
+	COMMONLOG((WorkerId(), KECommonBootingTag, _L8("CCommonPlayer::CanUnbindFromWorker(%d): %d subsess"), aWorker, iSubSessions.Count()));
+
+	TInt regItfCount = WorkerThread().Transport()->RegisteredCount();
+	TInt persistentItfCount = Messages::TlsGlobals::Get().PersistentItfCount();
+	if(regItfCount > persistentItfCount)
+		{
+		COMMONLOG((WorkerId(), KECommonBootingTag, _L8("-- can't; %d interfaces still registered (%d transient)"), regItfCount, regItfCount - persistentItfCount) );
+		return EFalse;
+		}
+
+	for(TInt idx = iSubSessions.Count() - 1; idx >= 0; --idx)
+		{
+		const CWorkerSubSession* ss = iSubSessions[idx];
+		COMMONLOG((WorkerId(),KECommonBootingTag, _L8("-- subsess %08x worker=%d"), ss, ss->Session()->WorkerId()));
+		if(ss->Session()->WorkerId() == aWorker)
+			{
+			COMMONLOG((WorkerId(),KECommonBootingTag, _L8("-- can't; subsess %08x belongs to this peer"), ss) );
+			return EFalse;
+			}
+		}
+	return ETrue;
+	}
+
+EXPORT_C void CCommonPlayer::MaybeSetPlayerShutdownComplete(TBool aForceShutdownNow)
+	{
+	//A forced shutdown trumps all other considerations
+	TBool shutdownComplete = aForceShutdownNow || (SubSessions().Count() == 0 && IsPlayerShutdownComplete());
+
+	COMMONLOG((WorkerId(), KECommonBootingTag, _L8("CPlayer::MaybeSetPlayerShutdownComplete(), shutdownComplete = %d [forced=%d, #subSess=%d]"),
+		shutdownComplete, aForceShutdownNow, SubSessions().Count()));
+
+	WorkerThread().SetPlayerShutdownComplete(shutdownComplete);
+	}
+
+EXPORT_C CWorkerSubSession* CCommonPlayer::SubSession(const TSubSessionUniqueId& aSubSessionUniqueId) const
+	{
+	__ASSERT_DEBUG(aSubSessionUniqueId!=0, User::Panic(KDenFaultPanic, ECommonBadSubSessionUniqueId));
+	CWorkerSubSession* ss = NULL;
+	for (TInt i = iSubSessions.Count() - 1; i>=0 && ss==NULL ; i--)
+		{
+		if (iSubSessions[i]->UniqueId()==aSubSessionUniqueId)
+			{
+			ss = iSubSessions[i];
+			}
+		}
+	return ss;
+	}
+
+EXPORT_C void CCommonPlayer::ProcessMessageL(const RSafeMessage& aMessage, CWorkerSubSession* aSubSession)
+	{
+	SetSession(static_cast<CWorkerSession*>(aMessage.Session()));
+	iCurrentSessionProxy = NULL;
+	Reset();
+	iCurrentMessage = &aMessage;
+
+	DoProcessMessageL(aMessage,aSubSession);
+	}
+
+/**
+Panic the client in response to an operation on an existing subsession, after
+which the request mustn't be completed
+*/
+void CCommonPlayer::PanicClient(CWorkerSubSession& /*aSubSession*/, TInt aPanic)
+	{
+	SafeMessage().PanicClient(KDenFaultPanic, aPanic);
+	DontCompleteCurrentRequest();
+	}
+
+/**
+Panic the client in response to an operation on an existing subsession, after
+which the request mustn't be completed
+
+This version allows panic categories to be passed in to support expected legacy
+behaviour.
+*/
+void CCommonPlayer::PanicClient(CWorkerSubSession& /*aSubSession*/, const TDesC &aCategory, TInt aPanic)
+	{
+	SafeMessage().PanicClient(aCategory, aPanic);
+	DontCompleteCurrentRequest();
+	}
+
+EXPORT_C void CCommonPlayer::PanicClient(TInt aPanic)
+	{
+	SafeMessage().PanicClient(KDenFaultPanic, aPanic);
+	DontCompleteCurrentRequest();
+	}
+
+EXPORT_C void CCommonPlayer::PanicClient(const TDesC& aCategory, TInt aPanic)
+	{
+	SafeMessage().PanicClient(aCategory, aPanic);
+	DontCompleteCurrentRequest();
+	}
+
+EXPORT_C void CCommonPlayer::DontCompleteCurrentRequest()
+	{
+	iComplete=EFalse;
+	}
+
+EXPORT_C TBool CCommonPlayer::ShouldCompleteCurrentRequest() const
+	{
+	return iComplete;
+	}
+
+EXPORT_C void CCommonPlayer::SetReturn(TInt aReturn/*, TInt anError, TUid anOwner*/)
+	{
+	iReturn=aReturn;
+
+	}
+
+/**
+Remove all subsessions belonging to sessions of the nominated worker. No locks required; only the
+Player accesses the object container.
+@see TWorkerMsg::ECleanupDeadPeer
+*/
+EXPORT_C void CCommonPlayer::CleanupDeadWorker(TWorkerId aWorkerId)
+	{
+	(void)aWorkerId;
+	//TBDAA - it's in ss_glob.h ASSERT_HOME_THREAD;
+
+	// 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)
+	for(TInt i = iSubSessions.Count() - 1; i >= 0; --i)
+		{
+		iSubSessions[i]->CleanupDeadWorker(aWorkerId);
+		}
+	}
+/**
+If an incoming shutdown request is of type EImmediate, informs the
+Worker Thread that Player shutdown is complete, otherwise do nothing here.
+*/
+void CCommonPlayer::ProcessShutdownRequest(TCFShutdownType aType)
+	{
+	COMMONLOG((WorkerId(),KECommonBootingTag, _L8("CPlayer::ProcessShutdownRequest(%d)"), aType));
+	WorkerThread().SetPlayerShutdownComplete(aType == CommsFW::EImmediate);
+	}
+
+/**
+Walk through all of the subsessions, telling each to complete all blocked requests
+with KErrServerAbort. We don't attempt to free them - although with a shared
+heap this could reduce the amount of leakage it seems likely that the more we do
+with these data structures, the greater the risk that the contagion spreads to
+other users of the heap. Clients that attempt almost any further operation after KErrAbort
+will get panicked by the Dealer in the usual bad handle way - this is harsh but fair, and
+better than simply leaving them hung.
+*/
+TInt CCommonPlayer::PostMortemCleanup()
+	{
+	// No locks are required - we're only here because the owner has died
+	for(TInt i = 0; i < iSubSessions.Count(); ++i)
+		{
+		CWorkerSubSession* subSession = static_cast<CWorkerSubSession*>((iSubSessions[i]));
+		subSession->FinalCompleteAllBlockedMessages(KErrAbort);
+		}
+	return KErrNone;
+	}
+