|
|
/*==========================================================================
* * Copyright (C) 1998-2002 Microsoft Corporation. All Rights Reserved. * * File: Initialize.cpp * Content: This file contains code to both initialize and shutdown the * protocol, as well as to Add and Remove service providers * * History: * Date By Reason * ==== == ====== * 11/06/98 ejs Created * 07/01/2000 masonb Assumed Ownership * ****************************************************************************/
#include "dnproti.h"
/*
** GLOBAL VARIABLES ** ** There are two kinds of global variables. Instance specific globals ** (not really global, i know) which are members of the ProtocolData structure, ** and true globals which are shared among all instances. The following ** definitions are true globals, such as FixedPools and Timers. */
CFixedPool ChkPtPool; // Pool of CheckPoint data structure
CFixedPool EPDPool; // Pool of End Point descriptors
CFixedPool MSDPool; // Pool of Message Descriptors
CFixedPool FMDPool; // Pool of Frame Descriptors
CFixedPool RCDPool; // Pool of Receive Descriptors
CFixedPool BufPool; // Pool of buffers to store rcvd frames
CFixedPool MedBufPool; CFixedPool BigBufPool;
#ifdef DBG
CBilink g_blProtocolCritSecsHeld; #endif // DBG
#ifndef DPNBUILD_NOPROTOCOLTESTITF
PFNASSERTFUNC g_pfnAssertFunc = NULL; PFNMEMALLOCFUNC g_pfnMemAllocFunc = NULL; #endif // !DPNBUILD_NOPROTOCOLTESTITF
//////////////////////////////////
#define CHKPTPOOL_INITED 0x00000001
#define EPDPOOL_INITED 0x00000002
#define MSDPOOL_INITED 0x00000004
#define FMDPOOL_INITED 0x00000008
#define RCDPOOL_INITED 0x00000010
#define BUFPOOL_INITED 0x00000020
#define MEDBUFPOOL_INITED 0x00000040
#define BIGBUFPOOL_INITED 0x00000080
DWORD g_dwProtocolInitFlags = 0; //////////////////////////////////
/*
** Pools Initialization ** ** This procedure should be called once at Dll load */ #undef DPF_MODNAME
#define DPF_MODNAME "DNPPoolsInit"
BOOL DNPPoolsInit(HANDLE hModule) { DPFX(DPFPREP,DPF_CALLIN_LVL, "Enter");
#ifdef DBG
g_blProtocolCritSecsHeld.Initialize(); #endif // DBG
if(!ChkPtPool.Initialize(sizeof(CHKPT), NULL, NULL, NULL, NULL)) { DNPPoolsDeinit(); return FALSE; } g_dwProtocolInitFlags |= CHKPTPOOL_INITED; if(!EPDPool.Initialize(sizeof(EPD), EPD_Allocate, EPD_Get, EPD_Release, EPD_Free)) { DNPPoolsDeinit(); return FALSE; } g_dwProtocolInitFlags |= EPDPOOL_INITED; if(!MSDPool.Initialize(sizeof(MSD), MSD_Allocate, MSD_Get, MSD_Release, MSD_Free)) { DNPPoolsDeinit(); return FALSE; } g_dwProtocolInitFlags |= MSDPOOL_INITED; if(!FMDPool.Initialize(sizeof(FMD), FMD_Allocate, FMD_Get, FMD_Release, FMD_Free)) { DNPPoolsDeinit(); return FALSE; } g_dwProtocolInitFlags |= FMDPOOL_INITED; if(!RCDPool.Initialize(sizeof(RCD), RCD_Allocate, RCD_Get, RCD_Release, RCD_Free)) { DNPPoolsDeinit(); return FALSE; } g_dwProtocolInitFlags |= RCDPOOL_INITED; if(!BufPool.Initialize(sizeof(BUF), Buf_Allocate, Buf_Get, NULL, NULL)) { DNPPoolsDeinit(); return FALSE; } g_dwProtocolInitFlags |= BUFPOOL_INITED; if(!MedBufPool.Initialize(sizeof(MEDBUF), Buf_Allocate, Buf_GetMed, NULL, NULL)) { DNPPoolsDeinit(); return FALSE; } g_dwProtocolInitFlags |= MEDBUFPOOL_INITED; if(!BigBufPool.Initialize(sizeof(BIGBUF), Buf_Allocate, Buf_GetBig, NULL, NULL)) { DNPPoolsDeinit(); return FALSE; } g_dwProtocolInitFlags |= BIGBUFPOOL_INITED;
return TRUE; }
/*
** Pools Deinitialization ** ** This procedure should be called by DllMain at shutdown time */ #undef DPF_MODNAME
#define DPF_MODNAME "DNPPoolsDeinit"
void DNPPoolsDeinit() { DPFX(DPFPREP,DPF_CALLIN_LVL, "Enter");
if(g_dwProtocolInitFlags & CHKPTPOOL_INITED) { ChkPtPool.DeInitialize(); } if(g_dwProtocolInitFlags & EPDPOOL_INITED) { EPDPool.DeInitialize(); } if(g_dwProtocolInitFlags & MSDPOOL_INITED) { MSDPool.DeInitialize(); } if(g_dwProtocolInitFlags & FMDPOOL_INITED) { FMDPool.DeInitialize(); } if(g_dwProtocolInitFlags & RCDPOOL_INITED) { RCDPool.DeInitialize(); } if(g_dwProtocolInitFlags & BUFPOOL_INITED) { BufPool.DeInitialize(); } if(g_dwProtocolInitFlags & MEDBUFPOOL_INITED) { MedBufPool.DeInitialize(); } if(g_dwProtocolInitFlags & BIGBUFPOOL_INITED) { BigBufPool.DeInitialize(); } g_dwProtocolInitFlags = 0; }
#undef DPF_MODNAME
#define DPF_MODNAME "DNPProtocolCreate"
#ifdef DPNBUILD_PREALLOCATEDMEMORYMODEL
HRESULT DNPProtocolCreate(const XDP8CREATE_PARAMS * const pDP8CreateParams, VOID** ppvProtocol) #else // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
HRESULT DNPProtocolCreate(VOID** ppvProtocol) #endif // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
{ ASSERT(ppvProtocol);
#ifdef DPNBUILD_PREALLOCATEDMEMORYMODEL
ASSERT(pDP8CreateParams); DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: pDP8CreateParams[%p], ppvProtocol[%p]", pDP8CreateParams, ppvProtocol); #else // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: ppvProtocol[%p]", ppvProtocol); #endif // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
#ifndef DPNBUILD_NOPROTOCOLTESTITF
g_pfnAssertFunc = NULL; g_pfnMemAllocFunc = NULL; #endif // !DPNBUILD_NOPROTOCOLTESTITF
if ((*ppvProtocol = MEMALLOC(MEMID_PPD, sizeof(ProtocolData))) == NULL) { DPFERR("DNMalloc() failed"); return(E_OUTOFMEMORY); } memset(*ppvProtocol, 0, sizeof(ProtocolData));
// The sign needs to be valid by the time DNPProtocolInitialize is called
((ProtocolData*)*ppvProtocol)->Sign = PPD_SIGN; #ifdef DPNBUILD_PREALLOCATEDMEMORYMODEL
DWORD dwNumToAllocate; DWORD dwAllocated;
#ifdef _XBOX
#define MAX_FRAME_SIZE 1462 // Note we are hard coding the expected frame size.
#else // ! _XBOX
#define MAX_FRAME_SIZE 1472 // Note we are hard coding the expected frame size.
#endif // ! _XBOX
dwNumToAllocate = (pDP8CreateParams->dwMaxNumPlayers - 1); dwAllocated = ChkPtPool.Preallocate(dwNumToAllocate, NULL); if (dwAllocated < dwNumToAllocate) { DPFX(DPFPREP, 0, "Only allocated %u of %u checkpoints!", dwAllocated, dwNumToAllocate); DNFree(*ppvProtocol); *ppvProtocol = NULL; return(E_OUTOFMEMORY); }
dwNumToAllocate = (pDP8CreateParams->dwMaxNumPlayers - 1); dwAllocated = EPDPool.Preallocate(dwNumToAllocate, NULL); if (dwAllocated < dwNumToAllocate) { DPFX(DPFPREP, 0, "Only allocated %u of %u EPDs!", dwAllocated, dwNumToAllocate); DNFree(*ppvProtocol); *ppvProtocol = NULL; return(E_OUTOFMEMORY); }
dwNumToAllocate = pDP8CreateParams->dwMaxSendsPerPlayer * (pDP8CreateParams->dwMaxNumPlayers - 1); dwNumToAllocate += pDP8CreateParams->dwNumSimultaneousEnumHosts; dwNumToAllocate += 1; // one for a listen operation
dwAllocated = MSDPool.Preallocate(dwNumToAllocate, NULL); if (dwAllocated < dwNumToAllocate) { DPFX(DPFPREP, 0, "Only allocated %u of %u MSDs!", dwAllocated, dwNumToAllocate); DNFree(*ppvProtocol); *ppvProtocol = NULL; return(E_OUTOFMEMORY); }
dwNumToAllocate = pDP8CreateParams->dwMaxSendsPerPlayer * (pDP8CreateParams->dwMaxNumPlayers - 1); // Include the possiblity of having to split a message across multiple frames.
dwNumToAllocate *= pDP8CreateParams->dwMaxMessageSize / MAX_FRAME_SIZE; dwNumToAllocate += pDP8CreateParams->dwNumSimultaneousEnumHosts; dwAllocated = FMDPool.Preallocate(dwNumToAllocate, NULL); if (dwAllocated < dwNumToAllocate) { DPFX(DPFPREP, 0, "Only allocated %u of %u FMDs!", dwAllocated, dwNumToAllocate); DNFree(*ppvProtocol); *ppvProtocol = NULL; return(E_OUTOFMEMORY); }
dwNumToAllocate = pDP8CreateParams->dwMaxReceivesPerPlayer * (pDP8CreateParams->dwMaxNumPlayers - 1); dwAllocated = RCDPool.Preallocate(dwNumToAllocate, NULL); if (dwAllocated < dwNumToAllocate) { DPFX(DPFPREP, 0, "Only allocated %u of %u RCDs!", dwAllocated, dwNumToAllocate); DNFree(*ppvProtocol); *ppvProtocol = NULL; return(E_OUTOFMEMORY); }
if (pDP8CreateParams->dwMaxMessageSize > MAX_FRAME_SIZE) { dwNumToAllocate = pDP8CreateParams->dwMaxReceivesPerPlayer * (pDP8CreateParams->dwMaxNumPlayers - 1); dwAllocated = BufPool.Preallocate(dwNumToAllocate, NULL); if (dwAllocated < dwNumToAllocate) { DPFX(DPFPREP, 0, "Only allocated %u of %u small receive buffers!", dwAllocated, dwNumToAllocate); DNFree(*ppvProtocol); *ppvProtocol = NULL; return(E_OUTOFMEMORY); }
if (pDP8CreateParams->dwMaxMessageSize > MEDIUM_BUFFER_SIZE) { dwNumToAllocate = pDP8CreateParams->dwMaxReceivesPerPlayer * (pDP8CreateParams->dwMaxNumPlayers - 1); dwAllocated = MedBufPool.Preallocate(dwNumToAllocate, NULL); if (dwAllocated < dwNumToAllocate) { DPFX(DPFPREP, 0, "Only allocated %u of %u medium receive buffers!", dwAllocated, dwNumToAllocate); DNFree(*ppvProtocol); *ppvProtocol = NULL; return(E_OUTOFMEMORY); }
if (pDP8CreateParams->dwMaxMessageSize > LARGE_BUFFER_SIZE) { dwNumToAllocate = pDP8CreateParams->dwMaxReceivesPerPlayer * (pDP8CreateParams->dwMaxNumPlayers - 1); dwAllocated = BigBufPool.Preallocate(dwNumToAllocate, NULL); if (dwAllocated < dwNumToAllocate) { DPFX(DPFPREP, 0, "Only allocated %u of %u big receive buffers!", dwAllocated, dwNumToAllocate); DNFree(*ppvProtocol); *ppvProtocol = NULL; return(E_OUTOFMEMORY); } } } } // end if (messages may span multiple frames)
#endif // DPNBUILD_PREALLOCATEDMEMORYMODEL
return DPN_OK; }
#undef DPF_MODNAME
#define DPF_MODNAME "DNPProtocolDestroy"
VOID DNPProtocolDestroy(VOID* pvProtocol) { if (pvProtocol) { DNFree(pvProtocol); } }
/*
** Protocol Initialize ** ** This procedure should be called by DirectPlay at startup time before ** any other calls in the protocol are made. */
#undef DPF_MODNAME
#define DPF_MODNAME "DNPProtocolInitialize"
HRESULT DNPProtocolInitialize(HANDLE hProtocolData, PVOID pCoreContext, PDN_PROTOCOL_INTERFACE_VTBL pVtbl, IDirectPlay8ThreadPoolWork *pDPThreadPoolWork, BOOL bAssumeLANConnections) { DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: pCoreContext[%p], hProtocolData[%p], pVtbl[%p], pDPThreadPoolWork[%p]", hProtocolData, pCoreContext, pVtbl, pDPThreadPoolWork);
// DPFX(DPFPREP,0, "Sizes: endpointdesc[%d], framedesc[%d], messagedesc[%d], protocoldata[%d], recvdesc[%d], spdesc[%d], _MyTimer[%d]", sizeof(endpointdesc), sizeof(framedesc), sizeof(messagedesc), sizeof(protocoldata), sizeof(recvdesc), sizeof(spdesc), sizeof(_MyTimer));
ProtocolData* pPData;
pPData = (ProtocolData*)hProtocolData; ASSERT_PPD(pPData);
IDirectPlay8ThreadPoolWork_AddRef(pDPThreadPoolWork); pPData->pDPThreadPoolWork = pDPThreadPoolWork;
pPData->ulProtocolFlags = 0; pPData->Parent = pCoreContext; pPData->pfVtbl = pVtbl;
pPData->lSPActiveCount = 0; pPData->tIdleThreshhold = DEFAULT_KEEPALIVE_INTERVAL; // 60 second keep-alive interval
pPData->dwConnectTimeout = CONNECT_DEFAULT_TIMEOUT; pPData->dwConnectRetries = CONNECT_DEFAULT_RETRIES; pPData->dwMaxRecvMsgSize=DEFAULT_MAX_RECV_MSG_SIZE; pPData->dwSendRetriesToDropLink=DEFAULT_SEND_RETRIES_TO_DROP_LINK; pPData->dwSendRetryIntervalLimit=DEFAULT_SEND_RETRY_INTERVAL_LIMIT; pPData->dwNumHardDisconnectSends=DEFAULT_HARD_DISCONNECT_SENDS; pPData->dwMaxHardDisconnectPeriod=DEFAULT_HARD_DISCONNECT_MAX_PERIOD; pPData->dwInitialFrameWindowSize = bAssumeLANConnections ? LAN_INITIAL_FRAME_WINDOW_SIZE : DEFAULT_INITIAL_FRAME_WINDOW_SIZE;
pPData->dwDropThresholdRate = DEFAULT_THROTTLE_THRESHOLD_RATE; pPData->dwDropThreshold = (32 * DEFAULT_THROTTLE_THRESHOLD_RATE) / 100; pPData->dwThrottleRate = DEFAULT_THROTTLE_BACK_OFF_RATE; pPData->fThrottleRate = (100.0 - (FLOAT)DEFAULT_THROTTLE_BACK_OFF_RATE) / 100.0; DPFX(DPFPREP, 2, "pPData->fThrottleRate [%f]", pPData->fThrottleRate);
#ifdef DBG
pPData->ThreadsInReceive = 0; pPData->BuffersInReceive = 0; #endif // DBG
pPData->ulProtocolFlags |= PFLAGS_PROTOCOL_INITIALIZED;
AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
return DPN_OK; }
/*
** Protocol Shutdown ** ** This procedure should be called at termination time, and should be the ** last call made to the protocol. ** ** All SPs should have been removed prior to this call which in turn means ** that we should not have any sends pending in a lower layer. */
#undef DPF_MODNAME
#define DPF_MODNAME "DNPProtocolShutdown"
HRESULT DNPProtocolShutdown(HANDLE hProtocolData) { ProtocolData* pPData;
DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: hProtocolData[%p]", hProtocolData);
pPData = (ProtocolData*)hProtocolData; ASSERT_PPD(pPData);
ASSERT(pPData->lSPActiveCount == 0);
IDirectPlay8ThreadPoolWork_Release(pPData->pDPThreadPoolWork); pPData->pDPThreadPoolWork = NULL; #ifdef DBG
if (pPData->BuffersInReceive != 0) { DPFX(DPFPREP,0, "*** %d receive buffers were leaked", pPData->BuffersInReceive); } #endif // DBG
pPData->ulProtocolFlags = 0;
AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
return DPN_OK; }
/*
** Add Service Provider ** ** This procedure is called by Direct Play to bind us to a service provider. ** We can bind up to 256 service providers at one time, although I would not ever ** expect to do so. This procedure will fail if Protocol Initialize has not ** been called. ** ** ** We check the size of the SP table to make sure we have a slot free. If table ** is full we double the table size until we reach maximum size. If table cannot grow ** then we fail the AddServiceProvider call. */
extern IDP8SPCallbackVtbl DNPLowerEdgeVtbl;
#undef DPF_MODNAME
#define DPF_MODNAME "DNPAddServiceProvider"
HRESULT DNPAddServiceProvider(HANDLE hProtocolData, IDP8ServiceProvider* pISP, HANDLE* phSPContext, DWORD dwFlags) { ProtocolData* pPData; PSPD pSPD; SPINITIALIZEDATA SPInitData; SPGETCAPSDATA SPCapsData; HRESULT hr;
DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: hProtocolData[%p], pISP[%p], phSPContext[%p]", hProtocolData, pISP, phSPContext);
hr = DPN_OK; pPData = (ProtocolData*)hProtocolData; ASSERT_PPD(pPData);
if(pPData->ulProtocolFlags & PFLAGS_PROTOCOL_INITIALIZED) { if ((pSPD = (PSPD)MEMALLOC(MEMID_SPD, sizeof(SPD))) == NULL) { DPFX(DPFPREP,0, "Returning DPNERR_OUTOFMEMORY - couldn't allocate SP Descriptor"); hr = DPNERR_OUTOFMEMORY; goto Exit; }
// MAKE THE INITIALIZE CALL TO THE Service Provider... give him our Object
memset(pSPD, 0, sizeof(SPD)); // init to zero
pSPD->LowerEdgeVtable = &DNPLowerEdgeVtbl; // Put Vtbl into the interface Object
pSPD->Sign = SPD_SIGN;
SPInitData.pIDP = (IDP8SPCallback *) pSPD; SPInitData.dwFlags = dwFlags;
if (DNInitializeCriticalSection(&pSPD->SPLock) == FALSE) { DPFX(DPFPREP,0, "Returning DPNERR_OUTOFMEMORY - couldn't initialize SP CS, pSPD[%p]", pSPD); DNFree(pSPD); hr = DPNERR_OUTOFMEMORY; goto Exit; } DebugSetCriticalSectionRecursionCount(&pSPD->SPLock, 0); DebugSetCriticalSectionGroup(&pSPD->SPLock, &g_blProtocolCritSecsHeld);
AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
DPFX(DPFPREP,DPF_CALLOUT_LVL, "Calling SP->Initialize, pSPD[%p]", pSPD); if((hr = IDP8ServiceProvider_Initialize(pISP, &SPInitData)) != DPN_OK) { if (hr == DPNERR_UNSUPPORTED) { DPFX(DPFPREP,1, "SP unsupported, pSPD[%p]", pSPD); } else { DPFX(DPFPREP,0, "Returning hr=%x - SP->Initialize failed, pSPD[%p]", hr, pSPD); } DNDeleteCriticalSection(&pSPD->SPLock); DNFree(pSPD); goto Exit; }
pSPD->blSendQueue.Initialize(); pSPD->blPendingQueue.Initialize(); pSPD->blEPDActiveList.Initialize(); #ifdef DBG
pSPD->blMessageList.Initialize(); #endif // DBG
// MAKE THE SP GET CAPS CALL TO FIND FRAMESIZE AND LINKSPEED
SPCapsData.dwSize = sizeof(SPCapsData); SPCapsData.hEndpoint = INVALID_HANDLE_VALUE; AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
DPFX(DPFPREP,DPF_CALLOUT_LVL, "Calling SP->GetCaps, pSPD[%p]", pSPD); if((hr = IDP8ServiceProvider_GetCaps(pISP, &SPCapsData)) != DPN_OK) { DPFX(DPFPREP,DPF_CALLOUT_LVL, "SP->GetCaps failed - hr[%x], Calling SP->Close, pSPD[%p]", hr, pSPD); IDP8ServiceProvider_Close(pISP); DNDeleteCriticalSection(&pSPD->SPLock);
DPFX(DPFPREP,0, "Returning hr=%x - SP->GetCaps failed, pSPD[%p]", hr, pSPD);
DNFree(pSPD); goto Exit; }
pSPD->uiLinkSpeed = SPCapsData.dwLocalLinkSpeed; pSPD->uiFrameLength = SPCapsData.dwUserFrameSize; if (pSPD->uiFrameLength < MIN_SEND_MTU) { DPFX(DPFPREP,0, "SP MTU isn't large enough to support protocol pSPD[%p] Required MTU[%u] Available MTU[%u] " "Returning DPNERR_UNSUPPORTED", pSPD, MIN_SEND_MTU, pSPD->uiFrameLength); IDP8ServiceProvider_Close(pISP); DNDeleteCriticalSection(&pSPD->SPLock); DNFree(pSPD); hr=DPNERR_UNSUPPORTED; goto Exit; } pSPD->uiUserFrameLength = pSPD->uiFrameLength - MAX_SEND_DFRAME_NOCOALESCE_HEADER_SIZE; DPFX(DPFPREP, 3, "SPD 0x%p frame length = %u, single user frame length = %u.", pSPD, pSPD->uiFrameLength, pSPD->uiUserFrameLength);
// Place new SP in table
AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
DPFX(DPFPREP,DPF_CALLOUT_LVL, "Calling SP->AddRef, pSPD[%p]", pSPD); IDP8ServiceProvider_AddRef(pISP); pSPD->IISPIntf = pISP; pSPD->pPData = pPData; DNInterlockedIncrement(&pPData->lSPActiveCount); } else { pSPD = NULL;
DPFX(DPFPREP,0, "Returning DPNERR_UNINITIALIZED - DNPProtocolInitialize has not been called"); hr = DPNERR_UNINITIALIZED; goto Exit; }
*phSPContext = pSPD;
Exit: AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
return hr; }
/*
** Remove Service Provider ** ** It is higher layer's responsibility to make sure that there are no pending commands ** when this function is called, although we can do a certain amount of cleanup ourselves. ** For the moment we will ASSERT that everything is in fact finished up. ** */
#undef DPF_MODNAME
#define DPF_MODNAME "DNPRemoveServiceProvider"
HRESULT DNPRemoveServiceProvider(HANDLE hProtocolData, HANDLE hSPHandle) { ProtocolData* pPData; PSPD pSPD; PFMD pFMD; DWORD dwInterval;
#ifdef DBG
PEPD pEPD; PMSD pMSD; #endif // DBG
DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: hProtocolData[%p], hSPHandle[%x]", hProtocolData, hSPHandle);
pPData = (ProtocolData*)hProtocolData; ASSERT_PPD(pPData);
pSPD = (PSPD) hSPHandle; ASSERT_SPD(pSPD);
// There are several steps to shutdown:
// 1. All Core initiated commands must be cancelled prior to this function being called.
// We will assert in debug that the Core has done this.
// 2. All endpoints must be terminated by the Core prior to this function being called.
// We will assert in debug that the Core has done this.
// Now there are things on the SPD->SendQueue and SPD->PendingQueue that are not owned
// by any Command or Endpoint, and there may also be a SendThread Timer running held
// on SPD->SendHandle. No one else can clean these up, so these are our responsibility
// to clean up here. Items on the queues will be holding references to EPDs, so the
// EPDs will not be able to go away until we do this.
// 3. Cancel SPD->SendHandle Send Timer. This prevents items on the SendQueue from
// being submitted to the SP and moved to the PendingQueue.
// 4. Empty the SendQueue.
// 5. If we fail to cancel the SendHandle Send Timer, wait for it to run and figure out
// that we are going away. We do this after emptying the SendQueue for simplicity
// since the RunSendThread code checks for an empty SendQueue to know if it has work
// to do.
// 6. Wait for all messages to drain from the PendingQueue as the SP completes them.
// 7. Wait for any active EPDs to go away.
// 8. Call SP->Close only after all of the above so that we can ensure that we will make
// no calls to the SP after Close.
Lock(&pSPD->SPLock); pSPD->ulSPFlags |= SPFLAGS_TERMINATING; // Nothing new gets in...
#ifdef DBG
// Check for uncancelled commands, SPLock held
CBilink* pLink = pSPD->blMessageList.GetNext(); while (pLink != &pSPD->blMessageList) { pMSD = CONTAINING_OBJECT(pLink, MSD, blSPLinkage); ASSERT_MSD(pMSD); ASSERT(pMSD->ulMsgFlags1 & MFLAGS_ONE_ON_GLOBAL_LIST); DPFX(DPFPREP,0, "There are un-cancelled commands remaining on the Command List, Core didn't clean up properly - pMSD[%p], Context[%x]", pMSD, pMSD->Context); ASSERT(0); // This is fatal, we can't make the guarantees we need to below under these conditions.
pLink = pLink->GetNext(); }
// Check for EPDs that have not been terminated, SPLock still held
pLink = pSPD->blEPDActiveList.GetNext(); while (pLink != &pSPD->blEPDActiveList) { pEPD = CONTAINING_OBJECT(pLink, EPD, blActiveLinkage); ASSERT_EPD(pEPD);
if (!(pEPD->ulEPFlags & EPFLAGS_STATE_TERMINATING)) { DPFX(DPFPREP,0, "There are non-terminated endpoints remaining on the Endpoint List, Core didn't clean up properly - pEPD[%p], Context[%x]", pEPD, pEPD->Context); ASSERT(0); // This is fatal, we can't make the guarantees we need to below under these conditions.
}
pLink = pLink->GetNext(); }
#endif // DBG
// Clean off the Send Queue, SPLock still held
while(!pSPD->blSendQueue.IsEmpty()) { pFMD = CONTAINING_OBJECT(pSPD->blSendQueue.GetNext(), FMD, blQLinkage); ASSERT_FMD(pFMD);
ASSERT_EPD(pFMD->pEPD);
DPFX(DPFPREP,1, "Cleaning FMD off of SendQueue pSPD[%p], pFMD[%p], pEPD[%p]", pSPD, pFMD, pFMD->pEPD);
pFMD->blQLinkage.RemoveFromList();
// RELEASE_EPD will need to have the EPD lock, so we cannot hold the SPLock while calling it.
Unlock(&pSPD->SPLock);
Lock(&pFMD->pEPD->EPLock); RELEASE_EPD(pFMD->pEPD, "UNLOCK (Releasing Leftover CMD FMD)"); // releases EPLock
RELEASE_FMD(pFMD, "SP Submit");
Lock(&pSPD->SPLock); }
// In case we failed to cancel the SendHandle Timer above, wait for the send thread to run and figure
// out that we are going away. We want to be outside the SPLock while doing this.
dwInterval = 10; while(pSPD->ulSPFlags & SPFLAGS_SEND_THREAD_SCHEDULED) { Unlock(&pSPD->SPLock); IDirectPlay8ThreadPoolWork_SleepWhileWorking(pPData->pDPThreadPoolWork, dwInterval, 0); dwInterval += 5; ASSERT(dwInterval < 500); Lock(&pSPD->SPLock); }
// Clean off the Pending Queue, SPLock still held
dwInterval = 10; while (!pSPD->blPendingQueue.IsEmpty()) { Unlock(&pSPD->SPLock); IDirectPlay8ThreadPoolWork_SleepWhileWorking(pPData->pDPThreadPoolWork, dwInterval, 0); dwInterval += 5; ASSERT(dwInterval < 500); Lock(&pSPD->SPLock); }
// By now we are only waiting for the SP to do any final calls to CommandComplete that are needed to take
// our EPD ref count down to nothing. We will wait while the SP does this.
dwInterval = 10; while(!(pSPD->blEPDActiveList.IsEmpty())) { Unlock(&pSPD->SPLock); IDirectPlay8ThreadPoolWork_SleepWhileWorking(pPData->pDPThreadPoolWork, dwInterval, 0); dwInterval += 5; ASSERT(dwInterval < 500); Lock(&pSPD->SPLock); }
// By this time everything pending had better be gone!
ASSERT(pSPD->blEPDActiveList.IsEmpty()); // Should not be any Endpoints left
ASSERT(pSPD->blSendQueue.IsEmpty()); // Should not be any frames on sendQ.
ASSERT(pSPD->blPendingQueue.IsEmpty()); // Should not be any frame in SP either
// Leave SPLock for the last time
Unlock(&pSPD->SPLock);
// Now that all frames are cleared out of SP, there should be no more End Points waiting around to close.
// We are clear to tell the SP to go away.
AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
DPFX(DPFPREP,DPF_CALLOUT_LVL, "Calling SP->Close, pSPD[%p]", pSPD); IDP8ServiceProvider_Close(pSPD->IISPIntf); DPFX(DPFPREP,DPF_CALLOUT_LVL, "Calling SP->Release, pSPD[%p]", pSPD); IDP8ServiceProvider_Release(pSPD->IISPIntf);
// Clean up the SPD object
DNDeleteCriticalSection(&pSPD->SPLock); DNFree(pSPD);
// Remove the reference of this SP from the main Protocol object
ASSERT(pPData->lSPActiveCount > 0); DNInterlockedDecrement(&pPData->lSPActiveCount);
AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
return DPN_OK; }
|