datacommsserver/esockserver/ssock/ss_thread.cpp
changeset 0 dfb7c4ff071f
child 1 21d2ab05f085
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/datacommsserver/esockserver/ssock/ss_thread.cpp	Thu Dec 17 09:22:25 2009 +0200
@@ -0,0 +1,1096 @@
+// 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 <ss_std.h>
+#include <ss_glob.h>
+#include <comms-infras/ss_roles.h>
+#include "ss_msgs.h"
+#include <es_ini.h>
+#include <comms-infras/ss_log.h>
+#include <ecom/ecom.h>
+#include <ss_protprov.h>
+#include <comms-infras/idquerynetmsg.h> //just to get Uids
+#include <comms-infras/ss_tiermanagerutils.h>
+#include <metadatabase.h> //CMDBSession
+#include <elements/cftransportmsg.h>
+#include <elements/nm_address_internal.h>
+#include <elements/sd_msgs.h>
+#include "sd_rootserverchannelhandler.h"
+#include "ss_connectionserver.h"
+#include <comms-infras/ss_common.h>
+#include <comms-infras/ss_tiermanager.h>
+#include <cfforwardmsg.h>
+#include "ss_tierthreadmap.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_ESockSSocks_thrd, "ESockSSocks_thrd");
+#endif
+
+using namespace CommsFW;
+using namespace ESock;
+using namespace Messages;
+using namespace Den;
+
+//
+// CWorkerThread class definitions
+//
+
+
+CWorkerThread* CWorkerThread::NewL(TCFModuleInfo* aModuleInfo)
+	{
+	CleanupStack::PushL(TCleanupItem(CCommonWorkerThread::DeleteHBufC8, aModuleInfo->iIniData));
+	CWorkerThread* self = new (ELeave) CWorkerThread;
+	CleanupStack::PushL(self);
+	self->ConstructL(aModuleInfo);
+	CleanupStack::Pop(self);
+	CleanupStack::PopAndDestroy();	// aModuleInfo->iIniData
+	return self;
+	}
+
+CWorkerThread::CWorkerThread()
+:	CCommonWorkerThread(),
+	iEskMaskList(KEskMaskListGranularity)
+	{
+	}
+
+CWorkerThread::~CWorkerThread()
+	{
+	iTransportUsers.Shutdown(EFalse);
+	iEskMaskList.Close();
+	}
+
+CPlayer* CWorkerThread::Player() const
+	{
+	return static_cast<CPlayer*>(CCommonWorkerThread::Player());
+	}
+
+CPitBoss& CWorkerThread::PitBoss() const
+	{
+	return static_cast<CPitBoss&>(CCommonWorkerThread::PitBoss());
+	}
+
+/**
+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.
+*/
+TInt SocketServerShutDownCb(TAny*)
+	{
+	SocketServer::ShutDown();
+	return KErrNone;
+	}
+
+void CWorkerThread::ConstructL(TCFModuleInfo* aModuleInfo)
+	{
+	LOG(ESockLog::Printf(KESockBootingTag, _L("CWorkerThread::ConstructL Determining roles")));
+	CCommonWorkerThread::ConstructL(*aModuleInfo, *Den::CShutdownWatchDog::NewL(this, TCallBack(SocketServerShutDownCb, NULL)));
+
+#ifdef ESOCK_HOME_THREAD_CHECK_ENABLED
+	iOwnThread = RThread().Id();
+#endif
+
+	LOG(ESockLog::Printf(KESockBootingTag, _L("CWorkerThread::ConstructL Init SockManGlobals")));
+	iSockManGlobals = CSockManData::NewL(*iGlobals, this);
+
+	SocketServer::InitL(this);
+
+	LOG(ESockLog::Printf(KESockBootingTag, _L("CWorkerThread::ConstructL Init ProtocolManager")));
+	ProtocolManager::InitL();
+
+	LOG(ESockLog::Printf(KESockBootingTag, _L("CWorkerThread::ConstructL Done Init ProtocolManager")));
+
+	if(iPlayer)
+		{
+		SockManGlobals()->InstallFactoryContainersL();
+		}
+
+	LOG(ESockLog::Printf(_L("CWorkerThread::ConstructL Done")));
+	}
+
+/** Will read the .ESK masks from the inidata, to determine which .ESK files to load. */
+void CWorkerThread::DoDeterminePlayerRoleL(HBufC8* aIniData, Den::TPlayerRole& aPlayerRole)
+	{
+	_LIT8(KEskMaskLabel, "EskMask");
+	_LIT8(KEskMaskLabelNumFormat, "EskMask%d");
+	_LIT8(KPlayerRoleLabel, "PlayerRole");
+
+	LOG(ESockLog::Printf(KESockBootingTag, _L("CWorkerThread::DetermineRoleL building .ESK mask list.")));
+	TPtrC8 currentEskMaskPtr;
+	TBuf8<KEskMaskLabelLength> currentEskMask;
+	currentEskMask.Format(KEskMaskLabel);
+	TEskMask currentMaskEntry;
+	TInt i=0;
+	do
+		{
+		if(GetVarFromIniData(*aIniData, KNullDesC8, currentEskMask, currentEskMaskPtr))
+			{
+			LOG(ESockLog::Printf(KESockBootingTag, _L8("Found EskMask under heading [IniData]: %S"), &currentEskMaskPtr));
+			// Sanity check length
+			if(currentEskMaskPtr.Length()>KMaxEskMaskLength)
+				{
+				LOG(ESockLog::Printf(KESockBootingTag, _L8("EskMask%d under heading [IniData] contains value of more than %d characters"), i, KMaxEskMaskLength));
+#ifdef _DEBUG
+				Panic(ECorruptIniData);
+#endif
+				// leave if ini data corrup rather than panic unless debug
+				User::Leave(KErrCorrupt);
+				}
+			// Add entry to list of EskMasks
+			currentMaskEntry.Copy(currentEskMaskPtr);
+			iEskMaskList.AppendL(currentMaskEntry);
+			}
+		else
+			{
+			/* If first iteration and nothing found in "EskMask" it is a critical error.
+			For EskMask0..max it is merely cause for breaking the loop */
+			if(i==0)
+				{
+				LOG(ESockLog::Printf(KESockBootingTag, _L("EskMask under heading [IniData] not found")));
+				// leave if ini data corruprather than panic unless debug
+#ifdef _DEBUG
+				Panic(ECorruptIniData);
+#endif
+				User::Leave(KErrCorrupt);
+				}
+			else
+				{
+				/* Break out of loop, if we didnt find EskMask<n> no reason to waste
+				time searching for EskMask<n+1> */
+				LOG(ESockLog::Printf(KESockBootingTag, _L("CWorkerThread::DetermineRoleL No more ESK masks found.")));
+				break;
+				}
+			}
+		// Getting ready for next one
+		++i;
+		currentEskMask.Format(KEskMaskLabelNumFormat, i);
+		}
+	while(i<KEskMaskLabelMax);
+
+	TInt playerRole = (iWorkerId == TCFWorkerThreadPublicInfo::EIpPlayerThread)?		// defaults for legacy configurations
+		(TCFPlayerRole::EDataPlane | TCFPlayerRole::ESubConnPlane | TCFPlayerRole::EConnPlane | TCFPlayerRole::EMetaConnPlane | TCFPlayerRole::ETierMgrPlane | TCFPlayerRole::ETierResolver | TCFPlayerRole::EPrePlanarArchitecture):
+		(TCFPlayerRole::EDataPlane | TCFPlayerRole::ESubConnPlane | TCFPlayerRole::EConnPlane | TCFPlayerRole::EMetaConnPlane | TCFPlayerRole::ETierMgrPlane | TCFPlayerRole::EPrePlanarArchitecture);
+	if(GetVarFromIniData(*aIniData, KNullDesC8, KPlayerRoleLabel, playerRole))
+		{
+		if((playerRole != 0) &&
+			((playerRole & ~(TCFPlayerRole::EDataPlane | TCFPlayerRole::ESubConnPlane | TCFPlayerRole::EConnPlane | TCFPlayerRole::EMetaConnPlane | TCFPlayerRole::ETierMgrPlane | TCFPlayerRole::ETierResolver)) == 0))	// no unknown flags
+			{
+			}
+		else
+			{
+			LOG(ESockLog::Printf(KESockBootingTag, _L("PlayerType=%04x - unsupported type (must be OR of TPlayerRole flags or unspecified for legacy"), playerRole));
+#ifdef _DEBUG
+			Panic(ECorruptIniData);
+#endif
+			// leave if ini data corrupt rather than panic if not debug
+			User::Leave(KErrCorrupt);
+			}
+		}
+	aPlayerRole = TCFPlayerRole(playerRole, aPlayerRole.Kindex());
+	}
+
+CCommonPitBoss* CWorkerThread::DoCreatePitBossL(CCommonWorkerThread* aWorkerThread)
+	{
+	return CPitBoss::NewL(static_cast<CWorkerThread*>(aWorkerThread));
+	}
+
+CCommonPlayer* CWorkerThread::DoCreatePlayerL(CCommonWorkerThread* aWorkerThread,TPlayerRole aPlayerRole)
+	{
+	CPlayer* player = CPlayer::NewL(static_cast<CWorkerThread*>(aWorkerThread),aPlayerRole);
+//	CleanupStack::PushL(player);
+//	CleanupStack::Pop(player);
+	return player;
+	}
+
+CCommonDealer* CWorkerThread::DoCreateDealerL(CCommonWorkerThread* aWorkerThread, Den::TPlayerRole aPlayerRole)
+	{
+	CCommonDealer* dealer = NULL;
+	if (aPlayerRole.HasRole(TCFPlayerRole::ETierResolver))
+		{
+		CConnectionServer* cs = CConnectionServer::NewL(static_cast<CWorkerThread*>(aWorkerThread));
+		CleanupStack::PushL(cs);
+		dealer = CCommonDealer::NewL(cs);
+		CleanupStack::Pop(cs);
+		cs->SetDealer(dealer);
+		}
+	else
+		{
+		CSocketServer* ss = CSocketServer::NewL(static_cast<CWorkerThread*>(aWorkerThread));
+		CleanupStack::PushL(ss);
+
+		if (IsMainThread())
+			{
+			dealer = CCommonDealer::NewL(ss);
+			}
+		else
+			{
+			dealer = CCommonWorkerDealer::NewL(ss);
+			}
+		CleanupStack::Pop(ss);
+		ss->SetDealer(dealer);
+		}
+	return dealer;
+	}
+
+/**
+Takes the total list of .ESK files and generates a new local list containing only the file names
+matching the .ESK list for this worker. Then creates a new ini data instance with the local list to be
+used by the protocol manager for initialisation.
+@see CESockIniData
+*/
+void CWorkerThread::ProcessMatchingEskFilesL(const COwnEntryList* aEskFiles)
+	{
+	LOG(ESockLog::Printf(KESockBootingTag, _L("CWorkerThread::ProcessMatchingEskFilesL: %d files"), aEskFiles->Count()));
+	TInt i;
+	for(i=0;i<iEskMaskList.Count();i++)
+		{
+		LOG(ESockLog::Printf(KESockBootingTag, _L("CWorkerThread::ProcessMatchingEskFilesL: mask %S"), &iEskMaskList[i]));
+		}
+
+	// The Gordian Knot of global data remains only half slashed... Also future work may eliminate the sharing of iEskData
+	// through this, rather than PitBoss()?
+	CESockIniData** iniData = &SockManGlobals::Get()->iEskData;
+	delete *iniData;
+	*iniData = NULL;
+	// Build a private, filtered copy of the total set of ESK files
+	CommsFW::COwnEntryList* ownEskFiles = new(ELeave) COwnEntryList(6);
+	CleanupStack::PushL(ownEskFiles);
+
+	TParse parsed;
+	const TInt cnt = aEskFiles->Count();
+	for(i = 0; i < cnt; ++i)
+		{
+		const COwnEntryList::TOwnEntry& entry = aEskFiles->operator[](i);
+		parsed.Set(entry.iName, NULL, NULL);
+		TPtrC nameAndExt = parsed.NameAndExt();
+		for(TInt j=0; j<iEskMaskList.Count(); ++j)
+			{
+			if(nameAndExt.MatchF(iEskMaskList[j]) >= 0)
+				{
+				LOG(ESockLog::Printf(KESockBootingTag, _L("- matched %S"), &entry.iName));
+				ownEskFiles->AddL(entry);
+				break; // Stop inner loop
+				}
+			}
+		}
+	iEskMaskList.Close();
+	LOG(ESockLog::Printf(KESockBootingTag, _L8("CWorkerThread::ProcessMatchingEskFilesL: %d files matched"), ownEskFiles->Count()));
+	if(ownEskFiles->Count() == 0 && (!iPlayer || static_cast<CPlayer*>(iPlayer)->HasDataPlane()))
+		{
+		// Threads with data planes must have one or more matching ESK files (otherwise
+		// they have no function and surely result from configuration errors)
+		User::Leave(KErrCorrupt);
+		}
+	// The ini data processing lives in the global protocol manager, for legacy reasons
+	*iniData = CESockIniData::NewL(ownEskFiles);
+
+	CleanupStack::Pop(ownEskFiles);
+	delete ownEskFiles;
+	if (iPlayer)
+		{
+		if(static_cast<CPlayer*>(iPlayer)->HasDataPlane())
+			{
+			ProtocolManager::ProcessIniDataL();
+			}
+		}
+	}
+
+void CWorkerThread::DoProcessWorkerIntroductionL(const TWorkerIntroductionMsg& aMsg)
+	{
+	TBuf8<TWorkerIntroductionMsg::KMaxIntroductionInfoSize> eskFilesList;
+	aMsg.IntroductionInfo(eskFilesList);
+	CommsFW::COwnEntryList* const* eskFiles = reinterpret_cast<CommsFW::COwnEntryList* const*>(eskFilesList.Ptr());
+	TRAPD(ret, ProcessMatchingEskFilesL(*eskFiles));
+
+	if (ret != KErrNone)
+		{
+		LOG(ESockLog::Printf(KESockBootingTag, _L("ERROR %d processing Esk files"), ret));
+	#ifdef _DEBUG
+		RDebug::Printf("CWorkerThread::DoProcessWorkerIntroductionL ERROR %d processing Esk files", ret);
+		Panic(EMisconfigured);
+	#endif
+		// Leave if problem with EskFiles instead of panic unless debug
+		User::Leave(KErrCorrupt);
+		}
+	}
+
+void CWorkerThread::DoFillIntroMessage(TWorkerId /*aPeerId*/, TWorkerIntroductionMsg& aIntroMsg)
+	{
+	if(IsMainThread())
+		{
+		const CommsFW::COwnEntryList* eskList = PitBoss().GetCompleteList();
+		TPtrC8 ptr((TUint8*)&eskList, sizeof(TAny*)); //Yes, pointer to pointer
+		aIntroMsg.SetIntroductionInfo(ptr);
+		}
+	else
+		{
+		TPckg<TBool> defaultOptDealer(DefaultOptimalDealer());
+		aIntroMsg.SetIntroductionInfo(defaultOptDealer);
+	}
+	}
+
+/**
+Deals with binding requests from the Root Server. Note that although the Root Server allows
+multiple identical bindings ESock does not allow this and will panic if the binding already exists.
+Bindings are expressed in ESock by worker transport objects stored in an array. Since all sub-module
+names are unique across all ESock 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
+CWorkerTransport 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 "peer Player: Immediately introduce ourselves, including cookies
+	-# This worker is the Main thread: Wait to receive its introduction message, which carries the ESK file list.
+@see CWorkerTransport
+*/
+void CWorkerThread::CFBindMessageReceived(const TCFBindMsg& aMsg)
+	{
+    __CFLOG_SMADDR2(( KESockComponentTag, KESockServerTag, _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_DEBUG(bindId <=  TWorkerThreadPublicInfo::EMaxWorkerThreadId, User::Panic(KSpecAssert_ESockSSocks_thrd, 1));
+	if(err == KErrNone)
+		{
+		if(iTransport->PeerReachable(bindId))
+			{
+			LOG(ESockLog::Printf(_L8("%d Already exists. Error in configuration of cmi files"),bindId));
+			Panic(EDuplicateWorkerId); //Must panic b/c the new module will be left in a half bound state and will tend to get out of hand
+			}
+		else
+			{
+			err = iTransport->EstablishTransportToPeer(bindId, aMsg.ChannelInput(), aMsg.ChannelOutput());
+			if(err == KErrNone)
+				{
+				iTransport->SetLastRequestIdConcerningPeer(bindId, aMsg.Identifier());
+
+				// If we're binding to the main thread then wait for its intro, with the all-important ESK file list
+				if(bindId != TWorkerThreadPublicInfo::EMainThread)
+					{
+					SendIntroMessage(aMsg.SubModule2()->Module(), bindId);
+
+					// If we have a player then follow up with a bucket of our own cookies (always a good way to make friends
+					// with the new neighbour...). Only "planar architecture" configurations share their cookies; this allows
+					// bindings between legacy threads for purposes of Dealer -> Player forwarding only.
+					// @TODO - either we stop forwarding from optimal Dealers to Players or we have to add of indicating which
+					// bindings are for sharing planes and which are for simply forwarding requests. Probably we should do the
+					// former - if someone asks for "optimal IP data" then why should they be able to send SMS from that session?
+					Messages::TNodeId dataFC;
+					Messages::TNodeId subConnFC;
+					Messages::TNodeId connFC;
+					Messages::TNodeId metaConnFC;
+					Messages::TNodeId tierManagerFC;
+					if(Player())
+						{
+						const TPlayerRole& role = Player()->PlayerRole();
+						if(!role.HasRole(TCFPlayerRole::EPrePlanarArchitecture))
+							{
+							// It's safe to simply copy the cookies we know of here - we cannot yet have learnt of any others so they must be ours
+							CSockManData* globs = Player()->SockManGlobals();
+							if(role.HasRole(TCFPlayerRole::EDataPlane))
+								{
+								dataFC = globs->GetPlaneFC(TCFPlayerRole(TCFPlayerRole::EDataPlane));
+								}
+							if(role.HasRole(TCFPlayerRole::ESubConnPlane))
+								{
+								subConnFC = globs->GetPlaneFC(TCFPlayerRole(TCFPlayerRole::ESubConnPlane));
+								}
+							if(role.HasRole(TCFPlayerRole::EConnPlane))
+								{
+								connFC = globs->GetPlaneFC(TCFPlayerRole(TCFPlayerRole::EConnPlane));
+								}
+							if(role.HasRole(TCFPlayerRole::EMetaConnPlane))
+								{
+								metaConnFC = globs->GetPlaneFC(TCFPlayerRole(TCFPlayerRole::EMetaConnPlane));
+								}
+							if(role.HasRole(TCFPlayerRole::ETierMgrPlane))
+								{
+								tierManagerFC = globs->GetPlaneFC(TCFPlayerRole(TCFPlayerRole::ETierMgrPlane));
+								}
+							}
+						}
+
+                    NM_LOG_START_BLOCK(KESockComponentTag, _L8("PeerIntro"));
+                    NM_LOG((KESockComponentTag, _L8("CFBindMessageReceived(): Sending Cookies to peer #%d "), bindId));
+                    NM_LOG_ADDRESS_EXT(KESockComponentTag, dataFC, _L8("dataFC: "));
+                    NM_LOG_ADDRESS_EXT(KESockComponentTag, subConnFC, _L8("scFC: "));
+                    NM_LOG_ADDRESS_EXT(KESockComponentTag, connFC, _L8("cnFC: "));
+                    NM_LOG_ADDRESS_EXT(KESockComponentTag, metaConnFC, _L8("mcFC: "));
+                    NM_LOG_ADDRESS_EXT(KESockComponentTag, tierManagerFC, _L8("tmFC: "));
+				    NM_LOG_END_BLOCK(KESockComponentTag, _L8("PeerIntro"));
+					TWorkerCookiesMsg cookieMsg(dataFC, subConnFC, connFC, metaConnFC, tierManagerFC);
+					PostMessage(bindId, cookieMsg);
+
+					if(!IsMainThread())
+						{
+						if(Player()->HasConnPlane())
+							{
+							SendObjectBrokerIntroductionMessage(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 CWorkerThread::SendObjectBrokerIntroductionMessage(const CommsFW::TWorkerId& aPeerId)
+	{
+	// Share our central object broker node with a worker
+	__ASSERT_DEBUG(Player(), User::Panic(KSpecAssert_ESockSSocks_thrd, 2));
+	__ASSERT_DEBUG(Player()->HasConnPlane(), User::Panic(KSpecAssert_ESockSSocks_thrd, 3));
+	TBrokerIntroductionMsg brokerIntroMsg(SockManGlobals()->iCommsFactoryContainerBroker);
+	PostMessage(aPeerId, brokerIntroMsg);
+	}
+
+void CWorkerThread::DoCompleteUnbinding(CommsFW::TWorkerId aWorker)
+	{
+	TCFPlayerRole ourRole(TCFPlayerRole::EAllPlanes | TCFPlayerRole::ETierResolver);
+	TPlaneUnboundMsg unboundMsg(ourRole);
+	PostMessage(aWorker, unboundMsg);
+
+	CSockManData* globs = SockManGlobals();
+	globs->iFCMap.DropFCOfPeer(TCFPlayerRole(TCFPlayerRole::EAllPlanes), aWorker);
+
+	CommsFW::TCFUnbindCompleteMsg respMsg(iTransport->LastRequestIdConcerningPeer(aWorker), KErrNone);
+	iTransport->DropTransportToPeer(aWorker);
+	VERIFY_RESULT(iChannelHandler->Send(respMsg), KErrNone);
+	}
+
+/**
+It is possible for client applications outside of the Comms Process to send messages to
+an ESock Worker thread. This is done through the Root Server SendMessage method.
+The message is received by the destination ESock Worker in the CFMessageForward member,
+which will deal with it accordingly.
+Supported message type is TCFMessageType::EFactoryMsg destined for one or more
+CProtocolFamilyFactoryBase instances from globals->iProtocolFamilyFactories. ESock will
+traverse through this list and deliver (by calling SendMessage()) the message content to
+every instance present there.
+The message content for such a message sent to an ESock Worker is required to be based on
+the CNetMessage class.
+@note Completion of the client request the responsibility of the recipient.
+@see RRootServ::SendMessage
+*/
+void CWorkerThread::CFMessageForward(const CommsFW::TCFForwardMsg& aMessage)
+	{
+	const RMessage2& msg = aMessage.Msg();
+	if ( !SockManGlobals::Get()->iProtocolFamilyFactories )
+		{
+		msg.Complete( KErrNotReady );
+		return;
+		}
+	TInt len = msg.GetDesLength(2);
+	SMetaDataECom* pInstance = NULL;
+
+	TInt nErr=KErrArgument;
+
+	TPtr8 pDataPtr(0,0);
+	RBuf8 buf;
+
+	if( len >= 0 )
+		{
+		nErr = buf.Create( len );
+		if ( nErr == KErrNone )
+			{
+			pDataPtr.Set( const_cast<TUint8*>(buf.Ptr()), 0, buf.MaxLength() );
+			nErr = msg.Read( 2, pDataPtr );
+			}
+		if ( nErr == KErrNone )
+			{
+			TPtrC8 ptr(pDataPtr);
+			TRAP
+				(nErr,
+				pInstance = SMetaDataECom::LoadL( ptr )
+				)
+			}
+		}
+
+	if ( nErr == KErrNone )
+		{
+		if ( !pInstance->IsTypeOf( STypeId::CreateSTypeId( NetMessages::KInterfaceUid, NetMessages::EMessageAny ) ) )
+			{
+			nErr = KErrArgument;
+			}
+		else
+			{
+			NetMessages::CMessage* pNetMessage = static_cast<NetMessages::CMessage*>(pInstance);
+			switch ( msg.Int1() )
+				{
+			case /*TCFMessageType::*/EFactoryMsg:
+				{
+				TInt nIndex = 0;
+				nErr = KErrNotSupported;
+				ESock::CProtocolFamilyFactoryBase* pFactory = SockManGlobals::Get()->iProtocolFamilyFactories->Get( nIndex++ );
+				while ( pFactory != NULL )
+					{
+					if ( (nErr = pFactory->SendMessage( *pNetMessage )) != KErrNotSupported )
+						{
+						break;
+						}
+					pFactory = SockManGlobals::Get()->iProtocolFamilyFactories->Get( nIndex++ );
+					}
+				pDataPtr.Zero();
+				if(nErr == KErrNone)
+					{
+					nErr = pNetMessage->Store( pDataPtr );
+					}
+				if ( nErr == KErrNone )
+					{
+					nErr = msg.Write( 2, pDataPtr );
+					}
+				break;
+				}
+			default:
+				nErr = KErrArgument;
+				break;
+				}
+			}
+		}
+	buf.Close();
+	delete pInstance;
+	msg.Complete( nErr );
+	}
+
+void CWorkerThread::MaybeIncorporateFCL(const TPlayerRole& aPlane, const Messages::TNodeId& aPeerId)
+	{
+	CSockManData* globals = SockManGlobals();
+
+	if(!aPeerId.IsNull())
+		{
+		TNodeId ourId(globals->GetPlaneFC(aPlane));
+		if(!ourId.IsNull() && Player() && !Player()->HasDataPlane())
+			{
+			// We're meeting a peer with a role that we either have or have already acquired. This is acceptable for a data plane being bound to multiple control
+			// planes since modern session flows will be associated a conn or subconn, but in other cases is an error because of the need for explicit resolution
+			// it presents (eg would become "the correction SubConn plane for Conn X" rather than simply "the SubConn plane bound to current Conn Plane"
+			LOG(ESockLog::Printf(KESockBootingTag, _L8("WARNING: Worker thread %d being bound also has FC for plane 0x%x"), aPeerId.Thread(), aPlane.Role()));
+
+#ifdef _DEBUG
+            // If this stings you see DEF109193, and speak to ThomasG, BarryA, or PeterSau
+            Panic(EAmbigiousRoles);
+#endif
+			// Leave if problem with config instead of panic unless debug
+            User::Leave(KErrCorrupt);	// no benefit to specific error; unlikely to be visible in UREL (no logging, debugging)
+            return;
+			}
+
+		// The addition may fail if we're a multiply-bound data thread, as described above. This is harmless
+		TInt err = globals->iFCMap.AddPlaneFC(aPlane, aPeerId);
+
+		// Add the factory to our central factory container object broker (if we are hosting it)
+		if(Player()->HasConnPlane() && (err == KErrNone))
+			{
+			// Must have installed our broker already
+			__ASSERT_DEBUG(globals->iCommsFactoryContainerBrokerSingleton, User::Panic(KSpecAssert_ESockSSocks_thrd, 4));
+
+			// Can only have one factory container from each unique player role (plane)
+			__ASSERT_DEBUG(0 == globals->iCommsFactoryContainerBrokerSingleton->CountClients<TDefaultClientMatchPolicy>(aPlane), User::Panic(KSpecAssert_ESockSSocks_thrd, 5));
+
+			// Add the factory container to our broker's list
+			globals->iCommsFactoryContainerBrokerSingleton->AddClientL(aPeerId, aPlane);
+			}
+		}
+	}
+
+
+/**
+Deal with incoming messages from other workers. If the message is not a known type it might
+be a generic transport message and an attempt to use a transporthandler ECOM plugin is made.
+*/
+TBool CWorkerThread::DoDispatchL(const CommsFW::TCFMessage& aMessage, CommsFW::TWorkerId aSenderId)
+	{
+	switch(aMessage.Code())
+		{
+		case TWorkerMsg::EWorkerCookies:
+			{
+			LOG(ESockLog::Printf(_L("PeerWorkerMessageReceived(%d). EWorkerCookies"), aSenderId));
+			const TWorkerCookiesMsg& msg = reinterpret_cast<const TWorkerCookiesMsg&>(aMessage);
+
+			if(aSenderId != TWorkerThreadPublicInfo::EMainThread && !IsMainThread())
+				{
+				// Not the PitBoss meeting us - if we're not the PitBoss then it should be a peer Player with some cookies to share
+				// For any given plane the rules are:
+				//	(1) if our Player has that role itself then we simply ignore their cookie (if any)
+				//	(2) if our Player lacks the role but we've already bound to a peer which does then its a config error
+				// The rules are driven by the need to avoid ambiguity in the coupling of planes, a subconn doesn't need (or have)
+				// the ability to specify which SubConnflowFC will create a flow for it. So it's either its own thread (if it implements
+				// the role) or the one & only peer bound to it which implements this. We don't support ideas like "the first peer" because
+				// that depends upon the startup ordering of the RootServer and although that is well-defined and user controllable it seems
+				// likely to be seen as unhelpfully complex and a source of mysterious config errors (eg BT data thread being asked to
+				// create a UDP flow)
+				TUint32 localRole = Player()? Player()->PlayerRole().Role() : TCFPlayerRole::EUnknown;
+				if ((localRole & (TCFPlayerRole::EAllPlanes | TCFPlayerRole::ETierResolver)) != TCFPlayerRole::ETierResolver)
+					{
+					// This is presuming that the tier resolver is running in its own thread
+					// and should not learn the roles from peers
+					MaybeIncorporateFCL(TCFPlayerRole(TCFPlayerRole::EDataPlane), msg.DataPlaneFC());
+					MaybeIncorporateFCL(TCFPlayerRole(TCFPlayerRole::ESubConnPlane), msg.SubConnPlaneFC());
+					MaybeIncorporateFCL(TCFPlayerRole(TCFPlayerRole::EConnPlane), msg.ConnPlaneFC());
+					MaybeIncorporateFCL(TCFPlayerRole(TCFPlayerRole::EMetaConnPlane), msg.MetaConnPlaneFC());
+					}
+
+	            // For the benefit of the connection server the tier resolver is allowed
+	            // to learn about the tier managers.
+				MaybeIncorporateFCL(TCFPlayerRole(TCFPlayerRole::ETierMgrPlane), msg.TierManagerFC());
+				}
+			break;
+			}
+
+		case TWorkerMsg::EBrokerIntroduction:
+			{
+			__ASSERT_DEBUG(Player(), User::Panic(KSpecAssert_ESockSSocks_thrd, 6));
+
+			// The tier resolver is virtually friends with everyone and so not a candidate for learning
+			// about a technology specific singleton like the object broker
+			if(!Player()->HasTierResolver() && !Player()->HasDataPlane())
+				{
+				CSockManData* globals = SockManGlobals();
+				const TBrokerIntroductionMsg& msg = reinterpret_cast<const TBrokerIntroductionMsg&>(aMessage);
+
+				// If we don't already have our own broker then accept the one being introduced
+				if(globals->iCommsFactoryContainerBroker == TNodeId::NullId())
+					{
+					globals->iCommsFactoryContainerBroker = msg.BrokerId();
+					}
+				}
+			break;
+			}
+
+		case TWorkerMsg::ELoadTierMappings:
+			{
+			if(IsMainThread())
+				{
+				static_cast<CPitBoss&>(PitBoss()).RequestLoadTierMapping();
+				}
+			else
+				{
+				__ASSERT_DEBUG(Player(), User::Panic(KSpecAssert_ESockSSocks_thrd, 7));
+				__ASSERT_DEBUG(static_cast<CPlayer*>(Player())->HasTierResolver(), User::Panic(KSpecAssert_ESockSSocks_thrd, 8));
+
+				RTierThreadMap* map = NULL;
+                TRAPD(err,
+				    CommsDat::CMDBSession* dbs = CommsDat::CMDBSession::NewLC(KCDVersion1_1);
+                    map = TierManagerUtils::BuildTierThreadMappingL(*dbs);
+                    CleanupStack::PopAndDestroy(dbs);
+                    );
+                (void) err;
+                LOG(ESockLog::Printf(_L8("CWorkerThread:\tDoDispatch, ELoadTierMappings err=%d, %d mappings"), err, map? map->Count(): 0));
+
+				TWorkerTierMappingsLoaded respMsg(map);
+				PostMessage(aSenderId, respMsg);
+				}
+			break;
+			}
+
+		case TWorkerMsg::ETierMappingsLoaded:
+			{
+			__ASSERT_DEBUG(IsMainThread(), User::Panic(KSpecAssert_ESockSSocks_thrd, 9));
+			static_cast<CPitBoss&>(PitBoss()).OnTierMappingLoadedL(&reinterpret_cast<const TWorkerTierMappingsLoaded&>(aMessage), aSenderId);
+			break;
+			}
+		case TWorkerMsg::EPlaneUnbound:
+			{
+			const TPlaneUnboundMsg& msg(reinterpret_cast<const TPlaneUnboundMsg&>(aMessage));
+			LOG(ESockLog::Printf(_L("PeerWorkerMessageReceived(%d). EPlaneUnbound (planes=0x%04x)"), aSenderId, msg.PlayerRole().Role()));
+			CSockManData* globs = SockManGlobals();
+			globs->iFCMap.DropFCOfPeer(msg.PlayerRole(), aSenderId);
+	//		MaybeCompleteUnbinding(aSenderId);
+			MaybeTriggerThreadShutdownCallback();
+			break;
+			}
+		LOG( //This "case" is only for logging, it is properly forwarded in the CCommonWorkerThread
+		case TPlayerMsg::EForwardRequest:
+			{
+			TPlayerForwardRequestMsg fwdReqMsg(reinterpret_cast<const TPlayerForwardRequestMsg&>(aMessage));
+			TBuf8<64> messBuf;
+			ESockLog::IPCMessName((TSockMess) fwdReqMsg.SafeMessage().Function(), messBuf);
+			ESockLog::Printf(KESockSessDetailTag, _L8("PeerWorkerMessageReceived(%d). EForwardRequest(%S)"), aSenderId, &messBuf);
+			break;
+			}
+		);
+		default:
+			{
+			// If it isn't an internal message then it should be a TWorkerTransportMsg for a plugin
+			const TWorkerTransportMsg& msg = reinterpret_cast<const TWorkerTransportMsg&>(aMessage);
+			const RThread& peerThread = PitBoss().RThreadRef(aSenderId);
+			TRequestWrapper request(msg.Status(), EFalse);
+			request.SetPeerThread(peerThread);
+			TransportMessageReceived(msg, request, aSenderId);
+			}
+		}
+
+	return ETrue; //We always process the message (TransportMessageReceived() may panic if not recognised)
+	}
+
+/**
+This override ensures we do not check an ECNCreate message to see if the subsession is orphaned - the subsession
+pointer is reused to hold a tier ID rather than a pointer.
+@see CPlayer::ProcessMessageL
+*/
+void CWorkerThread::OnDispatchLeave(const CommsFW::TCFMessage& aMessage, CommsFW::TWorkerId aSenderId, TInt aError)
+	{
+	__ASSERT_DEBUG(aError!=KErrNone, User::Panic(KSpecAssert_ESockSSocks_thrd, 10));
+	switch(aMessage.Code())
+		{
+        case TPlayerMsg::EForwardRequest:
+            {
+            if(aError != KErrBadDescriptor)
+                {
+                TPlayerForwardRequestMsg fwdReqMsg(reinterpret_cast<const TPlayerForwardRequestMsg&>(aMessage));
+                // Should we also test Message().IsNull(). Should be no aliasing of it now?
+                LOG(ESockLog::Printf(_L8("CWorkerThread:\tOnDispatchLeave, Complete message(%08x) with %d."), fwdReqMsg.SafeMessage().Handle(), aError));
+                __ASSERT_DEBUG(Player(), User::Panic(KSpecAssert_ESockSSocks_thrd, 11));
+                if (Player()->ShouldCompleteCurrentRequest())
+                    {
+                    fwdReqMsg.SafeMessage().Complete(aError);
+                    }
+                }
+            }
+            break;
+
+        default:
+            CCommonWorkerThread::OnDispatchLeave(aMessage, aSenderId, aError);
+		}
+	}
+
+/**
+Attempt to locate and create a new transportuser (using ECOM).
+@leave KErrNotFound
+@leave KErrNoMemory
+*/
+void CWorkerThread::CreateTransportUserL(TTransportUser& aTemplateUser)
+	{
+	aTemplateUser.iUserStorage = new(ELeave) TTransportUserStorage;
+	TTransportReceiverFactoryArgs factoryArgs(aTemplateUser.iUserStorage, WorkerId());
+	aTemplateUser.iUser = reinterpret_cast<CWorkerTransportReceiver*>(REComSession::CreateImplementationL(TUid::Uid(aTemplateUser.iInterfaceId), aTemplateUser.iDtorId, &factoryArgs));
+	TInt ret = iTransportUsers.InsertInOrder(aTemplateUser, TLinearOrder<TTransportUser>(TTransportUser::Compare));
+	if(ret != KErrNone)
+		{
+		REComSession::DestroyedImplementation(aTemplateUser.iDtorId);
+		delete aTemplateUser.iUser;
+		User::Leave(ret);
+		}
+	}
+
+/**
+Given a transport message try to look up its code in the list of loaded ECOM plugins. If this doesnt
+work ask ECOM whether it has any plugins supporting this interface ID.
+*/
+TInt CWorkerThread::TransportMessageReceived(const TWorkerTransportMsg& aMessage, TRequestWrapper& aRequestWrapper, TWorkerId aWorkerId)
+	{
+	// A message other than the private ESOCKSVR ones was received. Its id should be that of a plugin which will process it
+	TTransportUser user;
+	user.iInterfaceId = aMessage.Code();
+	TInt ret = iTransportUsers.FindInOrder(user, TLinearOrder<TTransportUser>(TTransportUser::Compare));
+	if(ret == KErrNotFound)
+		{
+		TRAP(ret, CreateTransportUserL(user));
+		if(ret == KErrNone)
+			{
+			user.iUser->ProcessMessage(aMessage, aRequestWrapper, *user.iUserStorage, aWorkerId);
+			}
+		else
+			{
+			delete user.iUserStorage;
+			if(ret != KErrNoMemory)
+				{
+				// Message was neither an internal one nor a loadable plugin. Asserting is helpful here
+				LOG(ESockLog::Printf(_L("UNKNOWN: Transport message #%d (#%08x)"), aMessage.Code(), aMessage.Code()));
+				__ASSERT_DEBUG(0, User::Panic(KSpecAssert_ESockSSocks_thrd, 12));
+				}
+			aRequestWrapper.RequestComplete(ret);
+			}
+		}
+	else
+		{
+		const TTransportUser& refUser = iTransportUsers[ret];
+		refUser.iUser->ProcessMessage(aMessage, aRequestWrapper, *refUser.iUserStorage, aWorkerId);
+		}
+	return ret;
+	}
+
+
+void CWorkerThread::DoPostMortemCleanup()
+	{
+	// First cleanup any transport users - these are more important than clients since they
+	// may have peer threads blocked on them
+	iTransportUsers.Shutdown(ETrue);
+	}
+
+void CWorkerThread::DoSetShuttingDown()
+	{
+	SockManGlobals::Get()->iShutdownGracefully = ETrue;
+	}
+
+/**
+Given address family and socket type will locate the sender object for the worker thread
+hosting the protocol. If it is the same worker as the current one, returns the self sender,
+which use direct calls instead of channels for processing messages, otherwise return the
+appropriate CWorkerTransport.
+Although an exported function it is only allowed to call it from the context of an ESock worker thread.
+It is mainly for use by plugins.
+@see CWorkerTransport
+@see TWorkerTransportSelfSender
+*/
+EXPORT_C TInt CWorkerThread::CookieForProtocol(TUint aAddrFamily, TUint aSockType, TUint aProtocol, Messages::TRuntimeCtxId& aCookie)
+	{
+	CWorkerThread* selfWorker = SockManGlobals::Get()->SelfWorker();
+	__ASSERT_DEBUG(selfWorker, User::Panic(KSpecAssert_ESockSSocks_thrd, 13));	// calling this function from other than a ESOCK Worker thread cannot work
+	if(selfWorker)
+		{
+		TWorkerId workerId;
+		if(selfWorker->PitBoss().GetWorkerForProtocol(aAddrFamily, aSockType, aProtocol, workerId))
+			{
+			if(selfWorker->Transport()->PeerReachable(workerId))
+				{
+				Messages::TRuntimeCtxIdOp(aCookie).Set(0, workerId);
+				return KErrNone;
+				}
+			}
+		}
+	return KErrBadName;
+	}
+
+/**
+Given protocol name will locate the sender object for the worker thread
+hosting the protocol. If it is the same worker as the current one, returns the self sender,
+which use direct calls instead of channels for processing messages, otherwise return the
+appropriate CWorkerTransport.
+Although an exported function it is only allowed to call it from the context of an ESock worker thread.
+It is mainly for use by plugins.
+@see CWorkerTransport
+@see TWorkerTransportSelfSender
+*/
+EXPORT_C TInt CWorkerThread::CookieForProtocolByName(const TProtocolName& aName, Messages::TRuntimeCtxId& aCookie)
+	{
+	CWorkerThread* selfWorker = SockManGlobals::Get()->SelfWorker();
+	__ASSERT_DEBUG(selfWorker, User::Panic(KSpecAssert_ESockSSocks_thrd, 14));	// calling this function from other than a ESOCK Worker thread cannot work
+	if(selfWorker)
+		{
+		TWorkerId workerId;
+		if(selfWorker->PitBoss().GetWorkerForProtocolByName(aName, workerId))
+			{
+			if(selfWorker->Transport()->PeerReachable(workerId))
+				{
+				Messages::TRuntimeCtxIdOp(aCookie).Set(0, workerId);
+				return KErrNone;
+				}
+			}
+		}
+	return KErrNotFound;
+	}
+
+/**
+Given Worker ID will locate the sender object for the worker thread
+hosting the protocol. If it is the same worker as the current one, returns the self sender,
+which use direct calls instead of channels for processing messages, otherwise return the
+appropriate CWorkerTransport.
+Although an exported function it is only allowed to call it from the context of an ESock worker thread.
+It is mainly for use by plugins.
+@see CWorkerTransport
+@see TWorkerTransportSelfSender
+*/
+EXPORT_C TInt CWorkerThread::CookieForWorkerId(TWorkerId aWorkerId, Messages::TRuntimeCtxId& aCookie)
+	{
+	CWorkerThread* selfWorker = SockManGlobals::Get()->SelfWorker();
+	__ASSERT_DEBUG(selfWorker, User::Panic(KSpecAssert_ESockSSocks_thrd, 15));	// calling this function from other than a ESOCK Worker thread cannot work
+	if(selfWorker)
+		{
+		if(selfWorker->Transport()->PeerReachable(aWorkerId))
+			{
+			Messages::TRuntimeCtxIdOp(aCookie).Set(0, aWorkerId);
+			return KErrNone;
+			}
+		}
+	return KErrNotSupported;
+	}
+
+EXPORT_C TBool CWorkerThread::ResolveWorkerNameToId(const TDesC8& aWorkerName, TWorkerId& aWorkerId)
+	{
+	CSockManData* globs = SockManGlobals::Get();
+	__ASSERT_DEBUG(globs, User::Panic(KSpecAssert_ESockSSocks_thrd, 16));
+	return globs->SelfWorker()->PitBoss().ResolveWorkerNameToId(aWorkerName, aWorkerId);
+	}
+
+/**
+Use SockManGlobals in thread local storage (TLS) to retrieve WorkerID of the current worker thread.
+*/
+EXPORT_C TInt CWorkerThread::CurrentWorkerId(TWorkerId& aWorkerId)
+	{
+	CWorkerThread* selfWorker = SockManGlobals::Get()->SelfWorker();
+	__ASSERT_DEBUG(selfWorker, User::Panic(KSpecAssert_ESockSSocks_thrd, 17));	// calling this function from other than a ESOCK Worker thread cannot work
+	if(selfWorker)
+		{
+		aWorkerId = selfWorker->WorkerId();
+		return KErrNone;
+		}
+	return KErrNotSupported;
+	}
+
+//
+// CWorkerThread::RTransportUsers
+//
+
+/** Notify all active transport user ECOM plugins of shutdown. */
+void CWorkerThread::RTransportUsers::Shutdown(TBool aAlreadyDead)
+	{
+	for(TInt idx = Count() - 1; idx >= 0; --idx)
+		{
+		TTransportUser& user = operator[](idx);
+		user.iUser->Shutdown(aAlreadyDead, *user.iUserStorage);
+		if(!aAlreadyDead)
+			{
+			REComSession::DestroyedImplementation(user.iDtorId);
+			delete user.iUser;
+			delete user.iUserStorage;
+			}
+		}
+	Close();
+	}
+
+/** Notify all active transport user ECOM plugins that a thread have died. */
+void CWorkerThread::RTransportUsers::NotifyPeerDeath(TWorkerId aPeer)
+	{
+	for(TInt idx = Count() - 1; idx >= 0; --idx)
+		{
+		TTransportUser& user = operator[](idx);
+		user.iUser->OnPeerDeath(aPeer, *user.iUserStorage);
+		}
+	}
+
+//
+// TRequestWrapper
+//
+
+EXPORT_C TRequestWrapper::TRequestWrapper()
+: iStatus(NULL),
+  iPeerThread(NULL),
+  iSyncLocalCall(EFalse)
+	{
+	}
+
+EXPORT_C TRequestWrapper::TRequestWrapper(TRequestStatus& aStatus, TBool aSyncLocalCall)
+: iStatus(&aStatus),
+  iPeerThread(NULL),
+  iSyncLocalCall(aSyncLocalCall)
+	{
+	}
+
+/**
+Complete the request in a way relevant to the context:
+    -# If within the same thread, just set the status member as value is later used further up in the call stack.
+	-# If within other thread, use "real" completion function to signal status/thread semaphore.
+*/
+EXPORT_C void TRequestWrapper::RequestComplete(TInt aReason)
+	{
+	if(iSyncLocalCall)
+		{
+		LOG(ESockLog::Printf(KESockSessDetailTag, _L8("TRequestWrapper::RequestComplete() %08x = %d (local, synchronous)"), iStatus, aReason));
+		// Don't need to complete request since it's a synchronous call down within
+		// the requesting thread, which isn't waiting for the return
+		__ASSERT_DEBUG(iStatus, User::Panic(KSpecAssert_ESockSSocks_thrd, 18));	// no valid case for completing twice
+		*iStatus = aReason;
+		iStatus = NULL;
+		}
+	else
+		{
+		LOG(ESockLog::Printf(KESockSessDetailTag, _L8("TRequestWrapper::RequestComplete() %08x = %d (requester tid = %x)"), iStatus, aReason, static_cast<TUint>(iPeerThread->Id())));
+		iPeerThread->RequestComplete(iStatus, aReason);
+		}
+	}
+
+//
+// TTransportUser
+//
+TInt TTransportUser::Compare(const TTransportUser& aLHS, const TTransportUser& aRHS)
+	{
+	if(aLHS.iInterfaceId < aRHS.iInterfaceId)
+		{
+		return -1;
+		}
+	else if(aLHS.iInterfaceId > aRHS.iInterfaceId)
+		{
+		return 1;
+		}
+	return 0;
+	}
+
+/**
+The socket server thread. This is where control will resume when the RootServer starts an
+ESock instance.
+This function creates the worker thread object and starts the active scheduler.
+*/
+EXPORT_C TInt CWorkerThread::ThreadEntrypoint(TAny* aArg)
+	{
+#if defined (__EPOC32__)
+	// Ensure that our thread has a handle on this library.
+	RLibrary sockLib;
+	__ASSERT_ALWAYS(sockLib.Load(ESOCK_SERVER_LIB_NAME)==KErrNone, Fault(ESvrStartServer));
+#endif
+
+	CTrapCleanup* tc=CTrapCleanup::New();
+	if (!tc)
+		{
+		return KErrNoMemory;
+		}
+
+	CSocketScheduler* ss = CSocketScheduler::New();
+
+	TCFModuleInfo* moduleInfo = static_cast<TCFModuleInfo*>(aArg);
+
+	/* We instantiate our CWorkerThread here, but it is deleted through the
+	destruction of SockManGlobals by the CShutdownWatchdog. */
+	TRAPD(res, CWorkerThread::NewL(moduleInfo));
+	if(res != KErrNone)
+		{
+		return res;
+		}
+
+	RThread::Rendezvous(KErrNone);
+	CActiveScheduler::Start();
+
+	/**
+	   This is deleted here because the globals can theoretically be used
+	   at any time up until when the active scheduler stops */
+	CSockManData* globals=SockManGlobals::Get();
+	delete globals;
+	globals = NULL;
+
+	REComSession::FinalClose();
+
+	TInt returncode=KErrNone;
+
+	delete tc;
+	delete ss;
+	return returncode;
+	}
+
+