2416 lines
58 KiB
C++
2416 lines
58 KiB
C++
/*++
|
||
|
||
Copyright (c) 1998 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
icasync.cxx
|
||
|
||
Abstract:
|
||
|
||
Contains async socket (select) thread and support functions. Work items now
|
||
processed by SHLWAPI/KERNEL32 thread pool
|
||
|
||
Contents:
|
||
InitializeAsyncSupport
|
||
TerminateAsyncSupport
|
||
QueueSocketWorkItem
|
||
BlockWorkItem
|
||
UnblockWorkItems
|
||
CheckForBlockedWorkItems
|
||
ICAsyncThread::~ICAsyncThread
|
||
ICAsyncThread::QueueSocketWorkItem
|
||
ICAsyncThread::BlockWorkItem
|
||
ICAsyncThread::UnblockWorkItems
|
||
ICAsyncThread::CheckForBlockedWorkItems
|
||
ICAsyncThread::SelectThreadWrapper
|
||
ICAsyncThread::SelectThread
|
||
(ICAsyncThread::CreateSelectSocket)
|
||
(ICAsyncThread::DestroySelectSocket)
|
||
(ICAsyncThread::RecreateSelectSocket)
|
||
(ICAsyncThread::InterruptSelect)
|
||
(ICAsyncThread::DrainSelectSocket)
|
||
|
||
Author:
|
||
|
||
Richard L Firth (rfirth) 04-Mar-1998
|
||
|
||
Environment:
|
||
|
||
Win32 user-mode
|
||
|
||
Revision History:
|
||
|
||
04-Mar-1998 rfirth
|
||
Created
|
||
|
||
--*/
|
||
|
||
#include <wininetp.h>
|
||
#include <perfdiag.hxx>
|
||
|
||
DWORD CAsyncCount::AddRef()
|
||
{
|
||
DWORD error = ERROR_SUCCESS;
|
||
|
||
if (!GeneralInitCritSec.Lock())
|
||
{
|
||
error = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto quit;
|
||
}
|
||
|
||
++dwRef;
|
||
|
||
GeneralInitCritSec.Unlock();
|
||
quit:
|
||
return error;
|
||
}
|
||
|
||
VOID CAsyncCount::Release()
|
||
{
|
||
BOOL bUnlock = GeneralInitCritSec.Lock();
|
||
|
||
//Decrement the refcount always, but only Terminate if we obtained the critsec.
|
||
if (!--dwRef && bUnlock)
|
||
{
|
||
//BUGBUG-enable later.
|
||
TerminateAsyncSupport(TRUE);
|
||
}
|
||
|
||
if (bUnlock)
|
||
{
|
||
GeneralInitCritSec.Unlock();
|
||
}
|
||
}
|
||
|
||
//
|
||
// private classes
|
||
//
|
||
|
||
|
||
class ICAsyncThread {
|
||
|
||
private:
|
||
|
||
CPriorityList m_BlockedQueue;
|
||
SOCKET m_SelectSocket;
|
||
LONG m_lSelectInterrupts;
|
||
BOOL m_bTerminating;
|
||
DWORD m_dwError;
|
||
HANDLE m_hThread;
|
||
BOOL m_bCleanUp;
|
||
|
||
public:
|
||
|
||
ICAsyncThread() {
|
||
|
||
DEBUG_ENTER((DBG_ASYNC,
|
||
None,
|
||
"ICAsyncThread::ICAsyncThread",
|
||
NULL
|
||
));
|
||
|
||
m_SelectSocket = INVALID_SOCKET;
|
||
m_lSelectInterrupts = -1;
|
||
m_bTerminating = FALSE;
|
||
m_dwError = ERROR_SUCCESS;
|
||
|
||
DWORD dwThreadId;
|
||
|
||
m_hThread = CreateThread(
|
||
NULL,
|
||
0,
|
||
(LPTHREAD_START_ROUTINE)ICAsyncThread::SelectThreadWrapper,
|
||
(LPVOID)this,
|
||
0,
|
||
&dwThreadId
|
||
);
|
||
if (m_hThread == NULL) {
|
||
SetError();
|
||
}
|
||
|
||
m_bCleanUp = FALSE;
|
||
|
||
DEBUG_LEAVE(0);
|
||
}
|
||
|
||
~ICAsyncThread();
|
||
|
||
VOID SetCleanUp()
|
||
{
|
||
m_bCleanUp = TRUE;
|
||
}
|
||
|
||
DWORD GetError(VOID) const {
|
||
return m_dwError;
|
||
}
|
||
|
||
VOID SetError(DWORD dwError = GetLastError()) {
|
||
m_dwError = dwError;
|
||
}
|
||
|
||
BOOL IsTerminating(VOID) const {
|
||
return m_bTerminating;
|
||
}
|
||
|
||
VOID SetTerminating(VOID) {
|
||
m_bTerminating = TRUE;
|
||
}
|
||
|
||
DWORD
|
||
QueueSocketWorkItem(
|
||
IN CFsm * pFsm
|
||
);
|
||
|
||
BOOL
|
||
RemoveFsmFromAsyncList(
|
||
IN CFsm * pFsm
|
||
);
|
||
|
||
DWORD
|
||
BlockWorkItem(
|
||
IN CFsm * WorkItem,
|
||
IN DWORD_PTR dwBlockId,
|
||
IN DWORD dwTimeout = TP_NO_TIMEOUT
|
||
);
|
||
|
||
DWORD
|
||
UnblockWorkItems(
|
||
IN DWORD dwCount,
|
||
IN DWORD_PTR dwBlockId,
|
||
IN DWORD dwError,
|
||
IN LONG lPriority = TP_NO_PRIORITY_CHANGE
|
||
);
|
||
|
||
DWORD
|
||
CheckForBlockedWorkItems(
|
||
IN DWORD dwCount,
|
||
IN DWORD_PTR dwBlockId
|
||
);
|
||
|
||
static
|
||
DWORD
|
||
SelectThreadWrapper(
|
||
IN ICAsyncThread * pThread
|
||
);
|
||
|
||
DWORD
|
||
SelectThread(
|
||
VOID
|
||
);
|
||
|
||
DWORD
|
||
CreateSelectSocket(
|
||
VOID
|
||
);
|
||
|
||
PRIVATE
|
||
VOID
|
||
DestroySelectSocket(
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
RecreateSelectSocket(
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
InterruptSelect(
|
||
VOID
|
||
);
|
||
|
||
BOOL
|
||
DrainSelectSocket(
|
||
VOID
|
||
);
|
||
};
|
||
|
||
//
|
||
// private data
|
||
//
|
||
|
||
PRIVATE ICAsyncThread * p_AsyncThread = NULL;
|
||
PRIVATE HANDLE* p_ThreadHandleArray = NULL;
|
||
PRIVATE DWORD* p_ThreadIdArray = NULL;
|
||
PRIVATE int p_iNumIOCPThreads = 0;
|
||
|
||
//
|
||
// functions
|
||
//
|
||
|
||
|
||
|
||
VOID
|
||
TerminateIOCPGlobals()
|
||
{
|
||
if (g_lpCustomOverlapped)
|
||
{
|
||
delete g_lpCustomOverlapped;
|
||
g_lpCustomOverlapped = NULL;
|
||
}
|
||
if (g_hCompletionPort)
|
||
{
|
||
CloseHandle(g_hCompletionPort);
|
||
g_hCompletionPort = NULL;
|
||
}
|
||
}
|
||
|
||
|
||
DWORD
|
||
IOCompletionThreadFunc(
|
||
IN ULONG_PTR pContext
|
||
)
|
||
{
|
||
LPOVERLAPPED lpOverlapped;
|
||
DWORD dwBytes;
|
||
ULONG_PTR lpCompletionKey;
|
||
DWORD dwTimeout = 1000;
|
||
DWORD dwError = 0;
|
||
BOOL bDeleteOverlapped = FALSE;
|
||
LPINTERNET_THREAD_INFO lpThreadInfo;
|
||
|
||
BOOL fExitThread = FALSE;
|
||
lpThreadInfo = InternetGetThreadInfo();
|
||
|
||
while (TRUE)
|
||
{
|
||
|
||
BOOL bRet = GetQueuedCompletionStatus(g_hCompletionPort,
|
||
&dwBytes,
|
||
&lpCompletionKey,
|
||
&lpOverlapped,
|
||
fExitThread ? 0 : INFINITE);
|
||
|
||
DEBUG_ENTER((DBG_API,
|
||
Dword,
|
||
"***GetQueuedCompletionStatus",
|
||
"(hcomp)%#x, (dwBytes)%#x, (completionkey)%#x, (overlapped)%#x",
|
||
g_hCompletionPort,
|
||
dwBytes,
|
||
lpCompletionKey,
|
||
lpOverlapped
|
||
));
|
||
|
||
if (!bRet && !lpOverlapped)
|
||
{
|
||
DEBUG_LEAVE_API(NULL);
|
||
|
||
DWORD dwError = GetLastError();
|
||
|
||
if (dwError == WAIT_TIMEOUT)
|
||
{
|
||
break;
|
||
}
|
||
|
||
// other errors currently not possible since we only have custom completion packets.
|
||
INET_ASSERT (FALSE);
|
||
|
||
continue;
|
||
}
|
||
|
||
ICSocket* pObject;
|
||
CFsm* pFsm;
|
||
CWrapOverlapped* lpWrapOverlapped;
|
||
if (lpOverlapped != g_lpCustomOverlapped)
|
||
{
|
||
pObject = (ICSocket *) lpCompletionKey;
|
||
pFsm = pObject->GetAndSetCurrentFsm(NULL);
|
||
DEBUG_LEAVE(pFsm);
|
||
|
||
#if INET_DEBUG
|
||
InterlockedDecrement(&g_cWSACompletions);
|
||
#endif
|
||
|
||
INET_ASSERT(pFsm);
|
||
bDeleteOverlapped = TRUE;
|
||
lpWrapOverlapped = GetWrapOverlappedObject(lpOverlapped);
|
||
|
||
if (pFsm->HasTimeout())
|
||
{
|
||
if (!RemoveFsmFromAsyncList(pFsm))
|
||
{
|
||
//failure! the select thread already enforced timeout and updated state
|
||
//INET_ASSERT (FALSE && "COOL");
|
||
goto runworkitem;
|
||
}
|
||
}
|
||
|
||
((CFsm_SocketIOCP*)pFsm)->dwBytesTransferred = dwBytes;
|
||
((CFsm_SocketIOCP*)pFsm)->bIOCPSuccess = bRet;
|
||
|
||
pFsm->ResetSocket();
|
||
pFsm->SetPriority(TP_NO_PRIORITY_CHANGE);
|
||
|
||
if (bRet)
|
||
{
|
||
pFsm->SetError(ERROR_SUCCESS);
|
||
pFsm->SetState(pFsm->GetNextState());
|
||
}
|
||
else
|
||
{
|
||
//VENKATK_BUG-informational assert - remove later.
|
||
DWORD dwErrorDebug = GetLastError();
|
||
INET_ASSERT (FALSE && "IoCompletionError");
|
||
|
||
((CFsm_SocketIOCP*)pFsm)->dwIOCPError = GetLastError();
|
||
|
||
if (((CFsm_SocketIOCP*)pFsm)->dwIOCPError == WSA_OPERATION_ABORTED)
|
||
pFsm->SetErrorState(ERROR_WINHTTP_OPERATION_CANCELLED);
|
||
else
|
||
pFsm->SetErrorState(ERROR_WINHTTP_CONNECTION_ERROR);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
DEBUG_LEAVE(lpCompletionKey);
|
||
INET_ASSERT( lpOverlapped == g_lpCustomOverlapped );
|
||
INET_ASSERT( (dwBytes == COMPLETION_BYTES_CUSTOM) ||
|
||
(dwBytes == COMPLETION_BYTES_EXITIOCP) );
|
||
|
||
if (dwBytes == COMPLETION_BYTES_EXITIOCP)
|
||
{
|
||
INET_ASSERT (lpCompletionKey == NULL);
|
||
break;
|
||
}
|
||
|
||
bDeleteOverlapped = FALSE;
|
||
pFsm = (CFsm*) lpCompletionKey;
|
||
|
||
#if INET_DEBUG
|
||
InterlockedDecrement(&g_cCustomCompletions);
|
||
#endif
|
||
}
|
||
|
||
runworkitem:
|
||
if (pFsm)
|
||
{
|
||
INTERNET_HANDLE_BASE *pBase = ((INTERNET_HANDLE_BASE *)pFsm->GetMappedHandleObject());
|
||
pBase->Reference(); // might go away if closehandle is called.
|
||
|
||
lpThreadInfo->IsAsyncWorkerThread = TRUE;
|
||
dwError = CFsm::RunWorkItem(pFsm);
|
||
|
||
if (dwError != ERROR_IO_PENDING)
|
||
{
|
||
// If there are any pending async work items for this request,
|
||
// then schedule the first one in the list.
|
||
if (pBase->GetHandleType() == TypeHttpRequestHandle)
|
||
{
|
||
HTTP_REQUEST_HANDLE_OBJECT *pRequest = (HTTP_REQUEST_HANDLE_OBJECT *)pBase;
|
||
if (pRequest->LockAsync())
|
||
{
|
||
if (!pRequest->IsWorkItemListEmpty())
|
||
{
|
||
pRequest->ScheduleWorkItem();
|
||
}
|
||
else
|
||
{
|
||
pRequest->SetWorkItemInProgress(FALSE);
|
||
}
|
||
pRequest->UnlockAsync();
|
||
}
|
||
else
|
||
{
|
||
// Need to report an error even if async item was
|
||
// successful if we can't check for pending work items.
|
||
dwError = (dwError == ERROR_SUCCESS ?
|
||
ERROR_NOT_ENOUGH_MEMORY : dwError);
|
||
}
|
||
}
|
||
}
|
||
pBase->Dereference();
|
||
// Must stay marked as being on an async worker thread until after
|
||
// releasing the reference in order to prevent confusion in the async count.
|
||
lpThreadInfo->IsAsyncWorkerThread = FALSE;
|
||
}
|
||
else
|
||
{
|
||
INET_ASSERT (pFsm);
|
||
}
|
||
|
||
if (!lpThreadInfo)
|
||
{
|
||
lpThreadInfo = InternetGetThreadInfo();
|
||
}
|
||
|
||
if (lpThreadInfo && lpThreadInfo->fExitThread)
|
||
{
|
||
//exit this thread after dequeuing as many available completions as possible.
|
||
fExitThread = TRUE;
|
||
}
|
||
|
||
if (bDeleteOverlapped)
|
||
{
|
||
lpWrapOverlapped->Dereference();//VENKATKBUG - move it up later, but for now keep it here for debugging.
|
||
}
|
||
}
|
||
|
||
if (fExitThread)
|
||
{
|
||
if (GeneralInitCritSec.Lock())
|
||
{
|
||
if (g_pAsyncCount && !g_pAsyncCount->GetRef())
|
||
{
|
||
//Additional check to make sure we don't go and knock off a freshly created set of globals.
|
||
//These won't be leaked - would already have been deleted in the renewed InitializeIOCPSupport call.
|
||
TerminateIOCPGlobals();
|
||
}
|
||
GeneralInitCritSec.Unlock();
|
||
}
|
||
}
|
||
|
||
return dwError;
|
||
}
|
||
|
||
/*
|
||
* called from InitalizeAsyncSupport and synchronized there
|
||
*
|
||
* also, don't bother to cleanup - if there's an error TerminateIOCPSupport is called.
|
||
*/
|
||
|
||
DWORD
|
||
InitializeIOCPSupport(
|
||
VOID
|
||
)
|
||
{
|
||
int dwNumIOCPThreads = g_cNumIOCPThreads;
|
||
|
||
if (!dwNumIOCPThreads)
|
||
{
|
||
SYSTEM_INFO sSysInfo;
|
||
memset(&sSysInfo, 0, sizeof(SYSTEM_INFO));
|
||
GetSystemInfo(&sSysInfo);
|
||
if (sSysInfo.dwNumberOfProcessors)
|
||
dwNumIOCPThreads = sSysInfo.dwNumberOfProcessors;
|
||
|
||
if (!dwNumIOCPThreads)
|
||
{
|
||
dwNumIOCPThreads = WINHTTP_GLOBAL_IOCP_THREADS_BACKUP;
|
||
}
|
||
}
|
||
g_cNumIOCPThreads = dwNumIOCPThreads;
|
||
|
||
#if INET_DEBUG
|
||
g_cWSACompletions = 0;
|
||
g_cCustomCompletions = 0;
|
||
#endif
|
||
|
||
DWORD dwError = ERROR_SUCCESS;
|
||
|
||
//May be left over from previous run.
|
||
if (g_hCompletionPort)
|
||
{
|
||
CloseHandle(g_hCompletionPort);
|
||
g_hCompletionPort = NULL;
|
||
};
|
||
|
||
if (g_lpCustomOverlapped)
|
||
{
|
||
delete g_lpCustomOverlapped;
|
||
g_lpCustomOverlapped = NULL;
|
||
}
|
||
|
||
g_hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
|
||
|
||
if (!g_hCompletionPort)
|
||
{
|
||
dwError = GetLastError();
|
||
goto quit;
|
||
}
|
||
|
||
g_lpCustomOverlapped = New OVERLAPPED();
|
||
|
||
if (!g_lpCustomOverlapped)
|
||
{
|
||
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto quit;
|
||
}
|
||
|
||
memset(g_lpCustomOverlapped, 0, sizeof(OVERLAPPED));
|
||
|
||
p_ThreadHandleArray = New HANDLE[dwNumIOCPThreads];
|
||
p_ThreadIdArray = New DWORD[dwNumIOCPThreads];
|
||
|
||
if (!p_ThreadHandleArray || !p_ThreadIdArray)
|
||
{
|
||
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto quit;
|
||
}
|
||
|
||
for (int i=0; i<dwNumIOCPThreads; i++)
|
||
{
|
||
BOOL bSuccess;
|
||
DWORD dwThreadId;
|
||
HANDLE hThread;
|
||
|
||
hThread = CreateThread(
|
||
NULL,
|
||
0,
|
||
(LPTHREAD_START_ROUTINE)IOCompletionThreadFunc,
|
||
NULL,
|
||
0,
|
||
&dwThreadId
|
||
);
|
||
|
||
if (hThread)
|
||
{
|
||
p_ThreadHandleArray[p_iNumIOCPThreads++] = hThread;
|
||
p_ThreadIdArray[i] = dwThreadId;
|
||
}
|
||
else
|
||
{
|
||
//successfully queued functions terminated in TerminateIOCPSupport
|
||
dwError = GetLastError();
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
quit:
|
||
return dwError;
|
||
}
|
||
|
||
|
||
VOID
|
||
TerminateIOCPSupport(
|
||
VOID
|
||
)
|
||
{
|
||
DWORD dwWaitResult;
|
||
int iNumThreadsToEnd = p_iNumIOCPThreads;
|
||
HANDLE* pThreadHandleArray = p_ThreadHandleArray;
|
||
DWORD fDeleteHandleArray = FALSE;
|
||
BOOL fTerminatingOnWorker = FALSE;
|
||
|
||
if (!p_ThreadHandleArray)
|
||
goto quit;
|
||
|
||
#if INET_DEBUG
|
||
if (g_cWSACompletions || g_cCustomCompletions)
|
||
{
|
||
INET_ASSERT(FALSE);
|
||
WaitForMultipleObjects(p_iNumIOCPThreads, p_ThreadHandleArray, TRUE, 500);
|
||
}
|
||
#endif
|
||
|
||
LPINTERNET_THREAD_INFO lpThreadInfo;
|
||
|
||
lpThreadInfo = InternetGetThreadInfo();
|
||
|
||
if (!lpThreadInfo)
|
||
{
|
||
goto quit;
|
||
}
|
||
|
||
if (lpThreadInfo->IsAsyncWorkerThread)
|
||
{
|
||
//can't terminate the worker thread we're on.
|
||
--iNumThreadsToEnd;
|
||
fTerminatingOnWorker = TRUE;
|
||
|
||
lpThreadInfo->fExitThread = TRUE;
|
||
|
||
if (iNumThreadsToEnd)
|
||
{
|
||
int iIndex = -1;
|
||
DWORD dwThreadId = GetCurrentThreadId();
|
||
|
||
//verify we're on a worker thread.
|
||
for (int i=0; i<p_iNumIOCPThreads; i++)
|
||
{
|
||
if (p_ThreadIdArray[i] == dwThreadId)
|
||
{
|
||
iIndex = i;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (iIndex != -1)
|
||
{
|
||
pThreadHandleArray = New HANDLE[iNumThreadsToEnd];
|
||
|
||
if (!pThreadHandleArray)
|
||
{
|
||
goto quit;
|
||
}
|
||
fDeleteHandleArray = TRUE;
|
||
|
||
int i=0;
|
||
for (int j=0; j<p_iNumIOCPThreads; j++)
|
||
{
|
||
if (j != iIndex)
|
||
{
|
||
pThreadHandleArray[i++] = p_ThreadHandleArray[j];
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
INET_ASSERT(FALSE);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
goto quit;
|
||
}
|
||
}
|
||
|
||
for (int i=0; i<iNumThreadsToEnd; i++)
|
||
{
|
||
BOOL bSuccess = PostQueuedCompletionStatus(g_hCompletionPort,
|
||
COMPLETION_BYTES_EXITIOCP,
|
||
NULL,
|
||
g_lpCustomOverlapped
|
||
);
|
||
INET_ASSERT (bSuccess);
|
||
}
|
||
|
||
dwWaitResult = WaitForMultipleObjects(iNumThreadsToEnd, pThreadHandleArray, TRUE, 2000);
|
||
|
||
if ((dwWaitResult == WAIT_TIMEOUT) || (dwWaitResult == WAIT_FAILED))
|
||
{
|
||
goto forceTerminate;
|
||
}
|
||
else
|
||
{
|
||
INET_ASSERT ( ((LONG)dwWaitResult >= WAIT_OBJECT_0) && ((LONG)dwWaitResult < (WAIT_OBJECT_0+p_iNumIOCPThreads)));
|
||
}
|
||
|
||
quit:
|
||
INET_ASSERT ((g_cWSACompletions == 0) &&
|
||
(g_cCustomCompletions == 0));
|
||
|
||
if (p_ThreadHandleArray)
|
||
{
|
||
for (int i=0; i<p_iNumIOCPThreads; i++)
|
||
{
|
||
CloseHandle(p_ThreadHandleArray[i]);
|
||
}
|
||
|
||
delete [] p_ThreadHandleArray;
|
||
p_ThreadHandleArray = NULL;
|
||
}
|
||
if (p_ThreadIdArray)
|
||
{
|
||
delete [] p_ThreadIdArray;
|
||
p_ThreadIdArray = NULL;
|
||
}
|
||
if (fDeleteHandleArray)
|
||
{
|
||
delete [] pThreadHandleArray;
|
||
}
|
||
if (fTerminatingOnWorker)
|
||
{
|
||
//don't delete these globals since they may be in use on this last thread.
|
||
TerminateIOCPGlobals();
|
||
}
|
||
p_iNumIOCPThreads = 0;
|
||
|
||
return;
|
||
|
||
forceTerminate:
|
||
for (int i=0; i<iNumThreadsToEnd; i++)
|
||
{
|
||
if (WaitForSingleObject(pThreadHandleArray[i], 0) != WAIT_OBJECT_0)
|
||
{
|
||
TerminateThread(pThreadHandleArray[i], -1);
|
||
}
|
||
}
|
||
goto quit;
|
||
}
|
||
|
||
|
||
DWORD
|
||
InitializeAsyncSupport(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Create async select thread object
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Success - ERROR_SUCCESS
|
||
|
||
|
||
Failure -
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_ASYNC,
|
||
Dword,
|
||
"InitializeAsyncSupport",
|
||
NULL
|
||
));
|
||
|
||
DWORD error = ERROR_WINHTTP_SHUTDOWN;
|
||
|
||
if (!InDllCleanup) {
|
||
|
||
if (!GeneralInitCritSec.Lock())
|
||
{
|
||
error = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto quit;
|
||
}
|
||
|
||
if (!InDllCleanup) {
|
||
if (p_AsyncThread == NULL) {
|
||
|
||
HANDLE hThreadToken = NULL;
|
||
//
|
||
// If the current thread is impersonating, then grab its access token
|
||
// and revert the current thread (so it is nolonger impersonating).
|
||
// After creating the worker thread, we will make the main thread
|
||
// impersonate again. Apparently you should not call CreateThread
|
||
// while impersonating.
|
||
//
|
||
if (OpenThreadToken(GetCurrentThread(), (TOKEN_IMPERSONATE | TOKEN_READ),
|
||
FALSE,
|
||
&hThreadToken))
|
||
{
|
||
INET_ASSERT(hThreadToken != 0);
|
||
|
||
RevertToSelf();
|
||
}
|
||
|
||
p_AsyncThread = New ICAsyncThread();
|
||
if (p_AsyncThread == NULL) {
|
||
error = ERROR_NOT_ENOUGH_MEMORY;
|
||
} else {
|
||
error = p_AsyncThread->GetError();
|
||
if (error == ERROR_SUCCESS)
|
||
error = InitializeIOCPSupport();
|
||
if (error != ERROR_SUCCESS) {
|
||
TerminateAsyncSupport(TRUE);
|
||
}
|
||
}
|
||
|
||
if (hThreadToken)
|
||
{
|
||
SetThreadToken(NULL, hThreadToken);
|
||
|
||
CloseHandle(hThreadToken);
|
||
}
|
||
|
||
|
||
} else {
|
||
error = ERROR_SUCCESS;
|
||
}
|
||
}
|
||
GeneralInitCritSec.Unlock();
|
||
}
|
||
|
||
quit:
|
||
DEBUG_LEAVE(error);
|
||
|
||
return error;
|
||
}
|
||
|
||
|
||
VOID
|
||
TerminateAsyncSupport(
|
||
BOOL bCleanUp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Terminates async support
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_ASYNC,
|
||
None,
|
||
"TerminateAsyncSupport",
|
||
NULL
|
||
));
|
||
|
||
ICAsyncThread * pThread;
|
||
|
||
if (GeneralInitCritSec.Lock())
|
||
{
|
||
TerminateIOCPSupport();
|
||
|
||
pThread = (ICAsyncThread *)InterlockedExchangePointer((PVOID*)&p_AsyncThread,
|
||
(PVOID)NULL
|
||
);
|
||
|
||
if (pThread != NULL)
|
||
{
|
||
if (bCleanUp)
|
||
pThread->SetCleanUp();
|
||
delete pThread;
|
||
}
|
||
|
||
GeneralInitCritSec.Unlock();
|
||
}
|
||
|
||
DEBUG_LEAVE(0);
|
||
}
|
||
|
||
|
||
|
||
BOOL
|
||
RemoveFsmFromAsyncList(
|
||
IN CFsm * pFsm
|
||
)
|
||
{
|
||
BOOL bSuccess = TRUE;
|
||
if (p_AsyncThread != NULL)
|
||
{
|
||
bSuccess = p_AsyncThread->RemoveFsmFromAsyncList(pFsm);
|
||
}
|
||
|
||
return bSuccess;
|
||
}
|
||
|
||
|
||
DWORD
|
||
QueueSocketWorkItem(
|
||
IN CFsm * pFsm,
|
||
IN SOCKET Socket
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Adds a blocked socket operation/work item to the blocked queue
|
||
|
||
Arguments:
|
||
|
||
pFsm - in-progress socket operation (FSM)
|
||
|
||
Socket - socket handle to wait on
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Success - ERROR_IO_PENDING
|
||
|
||
Failure - ERROR_WINHTTP_INTERNAL_ERROR
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_ASYNC,
|
||
Dword,
|
||
"QueueSocketWorkItem",
|
||
"%#x, %#x",
|
||
pFsm,
|
||
Socket
|
||
));
|
||
|
||
DWORD error = ERROR_WINHTTP_INTERNAL_ERROR;
|
||
|
||
if (p_AsyncThread != NULL) {
|
||
pFsm->SetSocket(Socket);
|
||
error = p_AsyncThread->QueueSocketWorkItem(pFsm);
|
||
if (error == ERROR_SUCCESS) {
|
||
error = ERROR_IO_PENDING;
|
||
}
|
||
}
|
||
|
||
INET_ASSERT(error != ERROR_WINHTTP_INTERNAL_ERROR);
|
||
|
||
DEBUG_LEAVE(error);
|
||
|
||
return error;
|
||
}
|
||
|
||
|
||
|
||
DWORD
|
||
BlockWorkItem(
|
||
IN CFsm * pFsm,
|
||
IN DWORD_PTR dwBlockId,
|
||
IN DWORD dwTimeout
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Blocks a work item
|
||
|
||
Arguments:
|
||
|
||
pFsm - work item to block
|
||
|
||
dwBlockId - block on this id
|
||
|
||
dwTimeout - for this number of milliseconds
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Error - ERROR_SUCCESS
|
||
|
||
Failure -
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_ASYNC,
|
||
Dword,
|
||
"BlockWorkItem",
|
||
"%#x, %#x, %d",
|
||
pFsm,
|
||
dwBlockId,
|
||
dwTimeout
|
||
));
|
||
|
||
DWORD error = ERROR_WINHTTP_INTERNAL_ERROR;
|
||
|
||
if (p_AsyncThread != NULL) {
|
||
error = p_AsyncThread->BlockWorkItem(pFsm, dwBlockId, dwTimeout);
|
||
}
|
||
|
||
INET_ASSERT(error != ERROR_WINHTTP_INTERNAL_ERROR);
|
||
|
||
DEBUG_LEAVE(error);
|
||
|
||
return error;
|
||
}
|
||
|
||
|
||
DWORD
|
||
UnblockWorkItems(
|
||
IN DWORD dwCount,
|
||
IN DWORD_PTR dwBlockId,
|
||
IN DWORD dwError,
|
||
IN LONG lPriority
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Unblocks 1 or more work items
|
||
|
||
Arguments:
|
||
|
||
dwCount - unblock this many work items
|
||
|
||
dwBlockId - that are blocked on this id
|
||
|
||
dwError - with this error
|
||
|
||
lPriority - new priority unless default value of TP_NO_PRIORITY_CHANGE
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Success - ERROR_SUCCESS
|
||
|
||
Failure -
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_ASYNC,
|
||
Int,
|
||
"UnblockWorkItems",
|
||
"%d, %#x, %d (%s), %d",
|
||
dwCount,
|
||
dwBlockId,
|
||
dwError,
|
||
InternetMapError(dwError),
|
||
lPriority
|
||
));
|
||
|
||
DWORD dwUnblocked = 0;
|
||
|
||
if (p_AsyncThread != NULL) {
|
||
dwUnblocked = p_AsyncThread->UnblockWorkItems(dwCount,
|
||
dwBlockId,
|
||
dwError,
|
||
lPriority
|
||
);
|
||
}
|
||
|
||
DEBUG_LEAVE(dwUnblocked);
|
||
|
||
return dwUnblocked;
|
||
}
|
||
|
||
|
||
DWORD
|
||
CheckForBlockedWorkItems(
|
||
IN DWORD dwCount,
|
||
IN DWORD_PTR dwBlockId
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Checks if there are any items blocked on dwBlockId
|
||
|
||
Arguments:
|
||
|
||
dwCount - number of items to look for
|
||
|
||
dwBlockId - blocked on this id
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Number of blocked items found
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_ASYNC,
|
||
Int,
|
||
"CheckForBlockedWorkItems",
|
||
"%d, %#x",
|
||
dwCount,
|
||
dwBlockId
|
||
));
|
||
|
||
DWORD dwFound = 0;
|
||
|
||
if (p_AsyncThread != NULL) {
|
||
dwFound = p_AsyncThread->CheckForBlockedWorkItems(dwCount, dwBlockId);
|
||
}
|
||
|
||
DEBUG_LEAVE(dwFound);
|
||
|
||
return dwFound;
|
||
}
|
||
|
||
//
|
||
// private functions
|
||
//
|
||
|
||
//
|
||
// ICAsyncThread methods
|
||
//
|
||
|
||
|
||
ICAsyncThread::~ICAsyncThread(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
ICAsyncThread destructor. If we are being dynamically unloaded, signal the
|
||
selecter thread and allow it to cleanup. Else the thread is already dead and
|
||
we just need to reclaim the resources
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_ASYNC,
|
||
None,
|
||
"ICAsyncThread::~ICAsyncThread",
|
||
NULL
|
||
));
|
||
|
||
SetTerminating();
|
||
if (GlobalDynaUnload || m_bCleanUp) {
|
||
InterruptSelect();
|
||
|
||
//
|
||
// Assuming the async thread was successfully created, the above clean-up
|
||
// will have put it in a state where it's going to exit. Need to wait
|
||
// for it to exit before returning from here so it doesn't get scheduled
|
||
// after wininet has been unloaded.
|
||
//
|
||
if(m_hThread)
|
||
{
|
||
DWORD dwRes = WaitForSingleObject(m_hThread, 5 * 1000);
|
||
INET_ASSERT(dwRes == WAIT_OBJECT_0);
|
||
}
|
||
}
|
||
DestroySelectSocket();
|
||
|
||
if(m_hThread)
|
||
{
|
||
CloseHandle(m_hThread);
|
||
}
|
||
|
||
DEBUG_LEAVE(0);
|
||
}
|
||
|
||
|
||
DWORD
|
||
ICAsyncThread::QueueSocketWorkItem(
|
||
IN CFsm * pFsm
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Add the work-item waiting on a blocked socket to the blocked queue.
|
||
Interrupt the SelectThread to alert it to new work
|
||
|
||
Arguments:
|
||
|
||
pFsm - blocked work-item to queue
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Success - ERROR_SUCCESS
|
||
|
||
Failure - ERROR_WINHTTP_INTERNAL_ERROR
|
||
|
||
Async Issues: VENKATK_BUG
|
||
1. Reduce contention for m_BlockedQueue by:
|
||
maintaining sorted queue for timeout-only fsms.
|
||
2. Don't call InterruptSelect() for timeout queueing
|
||
3. check if content can be moved to per-fsm instead of
|
||
global queue..
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_ASYNC,
|
||
Dword,
|
||
"ICAsyncThread::QueueSocketWorkItem",
|
||
"%#x",
|
||
pFsm
|
||
));
|
||
|
||
LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
|
||
DWORD error = ERROR_WINHTTP_INTERNAL_ERROR;
|
||
|
||
INET_ASSERT(lpThreadInfo != NULL);
|
||
|
||
if (lpThreadInfo != NULL) {
|
||
pFsm->StartTimer();
|
||
error = m_BlockedQueue.Insert((CPriorityListEntry *)pFsm->List());
|
||
lpThreadInfo->Fsm = NULL;
|
||
InterruptSelect();
|
||
}
|
||
|
||
DEBUG_LEAVE(error);
|
||
|
||
return error;
|
||
}
|
||
|
||
|
||
BOOL
|
||
ICAsyncThread::RemoveFsmFromAsyncList(
|
||
IN CFsm * pFsm
|
||
)
|
||
{
|
||
BOOL bSuccess = FALSE;
|
||
if (m_BlockedQueue.Acquire())
|
||
{
|
||
if (pFsm->IsOnAsyncList())
|
||
{
|
||
m_BlockedQueue.Remove((CPriorityListEntry *)pFsm);
|
||
pFsm->SetOnAsyncList(FALSE);
|
||
bSuccess = TRUE;
|
||
}
|
||
m_BlockedQueue.Release();
|
||
}
|
||
|
||
return bSuccess;
|
||
}
|
||
|
||
|
||
DWORD
|
||
ICAsyncThread::BlockWorkItem(
|
||
IN CFsm * pFsm,
|
||
IN DWORD_PTR dwBlockId,
|
||
IN DWORD dwTimeout
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Blocks a work item (FSM)
|
||
|
||
Arguments:
|
||
|
||
pFsm - work item (FSM) to block
|
||
|
||
dwBlockId - block on this
|
||
|
||
dwTimeout - for this amount of time (mSec)
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Success - ERROR_SUCCESS
|
||
|
||
Failure -
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_ASYNC,
|
||
Dword,
|
||
"ICAsyncThread::BlockWorkItem",
|
||
"%#x [%d], %#x, %d",
|
||
pFsm,
|
||
pFsm->GetPriority(),
|
||
dwBlockId,
|
||
dwTimeout
|
||
));
|
||
|
||
DWORD error = error = ERROR_WINHTTP_INTERNAL_ERROR;
|
||
LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
|
||
|
||
INET_ASSERT(lpThreadInfo != NULL);
|
||
|
||
if (lpThreadInfo != NULL) {
|
||
pFsm->SetBlockId(dwBlockId);
|
||
pFsm->SetTimeout(dwTimeout);
|
||
|
||
RESET_FSM_OWNED(pFsm);
|
||
|
||
DEBUG_PRINT(ASYNC,
|
||
INFO,
|
||
("!!! FSM %#x unowned\n",
|
||
pFsm
|
||
));
|
||
|
||
error = m_BlockedQueue.Insert((CPriorityListEntry *)pFsm->List());
|
||
lpThreadInfo->Fsm = NULL;
|
||
}
|
||
|
||
DEBUG_LEAVE(error);
|
||
return error;
|
||
}
|
||
|
||
|
||
DWORD
|
||
ICAsyncThread::UnblockWorkItems(
|
||
IN DWORD dwCount,
|
||
IN DWORD_PTR dwBlockId,
|
||
IN DWORD dwError,
|
||
IN LONG lPriority
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Unblock a nunber of work items waiting on a block id
|
||
|
||
Arguments:
|
||
|
||
dwCount - unblock this many work items
|
||
|
||
dwBlockId - unblock work items waiting on this id
|
||
|
||
dwError - unblock work items with this error code
|
||
|
||
lPriority - if not TP_NO_PRIORITY_CHANGE, change priority to this value
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Number of work items unblocked
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_ASYNC,
|
||
Int,
|
||
"ICAsyncThread::UnblockWorkItems",
|
||
"%d, %#x, %d (%s), %d",
|
||
dwCount,
|
||
dwBlockId,
|
||
dwError,
|
||
InternetMapError(dwError),
|
||
lPriority
|
||
));
|
||
|
||
DWORD dwUnblocked = 0;
|
||
|
||
if (!m_BlockedQueue.Acquire())
|
||
goto quit;
|
||
|
||
CPriorityListEntry * pCur = (CPriorityListEntry *)m_BlockedQueue.Head();
|
||
CPriorityListEntry * pPrev = (CPriorityListEntry *)m_BlockedQueue.Self();
|
||
|
||
while ((dwCount != 0) && (pCur != (CPriorityListEntry *)m_BlockedQueue.Self())) {
|
||
|
||
CFsm * pFsm = ContainingFsm((LPVOID)pCur);
|
||
|
||
//CHECK_FSM_UNOWNED(pFsm);
|
||
|
||
if (pFsm->IsBlockedOn(dwBlockId)) {
|
||
m_BlockedQueue.Remove((CPriorityListEntry *)pFsm);
|
||
pFsm->SetError(dwError);
|
||
if (lPriority != TP_NO_PRIORITY_CHANGE) {
|
||
pFsm->SetPriority(lPriority);
|
||
}
|
||
//dprintf("UNBLOCKED %s FSM %#x state %s socket %#x\n", pFsm->MapType(), pFsm, pFsm->MapState(), pFsm->GetSocket());
|
||
pFsm->QueueWorkItem();
|
||
++dwUnblocked;
|
||
--dwCount;
|
||
} else {
|
||
pPrev = pCur;
|
||
}
|
||
pCur = (CPriorityListEntry *)pPrev->Next();
|
||
}
|
||
m_BlockedQueue.Release();
|
||
|
||
quit:
|
||
DEBUG_LEAVE(dwUnblocked);
|
||
|
||
return dwUnblocked;
|
||
}
|
||
|
||
|
||
DWORD
|
||
ICAsyncThread::CheckForBlockedWorkItems(
|
||
IN DWORD dwCount,
|
||
IN DWORD_PTR dwBlockId
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Examines to see if a blocked FSM is still blocked in order to prevent
|
||
wasted processing if it isn't.
|
||
|
||
Arguments:
|
||
|
||
dwCount - unblock this many work items
|
||
|
||
dwBlockId - unblock work items waiting on this id
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Number of work items that are currently blocked
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_ASYNC,
|
||
Int,
|
||
"ICAsyncThread::CheckForBlockedWorkItems",
|
||
"%d, %#x",
|
||
dwCount,
|
||
dwBlockId
|
||
));
|
||
|
||
DWORD dwFound = 0;
|
||
|
||
if (!m_BlockedQueue.Acquire())
|
||
goto quit;
|
||
|
||
CPriorityListEntry * pCur = (CPriorityListEntry *)m_BlockedQueue.Head();
|
||
CPriorityListEntry * pPrev = (CPriorityListEntry *)m_BlockedQueue.Self();
|
||
|
||
while ((dwCount != 0) && (pCur != (CPriorityListEntry *)m_BlockedQueue.Self())) {
|
||
|
||
CFsm * pFsm = ContainingFsm((LPVOID)pCur);
|
||
|
||
if (pFsm->IsBlockedOn(dwBlockId)) {
|
||
++dwFound;
|
||
--dwCount;
|
||
}
|
||
pCur = (CPriorityListEntry *)pCur->Next();
|
||
}
|
||
m_BlockedQueue.Release();
|
||
|
||
quit:
|
||
DEBUG_LEAVE(dwFound);
|
||
|
||
return dwFound;
|
||
}
|
||
|
||
|
||
DWORD
|
||
ICAsyncThread::SelectThreadWrapper(
|
||
IN ICAsyncThread * pThread
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Kicks off select thread as member function of pThread object
|
||
|
||
Arguments:
|
||
|
||
pThread - pointer to thread object
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
return code from SelectThread (not used)
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_ASYNC,
|
||
Dword,
|
||
"ICAsyncThread::SelectThreadWrapper",
|
||
"%#x",
|
||
pThread
|
||
));
|
||
|
||
DWORD error = pThread->SelectThread();
|
||
|
||
DEBUG_LEAVE(error);
|
||
|
||
return error;
|
||
}
|
||
|
||
|
||
DWORD
|
||
ICAsyncThread::SelectThread(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Waits for completed items on blocked queue to finish, either due to timeout,
|
||
invalidated request handle or successful or error completion of the socket
|
||
operation.
|
||
|
||
Completed items are put on the work queue and a worker signalled to process
|
||
it
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Success - ERROR_SUCCESS
|
||
|
||
Failure -
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// we need thread info for debug output
|
||
//
|
||
|
||
LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
|
||
|
||
if (lpThreadInfo == NULL) {
|
||
|
||
DEBUG_PRINT(ASYNC,
|
||
FATAL,
|
||
("Can't get thread info block\n"
|
||
));
|
||
|
||
INET_ASSERT(FALSE);
|
||
|
||
return ERROR_WINHTTP_INTERNAL_ERROR;
|
||
}
|
||
|
||
DEBUG_ENTER((DBG_ASYNC,
|
||
Dword,
|
||
"ICAsyncThread::SelectThread",
|
||
NULL
|
||
));
|
||
|
||
//
|
||
// have to create select socket in this thread or winsock blocks main thread
|
||
// on Win95 when autodial enabled
|
||
//
|
||
|
||
DWORD error = CreateSelectSocket();
|
||
|
||
if (error != ERROR_SUCCESS) {
|
||
|
||
DEBUG_LEAVE(error);
|
||
|
||
return error;
|
||
}
|
||
|
||
DWORD ticks = GetTickCountWrap();
|
||
|
||
while (!IsTerminating()) {
|
||
|
||
//
|
||
// run through the blocked items finding sockets to wait on and minimum
|
||
// time to wait. If we find any items already timed out or invalidated
|
||
// then remove them and put on the work queue
|
||
//
|
||
|
||
if (!m_BlockedQueue.Acquire())
|
||
{
|
||
// wait and try again when more memory might be available
|
||
goto wait_again;
|
||
}
|
||
|
||
PLIST_ENTRY pEntry;
|
||
PLIST_ENTRY pPrev;
|
||
|
||
pPrev = m_BlockedQueue.Self();
|
||
|
||
//
|
||
// BUGBUG - queue limited by size of FD_SET
|
||
//
|
||
|
||
struct fd_set read_fds;
|
||
int nTimeouts = 0;
|
||
struct fd_set write_fds;
|
||
struct fd_set except_fds;
|
||
int n = 0;
|
||
BOOL bLazy = FALSE;
|
||
DWORD timeout = 0xffffffff;
|
||
DWORD timeNow = GetTickCountWrap();
|
||
|
||
FD_ZERO(&read_fds);
|
||
FD_ZERO(&write_fds);
|
||
FD_ZERO(&except_fds);
|
||
|
||
CFsm * pFsm;
|
||
|
||
for (pEntry = m_BlockedQueue.Head();
|
||
pEntry != m_BlockedQueue.Self();
|
||
pEntry = ((CPriorityListEntry *)pPrev)->Next()) {
|
||
|
||
pFsm = ContainingFsm((LPVOID)pEntry);
|
||
if (pFsm->IsInvalid() || pFsm->IsTimedOut(timeNow)) {
|
||
|
||
DEBUG_PRINT(ASYNC,
|
||
INFO,
|
||
("%s FSM %#x %s\n",
|
||
pFsm->MapType(),
|
||
pFsm,
|
||
pFsm->IsInvalid() ? "invalid" : "timed out"
|
||
));
|
||
|
||
m_BlockedQueue.Remove((CPriorityListEntry *)pEntry);
|
||
|
||
if (pFsm->IsOnAsyncList())
|
||
{
|
||
INET_ASSERT( (pFsm->GetAction() == FSM_ACTION_SEND) ||
|
||
(pFsm->GetAction() == FSM_ACTION_RECEIVE) );
|
||
|
||
((INTERNET_HANDLE_BASE *)pFsm->GetMappedHandleObject())->AbortSocket();
|
||
|
||
pFsm->SetErrorState(pFsm->IsInvalid()
|
||
? ERROR_WINHTTP_OPERATION_CANCELLED
|
||
: ERROR_WINHTTP_TIMEOUT
|
||
);
|
||
|
||
pFsm->SetOnAsyncList(FALSE);
|
||
continue;
|
||
}
|
||
|
||
pFsm->SetErrorState(pFsm->IsInvalid()
|
||
? ERROR_WINHTTP_OPERATION_CANCELLED
|
||
: ERROR_WINHTTP_TIMEOUT
|
||
);
|
||
pFsm->ResetSocket();
|
||
pFsm->QueueWorkItem();
|
||
continue;
|
||
}
|
||
else if (pFsm->IsOnAsyncList())
|
||
{
|
||
INET_ASSERT (pFsm->IsActive());
|
||
++nTimeouts;
|
||
}
|
||
else if (pFsm->IsActive())
|
||
{
|
||
SOCKET sock = pFsm->GetSocket();
|
||
|
||
if (pFsm->GetAction() == FSM_ACTION_RECEIVE)
|
||
{
|
||
DEBUG_PRINT(ASYNC,
|
||
INFO,
|
||
("FSM %#x READ waiting on socket %#x\n",
|
||
pFsm,
|
||
sock
|
||
));
|
||
|
||
FD_SET(sock, &read_fds);
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// connect() & send()
|
||
//
|
||
DEBUG_PRINT(ASYNC,
|
||
INFO,
|
||
("%s FSM %#x WRITE waiting on socket %#x\n",
|
||
pFsm->MapType(),
|
||
pFsm,
|
||
sock
|
||
));
|
||
|
||
FD_SET(sock, &write_fds);
|
||
}
|
||
|
||
//
|
||
// all sockets are checked for exception
|
||
//
|
||
FD_SET(sock, &except_fds);
|
||
++n;
|
||
|
||
//DWORD t;
|
||
//if ((t = pFsm->GetElapsedTime()) > 10) {
|
||
// dprintf("%s FSM %#x socket %#x on queue %d mSec times-out in %d\n",
|
||
// pFsm->MapType(),
|
||
// pFsm,
|
||
// sock,
|
||
// t,
|
||
// pFsm->GetTimeout() - GetTickCount());
|
||
//}
|
||
}// if pFsm->IsActive()
|
||
|
||
DWORD interval = pFsm->GetTimeout() - timeNow;
|
||
|
||
//VENKATKBUG - check negative interval issue?
|
||
if (interval < timeout) {
|
||
timeout = interval;
|
||
//dprintf("min timeout = %d\n", timeout);
|
||
}
|
||
pPrev = pEntry;
|
||
}
|
||
|
||
m_BlockedQueue.Release();
|
||
|
||
wait_again:
|
||
//
|
||
// BUGBUG - wait for default (5 secs) timeout if nothing currently on
|
||
// list
|
||
//
|
||
if ((n == 0) && (nTimeouts == 0))
|
||
{
|
||
timeout = 5000;
|
||
bLazy = TRUE;
|
||
}
|
||
|
||
INET_ASSERT(n < FD_SETSIZE);
|
||
|
||
FD_SET(m_SelectSocket, &read_fds);
|
||
++n;
|
||
|
||
struct timeval to;
|
||
|
||
to.tv_sec = timeout / 1000;
|
||
to.tv_usec = (timeout % 1000) * 1000;
|
||
|
||
DEBUG_PRINT(ASYNC,
|
||
INFO,
|
||
("waiting %d mSec (%d.%06d) for select(). %d sockets\n",
|
||
timeout,
|
||
to.tv_sec,
|
||
to.tv_usec,
|
||
n
|
||
));
|
||
|
||
//SuspendCAP();
|
||
|
||
if (IsTerminating()) {
|
||
break;
|
||
}
|
||
n = PERF_Select(n, &read_fds, &write_fds, &except_fds, &to);
|
||
if (IsTerminating()) {
|
||
break;
|
||
}
|
||
|
||
//ResumeCAP();
|
||
|
||
DEBUG_PRINT(ASYNC,
|
||
INFO,
|
||
("select() returns %d\n",
|
||
n
|
||
));
|
||
|
||
//
|
||
// if the only thing that's happened is that a new request has been
|
||
// added to the list then rebuild the list and re-select
|
||
//
|
||
|
||
if ((n == 1) && FD_ISSET(m_SelectSocket, &read_fds)) {
|
||
if (!DrainSelectSocket() && !IsTerminating()) {
|
||
RecreateSelectSocket();
|
||
}
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// if any items are completed (either successfully or with an error)
|
||
// or timed out or invalidated then put them on the work queue
|
||
//
|
||
|
||
|
||
if ((n>=0) || (nTimeouts >= 0))
|
||
{
|
||
if (m_BlockedQueue.Acquire())
|
||
{
|
||
|
||
pPrev = m_BlockedQueue.Self();
|
||
timeNow = GetTickCountWrap();
|
||
|
||
for (pEntry = m_BlockedQueue.Head();
|
||
pEntry != m_BlockedQueue.Self();
|
||
pEntry = ((CPriorityListEntry *)pPrev)->Next())
|
||
{
|
||
DWORD dwEntryError;
|
||
BOOL bComplete = FALSE;
|
||
LONG lPriority = TP_NO_PRIORITY_CHANGE;
|
||
|
||
pFsm = ContainingFsm((LPVOID)pEntry);
|
||
if (pFsm->IsInvalid()) {
|
||
|
||
DEBUG_PRINT(ASYNC,
|
||
INFO,
|
||
("%s FSM %#x invalid\n",
|
||
pFsm->MapType(),
|
||
pFsm
|
||
));
|
||
|
||
dwEntryError = ERROR_WINHTTP_OPERATION_CANCELLED;
|
||
bComplete = TRUE;
|
||
} else if (pFsm->IsTimedOut(timeNow)) {
|
||
|
||
DEBUG_PRINT(ASYNC,
|
||
INFO,
|
||
("%s FSM %#x timed out\n",
|
||
pFsm->MapType(),
|
||
pFsm
|
||
));
|
||
|
||
dwEntryError = ERROR_WINHTTP_TIMEOUT;
|
||
bComplete = TRUE;
|
||
} else if (pFsm->IsActive()) {
|
||
|
||
SOCKET sock = pFsm->GetSocket();
|
||
|
||
if (FD_ISSET(sock, &except_fds)) {
|
||
|
||
DEBUG_PRINT(ASYNC,
|
||
INFO,
|
||
("%s FSM %#x socket %#x exception\n",
|
||
pFsm->MapType(),
|
||
pFsm,
|
||
sock
|
||
));
|
||
|
||
switch (pFsm->GetAction()) {
|
||
case FSM_ACTION_CONNECT:
|
||
dwEntryError = ERROR_WINHTTP_CANNOT_CONNECT;
|
||
break;
|
||
|
||
case FSM_ACTION_SEND:
|
||
case FSM_ACTION_RECEIVE:
|
||
INET_ASSERT (! pFsm->IsOnAsyncList());
|
||
|
||
dwEntryError = ERROR_WINHTTP_CONNECTION_ERROR;
|
||
break;
|
||
|
||
default:
|
||
|
||
INET_ASSERT(FALSE);
|
||
|
||
break;
|
||
}
|
||
bComplete = TRUE;
|
||
} else if (FD_ISSET(sock, &read_fds)
|
||
|| FD_ISSET(sock, &write_fds)) {
|
||
|
||
DEBUG_PRINT(ASYNC,
|
||
INFO,
|
||
("%s FSM %#x socket %#x completed\n",
|
||
pFsm->MapType(),
|
||
pFsm,
|
||
sock
|
||
));
|
||
|
||
dwEntryError = ERROR_SUCCESS;
|
||
bComplete = TRUE;
|
||
|
||
//
|
||
// BUGBUG - the priority needs to be boosted
|
||
//
|
||
|
||
}
|
||
}
|
||
if (bComplete)
|
||
{
|
||
m_BlockedQueue.Remove((CPriorityListEntry *)pFsm);
|
||
if (dwEntryError != ERROR_SUCCESS)
|
||
{
|
||
if (pFsm->IsOnAsyncList())
|
||
{
|
||
INET_ASSERT( (pFsm->GetAction() == FSM_ACTION_SEND) ||
|
||
(pFsm->GetAction() == FSM_ACTION_RECEIVE) );
|
||
|
||
INET_ASSERT( (dwEntryError == ERROR_WINHTTP_TIMEOUT) ||
|
||
(dwEntryError == ERROR_WINHTTP_OPERATION_CANCELLED) );
|
||
|
||
((INTERNET_HANDLE_BASE *)pFsm->GetMappedHandleObject())->AbortSocket();
|
||
|
||
//INET_ASSERT (FALSE && "ICASYNC aborting FSM!");
|
||
pFsm->SetErrorState(dwEntryError);//VENKATKBUG - ?? to do?
|
||
pFsm->SetOnAsyncList(FALSE);
|
||
continue;
|
||
}
|
||
pFsm->SetErrorState(dwEntryError);
|
||
}
|
||
else
|
||
{
|
||
INET_ASSERT (! (pFsm->IsOnAsyncList()) );
|
||
|
||
pFsm->SetError(ERROR_SUCCESS);
|
||
pFsm->SetState(pFsm->GetNextState());
|
||
}
|
||
pFsm->SetPriority(lPriority);
|
||
|
||
//dprintf("%s FSM %#x socket %#x signalled, time on queue = %d\n", pFsm->MapType(), pFsm, pFsm->GetSocket(), pFsm->StopTimer());
|
||
//
|
||
// no longer waiting on this socket handle
|
||
//
|
||
|
||
pFsm->ResetSocket();
|
||
|
||
//
|
||
// BUGBUG - if the next operation will complete quickly
|
||
// (FSM_HINT_QUICK) then we should run it here
|
||
// instead of queuing to another thread
|
||
//
|
||
|
||
pFsm->QueueWorkItem();
|
||
}
|
||
else
|
||
{
|
||
pPrev = pEntry;
|
||
}
|
||
}//loop: for (pEntry = m_BlockedQueue.Head();pEntry != m_BlockedQueue.Self();pEntry = ((CPriorityListEntry *)pPrev)->Next())
|
||
m_BlockedQueue.Release();
|
||
}// if (m_BlockedQueue.Acquire())
|
||
else
|
||
{
|
||
error = ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
}
|
||
else // if! n >= 0
|
||
{
|
||
error = _I_WSAGetLastError();
|
||
|
||
DEBUG_PRINT(ASYNC,
|
||
ERROR,
|
||
("select() returns %d (%s)\n",
|
||
error,
|
||
InternetMapError(error)
|
||
));
|
||
|
||
//
|
||
// WSAENOTSOCK can happen if the socket was cancelled just
|
||
// before we waited on it. We can also get WSAEINTR if
|
||
// select() is terminated early (by APC)
|
||
//
|
||
|
||
INET_ASSERT((error == WSAENOTSOCK) || (error == WSAEINTR) || (error == WSAEBADF));
|
||
|
||
if (error == WSAEINTR) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// when running on a portable (& probably desktops also), if we
|
||
// suspend & resume, the select socket can be invalidated. We
|
||
// need to recognize this situation and handle it
|
||
//
|
||
|
||
FD_ZERO(&read_fds);
|
||
FD_ZERO(&write_fds);
|
||
FD_ZERO(&except_fds);
|
||
|
||
FD_SET(m_SelectSocket, &read_fds);
|
||
|
||
to.tv_sec = 0;
|
||
to.tv_usec = 0;
|
||
n = _I_select(1, &read_fds, &write_fds, &except_fds, &to);
|
||
if (n < 0) {
|
||
|
||
//
|
||
// the select socket is dead. Throw it away & create a new
|
||
// one. We should pick up any blocked requests that tried
|
||
// unsuccessfully to interrupt the old select socket
|
||
//
|
||
|
||
RecreateSelectSocket();
|
||
} else {
|
||
|
||
//
|
||
// some socket(s) other than the select socket has become
|
||
// invalid. Cancel the corresponding request(s)
|
||
//
|
||
}
|
||
} // if! n >= 0
|
||
|
||
//
|
||
// perform timed events
|
||
//
|
||
// BUGBUG - need variable for 5000
|
||
//
|
||
|
||
if ((GetTickCountWrap() - ticks) >= 5000) {
|
||
if( bLazy == TRUE && !InDllCleanup && !IsTerminating())
|
||
{
|
||
#ifndef WININET_SERVER_CORE
|
||
//
|
||
// wake background task mgr
|
||
// this may involve one of the background workitem
|
||
// to be queued and get executed
|
||
//
|
||
NotifyBackgroundTaskMgr();
|
||
#endif
|
||
}
|
||
#ifndef WININET_SERVER_CORE //now per-session
|
||
PurgeServerInfoList(FALSE);
|
||
#endif
|
||
ticks = GetTickCountWrap();
|
||
}
|
||
}
|
||
//REDUNDANT: TerminateAsyncSupport();
|
||
|
||
DEBUG_LEAVE(error);
|
||
//dprintf("!!! Waiter FSM is done\n");
|
||
return error;
|
||
}
|
||
|
||
|
||
DWORD
|
||
ICAsyncThread::CreateSelectSocket(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
In order to not have to keep inefficiently polling select() with a short
|
||
time-out, we create a 'trick' datagram socket that we can use to interrupt
|
||
select() with: this is a local socket, and if we send something to ourself
|
||
then select() will complete (assuming one of the sockets we are waiting on
|
||
is the one we create here)
|
||
|
||
N.B. Sockets support must be initialized by the time we get here
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Success - ERROR_SUCCESS
|
||
|
||
Failure - mapped socket error
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_ASYNC,
|
||
Dword,
|
||
"ICAsyncThread::CreateSelectSocket",
|
||
NULL
|
||
));
|
||
|
||
INET_ASSERT(m_SelectSocket == INVALID_SOCKET);
|
||
|
||
DWORD error;
|
||
SOCKET sock;
|
||
|
||
sock = _I_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||
if (sock == INVALID_SOCKET) {
|
||
|
||
DEBUG_PRINT(ASYNC,
|
||
ERROR,
|
||
("socket() failed\n"
|
||
));
|
||
|
||
goto socket_error;
|
||
}
|
||
|
||
SOCKADDR_IN sockAddr;
|
||
|
||
sockAddr.sin_family = AF_INET;
|
||
sockAddr.sin_port = 0;
|
||
*(LPDWORD)&sockAddr.sin_addr = _I_htonl(INADDR_LOOPBACK);
|
||
memset(&sockAddr.sin_zero, 0, sizeof(sockAddr.sin_zero));
|
||
|
||
int rc;
|
||
|
||
DEBUG_PRINT(ASYNC,
|
||
INFO,
|
||
("binding socket %#x to address %d.%d.%d.%d\n",
|
||
sock,
|
||
((LPBYTE)&sockAddr.sin_addr)[0] & 0xff,
|
||
((LPBYTE)&sockAddr.sin_addr)[1] & 0xff,
|
||
((LPBYTE)&sockAddr.sin_addr)[2] & 0xff,
|
||
((LPBYTE)&sockAddr.sin_addr)[3] & 0xff
|
||
));
|
||
|
||
rc = _I_bind(sock, (LPSOCKADDR)&sockAddr, sizeof(sockAddr));
|
||
if (rc == SOCKET_ERROR) {
|
||
|
||
DEBUG_PRINT(ASYNC,
|
||
ERROR,
|
||
("bind() failed\n"
|
||
));
|
||
|
||
goto socket_error;
|
||
}
|
||
|
||
int namelen;
|
||
SOCKADDR sockname;
|
||
namelen = sizeof(sockname);
|
||
|
||
rc = _I_getsockname(sock, &sockname, &namelen);
|
||
if (rc == SOCKET_ERROR) {
|
||
|
||
DEBUG_PRINT(ASYNC,
|
||
ERROR,
|
||
("getsockname() failed\n"
|
||
));
|
||
|
||
goto socket_error;
|
||
}
|
||
|
||
DEBUG_PRINT(ASYNC,
|
||
INFO,
|
||
("connecting to address %d.%d.%d.%d\n",
|
||
((LPBYTE)&sockname.sa_data)[2] & 0xff,
|
||
((LPBYTE)&sockname.sa_data)[3] & 0xff,
|
||
((LPBYTE)&sockname.sa_data)[4] & 0xff,
|
||
((LPBYTE)&sockname.sa_data)[5] & 0xff
|
||
));
|
||
|
||
rc = _I_connect(sock, &sockname, namelen);
|
||
if (rc == SOCKET_ERROR) {
|
||
|
||
DEBUG_PRINT(ASYNC,
|
||
ERROR,
|
||
("connect() failed\n"
|
||
));
|
||
|
||
goto socket_error;
|
||
}
|
||
|
||
m_SelectSocket = sock;
|
||
error = ERROR_SUCCESS;
|
||
|
||
quit:
|
||
|
||
DEBUG_LEAVE(error);
|
||
|
||
return error;
|
||
|
||
socket_error:
|
||
|
||
error = MapInternetError(_I_WSAGetLastError());
|
||
DestroySelectSocket();
|
||
goto quit;
|
||
}
|
||
|
||
|
||
VOID
|
||
ICAsyncThread::DestroySelectSocket(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Just closes SelectSocket (if we think its open)
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_ASYNC,
|
||
None,
|
||
"ICAsyncThread::DestroySelectSocket",
|
||
NULL
|
||
));
|
||
|
||
if (m_SelectSocket != INVALID_SOCKET) {
|
||
_I_closesocket(m_SelectSocket);
|
||
m_SelectSocket = INVALID_SOCKET;
|
||
}
|
||
|
||
DEBUG_LEAVE(0);
|
||
}
|
||
|
||
|
||
VOID
|
||
ICAsyncThread::RecreateSelectSocket(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Attempt to destroy & recreate select socket. Required when socket is killed
|
||
due to suspend, e.g.
|
||
|
||
Since the underlying net components may take a while to restart, we loop up
|
||
to 12 times, waiting up to ~16 secs (~32 secs cumulative)
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_ASYNC,
|
||
None,
|
||
"ICAsyncThread::RecreateSelectSocket",
|
||
NULL
|
||
));
|
||
|
||
DestroySelectSocket();
|
||
|
||
DEBUG_PRINT(ASYNC,
|
||
INFO,
|
||
("current interrupt count = %d\n",
|
||
m_lSelectInterrupts
|
||
));
|
||
|
||
m_lSelectInterrupts = -1;
|
||
|
||
int iterations = 12;
|
||
DWORD time = 8;
|
||
DWORD error;
|
||
|
||
do {
|
||
error = CreateSelectSocket();
|
||
if (error != ERROR_SUCCESS) {
|
||
PERF_Sleep(time);
|
||
time <<= 1;
|
||
}
|
||
} while ((error != ERROR_SUCCESS) && --iterations);
|
||
|
||
DEBUG_LEAVE(0);
|
||
}
|
||
|
||
|
||
VOID
|
||
ICAsyncThread::InterruptSelect(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
We interrupt a waiting select() by sending a small amount of data to ourself
|
||
on the 'trick datagram socket'
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_ASYNC,
|
||
None,
|
||
"ICAsyncThread::InterruptSelect",
|
||
NULL
|
||
));
|
||
|
||
//
|
||
// if the async select socket is already created then interrupt it. If it is
|
||
// not yet created then it probably means that the async scheduler thread
|
||
// hasn't gotten around to it yet, ipso facto the async scheduler can't be
|
||
// stuck in a select(), hence its okay to skip
|
||
//
|
||
|
||
if (m_SelectSocket != INVALID_SOCKET) {
|
||
if (InterlockedIncrement(&m_lSelectInterrupts) == 0) {
|
||
if (_I_send != NULL) {
|
||
#if INET_DEBUG
|
||
int nSent =
|
||
#endif
|
||
_I_send(m_SelectSocket, gszBang, 1, 0);
|
||
|
||
#if INET_DEBUG
|
||
if (nSent < 0) {
|
||
|
||
DWORD error = _I_WSAGetLastError();
|
||
|
||
DEBUG_PRINT(ASYNC,
|
||
INFO,
|
||
("send(%#x) returns %s (%d)\n",
|
||
m_SelectSocket,
|
||
InternetMapError(error),
|
||
error
|
||
));
|
||
|
||
}
|
||
|
||
INET_ASSERT(!InDllCleanup ? (nSent == 1) : TRUE);
|
||
#endif
|
||
}
|
||
} else {
|
||
InterlockedDecrement(&m_lSelectInterrupts);
|
||
|
||
DEBUG_PRINT(ASYNC,
|
||
INFO,
|
||
("select() already interrupted, count = %d\n",
|
||
m_lSelectInterrupts
|
||
));
|
||
|
||
}
|
||
} else {
|
||
|
||
DEBUG_PRINT(ASYNC,
|
||
WARNING,
|
||
("select socket not yet created\n"
|
||
));
|
||
|
||
}
|
||
|
||
DEBUG_LEAVE(0);
|
||
}
|
||
|
||
|
||
BOOL
|
||
ICAsyncThread::DrainSelectSocket(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Just reads the data written to the async select socket in order to wake up
|
||
select()
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
BOOL
|
||
TRUE - successfully drained
|
||
|
||
FALSE - error occurred
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_ASYNC,
|
||
Bool,
|
||
"ICAsyncThread::DrainSelectSocket",
|
||
NULL
|
||
));
|
||
|
||
BOOL bSuccess = TRUE;
|
||
|
||
if (m_SelectSocket != INVALID_SOCKET) {
|
||
|
||
//
|
||
// reduce the interrupt count. Threads making async requests will cause
|
||
// the select() to be interrupted again
|
||
//
|
||
|
||
InterlockedDecrement(&m_lSelectInterrupts);
|
||
|
||
char buf[32];
|
||
int nReceived;
|
||
|
||
nReceived = _I_recv(m_SelectSocket, buf, sizeof(buf), 0);
|
||
|
||
#ifdef unix
|
||
if(nReceived > -1)
|
||
{
|
||
#endif /* unix */
|
||
|
||
//INET_ASSERT(nReceived == 1);
|
||
//INET_ASSERT(buf[0] == '!');
|
||
|
||
#ifdef unix
|
||
}
|
||
#endif /* unix */
|
||
|
||
if (nReceived < 0) {
|
||
|
||
DWORD error = _I_WSAGetLastError();
|
||
|
||
INET_ASSERT(error != ERROR_SUCCESS);
|
||
|
||
DEBUG_PRINT(ASYNC,
|
||
ERROR,
|
||
("recv() returns %s [%d]\n",
|
||
InternetMapError(error),
|
||
error
|
||
));
|
||
|
||
bSuccess = FALSE;
|
||
}
|
||
} else {
|
||
|
||
DEBUG_PRINT(ASYNC,
|
||
INFO,
|
||
("m_SelectSocket == INVALID_SOCKET\n"
|
||
));
|
||
|
||
bSuccess = FALSE;
|
||
}
|
||
|
||
DEBUG_LEAVE(bSuccess);
|
||
|
||
return bSuccess;
|
||
}
|