diff -r 000000000000 -r dfb7c4ff071f datacommsserver/esockserver/ssock/ss_roles.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/datacommsserver/esockserver/ssock/ss_roles.cpp Thu Dec 17 09:22:25 2009 +0200 @@ -0,0 +1,1920 @@ +// 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_roles.h" + +#include +#include +#include +#include +#include +#include // logging tags +#include "ss_subconn.h" +#include +#include +#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS +#include +#endif +#include "SS_rslv.H" +#include "SS_conn.H" +#include +#include +#include +#include + +#include //for KNifEntireConnectionSubConnectionId +#include //CResponseMsg +#include "ss_connectionsession.h" +#include "ss_flowrequest.h" +#include "ss_msgs.h" +#include +#include + +#include "ss_connectionserver.h" +#include "ss_tierthreadmap.h" + +#include +#ifdef SYMBIAN_ZERO_COPY_NETWORKING +#include +#else +#include +#endif // SYMBIAN_ZERO_COPY_NETWORKING + + + +#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_rls, "ESockSSocks_rls."); +#endif + +using ESock::MProviderSelector; +using ESock::ISelectionNotify; + +#define MSG_PRM(prmIndex) (prmIndex) +using namespace NetInterfaces; + +using namespace NetInterfaces; +using namespace ESock; +using namespace Messages; +using namespace Den; +using namespace CommsFW; + +#include + + + +// +// CPlayer +// + +CPlayer* CPlayer::NewL(CWorkerThread* aOwnerThread, TPlayerRole aPlayerRole) + { + CPlayer* self = new(ELeave) CPlayer(aOwnerThread, aPlayerRole); + return self; + } + +/** +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. +*/ +CPlayer::~CPlayer() + { + // 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) + LOG(ESockLog::Printf(KESockBootingTag, _L8("CPlayer::~CPlayer()"))); + iTransferBuffer.Close(); + } + +CPlayer::CPlayer(CWorkerThread* aOwnerThread, TPlayerRole aPlayerRole) +: CCommonPlayer(aOwnerThread, aPlayerRole) + { + LOG(ESockLog::Printf(KESockBootingTag, _L8("CPlayer::CPlayer()"))); + } + +CSockSubSession* CPlayer::SubSession(const Den::TSubSessionUniqueId& aSubSessionUniqueId) const + { + return static_cast(CCommonPlayer::SubSession(aSubSessionUniqueId)); + } + +CSockSession* CPlayer::CurrentSession() const + { + return static_cast(CCommonPlayer::Session()); + } + +CWorkerThread& CPlayer::WorkerThread() const + { + return static_cast(CCommonPlayer::WorkerThread()); + } + +/** +Check whether the Player is ready to shut down and if so, tells the owning Worker Thread +*/ +TBool CPlayer::IsPlayerShutdownComplete() + { + CSockManData* globals = SockManGlobals::Get(); + // Basic reason: we have no subsessions or protocol families remaining open + TBool shutdownComplete = globals->iNumFamilies == 0; + LOG_STMT(TBool dataThreadShutdownPending = EFalse;) + if(shutdownComplete) + { + // But it could be that we're providing management services and have a data thread still bound; if so we hold-off from shutdown + // until the data thread completes its own shutdown. Being relatively dumb it certainly can't survive without us. + if(!HasDataPlane() && !SockManGlobals::Get()->GetPlaneFC(TCFPlayerRole(TCFPlayerRole::EDataPlane)).IsNull()) + { + shutdownComplete = EFalse; + LOG_STMT(dataThreadShutdownPending = ETrue;) + } + } + + LOG(ESockLog::Printf(KESockBootingTag, _L8("CPlayer::MaybeSetPlayerShutdownComplete(), shutdownComplete = %d [#subSess=%d, numFam=%d, dataPending=%d]"), + shutdownComplete, SubSessions().Count(), globals->iNumFamilies, dataThreadShutdownPending) ); + + if(globals->iNumFamilies) + { + // Help them figure out what (might be) blocking shutdown +#ifdef __FLOG_ACTIVE + TProtocolManagerLogger::LogLoadedInfo(); +#endif + } + + return shutdownComplete; + } + +/** +Called directly by the PitBoss to get protocol information. +@note Index is 1-based for legacy reasons. +*/ +TInt CPlayer::ProtocolInfo(TUint aIndex, TProtocolDesc& aProtocol) + { + // This could be optimised, eg to remember the last found position in case caller is iterating from 1..n protocols (since the + // code here also iterates that gives O2 complexity). However, it is likely that this is a seldom-used function + if((aIndex > SockManGlobals()->iNumProtocols) || aIndex<1) + { + return KErrNotSupported; + } + + // Simply iterate along the queue aIndex times + TSglQueIter i(*SockManGlobals()->iProtocols); + + while(--aIndex) + { + i++; + } + + aProtocol=((CProtocolRef*)i)->Info(); + + return KErrNone; + } + +/** +Write a handle back to Ptr3 of the current message +*/ +TInt CPlayer::WriteSubSessionHandle(TInt aHandle) + { + TPckgC pH(aHandle); + return SafeMessage().Write(MSG_PRM(3),pH); + } + +void CPlayer::CommsApiExtBindIfaceL(const RMessage2& aMessage, CSockSubSession& aSubSession) + { + aSubSession.CommsApiExtBindIfaceL(aMessage); + } + +void CPlayer::CommsApiExtIfaceSendReceiveL(const RMessage2& aMessage, CSockSubSession& aSubSession) + { + aSubSession.CommsApiExtIfaceSendReceiveL(aMessage); + } + +void CPlayer::CloseExtensionInterface(const RMessage2& aMessage, CSockSubSession& aSubSession) + { + aSubSession.CloseExtensionInterface(aMessage); + } + +/** +Called from a socket to delete it +Removing a socket has the side effect of Close()ing it. +*/ +void CPlayer::DeleteSocket(CSocket& aSocket) + { + aSocket.InitiateDestruction(); + } + + + +MShimControlClient* CPlayer::CSubConnectionProviderFromHandleL(ESock::CConnection& /*aConnection*/, TSubConnectionUniqueId /*aSubConnectionUniqueId*/) + {//this is a very link shim specific function to send a message to the shim connection provider factory + LOG( ESockLog::Printf(_L("CPlayer %08x:\tCSubConnectionProviderFromHandleL() KErrNotReady"), this) ); + User::Leave(KErrNotReady); + return NULL; + } + +/** + Process the client message forwarded from the Dealer. For subsession-Close commands + the Dealer provides the subsession pointer explicitly as it has already been removed from + the index. +*/ +void CPlayer::DoProcessMessageL(const RSafeMessage& aMessage, Den::CWorkerSubSession* aSubSession) + { + LOG( + TBuf8<64> messBuf; + ESockLog::IPCMessName((TSockMess) aMessage.Function(), messBuf); + ESockLog::Printf(KESockSessDetailTag, _L8("CPlayer:\tProcessMessageL: session=%08x, subsess=%08x, Message(%08x) [%S]"), Session(), aSubSession, aMessage.Handle(), &messBuf); + ); + + switch (aMessage.Function()) + { + case ESoCreateNull: + { + TInt dummyHandle; + NewSocketL(ETrue, dummyHandle); + break; + } + +// case ESSNumProtocols: +// NumProtocols(); +// break; + case ESSProtocolInfo: + ProtocolInfo(); + break; + + case ESSProtocolInfoByName: + ProtocolInfoByName(); + break; + + case ESSProtocolStart: + LoadProtocolL(aMessage.Int0(),aMessage.Int1(),aMessage.Int2()); + break; + + case ESSProtocolStop: + UnLoadProtocolL(aMessage.Int0(),aMessage.Int1(),aMessage.Int2()); + break; + +// socket messages + case ESoCreate: + NewSocketDefaultL(); + break; + + case ESoCreateWithConnection: + NewSocketWithConnectionL(); + break; + + case ESoCreateWithSubConnection: + NewSocketWithSubConnectionL(); + break; + + case ESoTransfer: + __ASSERT_DEBUG(aSubSession->Type().iType == TCFSubSessInfo::ESocket, User::Panic(KSpecAssert_ESockSSocks_rls, 2)); // Dealer does all of the validation for us + TransferSocketL(static_cast(aSubSession)); + break; + +// Host resolver message types + case EHRCreate: + NewHostResolverDefaultL(aMessage.Int0(),aMessage.Int1()); + break; + + case EHRCreateWithConnection: + NewHostResolverWithConnectionL(aMessage.Int0(),aMessage.Int1(), aMessage.Int2()); + break; + +// Service resolver message types + case ESRCreate: + NewServiceResolverL(aMessage.Int0(),aMessage.Int1(),aMessage.Int2()); + break; + +// Net database message types + case ENDCreate: + NewNetDatabaseL(aMessage.Int0(),aMessage.Int1()); + break; + + // Connection Messages + case ECNCreate: + { + NewConnectionL(); + break; + } + case ECNCreateWithName: + { + NewConnectionWithNameL(static_cast(aSubSession)); + break; + } +/* + case ESCPSStop: + case ESCPSProgressNotification: + case ESCPSCancelProgressNotification: + case ESCPSDataTransferred: + case ESCPSDataTransferredCancel: + case ESCPSDataSentNotificationRequest: + case ESCPSDataSentNotificationCancel: + case ESCPSDataReceivedNotificationRequest: + case ESCPSDataReceivedNotificationCancel: + case ESCPSIsSubConnectionActiveRequest: + case ESCPSIsSubConnectionActiveCancel: + case ESCPSGetSubConnectionInfo: + { + __ASSERT_DEBUG(aSubSession, User::Panic(KSpecAssert_ESockSSocks_rls, 3)); + ESock::CConnection* cn = static_cast(aSubSession); + TSubConnectionUniqueId subConnectionUniqueId = aMessage.Function() != ESCPSGetSubConnectionInfo ? static_cast(aMessage.Int0()) : KNifEntireConnectionSubConnectionId; + MShimControlClient* sc = CSubConnectionProviderFromHandleL(*cn, subConnectionUniqueId); + if (!sc) + { + LOG( ESockLog::Printf(_L("CPlayer %08x:\tProcessMessageL() ESCPS...... KErrNotReady"), this) ); + User::Leave(KErrNotReady); + } + __ASSERT_DEBUG(EFalse, User::Panic(KSpecAssert_ESockSSocks_rls, 4)); + //@TODO service RConnection sub-connection oriented calls + //iComplete = CSubConnection::ServiceL(*sc,aMessage); + + break; + } +*/ + case ESCCreate: + { + LOG( ESockLog::Printf(KESockSubConnectionTag, _L8("CSubConnection %08x\tESCCreate aMessage %08x"), this, &SafeMessage()) ); + ESock::CSubConnection* sc = NewSubConnectionWithConnectionL(); + __ASSERT_DEBUG(sc, User::Panic(KSpecAssert_ESockSSocks_rls, 5)); + //If successful, the ownership of the RMessage2 is taken by the activity, otherwise completed on leave + //The activity also takes ownership of the CSubConnection for the duration of the activity for cleanup purposes + sc->CMMSockSubSession::ReceivedL(ESCCreate, ESock::TCFInternalEsock::TSubSess(ESCCreate,SafeMessage()).CRef()); + break; + } + + case ECommsApiExtBindIface: + __ASSERT_DEBUG(aSubSession, User::Panic(KSpecAssert_ESockSSocks_rls, 6)); + CommsApiExtBindIfaceL(aMessage, static_cast(*aSubSession)); + break; + + case ECommsApiExtIfaceSend: + case ECommsApiExtIfaceSendReceive: + __ASSERT_DEBUG(aSubSession, User::Panic(KSpecAssert_ESockSSocks_rls, 7)); + CommsApiExtIfaceSendReceiveL(aMessage, static_cast(*aSubSession)); + break; + + case ECommsApiExtIfaceClose: + { + CloseExtensionInterface(aMessage, static_cast(*aSubSession)); + break; + } + + default: + __ASSERT_DEBUG(aSubSession, User::Panic(KSpecAssert_ESockSSocks_rls, 8)); + + aSubSession->ProcessMessageL(); + + break; + } + + // Message handlers can change the state of the iMessage if they want to hold onto the message. + // They can also write a return value to iReturn. + if (iComplete) + { + LOG(ESockLog::Printf(KESockServerTag, _L8("CPlayer:\tProcessMessageL, session=%08x, RMessage2::Complete (%08x) with %d."), Session(), aMessage.Handle(), iReturn) ); + aMessage.Complete(Return()); + } + + //This is a normal return so we do no longer need the reference + SetSession(NULL); + } + +/** +Get info for a protocol by index. +*/ +void CPlayer::ProtocolInfo() + { + TProtocolDesc prot; + TInt ret=0; + TInt localIndex = static_cast(PitBoss()).GetLocalProtocolIndex(SafeMessage().Int1()); + if((ret=ProtocolInfo(localIndex,prot))==KErrNone) + { + TPckgC p(prot); + ret = SafeMessage().Write(MSG_PRM(0),p); + } + SetReturn(ret); + } + +/** +Get protocol info by name. +*/ +void CPlayer::ProtocolInfoByName() + { + TProtocolName name; + TServerProtocolDesc prot; + TInt ret=0; + + ret = SafeMessage().Read(MSG_PRM(1),name); + + if((ret=ProtocolInfo(name, prot))==KErrNone) + { + TPckgC p(prot); + ret = SafeMessage().Write(MSG_PRM(0),p); + } + SetReturn(ret); + } + +/** +Find a protocol by name - no wildcard support. +*/ +TInt CPlayer::ProtocolInfo(const TDesC &aName, TServerProtocolDesc &aProtocol) + { + TSglQueIter i(*SockManGlobals::Get()->iProtocols); + + // Run the queue looking for a match. + do + { + if(((CProtocolRef *)i)->Info().iName.CompareF(aName)==0) + { + aProtocol=((CProtocolRef*)i)->Info(); + return KErrNone; + } + i++; + } + while((CProtocolRef *)i); + + return KErrNotFound; + } + + +/** +The socket reference is the object name preceded by the Worker's id and a space. This is +intended as an opaque cookie for use with socket transfer. +*/ +void CPlayer::TransferSocketL(CSocket* aSocket) + { + __ASSERT_DEBUG(&aSocket->Player() == this, User::Panic(KSpecAssert_ESockSSocks_rls, 9)); + + TInt id; + TInt ret; + + { + ProtocolManager::TransferSocketL(aSocket, this); + aSocket->CancelAll(); + + CurrentSession()->SubSessions().Lock(); + + ret = PitBoss().AddSubSession(aSocket, CurrentSession(), id); + aSocket->SetUniqueId(PitBoss().NextSubSessionUniqueId()); + + CurrentSession()->SubSessions().Unlock(); + } + + if(ret == KErrNone) + { + { + aSocket->Session()->SubSessions().Lock(); + + // Remove from original session + CSubSessionIx& srcSubSessions = aSocket->Session()->SubSessions(); + TInt oldIndex; + VERIFY_RESULT(srcSubSessions.Find(aSocket, oldIndex), KErrNone); + PitBoss().RemoveSubSession(oldIndex, static_cast(aSocket->Session())); + + aSocket->Session()->SubSessions().Unlock(); + } + + aSocket->SetSessionProxy(NULL); + aSocket->SetSession(CurrentSession()); + aSocket->SetSessionProxy(CurrentSessionProxyL()); + ret = WriteSubSessionHandle(id); + if (ret != KErrNone) + { + aSocket->InitiateDestruction(); // this should leave socket (almost) dead; we're not trying to reverse the transfer + } + if(ret == KErrNone && aSocket->RequiresOwnerInfo()) + { + // store information about the client that created this socket + aSocket->StoreOwnerInfo(); + aSocket->CommunicateOwner(); + } + } + if(ret != KErrNone) + { + User::Leave(ret); + } + } + +/** +Create a new empty socket to be used for accept. +We need to make empty sockets just to get a valid handle to accept into. +*/ +CSocket* CPlayer::NewSocketL(TBool aCompleteClientRequest, TInt& aHandle) + { + LOG(ESockLog::Printf(KESockSessDetailTag, _L8("CPlayer::NewSocketL(null sock)")) ); + + CSocket *sp = NULL; + TInt ret; + + { + + // Create a new socket and add it our list of subsessions + sp = CSocket::NewLC( + NULL, + CurrentSession(), + this, + 0, + PitBoss().NextSubSessionUniqueId(), + KUndefinedSockType); + + SubSessions().AppendL(sp); + CleanupStack::Pop(); + + // Now add the new socket, itself a subsession, to the global store + CurrentSession()->SubSessions().Lock(); + ret = PitBoss().AddSubSession(sp, CurrentSession(), aHandle); + if(ret == KErrNone) + { + if(aCompleteClientRequest) + { + ret = WriteSubSessionHandle(aHandle); + } + if(ret != KErrNone) + { + PitBoss().RemoveSubSession(aHandle, CurrentSession()); + } + } + + CurrentSession()->SubSessions().Unlock(); + } + + if(ret != KErrNone) + { + sp->InitiateDestruction(); + User::Leave(ret); + } + + return sp; + } + +/** +Create a new socket on this session for normal Open. The Player adds the new socket to +the session owned by the dealer (uses explicitly locked access). +*/ +CSocket* CPlayer::NewSocketL(TUint aAddrFamily, TUint aSocketType, TUint aProtocol) + { + LOG(ESockLog::Printf(KESockSessDetailTag, _L8("CPlayer:\tNewSocketL(family=%d, type=%d, protocol=%d"), aAddrFamily, aSocketType, aProtocol) ); + + CSocket* sp = NULL; + TInt ret; + + { + + // Look up the protocol for which we are creating a socket + CProtocolRef* protocolReference = ProtocolManager::FindProtocolL(aAddrFamily, aSocketType, aProtocol); + + // Create a new socket and add it our list of subsessions + sp = CSocket::NewLC( + &(protocolReference->Info()), + CurrentSession(), + this, + protocolReference->Protocol(), + PitBoss().NextSubSessionUniqueId(), + aSocketType); + + SubSessions().AppendL(sp); + CleanupStack::Pop(); + + CurrentSession()->SubSessions().Lock(); + TInt id; + ret = PitBoss().AddSubSession(sp, CurrentSession(), id); + if(ret == KErrNone) + { + ret = WriteSubSessionHandle(id); + if(ret != KErrNone) + { + // V1 ESock didn't remove after failure, but did for the other overload of NewSocketL(). + // Believed to have been in error + PitBoss().RemoveSubSession(id, CurrentSession()); + } + } + + CurrentSession()->SubSessions().Unlock(); + } + + if(sp->RequiresOwnerInfo()) + { + // store information about the client that created this socket, to + // communicate to the flow once we're bound + sp->StoreOwnerInfo(); + } + + if(ret != KErrNone) + { + sp->InitiateDestruction(); + User::Leave(ret); + } + + return sp; + } + +/** +Create a new socket on this session for normal Open. The Player adds the new socket to +the session owned by the dealer (uses explicitly locked access). +*/ +void CPlayer::NewSocketDefaultL() + { + TUint aAddrFamily = SafeMessage().Int0(); + TUint aSocketType = SafeMessage().Int1(); + TUint aProtocol = SafeMessage().Int2(); + + LOG(ESockLog::Printf(KESockSessDetailTag, _L8("CPlayer:\tNewSocketDefaultL(family=%d, type=%d, protocol=%d"), aAddrFamily, aSocketType, aProtocol) ); + CSocket* s = NewSocketL(aAddrFamily, aSocketType, aProtocol); + + TFlowParams flowParams( + aAddrFamily, + aSocketType, + aProtocol, + TFlowParams::EImplicit, + CurrentSessionProxyL()); + + //requesting a lower flow by throwing a self-dispatcher (TCFImplicitFlowRequest) through the + //fence (onto the control plane). TCFImplicitFlowRequest assumes no preexiting stack (hence implicit) + //and it will select and start a suitable stack based on the default access point. + //That stack will be interrogated for a suitable flow (flowParams), which will be returned back + //to the sender (socket) with TBindTo + RClientInterface::OpenPostMessageClose(s->Id(), //socket is the sender + SockManGlobals::Get()->GetPlaneFC(TCFPlayerRole(TCFPlayerRole::ETierMgrPlane)), //phoney recipient - we only care the recipient is on the control plane so that this message is dispatched on the correct thread + TCFImplicitFlowRequest(s->UniqueId(), flowParams)); + + s->SetFlowRequestPending(ETrue); + s->AdoptFlowRequestMessage(SafeMessage()); + + DontCompleteCurrentRequest(); + } + +/** +Create a new socket on this session for normal Open +*/ +void CPlayer::NewSocketWithConnectionL() + { + // If we don't host the data plane then the request was forwarded to us to determine which data thread shall create the socket + if(!HasDataPlane()) + { + Messages::TNodeId flowFC = SockManGlobals()->GetPlaneFC(TCFPlayerRole(TCFPlayerRole::EDataPlane)); + __ASSERT_DEBUG(!flowFC.IsNull(), User::Panic(KSpecAssert_ESockSSocks_rls, 10)); + TPlayerForwardRequestMsg msg(SafeMessage(), TPlayerForwardRequestMsg::NormalCreationFlag()); + WorkerThread().PostMessage(flowFC.Thread(), msg); + DontCompleteCurrentRequest(); + return; + } + + TPckgBuf argPkg; + SafeMessage().ReadL(MSG_PRM(0),argPkg); + + //!!PS remember the race conditions in case someone closes connection in control thread + //(Lock on the sub-session queue should persist over the code accessing cn* + CSocket* s = NewSocketL(argPkg().iAddrFamily, argPkg().iSockType, argPkg().iProtocol); + + // Send the flow request message to the connection plane + TFlowParams flowParams( + argPkg().iAddrFamily, + argPkg().iSockType, + argPkg().iProtocol, + TFlowParams::EExplicitConnection, + CurrentSessionProxyL()); + + //requesting a lower flow by throwing a self-dispatcher (TCFConnFlowRequest) through the + //fence (onto the control plane). TCFConnFlowRequest will interrogate the connection (argPkg().iHandle) + //for its default subconnection and the subconnection for a suitable (flowParams) flow. + //The flow will be returned back to the sender (socket) with TBindTo. + RClientInterface::OpenPostMessageClose(s->Id(), //socket is the sender + SockManGlobals::Get()->GetPlaneFC(TCFPlayerRole(TCFPlayerRole::EConnPlane)), //phoney recipient - we only care the recipient is on the control plane, so that this message will be dispatched on a correct thread. + TCFConnFlowRequest(s->UniqueId(), *CurrentSession(), argPkg().iHandle, flowParams)); + + s->SetFlowRequestPending(ETrue); + s->AdoptFlowRequestMessage(SafeMessage()); + + DontCompleteCurrentRequest(); + } + +/** +Create a new socket on this session for normal Open. +*/ +void CPlayer::NewSocketWithSubConnectionL() + { + // If we don't host the data plane then the request was forwarded to us to determine which data thread shall create the socket + if(!HasDataPlane()) + { + Messages::TNodeId flowFC = SockManGlobals()->GetPlaneFC(TCFPlayerRole(TCFPlayerRole::EDataPlane)); + __ASSERT_DEBUG(!flowFC.IsNull(), User::Panic(KSpecAssert_ESockSSocks_rls, 11)); + TPlayerForwardRequestMsg msg(SafeMessage(), TPlayerForwardRequestMsg::NormalCreationFlag()); + WorkerThread().PostMessage(flowFC.Thread(), msg); + DontCompleteCurrentRequest(); + return; + } + + TPckgBuf argPkg; + SafeMessage().ReadL(MSG_PRM(0),argPkg); + + LOG(ESockLog::Printf(KESockSessDetailTag, _L8("CPlayer:\tNewSocketWithSubConnectionL(family=%d, type=%d, protocol=%d"), argPkg().iAddrFamily, argPkg().iSockType, argPkg().iProtocol) ); + CSocket* s = NewSocketL(argPkg().iAddrFamily, argPkg().iSockType, argPkg().iProtocol); + + // Send the flow request message to the connection plane + TFlowParams flowParams( + argPkg().iAddrFamily, + argPkg().iSockType, + argPkg().iProtocol, + TFlowParams::EExplicitSubConnection, + CurrentSessionProxyL()); + + //requesting a lower flow by throwing a self-dispatcher (TCFSubConnFlowRequest) through the + //fence (onto the control plane). TCFConnFlowRequest will interrogate the subconnection (argPkg().iHandle) + //for a suitable (flowParams) flow. The flow will be returned back to the sender (socket) with TBindTo. + RClientInterface::OpenPostMessageClose(s->Id(), //socket is the sender + SockManGlobals::Get()->GetPlaneFC(TCFPlayerRole(TCFPlayerRole::ESubConnPlane)), //phoney recipient - we only care the recipient is on the control plane so that this message is dispatched on the correct thread + TCFSubConnFlowRequest(s->UniqueId(), *CurrentSession(), argPkg().iHandle, flowParams)); + + s->SetFlowRequestPending(ETrue); + s->AdoptFlowRequestMessage(SafeMessage()); + + DontCompleteCurrentRequest(); + } + +/** +Create a new host resolver for this session. +*/ +CHostResolver* CPlayer::NewHostResolverL(TUint anAddrFamily,TUint aProtocol) + { + CHostResolver* h = NULL; + TInt ret; + + { + + h=ProtocolManager::NewHostResolverL(anAddrFamily,aProtocol, this, PitBoss().NextSubSessionUniqueId()); + + TInt id; + CurrentSession()->SubSessions().Lock(); + ret = PitBoss().AddSubSession(h, CurrentSession(), id); + if(ret == KErrNone) + { + ret = WriteSubSessionHandle(id); + if(ret != KErrNone) + { + PitBoss().RemoveSubSession(id, CurrentSession()); + } + } + CurrentSession()->SubSessions().Unlock(); + } + + if(ret != KErrNone) + { + h->InitiateDestruction(); + User::Leave(ret); + } + + if(h->RequiresOwnerInfo()) + { + // store information about the client that created this host resolver + h->StoreOwnerInfo(); + } + + return (h); + } + +/** +Create a new host resolver for this session. +*/ +void CPlayer::NewHostResolverDefaultL(TUint aAddrFamily,TUint aProtocol) + { + CHostResolver* h = NewHostResolverL(aAddrFamily, aProtocol); + + // Send the flow parameters to the tier manager plane + TFlowParams flowParams( + aAddrFamily, + KUndefinedSockType, + aProtocol, + TFlowParams::EImplicit, + CurrentSessionProxyL(), + EFalse); + + //requesting a lower flow by throwing a self-dispatcher (TCFImplicitFlowRequest) through the + //fence (onto the control plane). TCFImplicitFlowRequest assumes no preexiting stack (hence implicit) + //and it will select and start a suitable stack based on the default access point. + //That stack will be interrogated for a suitable flow (flowParams), which will be returned back + //to the sender (host resolver) with TBindTo + RClientInterface::OpenPostMessageClose(h->Id(), //socket is the sender + SockManGlobals::Get()->GetPlaneFC(TCFPlayerRole(TCFPlayerRole::ETierMgrPlane)), //phoney recipient - we only care the recipient is on the control plane so that this message is dispatched on the correct thread + TCFImplicitFlowRequest(h->UniqueId(), flowParams)); + + h->SetFlowRequestPending(ETrue); + h->AdoptFlowRequestMessage(SafeMessage()); + + DontCompleteCurrentRequest(); + } + +/** +Create a new host resolver for this session. +*/ +void CPlayer::NewHostResolverWithConnectionL(TUint aAddrFamily, TUint aProtocol, TInt aHandle) + { + CHostResolver* h = NewHostResolverL(aAddrFamily, aProtocol); + + // Send the flow parameters to the tier manager plane + TFlowParams flowParams( + aAddrFamily, + KUndefinedSockType, + aProtocol, + TFlowParams::EExplicitConnection, + CurrentSessionProxyL(), + EFalse); + + //requesting a lower flow by throwing a self-dispatcher (TCFConnFlowRequest) through the + //fence (onto the control plane). TCFConnFlowRequest will interrogate the connection (aHandle) + //for its default subconnection and the subconnection for a suitable (flowParams) flow. + //The flow will be returned back to the sender (socket) with TBindTo. + RClientInterface::OpenPostMessageClose(h->Id(), //socket is the sender + SockManGlobals::Get()->GetPlaneFC(TCFPlayerRole(TCFPlayerRole::EConnPlane)), //phoney recipient - we only care the recipient is on the control plane, so that this message will be dispatched on a correct thread. + TCFConnFlowRequest(h->UniqueId(), *CurrentSession(), aHandle, flowParams)); + + h->SetFlowRequestPending(ETrue); + h->AdoptFlowRequestMessage(SafeMessage()); + + DontCompleteCurrentRequest(); + } + + +/** +Create a new service resolver for this session. +*/ +void CPlayer::NewServiceResolverL(TUint anAddrFamily,TUint aSocketType,TUint aProtocol) + { + CServiceResolver* r = NULL; + TInt ret; + + { + + r=ProtocolManager::NewServiceResolverL(anAddrFamily, aSocketType, aProtocol, this, PitBoss().NextSubSessionUniqueId()); + + TInt id; + CurrentSession()->SubSessions().Lock(); + ret = PitBoss().AddSubSession(r, CurrentSession(), id); + if(ret == KErrNone) + { + ret = WriteSubSessionHandle(id); + if(ret != KErrNone) + { + PitBoss().RemoveSubSession(id, CurrentSession()); + } + } + CurrentSession()->SubSessions().Unlock(); + } + + if (ret != KErrNone) + { + delete r; + User::Leave(ret); + } + } + +/** +Create a new net database for this session. +*/ +void CPlayer::NewNetDatabaseL(TUint anAddrFamily,TUint aProtocol) + { + CNetDatabase* n = NULL; + TInt ret; + + { + + n=ProtocolManager::NewNetDatabaseL(anAddrFamily, aProtocol, this, PitBoss().NextSubSessionUniqueId()); + + TInt id; + CurrentSession()->SubSessions().Lock(); + ret = PitBoss().AddSubSession(n, CurrentSession(), id); + if(ret == KErrNone) + { + ret = WriteSubSessionHandle(id); + if(ret != KErrNone) + { + PitBoss().RemoveSubSession(id, CurrentSession()); + } + } + + CurrentSession()->SubSessions().Unlock(); + } + + if (ret!=KErrNone) + { + delete n; + User::Leave(ret); + } + } + +/** +Create a new socket on this session for normal Open. +*/ +CSubConnection* CPlayer::NewSubConnectionWithConnectionL() + { + TPckgBuf argPkg; + SafeMessage().ReadL(MSG_PRM(0),argPkg); + + ESock::CConnection* cn=static_cast(CurrentSession())->CConnectionFromHandle(argPkg().iHandle); + if (!cn) + { + PanicClient(KESockClientPanic, ESockBadHandle); + return NULL; + } + + if (!cn->ServiceProvider()) + { + LOG( ESockLog::Printf(_L("CPlayer %08x:\tNewSubConnectionWithConnectionL() KErrNotReady"), this) ); + User::Leave(KErrNotReady); + } + + return NewSubConnectionL(*cn); + } + +/** +Create a new sub-connection for this session. +*/ +CSubConnection* CPlayer::NewSubConnectionL(ESock::CConnection& aConnection) + { + ESock::CSubConnection* subconn = NULL; + TInt ret; + + { + + subconn = ESock::CSubConnection::NewL(aConnection, this, PitBoss().NextSubSessionUniqueId()); + ret = SubSessions().Append(subconn); + if(ret == KErrNone) + { + CurrentSession()->SubSessions().Lock(); + TInt id; + TInt ret = PitBoss().AddSubSession(subconn, CurrentSession(), id); + if(ret == KErrNone) + { + ret = WriteSubSessionHandle(id); + if(ret != KErrNone) + { + PitBoss().RemoveSubSession(id, CurrentSession()); + } + } + CurrentSession()->SubSessions().Unlock(); + } + + } + + if (ret!=KErrNone) + { + delete subconn; + User::Leave(ret); + } + + return subconn; + } + +void CPlayer::NewConnectionL() + { + TUint family = SafeMessage().Int0(); + TUid tierId = TierManagerUtils::MapTierIdsL(TUid::Uid(family), 0); + ESock::CConnection* conn = ESock::CConnection::NewLC(static_cast(CurrentSession()), this, tierId, PitBoss().NextSubSessionUniqueId()); + SetupNewConnectionL(conn); + CleanupStack::Pop(conn); + } + +void CPlayer::NewConnectionWithNameL(CSockSubSession* aSubSession) + { + // The passed subsession is the original to base the creation off + __ASSERT_DEBUG(aSubSession->Type().iType == TCFSubSessInfo::EConnection, User::Panic(KSpecAssert_ESockSSocks_rls, 12)); // Dealer does all of the validation for us + + CConnection& origConn = static_cast(*aSubSession); + // Police the clone open request against the security policy stored applied to the original RConnection + User::LeaveIfError(origConn.CheckCloneOpenPolicy(SafeMessage())); + ESock::CConnection* conn = ESock::CConnection::NewLC(static_cast(CurrentSession()), this, origConn, PitBoss().NextSubSessionUniqueId()); + SetupNewConnectionL(conn); + CleanupStack::Pop(conn); + } + +/** +Handle creation of a new connection for this session +*/ +void CPlayer::SetupNewConnectionL(CConnection* aConn) + { + TInt ret; + + { + + SubSessions().AppendL(aConn); + TInt id; + CurrentSession()->SubSessions().Lock(); + ret = PitBoss().AddSubSession(aConn, CurrentSession(), id); + if(ret == KErrNone) + { + ret = WriteSubSessionHandle(id); + if(ret != KErrNone) + { + PitBoss().RemoveSubSession(id, CurrentSession()); + } + } + + CurrentSession()->SubSessions().Unlock(); + } + + if(ret!=KErrNone) + { + User::Leave(ret); + } + + // store information about the client that created this connection + // note that CConnections should always store their owner info + aConn->StoreOwnerInfo(); + } + +CCommonSessionProxy* CPlayer::DoCreateSessionProxyL(CWorkerSession* aSession) + { + return CSockSessionProxy::NewL(aSession, *this); + } + +void CPlayer::LoadProtocolL(TUint anAddrFamily,TUint aSocketType,TUint aProtocol) + { + ProtocolManager::LoadProtocolL(anAddrFamily,aSocketType,aProtocol,this); + } + +/** + Find the protocol, check it is not referenced and delete it +*/ +void CPlayer::UnLoadProtocolL(TUint anAddrFamily,TUint aSocketType,TUint aProtocol) + { + ProtocolManager::UnLoadProtocolL(anAddrFamily, aSocketType, aProtocol,this); + } + +/** Provides a sizeable buffer for strictly temporary use, eg within current stack frame: there's only one buffer + * and no protocol for returning so shared use can't be detected: the intended use is for transferring data to/from + * a client. + * @return pointer to a buffer of at least the requested size, or NULL if this proves impossible + */ +TDes8* CPlayer::BorrowTemporaryBuffer(TInt aSize) + { + if(iTransferBuffer.Size() < aSize) + { + if(iTransferBuffer.ReAlloc(aSize) != KErrNone) + { + return NULL; + } + } + iTransferBuffer.SetMax(); // indicate whole of buffer available when used for MBuf CopyOut() - in general overwrite not append is expected since it's a temporary buffer + return &iTransferBuffer; + } + + +#ifdef _DEBUG +TBool CPlayer::RunPostBootChecks() + { + CWorkerThread& owner = WorkerThread(); + if(owner.DefaultOptimalDealer()) + { + // Now that booting is completed verify that all protocols can be created via this dealer, ie that + // our worker thread is bound to all of the necessary players + TWorkerId host; + for(TInt idx = 1; owner.PitBoss().GetWorkerForProtocol(idx, host); ++idx) + { + if(!owner.PeerReachable(host)) + { + RDebug::Printf("ERROR worker %d is DefaultOptimalDealer but can't reach worker #%d for protocol #%d", owner.WorkerId(), host, idx); + LOG(ESockLog::Printf(KESockBootingTag, _L("ERROR worker %d is DefaultOptimalDealer but can't reach worker #%d for protocol #%d"), owner.WorkerId(), host, idx)); + Panic(EMisconfigured); + } + } + } + return ETrue; + } +#endif + +// +// CSockSessionProxy +// +CSockSessionProxy* CSockSessionProxy::NewL(CWorkerSession* aSockSession, CPlayer& aPlayer) + { + CSockSessionProxy* self = new(ELeave) CSockSessionProxy(aSockSession, aPlayer); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +CSockSessionProxy::CSockSessionProxy(CWorkerSession* aSockSession, CPlayer& aPlayer) +: CCommonSessionProxy(aSockSession, aPlayer) + { + LOG(ESockLog::Printf(KESockSessDetailTag, _L8("CSockSessionProxy %08x:\tCSockSessionProxy(), iSockSession %08x"), this, Session()) ); + } + +void CSockSessionProxy::ConstructL() + { + iProtocols=new(ELeave) CArrayFixFlat(16); + } + +CSockSessionProxy::~CSockSessionProxy() + { + LOG(ESockLog::Printf(KESockSessDetailTag, _L8("CSockSessionProxy %08x:\t~CSockSessionProxy(), iSockSession %08x"), this, Session()) ); + if(iProtocols) + { + for (TInt i=0;iCount();i++) + { + LOG( + CProtocolBase* p = iProtocols->operator[](i); + const TDesC& tag(p->Tag()); + ESockLog::Printf(KESockSessDetailTag, _L("CSockSessionProxy %08x:\t~CSockSessionProxy(): closing protocol %08x '%S'"), this, p, &tag) + ); + + iProtocols->operator[](i)->Close(); + } + iProtocols->Delete(0,iProtocols->Count()); + delete iProtocols; + } + } + +void CSockSessionProxy::AddProtocolL(CProtocolBase* aProtocol) + { + LOG( + const TDesC& tag(aProtocol->Tag()); + ESockLog::Printf(KESockSessDetailTag, _L("CSockSessionProxy %08x:\tAddProtocolL(aProtocol %08x '%S'), iSockSession %08x"), this, aProtocol, &tag, Session()); + ); + for(TInt i=0;iCount();i++) + { + if (iProtocols->operator[](i)==aProtocol) + { + return; + } + } + + iProtocols->AppendL(aProtocol); + aProtocol->Open(); + } + +void CSockSessionProxy::RemoveProtocolL(CProtocolBase* aProtocol) + { + LOG(ESockLog::Printf(KESockSessDetailTag, _L8("CSockSessionProxy %08x:\tRemoveProtocolL(), iSockSession %08x"), this, Session()) ); + + CProtocolBase* p=0; + TInt j; + for(j=0;jCount();j++) + { + if (iProtocols->operator[](j)==aProtocol) + { + p=iProtocols->operator[](j); + break; + } + } + if(!p) + { + User::Leave(KErrNotFound); + } + + // in the absence of checking this protocol is not specifically referenced + // check no resources are referenced + if(Session()->SubSessions().ActiveCount()) + { + User::Leave(KErrInUse); + } + + p->Close(); + iProtocols->Delete(j); + iProtocols->Compress(); + } + +// +// CPitBoss +// + +CPitBoss* CPitBoss::NewL(CWorkerThread* aOwnerThread) + { + CPitBoss* self = new(ELeave) CPitBoss(aOwnerThread); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +CPitBoss::~CPitBoss() + { + // Delete the list of removed protocols + TProtocolPairing* pair = iDeadPairHead; + while(pair) + { + TProtocolPairing* nextPair = pair->iNextDead; + delete pair; + pair = nextPair; + } + pair = iProtocolPairHead; + while(pair) + { + TProtocolPairing* nextPair = pair->iNextPair; + delete pair; + pair = nextPair; + } + delete iCompleteEskList; + } + + +CPitBoss::CPitBoss(CWorkerThread* aOwnerThread) +: CCommonPitBoss(aOwnerThread) + { + } + +void CPitBoss::ConstructL() + { + iCompleteEskList = new(ELeave) CommsFW::COwnEntryList(6); + iCompleteEskList->UniqueWildScanAcrossDrivesL(KEsockIniFileDir, KEsockWildCard); + CommsFW::COwnEntryList *noBackupEskList = new(ELeave) CommsFW::COwnEntryList(32); + CleanupStack::PushL(noBackupEskList); + noBackupEskList->UniqueWildScanAcrossDrivesL(KEsockNoBackupDir, KEsockWildCard); + iCompleteEskList->AddL(*noBackupEskList); + iPropertyKey=RootServer::KUidC32StartPropertyKey; + CleanupStack::PopAndDestroy(noBackupEskList); + CCommonPitBoss::ConstructL(); + } + +TBool CPitBoss::GetWorkerForProtocol(TUint aAddrFamily, TUint aSockType, TUint aProtocol, TWorkerId& aWorker) const + { +#ifdef _DEBUG + aWorker = TWorkerThreadPublicInfo::ENullWorkerId; // ensure arg is polluted, lest caller ignore return value +#endif + TProtocolPairing* pair = FindProtocolPairing(aAddrFamily, aSockType, aProtocol); + if(pair) + { + aWorker = pair->iWorkerId; + return aWorker != TWorkerThreadPublicInfo::ENullWorkerId; + } + return EFalse; + } + +void CPitBoss::DoFreeWorkerReferences(TWorkerId aWorkerId) +{ + LOG(ESockLog::Printf(_L("CPitBoss::DoFreeWorkerReferences(%d)"), aWorkerId) ); +#if defined _DEBUG && !defined(ESOCKV3_TEMPORARY_PAIN_RELIEF) + // The RootServer normally checks the heap for leaks when the module unloads + // but for ESOCK modules this is commonly too early, since the PitBoss holds + // its reference open until the cleanup completes. Hence here we check for + // remaining allocations if we hold the last reference and if no thread + // which used it died involuntarily + RCFSharedHeap& heap = static_cast(*WorkerDataGlobals().GetWorkerGlobals(aWorkerId)->iHeap); + TInt leakCount = heap.Count(); // make it accessible for conditional breakpoints + LOG(ESockLog::Printf(_L8("~~~CPitBoss::FreeWorkerReferences heap(%08x).AccessCount()==%d, cell count=%d"), (TUint) &heap, heap.AccessCount(), leakCount)); + if(!iForsakenHeapList.IsForsaken(&heap)) + { + if(heap.AccessCount() <= 2) // Oddity: where does the other count come from, ie why not "1" when RS has already closed? And who does close it later? + { + if(leakCount > 0) + { + LOG(ESockLog::Printf(_L8("(log recorded under tags \"%S\" \"%S\" - you may need to enable these)"), &RootServer::KLogSubSysRSModule, &RootServer::KLogRSLeakTag)); + heap.LogAllocatedCells(RootServer::KLogSubSysRSModule, RootServer::KLogRSLeakTag); + + RProperty pubsub; + TInt res = pubsub.Attach(RootServer::KUidCommsProcess, RootServer::KUidCommsModuleLeakCounter); + //No nead for cleanup stack, cannot leave before Close + if (res == KErrNone) + { + TInt count; + res =pubsub.Get(count); + if (res == KErrNone) + { + count += heap.Count(); + res =pubsub.Set(count); + } + } + pubsub.Close(); + if (res != KErrNone) + { + __CFLOG_1(RootServer::KLogSubSysRSModule, RootServer::KLogRSLeakTag, _L8("Unable to report leaks. Error: %d"), res); + } + // As much as anything this log line is here to make it apparent that the breakpoint above was hit + LOG(ESockLog::Printf(_L8("--- end of leaked cell log"))); + } + } + } + +#else + // Preventing unused variable warnings. + (void)aWorkerId; +#endif +} + +TBool CPitBoss::GetWorkerForProtocol(TUint aIndex, TWorkerId& aWorker) const + { +#ifdef _DEBUG + aWorker = TWorkerThreadPublicInfo::ENullWorkerId; // ensure arg is polluted, lest caller ignore return value +#endif + if (aIndex == 0) + { + return EFalse; // protocol indices are 1-based + } + + /* Move pointer to protocol@index or end of list */ + TProtocolPairing* pair = iProtocolPairHead; + TUint i = 1; + while(pair) + { + // This function only exists to support the client ability to retrieve a protocol by index, and they're only + // interested in externally accessible protocols. So we skip magic ones. See also CPitBoss::GetNumProtocols() + if(pair->iSockType < KReservedSockTypesBase) + { + if(i == aIndex) + { + break; + } + ++i; + } + pair = pair->iNextPair; + } + + if(pair) + { + aWorker = pair->iWorkerId; + return aWorker != TWorkerThreadPublicInfo::ENullWorkerId; + } + return EFalse; + } + +TBool CPitBoss::GetWorkerForProtocolByName(const TProtocolName& aName, TWorkerId& aWorker) const + { +#ifdef _DEBUG + aWorker = TWorkerThreadPublicInfo::ENullWorkerId; // ensure arg is polluted, lest caller ignore return value +#endif + for(TProtocolPairing* pair = iProtocolPairHead; pair != NULL; pair = pair->iNextPair) + { + // Match the name (case insensitive) + if(aName.CompareF(pair->iName) == 0) + { + aWorker = pair->iWorkerId; + return aWorker != TWorkerThreadPublicInfo::ENullWorkerId; + } + } + return EFalse; + } + +/** +Return worker which can accept a NULL Socket. Default favours a specific worker, but +if it is not installed return the first real worker in the list. This algorithmn could change +or default selection be made configurable. +@param aWorker Id of the worker which can accept a NULL socket. Unchanged if none found. +*/ +TBool CPitBoss::GetWorkerForNullSocket(TWorkerId& aWorker) const + { + TBool found=EFalse; + //First check if SMS WAP Worker is available by looking up heap pointer + if(WorkerDataGlobals().WorkerPresent(TCFWorkerThreadPublicInfo::ESmsWapPlayerThread)) + { + aWorker=TCFWorkerThreadPublicInfo::ESmsWapPlayerThread; + found=ETrue; + } + else if(iProtocolPairHead && iProtocolPairHead->iWorkerId>0) // Take first worker in list + { + // Scan for the first valid worker (not all entries are for real protocols and some have null worker ids) + for(TProtocolPairing* pair = iProtocolPairHead; !found && pair != NULL; pair = pair->iNextPair) + { + if(pair->iWorkerId != TWorkerThreadPublicInfo::ENullWorkerId) + { + aWorker = pair->iWorkerId; + found = ETrue; + } + } + } + return found; + } + +/** +Return the number of known protocols. Counts by walking through the short list, so +not a super fast operation but very rarely called. Implement threadsafe (if needed) counters +on a rainy day. +*/ +TUint CPitBoss::GetNumProtocols() + { + TUint num=0; + // Count pairings that represent real protocols. The top range of sock types are reserved for internal trickery + for(TProtocolPairing* pair = iProtocolPairHead; pair!=NULL; pair=pair->iNextPair) + { + if(pair->iSockType < KReservedSockTypesBase) + { + num++; + } + } + return num; + } + + +TBool CPitBoss::GetWorkerForTier(TInt aTierId, TWorkerId& aWorker) const + { + return GetWorkerForProtocol(KTierEntryProxyAddrFam, KTierEntryProxySockType, aTierId, aWorker); + } + + +/** +Lookup a entry with the specified characteristics in the list of protocol pairings. +@see CPitBoss::TProtocolPairing +*/ +CPitBoss::TProtocolPairing* CPitBoss::FindProtocolPairing(TUint aAddrFamily, TUint aSockType, TUint aProtocol) const + { + TProtocolPairing* pair = iProtocolPairHead; + while(pair) + { + if(pair->iAddrFamily == aAddrFamily && + (pair->iSockType == KUndefinedSockType || aSockType == KUndefinedSockType || pair->iSockType == aSockType) && + (pair->iProtocol == KUndefinedProtocol || aProtocol == KUndefinedProtocol || pair->iProtocol == aProtocol)) + { + LOG( ESockLog::Printf(KESockSessDetailTag, _L("CPitBoss::FindProtocolPairing() - [aAddrFamily=%08x] [aSockType=%08x] [aProtocol=%08x] found '%S' W%d"), + aAddrFamily, aSockType, aProtocol, &pair->iName, pair->iWorkerId)); + return pair; + } + pair = pair->iNextPair; + } + + LOG( ESockLog::Printf(KESockSessDetailTag, _L("CPitBoss::FindProtocolPairing() - [aAddrFamily=%08x] [aSockType=%08x] [aProtocol=%08x] - no match"), + aAddrFamily, aSockType, aProtocol)); + return NULL; + } + +/** +Given session preferences, see if the Player supporting those protocol +characteristics has its own Dealer. +*/ +TBool CPitBoss::FindOptimalDealer(const TSessionPref& aPref, CCommonWorkerDealer*& aDealer) + { + TBool found=EFalse; + if(aPref.iAddrFamily == KUndefinedAddressFamily && aPref.iProtocol == KUndefinedProtocol) + { + // Client is asking for the default optimal dealer - if this isn't (yet) known then we quickly fail the request rather than possibly + // waiting for boot to complete, which might disrupt carefully tuned boot orders - we're better off foregoing the mild optimisation + if(iDefaultOptimalDealer != Den::TWorkerThreadPublicInfo::EMainThread) + { + found = CCommonPitBoss::FindOptimalDealer(iDefaultOptimalDealer, aDealer); + } + } + else + { + const TProtocolPairing* pair=FindProtocolPairing(aPref.iAddrFamily, KUndefinedSockType, aPref.iProtocol); + if(pair) + { + found = CCommonPitBoss::FindOptimalDealer(pair->iWorkerId, aDealer); + } + } + return found; + } + +CPitBoss::TProtocolPairingOwner::TProtocolPairingOwner() +: iHead(NULL) + { + } + +void CPitBoss::TProtocolPairingOwner::Append(TProtocolPairing* aNode) + { + if(iHead == NULL) + { + iHead = aNode; + } + else + { + TProtocolPairing* curr = iHead; + TProtocolPairing* prev; + do + { + prev = curr; + curr = curr->iNextPair; + } + while(curr != NULL); + prev->iNextPair = aNode; + } + } + +void CPitBoss::TProtocolPairingOwner::Release() + { + TProtocolPairing* curr = iHead; + while(curr != NULL) + { + TProtocolPairing* next = curr->iNextPair; + delete curr; + curr = next; + } + iHead = NULL; + } + + +void CPitBoss::AddProtocolToListL(TUint aAddrFamily, TUint aSockType, TUint aProtocol, const TProtocolName& aName, TWorkerId aWorker, TProtocolPairingOwner& aList) + { + TProtocolPairing* pair = new(ELeave) TProtocolPairing; + pair->iAddrFamily = aAddrFamily; + pair->iSockType = aSockType; + pair->iProtocol = aProtocol; + pair->iName = aName; + pair->iWorkerId = aWorker; + pair->iNextDead = NULL; + pair->iNextPair = NULL; + aList.Append(pair); + } + +void CPitBoss::IncorporateProtocolListL(TProtocolPairingOwner& aList) + { + LOG(ESockLog::Printf(KESockBootingTag, _L("CPitBoss::IncorporateProtocolList(%08x)"), &aList) ); + // Only safe from the main thread; thread race below + __ASSERT_DEBUG(iOwnerThread->IsMainThread(), User::Panic(KSpecAssert_ESockSSocks_rls, 14)); + +#ifdef _DEBUG + // In UDEB check whether any pair entry already exists - if so that's a serious config mistake and we panic the miscreant diagnostically + // In UREL we just ignore this possibility and let things limp on as best they can + TProtocolPairing* curr = aList.iHead; + while(curr != NULL) + { + LOG(ESockLog::Printf(KESockBootingTag, _L("CPitBoss: Adding protocol %S [fam,sock,prot]=[%x,%x,%x] for W%d"), &curr->iName, curr->iAddrFamily, curr->iSockType, curr->iProtocol, curr->iWorkerId) ); + TProtocolPairing* existing = FindProtocolPairing(curr->iAddrFamily, curr->iSockType, curr->iProtocol); + if(existing) + { + LOG(ESockLog::Printf(KESockBootingTag, _L("CPitBoss: already present as protocol %S for W%d"), &existing->iName, existing->iWorkerId) ); + LOG(ESockLog::Printf(KESockBootingTag, _L("CPitBoss: Killing misconfigured W%d. To fix this look at the lists of .ESK files logged earlier for the two workers and ensure only one loads the protocol"), curr->iWorkerId) ); + WorkerDataGlobals().PanicWorker(curr->iWorkerId, KESockProtocolPanic, ECorruptIniData); + User::Leave(KErrAlreadyExists); + } + curr = curr->iNextPair; + } +#endif + aList.Append(iProtocolPairHead); + NETWORKING_ATOMIC(iProtocolPairHead = aList.iHead); // atomic write of new ptr is guaranteed; it's ok to link it in before the worker ids are set + // as any competing protocol lookups will politely fail + } + + +/** +When a worker thread dies (e.g. during shutdown) the PitBoss will discover and use this method to +remove the protocol pairings for that particular worker. +*/ +void CPitBoss::RemoveProtocolPairingsForWorker(TWorkerId aWorkerId) + { + LOG(ESockLog::Printf(KESockBootingTag, _L("CPitBoss::RemoveProtocolPairingsForWorker(%d)"), aWorkerId) ); + TProtocolPairing* pair = iProtocolPairHead; + TProtocolPairing* prevLive = NULL; + while(pair) + { + if(pair->iWorkerId == aWorkerId) + { + LOG(ESockLog::Printf(KESockBootingTag, _L("CPitBoss: Removing protocol %S [fam,sock,prot]=[%x,%x,%x] for W%d"), &pair->iName, pair->iAddrFamily, pair->iSockType, pair->iProtocol, pair->iWorkerId) ); + if(prevLive) + { + NETWORKING_ATOMIC(prevLive->iNextPair = pair->iNextPair); // atomic write of new ptr is guaranteed + } + else + { + NETWORKING_ATOMIC(iProtocolPairHead = pair->iNextPair); // atomic write of new ptr is guaranteed + } + pair->iNextDead = iDeadPairHead; + NETWORKING_ATOMIC(iDeadPairHead = pair); // atomic write of new ptr is guaranteed + } + else + { + prevLive = pair; + } + pair = pair->iNextPair; + } + } + +void CPitBoss::AddTierPairingToListL(TInt aTierUid, const TDesC& aTierName, TWorkerId aWorker, TProtocolPairingOwner& aList) + { + AddProtocolToListL(KTierEntryProxyAddrFam, KTierEntryProxySockType, aTierUid, aTierName, aWorker, aList); + } + +const CommsFW::COwnEntryList* CPitBoss::GetCompleteList() + { + return iCompleteEskList; + } + +/** +Used during binding when the PitBoss receives a introduction response message from a worker. +The PitBoss will set-up housekeeping datastructures for the worker and add the supported +protocols to its list of protocol pairings. +@see TWorkerMsg::EMainIntroductionResp +*/ +void CPitBoss::DoProcessWorkerIntroductionL(const TWorkerIntroductionMsg& aMsg) + { + // Now populate the protocol pairing list + TProtocolPairingOwner pairList; + CleanupReleasePushL(pairList); + const TWorkerThreadPublicInfo& msgInfo = aMsg.WorkerInfo(); + CPlayer* player=static_cast(GetPlayer(aMsg)); + + if(player) + { + TProtocolDesc prot; + for(TUint protNum = 1; player->ProtocolInfo(protNum, prot) == KErrNone; ++protNum) + { + AddProtocolToListL(prot.iAddrFamily, prot.iSockType, prot.iProtocol, prot.iName, msgInfo.iWorkerId, pairList); + } + + // Add proxy protocol pairings for any special roles + if(player->HasTierResolver()) + { +#ifdef _DEBUG + TWorkerId alternateTierResolverWorker; + __ASSERT_DEBUG(!GetWorkerForPlayerRole(KPlayerRoleTierResolver, alternateTierResolverWorker), User::Panic(KSpecAssert_ESockSSocks_rls, 15)); +#endif + _LIT(KTierResolverDesc, "TierResolver"); + AddPlayerRolePairingL(KPlayerRoleTierResolver, KTierResolverDesc, msgInfo.iWorkerId, pairList); + } + } + IncorporateProtocolListL(pairList); + CleanupStack::Pop(&pairList); + + TBuf8 introInfo; + aMsg.IntroductionInfo(introInfo); + TPckgBuf defaultOptDealer; + defaultOptDealer.Copy(introInfo.LeftTPtr(defaultOptDealer.MaxSize())); + if(defaultOptDealer()) + { + if(iDefaultOptimalDealer == Den::TWorkerThreadPublicInfo::EMainThread) + { + iDefaultOptimalDealer = msgInfo.iWorkerId; + } + else + { + LOG(ESockLog::Printf(KESockBootingTag, _L("ERROR worker %d claiming DefaultOptimalDealer after worker %d already did so"), msgInfo.iWorkerId, iDefaultOptimalDealer)); +#ifdef _DEBUG + RDebug::Printf("ERROR worker %d claiming DefaultOptimalDealer after worker %d already did so", msgInfo.iWorkerId, iDefaultOptimalDealer); + Panic(EMisconfigured); +#endif + } + } + } + +/** +The PitBoss monitors the Comms Configurator sequence level and when the core components +have been configured (this includes ESock) this method is called to delete any data structures +used only during startup of ESock. +@see CConfigurationLevelMonitor +@see CPitBoss::iPendingIntroResponses +*/ +void CPitBoss::DoOnCPMsConfigured() + { + // We can now delete the shared ESK data and enable simulated failure for the main thread (if configured) + if(iPendingIntroResponses == 0) + { + delete iCompleteEskList; + iCompleteEskList = NULL; + } + if(iLoadTierMappingPhase == EDealerRequest) + { + // Was awaiting boot completion to find the tier resolver + SendLoadTierMappingRequest(); + } + } + +/** +If a worker dies the PitBoss will call this method. It will clean up the housekeeping datastructures +related to the worker and it will spawn the RedShirt thread which will try to delete the workers own +data structures. It is a best effort attempt that doesn't guarantee to free up all the dead workers memory +and the RedShirt could be PANICed by the kernel, which is why a short lived seperate thread is doing it. +*/ +void CPitBoss::DoOnPeerDeath(TWorkerId aWorkerId) + { + // If worker ran a Player all its protocol pairings are now dead. We can't know whether the thread actually + // did run a Player as (presuming it exited cleanly) it cleaned up, but it's adequately cheap & a rare case + RemoveProtocolPairingsForWorker(aWorkerId); + } + +struct ESockThreadStartupInfo + { +#ifdef SYMBIAN_ZERO_COPY_NETWORKING + RCommsBufPond iCommsBufPond; +#else + CMBufManager* iMBufManager; +#endif // SYMBIAN_ZERO_COPY_NETWORKING + + TAny* iModuleArgs; + __CFLOG_STMT(CCFLogIf* iCFLogIf;) + }; + +TInt RESockCleanupThreadFunction(TAny* aStartupInfo) +/** +Intermediate function which masquerades as the main thread function in order to +perform some specific actions for the new thread in the correct context before +calling the new thread's actual main thread function. +The thread must be resumed after being created or the startup info structure +cannot be deleted by the thread and will be leaked +@param aStartupInfo structure containing pointers to MBufMger and CFlog. +@see RCFThread::ThreadStartupInfo +@internalComponent +*/ + { + ESockThreadStartupInfo* startInfo = reinterpret_cast(aStartupInfo); +#ifdef SYMBIAN_ZERO_COPY_NETWORKING + TCommsBufPondTLSOp tls(startInfo->iCommsBufPond); + tls.Set(); +#else + startInfo->iMBufManager->SetContext(); +#endif // SYMBIAN_ZERO_COPY_NETWORKING + + + __CFLOG_STMT( startInfo->iCFLogIf->SetContext(); ) + __CFLOG_OPEN; + + TInt result = CCommonWorkerThread::PostMortemCleanupThreadEntry(startInfo->iModuleArgs); + + __CFLOG_CLOSE; + delete startInfo; + + return result; + }; + +TInt CPitBoss::DoCreateRedShirt(RThread& aRedShirt, CommsFW::TWorkerId aWorkerId, Den::CCommonWorkerThread& aDeadWorker) + { + // Get the heap to assign to the red shirt thread and switch to it so we + // can allocate the thread startup info structure. + RAllocator* heap = WorkerDataGlobals().GetWorkerGlobals(aWorkerId)->iHeap; + RHeap* prevHeap = User::SwitchHeap(heap); + + // We must not pass the thread a structure allocated on the stack because it + // will go out of scope before the caller calls RThread::Resume so we create + // it dynamically in the thread's heap. + // The thread is responsible for deleting the structure when it no longer + // needs it. For this reason, if the thread is successfully created the + // caller MUST resume the thread and allow it to run or else it will be + // leaked. + ESockThreadStartupInfo* startupInfo = new ESockThreadStartupInfo; + User::SwitchHeap(prevHeap); + + TInt err = KErrNone; + + if( startupInfo ) + { + startupInfo->iModuleArgs = &aDeadWorker; +#ifdef SYMBIAN_ZERO_COPY_NETWORKING + startupInfo->iCommsBufPond = TCommsBufPondTLSOp::Get(); + if(startupInfo->iCommsBufPond.IsNull()) + { + err = KErrNotFound; + } +#else + startupInfo->iMBufManager = CMBufManager::Context(); + if(startupInfo->iMBufManager == NULL) + { + err = KErrNotFound; + } +#endif // SYMBIAN_ZERO_COPY_NETWORKING + + // Check to make sure the logger is available. + #ifdef __CFLOG_ACTIVE + else + { + __CFLOG_STMT(startupInfo->iCFLogIf = CCFLogIf::Context()); + if(!startupInfo->iCFLogIf) + { + RDebug::Print( _L( "RCFThread::Create - the log interface was not found. This normally means that the logging version of commsfw.dll has been mixed with a stub version of cflog.dll. See CommsDebugUtility How-To Document FAQ section for details on enabling logging in a release build." )); + + err = KErrNotFound; + } + } + #endif + + if(err == KErrNone) + { + err = aRedShirt.Create(KNullDesC, RESockCleanupThreadFunction, 8192, static_cast(heap), startupInfo); + } + // If any error occured, delete the startup info structure. + if(err != KErrNone) + { + prevHeap = User::SwitchHeap(heap); + delete startupInfo; + User::SwitchHeap(prevHeap); + } + } + else + { + err = KErrNoMemory; + CleanupStack::Pop(startupInfo); + } + + return err; + } + +/** +Converts pit boss's protocol index to the local index +The pit boss maintains a global list of protocols while each worker thread +maintains its own list. Obviously the local list is shorter than the global list +This function takes the Pitboss list index and comes back with one that is +applicable to the local list +@param aPitBossIndex the index to the pit boss's protocol list NB starts at 1 +@returns the index to the worker thread's list of protocols, -1 if not found +*/ +TInt CPitBoss::GetLocalProtocolIndex(TInt aPitBossIndex) const + { + __ASSERT_DEBUG(aPitBossIndex > 0, User::Panic(KSpecAssert_ESockSSocks_rls, 16)); + TInt workers[TWorkerThreadPublicInfo::EMaxWorkerThreadId]; + Mem::FillZ(workers,sizeof(workers)); + + TInt lastBox = 0; + TProtocolPairing* pair = iProtocolPairHead; + + TInt i = 0; + while(i < aPitBossIndex) + { + __ASSERT_DEBUG(pair, User::Panic(KSpecAssert_ESockSSocks_rls, 17)); + if(pair->iSockType < KReservedSockTypesBase) + { + lastBox = pair->iWorkerId - 1; + __ASSERT_DEBUG(lastBox < TWorkerThreadPublicInfo::EMaxWorkerThreadId, User::Panic(KSpecAssert_ESockSSocks_rls, 18)); + ++(workers[lastBox]); + ++i; + } + pair = pair->iNextPair; + } + + TInt ret = workers[lastBox]; + return ret > 0 ? ret : -1; + } + +void CPitBoss::RequestLoadTierMapping() + { + if(iLoadTierMappingPhase == EStart) + { + if(ModuleConfigurationComplete()) + { + SendLoadTierMappingRequest(); + } + else + { + iLoadTierMappingPhase = EDealerRequest; + } + } + } + +void CPitBoss::SendLoadTierMappingRequest() + { + TWorkerId worker; + if(GetWorkerForPlayerRole(KPlayerRoleTierResolver, worker)) + { + TWorkerLoadTierMappings msg; + WorkerThread().PostMessage(worker, msg); + iLoadTierMappingPhase = EResolverRequested; + } + else + { + LOG(ESockLog::Printf(_L8("CPitBoss::SendLoadTierMappingRequest() *** NO TIER RESOLVER CONFIGURED *** "))); + __ASSERT_DEBUG(0, Panic(EMisconfigured)); // configuration is so broken that leaving user in ignorance is probably unhelpful + TRAP_IGNORE(OnTierMappingLoadedL(NULL, TWorkerThreadPublicInfo::ENullWorkerId)); // without the resolver's help all we can do is fail the tier requests + } + } + +void CPitBoss::PopulateAndAddProtocolPairListL(TProtocolName& tierDesc, RTierThreadMap* map) + { + TProtocolPairingOwner pairList; + CleanupReleasePushL(pairList); + + for(TInt i = map->Count() - 1; i >=0; --i) + { + const TTierMapEntry& entry = (*map)[i]; + +#ifdef _DEBUG + tierDesc.Format(_L("#!#! Tier %08x"), entry.iUid); // make protocol pairing record self-descriptive for debugging +#endif + AddTierPairingToListL(entry.iUid, tierDesc, entry.iWorker, pairList); + } + IncorporateProtocolListL(pairList); + + CleanupStack::Pop(&pairList); + } + +void CPitBoss::OnTierMappingLoadedL(const TWorkerTierMappingsLoaded* aMappingMsg, TWorkerId aSenderId) + { + __ASSERT_DEBUG(iLoadTierMappingPhase == EResolverRequested, User::Panic(KSpecAssert_ESockSSocks_rls, 19)); + TProtocolName tierDesc; + + LOG(TInt numAdded = (aMappingMsg && aMappingMsg->TierMap())? aMappingMsg->TierMap()->Count(): 0); + if (aMappingMsg && aMappingMsg->TierMap()) + { + RTierThreadMap* map = const_cast(aMappingMsg->TierMap()); + + TRAPD(err, PopulateAndAddProtocolPairListL(tierDesc, map)); + + // THeapSwitcher is not leave-safe and must be destroyed by going out of scope only. + // Our leave implementation does not guarantee that stack objects will be destroyed + // during a leave and although the current ARM implementation does do this because it + // uses the C++ exception handling mechanism internally, x86gcc does not support this. + { + THeapSwitcher switcher(*this, aSenderId); + + map->Close(); + delete map; + } + + if (err) + { + User::Leave(err); + } + } + + iLoadTierMappingPhase = EComplete; + + LOG(ESockLog::Printf(_L8("CPitBoss::OnTierMappingLoadedL() - %d tier entries added"), numAdded)); + // And tell all of the workers that tier mappings are ready for digestion + BroadcastConfigurationComplete(ETierMapping); + } +