serialserver/c32serialserver/SCOMM/CS_SES.CPP
changeset 0 dfb7c4ff071f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/serialserver/c32serialserver/SCOMM/CS_SES.CPP	Thu Dec 17 09:22:25 2009 +0200
@@ -0,0 +1,1263 @@
+// Copyright (c) 1997-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
+ *
+ * Implements CCommSession, CCommSubSession
+ */
+
+#include "CS_STD.H"
+#include "C32LOG.H"
+#include "cs_thread.h"
+#include "cs_roles.h"
+#include "cs_msgs.h"
+#include "cs_glob.h"
+#include <comms-infras/c32startcli.h>
+#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
+#include <c32comm_internal.h>
+#endif
+
+
+class CCommSubSession;
+
+void PanicClient(TInt aPanic, const RMessagePtr2& aMessage)
+	{
+	if(aMessage.Handle() != 0)
+		{
+		aMessage.Panic(KCommServerName(), aPanic);
+		}
+	}
+
+
+
+
+
+
+TWorkerId CCommSession::WorkerId() const
+	{
+	return Dealer().WorkerId();
+	}
+
+
+/**
+@param aMessage IPC message received from client.
+@param aSubSess Subsession that needs to deal with this message.
+*/
+void CCommSession::ForwardMessageL(const RMessage2 &aMessage, CCommSubSession& aSubSess)
+	{
+	ForwardMessageL(aMessage, &aSubSess, aSubSess.Player().WorkerId());
+	}
+
+/** Determine whether to deal with message directly or to send it on a channel.
+@param aMessage IPC message received from client.
+@param aWorkerId Id of worker that can handle this message.
+*/
+void CCommSession::ForwardMessageL(const RMessage2& aMessage, CCommSubSession* aSS, TWorkerId aWorker)
+	{
+	ASSERT(aWorker <= TC32WorkerThreadPublicInfo::EMaxWorkerThreadId);
+
+	CC32WorkerThread& owner = C32WorkerThread();
+
+	/**
+	While forwarding the message to player, Add player to disconnect list. While closing the session
+	the disconnect list is used and session close message is forwarded to player. Only when session
+	close response is received, the session is destroyed.
+	*/
+	// If the Player is co-resident in this worker thread
+	if(owner.WorkerId()==aWorker)
+		{
+		AddPlayerToDisconnectList(aWorker);
+		owner.Player()->ProcessMessageL(aMessage, aSS);
+		DontCompleteCurrentRequest();
+		}
+	else if(owner.PeerReachable(aWorker))	// Forward to Player thread
+		{
+		AddPlayerToDisconnectList(aWorker);
+		TC32PlayerForwardRequestMsg msg(aMessage, aSS);
+		owner.PostMessage(aWorker, msg);
+		DontCompleteCurrentRequest();
+		const RNullableMessage& forwardedMsg = static_cast<const RNullableMessage&>(aMessage);
+		forwardedMsg.NullHandle();
+		}
+	else
+		{
+		C32LOG1(KC32Warning, _L8("CCommSession::ForwardMessageL() Peer Thread Unreachable"));
+		DontCompleteCurrentRequest();
+		__ASSERT_DEBUG(0, User::Panic(KCFTransportPanic, ECFTransAbsentWorker));
+		}
+	}
+
+void CCommSession::ServiceError(const RMessage2& aMessage, TInt aError)
+	{
+	if (aError==KErrBadDescriptor)
+		{
+		// this completes message as well so we have to handle it in ServiceL
+		PanicClient(EBadDescriptor,aMessage);
+		return;
+		}
+	C32LOG1(KC32Detail, _L8("CCommSession::ServiceError() ServiceL leave occured"));
+	inherited::ServiceError(aMessage,aError);
+	}
+
+void CCommSession::ServiceL(const RMessage2& aMessage)
+/**
+ * Handle messages for this session.
+ *
+ * @param aMessage handle to the IPC message from the client
+ */
+	{
+	C32LOG5(KC32Detail,_L8("CCommSession::ServiceL(), Session : 0x%x, IPC: %d (%S). Message: %08x"), this, aMessage.Function(), &TC32Log::C32RequestStr(aMessage.Function()),aMessage.Handle());
+	iComplete = ETrue;
+	const CC32WorkerThread& owner=C32WorkerThread();
+	CC32Dealer& c32Dealer = owner.DealerByRef();
+
+	if (c32Dealer.StartupFailed())
+		{
+		SafeComplete(aMessage, KErrNotReady);
+		return;
+		}
+
+	// TestImmediateShutdownPresent is only set when EImmediate shutdown is present, which is
+	// used only in testing phase.
+	if(c32Dealer.TestImmediateShutdownPresent())
+		{
+		User::Leave(KErrServerTerminated);
+		}
+
+	if((aMessage.Function()==ECommOpen)
+	   ||
+	   (aMessage.Function()==ECommOpenWhenAvailable))
+		{
+		NewPortL(aMessage);
+		return;
+		}
+
+#if defined (_DEBUG)
+	switch (aMessage.Function())
+		{
+		case ECommDbgMarkHeap:
+			__UHEAP_MARK;
+			SafeComplete(aMessage, KErrNone);
+			return;
+		case ECommDbgCheckHeap:
+			__UHEAP_CHECK(aMessage.Int0());
+			SafeComplete(aMessage, KErrNone);
+			return;
+		case ECommDbgMarkEnd:
+			__UHEAP_MARKENDC(aMessage.Int0());
+			SafeComplete(aMessage, KErrNone);
+			return;
+		case ECommDbgFailNext:
+  			// We set the fail point for all heaps, rather than just the current Dealer. This could lead to a failure not related
+  			// directly to whatever the client test code is trying to exercise but it all helps find bugs
+  			c32Dealer.SetFailNextForAllHeaps(aMessage.Int0());
+			SafeComplete(aMessage, KErrNone);
+			return;
+		}
+#endif
+
+
+	switch ((aMessage.Function()))
+		{
+		case ECommLoadCommModule:
+			{
+			TFileName fullCSYFilename;
+			TInt ret = Read(0,aMessage,fullCSYFilename);
+			if (ret != KErrNone)
+				{
+				C32LOG2(KC32Warning, _L8("ServiceL: LoadCommModule  Read returned %d instead of KErrNone, cannot proceed"), ret);
+				PanicClient(EBadDescriptor,aMessage);
+				return;
+				}
+
+			ret = AddCSYExtension(fullCSYFilename,aMessage);
+			if(ret != KErrNone)
+				{
+				C32LOG2(KC32Warning, _L8("ServiceL: LoadCommModule AddCSYExtension returned %d instead of KErrNone, cannot proceed"), ret);
+				return;
+				}
+
+ 		 	CommsFW::TWorkerId worker;
+	 		TBuf8<KMaxFileName> fileName8;
+			fileName8.Copy(fullCSYFilename);
+
+ 		    TBool found = iThreadManager->FindThreadByFileName(fileName8, worker);
+	 		if(!found)
+		 		{
+		 		worker = iThreadManager->iDefaultThreadIndex;
+		 		}
+
+ 		 	if(c32Dealer.WorkerExists(worker))
+ 		 		{
+ 		 		LoadCommModuleL(aMessage,worker,!found,fullCSYFilename);
+ 		 		}
+ 		 	else
+ 		 		{
+				C32LOG2(KC32Dealer,_L8("ServiceL: LoadCommModule requires worker %d. This worker does not exist so starting"),worker);
+ 		 		ret = c32Dealer.LoadCPMOnLoadCommModule(worker);
+ 		 		if ((ret!=KErrNone) && (ret!=KErrInUse))
+ 		 			{
+ 		 			// only likely return codes here are KErrNoMemory or KErrNotFound if
+ 		 			// the RS server could not be found - which means system is probably in pretty bad state (ie, no memory)
+ 		 			// luckily at this point there isn't anything to clean up!
+ 		 			SafeComplete(aMessage,ret);
+ 		 			}
+ 		 		else
+ 		 			{
+	 		 		ret = c32Dealer.ParkRequest(this, aMessage);
+	 		 		if(ret != KErrNone)
+	 		 			{
+	 		 			SafeComplete(aMessage, ret);
+	 		 			}
+ 		 			}
+ 		 		}
+ 		 	return;
+ 		 	}
+		case ECommCloseCommModule:
+			CloseCommModuleL(aMessage);
+			return;
+		case ECommPortInfoByName:
+			{
+			TPortName name;
+			TInt ret = Read(1,aMessage,name);
+			if (ret != KErrNone)
+				{
+				C32LOG2(KC32Warning, _L8("ServiceL: LoadCommModule  Read returned %d instead of KErrNone, cannot proceed"), ret);
+				PanicClient(EBadDescriptor,aMessage);
+				return;
+				}
+			PortInfoL(aMessage,name);
+			return;
+			}
+		case ECommPortInfoByNumber:		// original msg is not forwarded as global as aMessage.Int2() is not valid in player, instead CSerial* is wrapped in TC32PlayerGetPortInfoMsg
+			PortInfo(aMessage,aMessage.Int2());
+			return;
+		case ECommNumPorts:				// get information from ThreadManager in dealer
+			NumPorts(aMessage);
+			return;
+		case ECommStartServerThread:	// KErrNotSupported
+			C32LOG2(KC32Dealer, _L8("WARNING: deprecated function ECommStartServerThread called, CCommSession(%08x)"), this);
+			SafeComplete(aMessage, KErrNotSupported);
+			return;
+		}
+
+	// obtain subsession* from aMessage.Int3()
+	CCommSubSession *p = SubSessionFromHandle(aMessage.Int3(), CCommSubSession::ECPort);
+
+	if (aMessage.Function()==ECommClose)
+		{
+		if (p==NULL)	// not a valid aMessage.Int3()
+			{
+			SafeComplete(aMessage, KErrBadHandle);
+			return;
+			}
+		else
+			{
+			CloseSubSessionL(aMessage, CCommSubSession::ECPort);
+			return;
+			}
+		}
+
+	if (p==NULL)	// not a valid aMessage.Int3()
+		{
+		PanicClient(EBadCommHandle, aMessage);
+		return;
+		}
+
+	// Its OK to proceed with the dispatch of other requests
+	switch (aMessage.Function())
+        {
+	    case ECommRead:
+		case ECommReadCancel:
+		case ECommQueryReceiveBuffer:
+		case ECommResetBuffers:
+		case ECommWrite:
+		case ECommWriteCancel:
+		case ECommBreak:
+		case ECommBreakCancel:
+		case ECommCancel:
+		case ECommConfig:
+		case ECommSetConfig:
+		case ECommCaps:
+		case ECommSetMode:
+		case ECommGetMode:
+		case ECommSignals:
+		case ECommSetSignalsToMark:
+		case ECommSetSignalsToSpace:
+		case ECommReceiveBufferLength:
+		case ECommSetReceiveBufferLength:
+		case ECommSetAccess:
+		case ECommOpenWhenAvailableCancel:
+	#ifdef _DEBUG
+		case ECommDebugState:
+	#endif
+
+		// Extensions to the CCommSession starts from here
+
+		case ECommNotifySignals:
+		case ECommNotifyFlowControl:
+		case ECommNotifySignalsCancel:
+		case ECommNotifyFlowControlCancel:
+		case ECommGetFlowControl:
+		case ECommNotifyConfigChange:
+		case ECommNotifyConfigChangeCancel:
+		case ECommNotifyBreak:
+		case ECommNotifyBreakCancel:
+		case ECommGetRole:
+		case ECommNotifyDataAvailable:
+		case ECommNotifyDataAvailableCancel:
+		case ECommNotifyOutputEmpty:
+		case ECommNotifyOutputEmptyCancel:
+			ForwardMessageL(aMessage, *p);
+			break;
+
+		// Extensions to the CCommSession ends to here
+
+		default:
+			SafeComplete(aMessage, KErrNotSupported);
+			break;
+
+		}
+	C32LOG(KC32Detail,_L8("CCommSession::ServiceL() end"));
+	}
+
+TInt CCommSession::DisconnectPlayers()
+	{
+	return iDisconnectPlayers;
+	}
+
+CCommSubSession* CCommSession::SubSessionFromHandle(TUint aHandle, CCommSubSession::TSubSessionType aType) const
+    {
+	iSubSessions.Lock();
+	CCommSubSession* subSess = iSubSessions.At(aHandle, aType);
+	iSubSessions.Unlock();
+	return subSess;
+	}
+
+CCommSession::CC32SessionCloseTimer::CC32SessionCloseTimer(CCommSession* aSession)
+: CTimer(EPriorityLow),
+  iSession(aSession)
+	{
+	}
+
+CCommSession::CC32SessionCloseTimer* CCommSession::CC32SessionCloseTimer::NewL(CCommSession* aSession)
+	{
+	CC32SessionCloseTimer* self = new (ELeave) CCommSession::CC32SessionCloseTimer(aSession);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+void CCommSession::CC32SessionCloseTimer::Start()
+	{
+	CActiveScheduler::Add(this);
+	After(KC32SessionCloseDelay);
+	}
+
+void CCommSession::CC32SessionCloseTimer::RunL()
+	{
+	Deque();
+	if(iStatus == KErrNone)
+		{
+		C32LOG3(KC32Detail, _L8("CCommSession::CC32SessionCloseTimer::RunL session = %08x, DisconnectList = %08x. Will panic in debug."), iSession,	iSession->DisconnectPlayers());
+
+		// After sending a sessionclose message to the player the timeout is set and it is in the order
+		// of seconds. IF the player doesn't respond (may be unloaded during shutdown, see below comment
+		// about stackable csy) and we get to this place, it is a defect and we'd like to catch that in
+		// debug mode.
+
+		// NOTE FOR STACKABLE CSY WRITERS
+		// The session from csy loading another csy was not closed properly and hence when shutdown was
+		// commanded, player CPMs unload, while the session remains. When EDisconnect message is received
+		// by the comms server from kernel, session close message is sent to player CPM which has already
+		// been unloaded and thus this timer expires. Ensure that all sessions from within csy is closed
+		// before shutdown is called
+
+		ASSERT(0);
+
+		CC32Dealer* dealer = const_cast<CC32Dealer*>(&iSession->Dealer());
+		dealer->DeleteSession(iSession);
+		}
+	}
+
+void CCommSession::AddPlayerToDisconnectList(TWorkerId aPlayerId)
+	{
+	__ASSERT_COMPILE(TC32WorkerThreadPublicInfo::EMaxWorkerThreadId < 32);	// Using a TUint32 as a flag container
+	ASSERT(aPlayerId <= TC32WorkerThreadPublicInfo::EMaxWorkerThreadId);
+	__FLOG_STMT(
+		if(!IsPlayerInDisconnectList(aPlayerId))
+			{
+			C32LOG3(KC32Detail, _L8("CCommSession(%08x):\tAddPlayerToDisconnectList(%d)"), this, aPlayerId );
+			}
+		)
+	iDisconnectPlayers |= (1 << aPlayerId);
+	}
+
+void CCommSession::RemovePlayerFromDisconnectList(TWorkerId aPlayerId)
+	{
+	ASSERT(aPlayerId <= TC32WorkerThreadPublicInfo::EMaxWorkerThreadId);
+	__FLOG_STMT(
+		if(IsPlayerInDisconnectList(aPlayerId))
+			{
+			C32LOG3(KC32Detail, _L8("CCommSession(%08x):\tRemovePlayerFromDisconnectList(%d)"), this, aPlayerId );
+			}
+		)
+	iDisconnectPlayers &= ~(1 << aPlayerId);
+	}
+
+TBool CCommSession::IsDisconnectListEmpty() const
+	{
+	C32LOG2(KC32Detail, _L8("CCommSession::IsDisconnectListEmpty %d "), iDisconnectPlayers );
+	return iDisconnectPlayers == 0;
+	}
+
+TBool CCommSession::IsPlayerInDisconnectList(TWorkerId aPlayerId) const
+	{
+	// A potential optimisation for the future would be to actively check whether the player still has subsessions here, rather
+	// than blinding sending the session close message
+	ASSERT(aPlayerId <= TC32WorkerThreadPublicInfo::EMaxWorkerThreadId);
+	return iDisconnectPlayers & (1 << aPlayerId);
+	}
+
+void CCommSession::ForgetSubSession(CCommSession* aSelf, CCommSubSession* aSubSession, TInt aSubSessionHandle, TAny*)
+	{
+	(void) aSubSession;	// suppress UREL warnings
+	VERIFY_RESULT(aSelf->iSubSessions.Remove(aSubSessionHandle), aSubSession);
+	}
+
+void CCommSession::CountSubSessions(CCommSession*, CCommSubSession*, TInt, TAny* aArg)
+	{
+	TInt* counter = reinterpret_cast<TInt*>(aArg);
+	++*counter;
+	}
+
+CCommSession::CCommSession(CC32ThreadManager* aThreadManager)
+/**
+ * C'Tor - must pass client to CSession
+ *
+ * @param aClient handle to the Clients thread
+ * @param aPortManager pointer to th port manager
+ */
+	:CSession2(),
+	iThreadManager(aThreadManager)
+	{
+	C32GlobalUtil::NewSession();		// increment iNumSessions
+	C32LOG1(KC32Detail, _L8("CCommSession::CCommSession()"));
+	}
+
+CCommSession* CCommSession::NewL(CC32ThreadManager* aThreadManager)
+	{
+	CCommSession* self = new (ELeave) CCommSession(aThreadManager);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+void CCommSession::ConstructL()
+	{
+	iSessionCloseTimer = CCommSession::CC32SessionCloseTimer::NewL(this);
+	iSubSessions.InitialiseL();			// initialise CC32SubSessionIx
+	}
+
+void CCommSession::CloseSubSessionL(const RMessage2& aMessage, CCommSubSession::TSubSessionType aType)
+	{
+	iSubSessions.Lock();
+	CCommSubSession* subSess = iSubSessions.At(aMessage.Int3(), aType);
+	C32LOG5(KC32Detail, _L8("CCommSession(%08x):\tCloseSubSession(%08x, %d) - subSess %08x"), this, aMessage.Int3(), aType, subSess);
+	ASSERT(subSess);
+	iSubSessions.Unlock();
+	ForwardMessageL(aMessage, *subSess);
+	}
+
+TInt CCommSession::AddCSYToSessionL(const TDesC& aCSYFileName, TBool& aIsDuplicate)
+	{
+	aIsDuplicate=EFalse;
+
+	TInt k = 0;
+	while (k < iCsyCon.Count() && !aIsDuplicate)
+		{
+		if (aCSYFileName.CompareF(*iCsyCon[k])==0)
+			{
+			aIsDuplicate=ETrue;
+			}
+		k++;
+		}
+
+	HBufC* name = aCSYFileName.AllocL();
+	return iCsyCon.Append(name);
+	}
+
+TInt CCommSession::RemoveCSYFromSession(const TDesC& aCSYFileName, TBool& aIsLast)
+	{
+	TBool aFound=EFalse;
+	TInt k = 0;
+	while (k < iCsyCon.Count())
+		{
+		if (iCsyCon[k]->CompareF(aCSYFileName)==0)
+			{
+            // coverity [dead_error_condition] aFound can be true on later iterations of the while loop
+            if (aFound)
+				{
+				aIsLast=EFalse;
+				}
+			else
+				{
+				delete iCsyCon[k];
+				iCsyCon.Remove(k);
+				aIsLast=ETrue;
+				aFound=ETrue;
+				return KErrNone;
+				}
+			}
+		k++;
+		}
+	return KErrNotFound;
+	}
+
+CC32WorkerThread& CCommSession::C32WorkerThread() const
+	{
+	return Dealer().WorkerThread();
+	}
+
+void CCommSession::LoadCommModuleL(const RMessage2& aMessage, CommsFW::TWorkerId aWorker, TBool aDefaulted, const TDesC& aFilename)
+/**
+ Load a comm module
+ We assume that the worker we need is loaded if we've come this far.
+ aFilename - full filename of CSY
+ aDefaulted - True if the supplied filename is for a CSY that is not known via CSYList values in CMI file.
+ In this case aWorker will be the default thread
+ */
+	{
+	TBool isDuplicate;		// used both for AddCSYToSessionL and RemoveCSYFromSession (where it is used as isLast)
+	TInt ret;
+	// Add csy to session, if present we still add csyfilename to session CSY container to keep accesscount but do not
+	// forward message, (preserve legacy behaviour where c32 allowed multiple load attempts with KErrNone)
+	ret = AddCSYToSessionL(aFilename,isDuplicate);
+	if(ret != KErrNone)
+		{
+		C32LOG2(KC32Warning,_L8("CCommSession::LoadCommModuleL() failed to append CSY to csy container returned %d "), ret);
+		SafeComplete(aMessage, ret);
+		return;
+		}
+	// if no memory, or other error, return as such to client
+	if (isDuplicate)
+		{
+		ret = iThreadManager->IncrementCountOnLoad(aFilename);	// increment count for multiple load attempt
+		__ASSERT_DEBUG(ret == KErrNone, Fault(EBadState,_L("CCommSession::LoadCommModuleL(): duplicate entry of CSY but not found in Thread Manager !")));
+		SafeComplete(aMessage,KErrNone);
+		}
+	else
+		{
+		if (aDefaulted)
+			{
+			C32LOG2(KC32Dealer,_L("CCommSession::LoadCommModuleL(): CSY (%S) not found in Threadmanager so adding"),&aFilename);
+			// add csy record since it is unknown, probably after-marked csy
+			TBuf8<KMaxFileName> fileName8;   // huge stack usage, but no other way
+			fileName8.Copy(aFilename);
+
+			CCSYInfo* csyPtr = NULL;
+			TRAP(ret,csyPtr = CCSYInfo::NewL(fileName8, aWorker));
+			if (ret==KErrNone)
+				{
+				ret = iThreadManager->iCSYList.Append(csyPtr);
+				if (ret!=KErrNone)
+					{
+					// failed to add so undo and abort
+					delete csyPtr;
+					(void)RemoveCSYFromSession(aFilename,isDuplicate);
+					SafeComplete(aMessage,ret);
+					return;
+					}
+				}
+            else
+				{
+				// probably OOM
+				(void)RemoveCSYFromSession(aFilename,isDuplicate);
+				SafeComplete(aMessage,ret);
+				return;
+				}
+			}
+
+		// if this module hasn't been loaded before, it won't have had its portname set. As
+		// the portname could be quite long we need to allocate it now to its max size
+		// since we can't afford to run out of memory after player responds with the portname after load.
+
+		ret = iThreadManager->GrowCSYPortNameToMaximumIfNotLoaded(aFilename);
+
+		if (ret == KErrNoMemory)
+			{
+			C32LOG2(KC32Dealer,_L("CCommSession::LoadCommModuleL(): CSY (%S).Ran out of memory growing portname - cleaning up"),&aFilename);
+			(void)RemoveCSYFromSession(aFilename,isDuplicate);
+			if (aDefaulted)
+				{
+				C32LOG3(KC32Dealer,_L("CCommSession::LoadCommModuleL(): CSY (%S), cleanup for abort: This was a newly-added default record (at index %d), so deleting"),&aFilename,iThreadManager->iCSYList.Count()-1);
+				// we just appended csy record, so remove it
+				delete iThreadManager->iCSYList[iThreadManager->iCSYList.Count()-1];
+				iThreadManager->iCSYList.Remove(iThreadManager->iCSYList.Count()-1);
+				}
+			SafeComplete(aMessage,ret);
+			return;
+			}
+		else
+			{
+			__ASSERT_DEBUG(ret==KErrNone,Fault(EBadState,_L("CCommSession::LoadCommModuleL(): Panicking due to failure to grow CSY portname for unexpected reason: %d" ),ret));
+			}
+
+
+		// entry for unlisted CSY created now, increment count for this CSY, count as in number of times its loaded
+		ret = iThreadManager->IncrementCountOnLoad(aFilename);
+		__ASSERT_DEBUG(ret==KErrNone,Fault(EBadState,_L("CCommSession::LoadCommModuleL(): Panicking due to failure to increment count for CSY: %d" ),ret));
+
+		// send Message to player
+		CC32WorkerThread& owner = C32WorkerThread();
+		if(aWorker==TC32WorkerThreadPublicInfo::EMainThread)
+			{
+			// co-resident player, make direct function call, avoid ProcessMessageL(), as aMessage.Int2() points to global index
+			AddPlayerToDisconnectList(aWorker);
+			owner.Player()->LoadCommModule(aMessage);
+			DontCompleteCurrentRequest();
+			}
+		else if (owner.PeerReachable(aWorker))
+			{
+			// send the message via transports wrapped in TC32PlayerLoadCommModuleMsg
+			AddPlayerToDisconnectList(aWorker);
+			TC32PlayerLoadCommModuleMsg respMsg(aMessage);
+			owner.PostMessage(aWorker, respMsg);
+			DontCompleteCurrentRequest();
+			const RNullableMessage& forwardedMsg = static_cast<const RNullableMessage&>(aMessage);
+			forwardedMsg.NullHandle();
+			}
+		else
+			{
+			C32LOG2(KC32Warning,_L8("CCommSession::LoadCommModuleL() Peer Thread Unreachable.  RMessage2& = %08x. Will panic in debug"), aMessage.Handle());
+			DontCompleteCurrentRequest();
+			__ASSERT_DEBUG(0, User::Panic(KCFTransportPanic, ECFTransAbsentWorker));
+			}
+
+		}
+	}
+
+void CCommSession::SafeComplete(const RMessagePtr2& aMessage, TInt aCompletionCode)
+	{
+	if(iComplete)
+		{
+		::SafeComplete(aMessage, aCompletionCode);
+		}
+	}
+
+void CCommSession::DontCompleteCurrentRequest()
+	{
+	iComplete = EFalse;
+	}
+
+void CCommSession::CloseCommModuleL(const RMessage2& aMessage)
+/**
+ * Close a comm module
+ */
+	{
+	TFullName name;      // not a TPortName for backwards compat reasons
+	Read(0,aMessage,name);
+
+	TFileName csyfilename;
+	TInt ret = iThreadManager->MapPortprefixToCSYFileName(name, csyfilename);	// fill csyfilename
+	if (ret!=KErrNone)
+		{
+		SafeComplete(aMessage, KErrNotFound);
+		return;
+		}
+
+	TBool isLast;
+	TInt res = RemoveCSYFromSession(csyfilename,isLast);	// can return either KErrNone or KErrNotFound in case when csy is not found listed
+
+	if (res==KErrNone)
+		{
+		// decrement the LoadCount for CSY
+		iThreadManager->DecrementCountOnCSYUnLoad(csyfilename);
+		if (isLast)
+			{
+			// if CSY is the last one for the session, then remove from session and forward mesage to player
+			// unload message is forwarded "per session basis", so every session sends unload message to player,
+			// if this csy was previously loaded by this session, unload message is sent to player either when
+			// unloadcommmodule is explicitly called or when session is closed
+			CommsFW::TWorkerId worker;
+	 		TBuf8<KMaxFileName> fileName8;
+			fileName8.Copy(csyfilename);
+
+			__ASSERT_ALWAYS(iThreadManager->FindThreadByFileName(fileName8,worker),Fault(EBadState)); //  find worker
+			ForwardMessageL(aMessage, NULL, worker);
+			}
+		else
+			{
+			SafeComplete(aMessage, KErrNone);
+			}
+		}
+	else
+		{
+		// old api used to just return and not panic during an attempt to unload a module not loaded
+		// so we do too, altho we log to record this unsavoury behavior
+		C32LOG(KC32Warning, _L8("CCommSession::CloseCommModuleL(), Attempt to Close an unloaded module. Continuing due to old API allowing this unadvised behavior."));
+		SafeComplete(aMessage, res);	// res = KErrNotFound
+		}
+
+	}
+
+void CCommSession::PortInfoL(const RMessage2& aMessage,const TPortName& aName)
+/**
+ * Write back the port info to the client for a specified port
+ *
+ * @param aName name of the port/filename to get information about
+ */
+	{
+	TWorkerId worker;
+	if(iThreadManager->FindThreadOfActiveCSYByName(aName, worker))
+		{
+		ForwardMessageL(aMessage, NULL, worker);
+		}
+	else
+		{
+		SafeComplete(aMessage, KErrNotFound);
+		}
+	}
+
+// PortInfo overload requires the CSerial* to be forwarded to player, as the
+// aMessage.Int2() has the global index which is not valid in player.
+void CCommSession::PortInfo(const RMessage2& aMessage,TInt aNumber)
+/**
+ * Write back the port info to the client for a specified port
+ * aNumber is the client supplied index of the CSY
+ * @param aNumber number of the port to get information about
+ */
+	{
+	// check if the aNumber is sane i.e. within the # of CSYs loaded in C32
+	if (aNumber < 0)
+		{
+		SafeComplete(aMessage,KErrArgument);
+		return;
+		}
+
+	TInt workerId = iThreadManager->FindThreadByGlobalIndex(aNumber);
+	if (workerId == KErrNotFound)
+		{
+		SafeComplete(aMessage, KErrTooBig); // preserve old API behaviour
+		}
+	else
+		{
+		CC32WorkerThread& owner = C32WorkerThread();
+		CommsFW::TWorkerId thisworker = owner.WorkerId();
+		CSerial* s = iThreadManager->FindSerialObjectByGlobalIndex(aNumber);
+		// Find Thread
+		if(workerId==thisworker)
+			{
+			// co-resident player, make direct function call, avoid ProcessMessageL(), as aMessage.Int2() points to global index
+			AddPlayerToDisconnectList(workerId);
+			owner.Player()->PortInfo(aMessage, s);
+			DontCompleteCurrentRequest();
+			}
+		else if (owner.PeerReachable(workerId))
+			{
+			// send the message via transports wrapped in TC32PlayerGetPortInfoMsg
+			C32LOG4(KC32Dealer, _L8("CCommSession::PortInfo(aNumber) Sending Peer Thread %d request for info on CSerial %x which is for CSY at active idx %d"),workerId,s,aNumber);
+			AddPlayerToDisconnectList(workerId);
+			TC32PlayerGetPortInfoMsg respMsg(aMessage, s);
+			owner.PostMessage(workerId, respMsg);
+			DontCompleteCurrentRequest();
+			const RNullableMessage& forwardedMsg = static_cast<const RNullableMessage&>(aMessage);
+			forwardedMsg.NullHandle();
+			}
+		else
+			{
+			C32LOG2(KC32Warning, _L8("CCommSession::PortInfo(aNumber) Peer Thread %d Unreachable. Will panic in debug."),workerId);
+			DontCompleteCurrentRequest();
+			__ASSERT_DEBUG(0, User::Panic(KCFTransportPanic, ECFTransAbsentWorker));
+			}
+		}
+	}
+
+void CCommSession::NumPorts(const RMessage2& aMessage)
+/**
+ * Write back the number of CSYs loaded to the client
+ */
+	{
+	// returns the number of CSYs loaded in all sessions
+	TPckgBuf<TInt> num;
+
+	TInt idx = 0;
+	TInt count = 0;
+	while (idx < iThreadManager->iCSYList.Count())
+		{
+		if (iThreadManager->iCSYList[idx]->IsLoaded())
+			{
+			count++;
+			}
+		idx++;
+		}
+
+	C32LOG2(KC32Detail, _L8("CCommSession::NumPorts = %d "), count);
+	num() = count;
+	Write(0,aMessage,num);
+	SafeComplete(aMessage, KErrNone);
+	}
+
+CCommSession::~CCommSession()
+/**
+ *	D'tor - clean up and go home.
+ */
+	{
+	C32LOG1(KC32Detail, _L8("CCommSession::~CCommSession()"));
+
+	delete iSessionCloseTimer;
+
+	iCsyCon.ResetAndDestroy();		// delete session CSY container
+
+	C32GlobalUtil::SessionClosing();	// decrement iNumSessions
+
+	}
+
+void CCommSession::ProcessSubSessions(TWorkerId aPeerId, TSubSessionProcessor aSubSessionProcessor, TAny* aPtr)
+	{
+	iSubSessions.Lock();
+	CC32SubSessionIx::TIter iter(iSubSessions);
+
+	CCommSubSession* ss;
+	TInt ssHandle;
+	while((ss = iter.Next(ssHandle)) != NULL)
+		{
+		if(aPeerId == TC32WorkerThreadPublicInfo::ENullWorkerId || aPeerId == ss->Player().WorkerId())
+			{
+			aSubSessionProcessor(this, ss, ssHandle, aPtr);
+			}
+		}
+	iSubSessions.Unlock();
+	}
+
+void CCommSession::NewPortL(const RMessage2& aMessage)
+/**
+ * Ask the port manager to open a port in the CSY which is then added
+ * to this session's port list. 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(KC32Detail, _L8("CCommSession::NewPort()"));
+
+	TFullName name;      // not a TPortName for backwards compat reasons
+	TUint port;
+	TInt len;
+
+	TInt ret = ExtractPortNameAndNumber(aMessage, name, port, len);
+	if(ret != KErrNone)
+		{
+		C32LOG2(KC32Detail, _L8("CCommSession::NewPort() - returning KErrBadName to client. Offending portname was: %S"),&name);
+        // Bad Handle, (session) dealer completes.
+        // it would be nicer to complete with value of "ret", but as old C32 always completed with KErrBadName
+        // it seems an easy way to reduce compatibility problems by continuing this
+		SafeComplete(aMessage, KErrBadName);
+		return;
+		}
+	else
+		{
+		// message details are valid, forward the message based on portname "COMM"
+		// lookup in ThreadManager, fails if no port-prefix found (if RComm::Open called directly without loading the CSY)
+		TWorkerId worker;
+		if(iThreadManager->FindThreadByPortPrefix(TPtrC(name.Left(len)), worker))
+			{
+			ForwardMessageL(aMessage, NULL, worker);
+			}
+		else
+			{
+			C32LOG2(KC32Dealer, _L("CCommSession::NewPort() - returning KErrNotFound to client due to no loaded CSY for supplied port: %S"),&name);
+			SafeComplete(aMessage, KErrNotFound);
+			}
+		}
+	}
+
+TInt CCommSession::ExtractPortNameAndNumber(const RMessagePtr2& aMessage, TDes& aPortName, TUint& aPortNumber, TInt& aLength)
+/**
+Extract the port name and number from RMessage
+aPortName on return is full name including double-colon and number. The length is not policed for compliance with
+TPortName type since this would break a backwards compatibility behaviour.
+aLength is length of the actual name part without the double-colon and port number.
+*/
+	{
+  	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;
+	if (numPos == aPortName.Length())
+		{
+		C32LOG1(KC32Warning, _L("CCommSession::ExtractPortNameAndNumber() - no port number mentioned to open"));
+		return KErrBadName; // kerrbadname preserves old c32 return code for this function
+		}
+
+	TPtrC numPtr(&aPortName[numPos], aPortName.Length() - numPos);
+	TLex lexer(numPtr);
+	TInt ret = lexer.Val(aPortNumber);
+
+	return ret;
+	}
+
+/** Sends SessionClosed to all no-resident players. Add self to Dealers list of disconnecting sessions.
+*/
+void CCommSession::Disconnect()
+	{
+	C32LOG2(KC32Detail, _L8("CCommSession(%08x):\tDisconnect()"), this );
+
+	// Remove all subsessions from the session, ie just as if the client had closed them all individually
+	ProcessSubSessions(TC32WorkerThreadPublicInfo::ENullWorkerId, ForgetSubSession, NULL);
+
+	CC32WorkerThread& owner = C32WorkerThread();
+	TWorkerId self = owner.WorkerId();
+
+	// remove any parked requests if any as this session is going down.
+	owner.Dealer()->RemoveParkedRequestsOnSessionClose(this);
+
+	// run thru our list of csys we've loaded for this session and remove each one
+	// and send thru to player to unload each one. As the list allows duplicates,
+	// we only send unload for each csy when its last ref for this session
+	for(TInt i = iCsyCon.Count() -1; i >= 0; i--)
+		{
+		// find worker
+		TWorkerId worker;
+ 		TBuf8<KMaxFileName> fileName8;
+		fileName8.Copy(*iCsyCon[i]);
+
+		TBool found = iThreadManager->FindThreadByFileName(fileName8, worker);	// TBool found is re-used for testing last csy in session CSY container
+		__ASSERT_DEBUG(found, Fault(EBadState,_L("CCommSession::Disconnect: Csy not found in ThreadManager !")));
+		// find CSerial object
+		CSerial* s = iThreadManager->GetSerialObjectFromCSYFileName(*iCsyCon[i]);
+#ifdef _DEBUG
+		if (s == NULL)
+			{
+#ifdef __FLOG_ACTIVE
+			iThreadManager->DumpThreadInfoAndCSYLists();
+#endif
+			TPtrC csyFilename = *iCsyCon[i];
+			Fault(EBadState,_L("CCommSession::Disconnect: CSerial not found for CSY (%S) in ThreadManager !"),&csyFilename);
+			}
+#endif
+
+		TInt res = iThreadManager->DecrementCountOnCSYUnLoad(*iCsyCon[i]);
+
+		TBool isLast;
+		res = RemoveCSYFromSession((*iCsyCon[i]), isLast);
+		// if its last entry for this csy in TCSYFileNameContainer, send the message to player
+		if(res == KErrNone)
+			{
+			if(isLast)	// last CSY in session CSY container - same TBool found re-used
+				{
+				if(worker==self)
+					{
+					// co-resident player, make direct function call, avoid ProcessMessageL(), as aMessage.Int2() points to global index
+					owner.Player()->ProcessUnLoadCommModuleMsg(s);
+					}
+				else if (owner.PeerReachable(worker))
+					{
+					// send the message via transports wrapped in TC32PlayerUnLoadCommModuleMsg
+					TC32PlayerUnLoadCommModuleMsg respMsg(s);
+					owner.PostMessage(worker, respMsg);
+					}
+				else
+					{
+					C32LOG2(KC32Warning, _L8("CCommSession::Disconnect() Peer Thread (%d) Unreachable. Will Panic in Debug"), worker);
+					__ASSERT_DEBUG(0, User::Panic(KCFTransportPanic, ECFTransAbsentWorker));
+					}
+				}
+			}
+		}
+
+	TTime time;
+	time.HomeTime();
+	time+=TTimeIntervalMicroSeconds32(KC32SessionClosePlayerDeadline);
+	for(TWorkerId player = TC32WorkerThreadPublicInfo::EMainThread; player <= TC32WorkerThreadPublicInfo::EMaxWorkerThreadId; ++player)
+		{
+		if(IsPlayerInDisconnectList(player))
+			{
+			if(player == self)
+				{
+				RemovePlayerFromDisconnectList(player);
+				owner.Player()->CloseSession(this);
+				}
+			else if(owner.PeerReachable(player))
+				{
+				/* Sending closed session message to involved player, dont care if it times out. After this we just wait
+				anyway for a while and then obliterate it.*/
+				C32LOG3(KC32Detail, _L8("CCommSession(%08x):\tDisconnect() Sending SessionClose to W%d"), this, player);
+				TC32PlayerSessionCloseMsg msg(this, time.Int64());
+				owner.PostMessage(player, msg);
+				}
+			else
+				{
+				C32LOG(KC32Warning,_L8("CCommSession::Disconnect: Peer not reachable. Will panic in debug."));
+				__ASSERT_DEBUG(0, User::Panic(KCFTransportPanic, ECFTransAbsentWorker));
+				}
+			}
+		}
+	if(!IsDisconnectListEmpty())
+		{
+		C32LOG3(KC32Detail, _L8("CCommSession(%08x):\tDisconnect() Starting timer [discoList = 0x%x]"), this, iDisconnectPlayers );
+		iSessionCloseTimer->Start();
+		}
+	}
+
+void CCommSession::Disconnect(const RMessage2& aMessage)
+	{
+	iDisconnectMessage = aMessage;
+	C32LOG3(KC32Detail, _L("CCommSession(%08x):\tDisconnect(const RMessage2& = %08x)"), this, aMessage.Handle() );
+
+	Disconnect();
+
+	// If co-resident Player was only one then complete immediately
+	if(IsDisconnectListEmpty())
+		{
+		C32LOG2(KC32Detail, _L8("CCommSession(%08x)::Disconnect(const RMessage2&) inherited disconnect"), this );
+		inherited::Disconnect(aMessage);
+		}
+	}
+
+/** The player has handled the SessionClose message and has cleaned up */
+void CCommSession::SessionCloseResp(TWorkerId aPlayerId)
+	{
+	RemovePlayerFromDisconnectList(aPlayerId);
+	if(IsDisconnectListEmpty())
+		{
+		C32LOG2(KC32Detail, _L8("CCommSession::SessionCloseResp(%08x), Stopping the timer"),this);
+		iSessionCloseTimer->Cancel();
+		// Dealer()->DeleteSession(...) is called by the caller of this function after this
+		}
+	}
+
+
+TInt CCommSession::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(KC32Detail, _L8("CCommSession::Write() Data = (%s), Pos (%d) Offset (%d)"), aDes.Ptr(), aPos, aOffset);
+
+	TInt ret = aMessage.Write(aPos, aDes, aOffset);
+	if (ret!=KErrNone)
+		{
+		PanicClient(EBadDescriptor,aMessage);
+		}
+	return ret;
+	}
+
+
+TInt CCommSession::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(KC32Detail, _L8("CCommSession::Read(), Pos (%d), Offset (%d)"), aPos, aOffset);
+
+	TInt ret = aMessage.Read(aPos, aDes, aOffset);
+	if (ret!=KErrNone)
+		{
+		C32LOG1(KC32Detail, _L8("Error at the time of reading data from client"));
+		PanicClient(EBadDescriptor,aMessage);
+		}
+	return ret;
+	}
+
+
+TInt CCommSession::Write(TInt aPos, const RMessagePtr2& aMessage , const TDesC16& aDes, TInt aOffset)
+/**
+ * Write and kill the client if it leaves.
+ *
+ * (see CCommSession::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(KC32Detail, _L8("CCommSession::Write(), Data = (%s), Pos (%d), Offset (%d)"), aDes.Ptr(), aPos, aOffset);
+
+	TInt ret = aMessage.Write(aPos, aDes, aOffset);
+	if (ret!=KErrNone)
+		{
+		PanicClient(EBadDescriptor,aMessage);
+		}
+	return ret;
+	}
+
+
+TInt CCommSession::Read(TInt aPos, const RMessagePtr2& aMessage , TDes16& aDes, TInt aOffset)
+/**
+ * Read and kill the client if it leaves.
+ *
+ * (see CCommSession::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(KC32Detail, _L8("CCommSession::Read(), Pos (%d), Offset (%d)"), aPos, aOffset);
+
+	TInt ret = aMessage.Read(aPos, aDes, aOffset);
+	if (ret!=KErrNone)
+		{
+		C32LOG1(KC32Detail, _L8("Error at the time of reading data from client"));
+		PanicClient(EBadDescriptor,aMessage);
+		}
+	return ret;
+	}
+
+/**
+Checks for a null'd handle before attempting complete.
+If handle is null'd then don't complete as this will panic server.
+
+@param aMessage Message to complete
+@param aCompletionCode Completion code.
+*/
+void SafeComplete(const RMessagePtr2& aMessage, TInt aCompletionCode)
+	{
+	if(!aMessage.IsNull())
+		{
+		C32LOG3(KC32Dealer,_L8("CCommSession::SafeComplete(%08x) Completion code: %d"),aMessage.Handle(),aCompletionCode);
+		aMessage.Complete(aCompletionCode);
+		}
+	else
+		{
+		C32LOG3(KC32Detail,_L8("CCommSession::SafeComplete(%08x) - cannot complete message since Null! Completion code was meant to be: %d"),aMessage.Handle(),aCompletionCode);
+		}
+	}
+
+
+
+TInt AddCSYExtension(TFileName& aFilename, const RMessage2& aMessage)
+// implements variation whereby filename is 16-bit, and we want to panic client if the extension won't fit
+// KErrNone if no probs or extension already present, KErrBadDescriptor if too short
+	{
+	TInt r=aFilename.LocateReverse('.');
+ 	if (r==KErrNotFound)
+		{
+		if (aFilename.Length()>KMaxFileName - (TInt)KCSYExtension.iTypeLength)	//< valid filename is checked here
+			{
+			PanicClient(EBadDescriptor,aMessage);
+			return KErrBadDescriptor;
+			}
+		aFilename.Append(KCSYExtension);
+		}
+	return KErrNone;
+	}
+
+
+//
+// CCommSubSession
+//
+
+/**
+Constructor
+*/
+CCommSubSession::CCommSubSession(CCommSession* aSession, CPort* aPort, CC32Player* aPlayer)
+: iPlayer(aPlayer),
+  iAccessCount(1),
+  iPort(aPort),
+  iSession(aSession)
+	{
+	C32LOG4(KC32Detail, _L8("CCommSubSession(%08x):\tCPort Port(%08x):\tCCommSubSession Session(%08x)"), this, iPort, iSession );
+	}
+
+#ifdef _DEBUG
+CCommSubSession::~CCommSubSession()
+	{
+	C32LOG3(KC32Detail, _L8("CCommSubSession(%08x):\t~CCommSubSession Session(%08x)"), this, iSession );
+	}
+#endif
+
+void CCommSubSession::RemoveAndDestroy()
+	{
+	// Remove from the Player's container and delete
+	C32LOG3(KC32Detail, _L8("CCommSubSession(%08x):\tRemoveAndDestroy(), session=%08x"), this, iSession );
+	CC32Player::TSubSessionContainer& subSessions = Player().SubSessions();
+	TInt sessIdx = subSessions.Find(this);
+	if(sessIdx >= 0)
+		{
+		subSessions.Remove(sessIdx);
+		if(subSessions.Count() == 0)
+			{
+			CC32WorkerThread& workerThread = Player().WorkerThread();
+			if(workerThread.ShuttingDown())
+				{
+				workerThread.MaybeTriggerThreadShutdownCallback();
+				}
+			}
+		}
+	delete this;
+	}
+
+CCommSubSession* CCommSubSession::NewL(CCommSession *aSession, CPort* aPort, CC32Player* aPlayer)
+	{
+	CCommSubSession *s=new (ELeave) CCommSubSession(aSession, aPort, aPlayer);
+	return s;
+	}
+
+// EOF - CS_SES.CPP
+