Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2415 lines
58 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. icasync.cxx
  5. Abstract:
  6. Contains async socket (select) thread and support functions. Work items now
  7. processed by SHLWAPI/KERNEL32 thread pool
  8. Contents:
  9. InitializeAsyncSupport
  10. TerminateAsyncSupport
  11. QueueSocketWorkItem
  12. BlockWorkItem
  13. UnblockWorkItems
  14. CheckForBlockedWorkItems
  15. ICAsyncThread::~ICAsyncThread
  16. ICAsyncThread::QueueSocketWorkItem
  17. ICAsyncThread::BlockWorkItem
  18. ICAsyncThread::UnblockWorkItems
  19. ICAsyncThread::CheckForBlockedWorkItems
  20. ICAsyncThread::SelectThreadWrapper
  21. ICAsyncThread::SelectThread
  22. (ICAsyncThread::CreateSelectSocket)
  23. (ICAsyncThread::DestroySelectSocket)
  24. (ICAsyncThread::RecreateSelectSocket)
  25. (ICAsyncThread::InterruptSelect)
  26. (ICAsyncThread::DrainSelectSocket)
  27. Author:
  28. Richard L Firth (rfirth) 04-Mar-1998
  29. Environment:
  30. Win32 user-mode
  31. Revision History:
  32. 04-Mar-1998 rfirth
  33. Created
  34. --*/
  35. #include <wininetp.h>
  36. #include <perfdiag.hxx>
  37. DWORD CAsyncCount::AddRef()
  38. {
  39. DWORD error = ERROR_SUCCESS;
  40. if (!GeneralInitCritSec.Lock())
  41. {
  42. error = ERROR_NOT_ENOUGH_MEMORY;
  43. goto quit;
  44. }
  45. ++dwRef;
  46. GeneralInitCritSec.Unlock();
  47. quit:
  48. return error;
  49. }
  50. VOID CAsyncCount::Release()
  51. {
  52. BOOL bUnlock = GeneralInitCritSec.Lock();
  53. //Decrement the refcount always, but only Terminate if we obtained the critsec.
  54. if (!--dwRef && bUnlock)
  55. {
  56. //BUGBUG-enable later.
  57. TerminateAsyncSupport(TRUE);
  58. }
  59. if (bUnlock)
  60. {
  61. GeneralInitCritSec.Unlock();
  62. }
  63. }
  64. //
  65. // private classes
  66. //
  67. class ICAsyncThread {
  68. private:
  69. CPriorityList m_BlockedQueue;
  70. SOCKET m_SelectSocket;
  71. LONG m_lSelectInterrupts;
  72. BOOL m_bTerminating;
  73. DWORD m_dwError;
  74. HANDLE m_hThread;
  75. BOOL m_bCleanUp;
  76. public:
  77. ICAsyncThread() {
  78. DEBUG_ENTER((DBG_ASYNC,
  79. None,
  80. "ICAsyncThread::ICAsyncThread",
  81. NULL
  82. ));
  83. m_SelectSocket = INVALID_SOCKET;
  84. m_lSelectInterrupts = -1;
  85. m_bTerminating = FALSE;
  86. m_dwError = ERROR_SUCCESS;
  87. DWORD dwThreadId;
  88. m_hThread = CreateThread(
  89. NULL,
  90. 0,
  91. (LPTHREAD_START_ROUTINE)ICAsyncThread::SelectThreadWrapper,
  92. (LPVOID)this,
  93. 0,
  94. &dwThreadId
  95. );
  96. if (m_hThread == NULL) {
  97. SetError();
  98. }
  99. m_bCleanUp = FALSE;
  100. DEBUG_LEAVE(0);
  101. }
  102. ~ICAsyncThread();
  103. VOID SetCleanUp()
  104. {
  105. m_bCleanUp = TRUE;
  106. }
  107. DWORD GetError(VOID) const {
  108. return m_dwError;
  109. }
  110. VOID SetError(DWORD dwError = GetLastError()) {
  111. m_dwError = dwError;
  112. }
  113. BOOL IsTerminating(VOID) const {
  114. return m_bTerminating;
  115. }
  116. VOID SetTerminating(VOID) {
  117. m_bTerminating = TRUE;
  118. }
  119. DWORD
  120. QueueSocketWorkItem(
  121. IN CFsm * pFsm
  122. );
  123. BOOL
  124. RemoveFsmFromAsyncList(
  125. IN CFsm * pFsm
  126. );
  127. DWORD
  128. BlockWorkItem(
  129. IN CFsm * WorkItem,
  130. IN DWORD_PTR dwBlockId,
  131. IN DWORD dwTimeout = TP_NO_TIMEOUT
  132. );
  133. DWORD
  134. UnblockWorkItems(
  135. IN DWORD dwCount,
  136. IN DWORD_PTR dwBlockId,
  137. IN DWORD dwError,
  138. IN LONG lPriority = TP_NO_PRIORITY_CHANGE
  139. );
  140. DWORD
  141. CheckForBlockedWorkItems(
  142. IN DWORD dwCount,
  143. IN DWORD_PTR dwBlockId
  144. );
  145. static
  146. DWORD
  147. SelectThreadWrapper(
  148. IN ICAsyncThread * pThread
  149. );
  150. DWORD
  151. SelectThread(
  152. VOID
  153. );
  154. DWORD
  155. CreateSelectSocket(
  156. VOID
  157. );
  158. PRIVATE
  159. VOID
  160. DestroySelectSocket(
  161. VOID
  162. );
  163. VOID
  164. RecreateSelectSocket(
  165. VOID
  166. );
  167. VOID
  168. InterruptSelect(
  169. VOID
  170. );
  171. BOOL
  172. DrainSelectSocket(
  173. VOID
  174. );
  175. };
  176. //
  177. // private data
  178. //
  179. PRIVATE ICAsyncThread * p_AsyncThread = NULL;
  180. PRIVATE HANDLE* p_ThreadHandleArray = NULL;
  181. PRIVATE DWORD* p_ThreadIdArray = NULL;
  182. PRIVATE int p_iNumIOCPThreads = 0;
  183. //
  184. // functions
  185. //
  186. VOID
  187. TerminateIOCPGlobals()
  188. {
  189. if (g_lpCustomOverlapped)
  190. {
  191. delete g_lpCustomOverlapped;
  192. g_lpCustomOverlapped = NULL;
  193. }
  194. if (g_hCompletionPort)
  195. {
  196. CloseHandle(g_hCompletionPort);
  197. g_hCompletionPort = NULL;
  198. }
  199. }
  200. DWORD
  201. IOCompletionThreadFunc(
  202. IN ULONG_PTR pContext
  203. )
  204. {
  205. LPOVERLAPPED lpOverlapped;
  206. DWORD dwBytes;
  207. ULONG_PTR lpCompletionKey;
  208. DWORD dwTimeout = 1000;
  209. DWORD dwError = 0;
  210. BOOL bDeleteOverlapped = FALSE;
  211. LPINTERNET_THREAD_INFO lpThreadInfo;
  212. BOOL fExitThread = FALSE;
  213. lpThreadInfo = InternetGetThreadInfo();
  214. while (TRUE)
  215. {
  216. BOOL bRet = GetQueuedCompletionStatus(g_hCompletionPort,
  217. &dwBytes,
  218. &lpCompletionKey,
  219. &lpOverlapped,
  220. fExitThread ? 0 : INFINITE);
  221. DEBUG_ENTER((DBG_API,
  222. Dword,
  223. "***GetQueuedCompletionStatus",
  224. "(hcomp)%#x, (dwBytes)%#x, (completionkey)%#x, (overlapped)%#x",
  225. g_hCompletionPort,
  226. dwBytes,
  227. lpCompletionKey,
  228. lpOverlapped
  229. ));
  230. if (!bRet && !lpOverlapped)
  231. {
  232. DEBUG_LEAVE_API(NULL);
  233. DWORD dwError = GetLastError();
  234. if (dwError == WAIT_TIMEOUT)
  235. {
  236. break;
  237. }
  238. // other errors currently not possible since we only have custom completion packets.
  239. INET_ASSERT (FALSE);
  240. continue;
  241. }
  242. ICSocket* pObject;
  243. CFsm* pFsm;
  244. CWrapOverlapped* lpWrapOverlapped;
  245. if (lpOverlapped != g_lpCustomOverlapped)
  246. {
  247. pObject = (ICSocket *) lpCompletionKey;
  248. pFsm = pObject->GetAndSetCurrentFsm(NULL);
  249. DEBUG_LEAVE(pFsm);
  250. #if INET_DEBUG
  251. InterlockedDecrement(&g_cWSACompletions);
  252. #endif
  253. INET_ASSERT(pFsm);
  254. bDeleteOverlapped = TRUE;
  255. lpWrapOverlapped = GetWrapOverlappedObject(lpOverlapped);
  256. if (pFsm->HasTimeout())
  257. {
  258. if (!RemoveFsmFromAsyncList(pFsm))
  259. {
  260. //failure! the select thread already enforced timeout and updated state
  261. //INET_ASSERT (FALSE && "COOL");
  262. goto runworkitem;
  263. }
  264. }
  265. ((CFsm_SocketIOCP*)pFsm)->dwBytesTransferred = dwBytes;
  266. ((CFsm_SocketIOCP*)pFsm)->bIOCPSuccess = bRet;
  267. pFsm->ResetSocket();
  268. pFsm->SetPriority(TP_NO_PRIORITY_CHANGE);
  269. if (bRet)
  270. {
  271. pFsm->SetError(ERROR_SUCCESS);
  272. pFsm->SetState(pFsm->GetNextState());
  273. }
  274. else
  275. {
  276. //VENKATK_BUG-informational assert - remove later.
  277. DWORD dwErrorDebug = GetLastError();
  278. INET_ASSERT (FALSE && "IoCompletionError");
  279. ((CFsm_SocketIOCP*)pFsm)->dwIOCPError = GetLastError();
  280. if (((CFsm_SocketIOCP*)pFsm)->dwIOCPError == WSA_OPERATION_ABORTED)
  281. pFsm->SetErrorState(ERROR_WINHTTP_OPERATION_CANCELLED);
  282. else
  283. pFsm->SetErrorState(ERROR_WINHTTP_CONNECTION_ERROR);
  284. }
  285. }
  286. else
  287. {
  288. DEBUG_LEAVE(lpCompletionKey);
  289. INET_ASSERT( lpOverlapped == g_lpCustomOverlapped );
  290. INET_ASSERT( (dwBytes == COMPLETION_BYTES_CUSTOM) ||
  291. (dwBytes == COMPLETION_BYTES_EXITIOCP) );
  292. if (dwBytes == COMPLETION_BYTES_EXITIOCP)
  293. {
  294. INET_ASSERT (lpCompletionKey == NULL);
  295. break;
  296. }
  297. bDeleteOverlapped = FALSE;
  298. pFsm = (CFsm*) lpCompletionKey;
  299. #if INET_DEBUG
  300. InterlockedDecrement(&g_cCustomCompletions);
  301. #endif
  302. }
  303. runworkitem:
  304. if (pFsm)
  305. {
  306. INTERNET_HANDLE_BASE *pBase = ((INTERNET_HANDLE_BASE *)pFsm->GetMappedHandleObject());
  307. pBase->Reference(); // might go away if closehandle is called.
  308. lpThreadInfo->IsAsyncWorkerThread = TRUE;
  309. dwError = CFsm::RunWorkItem(pFsm);
  310. if (dwError != ERROR_IO_PENDING)
  311. {
  312. // If there are any pending async work items for this request,
  313. // then schedule the first one in the list.
  314. if (pBase->GetHandleType() == TypeHttpRequestHandle)
  315. {
  316. HTTP_REQUEST_HANDLE_OBJECT *pRequest = (HTTP_REQUEST_HANDLE_OBJECT *)pBase;
  317. if (pRequest->LockAsync())
  318. {
  319. if (!pRequest->IsWorkItemListEmpty())
  320. {
  321. pRequest->ScheduleWorkItem();
  322. }
  323. else
  324. {
  325. pRequest->SetWorkItemInProgress(FALSE);
  326. }
  327. pRequest->UnlockAsync();
  328. }
  329. else
  330. {
  331. // Need to report an error even if async item was
  332. // successful if we can't check for pending work items.
  333. dwError = (dwError == ERROR_SUCCESS ?
  334. ERROR_NOT_ENOUGH_MEMORY : dwError);
  335. }
  336. }
  337. }
  338. pBase->Dereference();
  339. // Must stay marked as being on an async worker thread until after
  340. // releasing the reference in order to prevent confusion in the async count.
  341. lpThreadInfo->IsAsyncWorkerThread = FALSE;
  342. }
  343. else
  344. {
  345. INET_ASSERT (pFsm);
  346. }
  347. if (!lpThreadInfo)
  348. {
  349. lpThreadInfo = InternetGetThreadInfo();
  350. }
  351. if (lpThreadInfo && lpThreadInfo->fExitThread)
  352. {
  353. //exit this thread after dequeuing as many available completions as possible.
  354. fExitThread = TRUE;
  355. }
  356. if (bDeleteOverlapped)
  357. {
  358. lpWrapOverlapped->Dereference();//VENKATKBUG - move it up later, but for now keep it here for debugging.
  359. }
  360. }
  361. if (fExitThread)
  362. {
  363. if (GeneralInitCritSec.Lock())
  364. {
  365. if (g_pAsyncCount && !g_pAsyncCount->GetRef())
  366. {
  367. //Additional check to make sure we don't go and knock off a freshly created set of globals.
  368. //These won't be leaked - would already have been deleted in the renewed InitializeIOCPSupport call.
  369. TerminateIOCPGlobals();
  370. }
  371. GeneralInitCritSec.Unlock();
  372. }
  373. }
  374. return dwError;
  375. }
  376. /*
  377. * called from InitalizeAsyncSupport and synchronized there
  378. *
  379. * also, don't bother to cleanup - if there's an error TerminateIOCPSupport is called.
  380. */
  381. DWORD
  382. InitializeIOCPSupport(
  383. VOID
  384. )
  385. {
  386. int dwNumIOCPThreads = g_cNumIOCPThreads;
  387. if (!dwNumIOCPThreads)
  388. {
  389. SYSTEM_INFO sSysInfo;
  390. memset(&sSysInfo, 0, sizeof(SYSTEM_INFO));
  391. GetSystemInfo(&sSysInfo);
  392. if (sSysInfo.dwNumberOfProcessors)
  393. dwNumIOCPThreads = sSysInfo.dwNumberOfProcessors;
  394. if (!dwNumIOCPThreads)
  395. {
  396. dwNumIOCPThreads = WINHTTP_GLOBAL_IOCP_THREADS_BACKUP;
  397. }
  398. }
  399. g_cNumIOCPThreads = dwNumIOCPThreads;
  400. #if INET_DEBUG
  401. g_cWSACompletions = 0;
  402. g_cCustomCompletions = 0;
  403. #endif
  404. DWORD dwError = ERROR_SUCCESS;
  405. //May be left over from previous run.
  406. if (g_hCompletionPort)
  407. {
  408. CloseHandle(g_hCompletionPort);
  409. g_hCompletionPort = NULL;
  410. };
  411. if (g_lpCustomOverlapped)
  412. {
  413. delete g_lpCustomOverlapped;
  414. g_lpCustomOverlapped = NULL;
  415. }
  416. g_hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
  417. if (!g_hCompletionPort)
  418. {
  419. dwError = GetLastError();
  420. goto quit;
  421. }
  422. g_lpCustomOverlapped = New OVERLAPPED();
  423. if (!g_lpCustomOverlapped)
  424. {
  425. dwError = ERROR_NOT_ENOUGH_MEMORY;
  426. goto quit;
  427. }
  428. memset(g_lpCustomOverlapped, 0, sizeof(OVERLAPPED));
  429. p_ThreadHandleArray = New HANDLE[dwNumIOCPThreads];
  430. p_ThreadIdArray = New DWORD[dwNumIOCPThreads];
  431. if (!p_ThreadHandleArray || !p_ThreadIdArray)
  432. {
  433. dwError = ERROR_NOT_ENOUGH_MEMORY;
  434. goto quit;
  435. }
  436. for (int i=0; i<dwNumIOCPThreads; i++)
  437. {
  438. BOOL bSuccess;
  439. DWORD dwThreadId;
  440. HANDLE hThread;
  441. hThread = CreateThread(
  442. NULL,
  443. 0,
  444. (LPTHREAD_START_ROUTINE)IOCompletionThreadFunc,
  445. NULL,
  446. 0,
  447. &dwThreadId
  448. );
  449. if (hThread)
  450. {
  451. p_ThreadHandleArray[p_iNumIOCPThreads++] = hThread;
  452. p_ThreadIdArray[i] = dwThreadId;
  453. }
  454. else
  455. {
  456. //successfully queued functions terminated in TerminateIOCPSupport
  457. dwError = GetLastError();
  458. break;
  459. }
  460. }
  461. quit:
  462. return dwError;
  463. }
  464. VOID
  465. TerminateIOCPSupport(
  466. VOID
  467. )
  468. {
  469. DWORD dwWaitResult;
  470. int iNumThreadsToEnd = p_iNumIOCPThreads;
  471. HANDLE* pThreadHandleArray = p_ThreadHandleArray;
  472. DWORD fDeleteHandleArray = FALSE;
  473. BOOL fTerminatingOnWorker = FALSE;
  474. if (!p_ThreadHandleArray)
  475. goto quit;
  476. #if INET_DEBUG
  477. if (g_cWSACompletions || g_cCustomCompletions)
  478. {
  479. INET_ASSERT(FALSE);
  480. WaitForMultipleObjects(p_iNumIOCPThreads, p_ThreadHandleArray, TRUE, 500);
  481. }
  482. #endif
  483. LPINTERNET_THREAD_INFO lpThreadInfo;
  484. lpThreadInfo = InternetGetThreadInfo();
  485. if (!lpThreadInfo)
  486. {
  487. goto quit;
  488. }
  489. if (lpThreadInfo->IsAsyncWorkerThread)
  490. {
  491. //can't terminate the worker thread we're on.
  492. --iNumThreadsToEnd;
  493. fTerminatingOnWorker = TRUE;
  494. lpThreadInfo->fExitThread = TRUE;
  495. if (iNumThreadsToEnd)
  496. {
  497. int iIndex = -1;
  498. DWORD dwThreadId = GetCurrentThreadId();
  499. //verify we're on a worker thread.
  500. for (int i=0; i<p_iNumIOCPThreads; i++)
  501. {
  502. if (p_ThreadIdArray[i] == dwThreadId)
  503. {
  504. iIndex = i;
  505. break;
  506. }
  507. }
  508. if (iIndex != -1)
  509. {
  510. pThreadHandleArray = New HANDLE[iNumThreadsToEnd];
  511. if (!pThreadHandleArray)
  512. {
  513. goto quit;
  514. }
  515. fDeleteHandleArray = TRUE;
  516. int i=0;
  517. for (int j=0; j<p_iNumIOCPThreads; j++)
  518. {
  519. if (j != iIndex)
  520. {
  521. pThreadHandleArray[i++] = p_ThreadHandleArray[j];
  522. }
  523. }
  524. }
  525. else
  526. {
  527. INET_ASSERT(FALSE);
  528. }
  529. }
  530. else
  531. {
  532. goto quit;
  533. }
  534. }
  535. for (int i=0; i<iNumThreadsToEnd; i++)
  536. {
  537. BOOL bSuccess = PostQueuedCompletionStatus(g_hCompletionPort,
  538. COMPLETION_BYTES_EXITIOCP,
  539. NULL,
  540. g_lpCustomOverlapped
  541. );
  542. INET_ASSERT (bSuccess);
  543. }
  544. dwWaitResult = WaitForMultipleObjects(iNumThreadsToEnd, pThreadHandleArray, TRUE, 2000);
  545. if ((dwWaitResult == WAIT_TIMEOUT) || (dwWaitResult == WAIT_FAILED))
  546. {
  547. goto forceTerminate;
  548. }
  549. else
  550. {
  551. INET_ASSERT ( ((LONG)dwWaitResult >= WAIT_OBJECT_0) && ((LONG)dwWaitResult < (WAIT_OBJECT_0+p_iNumIOCPThreads)));
  552. }
  553. quit:
  554. INET_ASSERT ((g_cWSACompletions == 0) &&
  555. (g_cCustomCompletions == 0));
  556. if (p_ThreadHandleArray)
  557. {
  558. for (int i=0; i<p_iNumIOCPThreads; i++)
  559. {
  560. CloseHandle(p_ThreadHandleArray[i]);
  561. }
  562. delete [] p_ThreadHandleArray;
  563. p_ThreadHandleArray = NULL;
  564. }
  565. if (p_ThreadIdArray)
  566. {
  567. delete [] p_ThreadIdArray;
  568. p_ThreadIdArray = NULL;
  569. }
  570. if (fDeleteHandleArray)
  571. {
  572. delete [] pThreadHandleArray;
  573. }
  574. if (fTerminatingOnWorker)
  575. {
  576. //don't delete these globals since they may be in use on this last thread.
  577. TerminateIOCPGlobals();
  578. }
  579. p_iNumIOCPThreads = 0;
  580. return;
  581. forceTerminate:
  582. for (int i=0; i<iNumThreadsToEnd; i++)
  583. {
  584. if (WaitForSingleObject(pThreadHandleArray[i], 0) != WAIT_OBJECT_0)
  585. {
  586. TerminateThread(pThreadHandleArray[i], -1);
  587. }
  588. }
  589. goto quit;
  590. }
  591. DWORD
  592. InitializeAsyncSupport(
  593. VOID
  594. )
  595. /*++
  596. Routine Description:
  597. Create async select thread object
  598. Arguments:
  599. None.
  600. Return Value:
  601. DWORD
  602. Success - ERROR_SUCCESS
  603. Failure -
  604. --*/
  605. {
  606. DEBUG_ENTER((DBG_ASYNC,
  607. Dword,
  608. "InitializeAsyncSupport",
  609. NULL
  610. ));
  611. DWORD error = ERROR_WINHTTP_SHUTDOWN;
  612. if (!InDllCleanup) {
  613. if (!GeneralInitCritSec.Lock())
  614. {
  615. error = ERROR_NOT_ENOUGH_MEMORY;
  616. goto quit;
  617. }
  618. if (!InDllCleanup) {
  619. if (p_AsyncThread == NULL) {
  620. HANDLE hThreadToken = NULL;
  621. //
  622. // If the current thread is impersonating, then grab its access token
  623. // and revert the current thread (so it is nolonger impersonating).
  624. // After creating the worker thread, we will make the main thread
  625. // impersonate again. Apparently you should not call CreateThread
  626. // while impersonating.
  627. //
  628. if (OpenThreadToken(GetCurrentThread(), (TOKEN_IMPERSONATE | TOKEN_READ),
  629. FALSE,
  630. &hThreadToken))
  631. {
  632. INET_ASSERT(hThreadToken != 0);
  633. RevertToSelf();
  634. }
  635. p_AsyncThread = New ICAsyncThread();
  636. if (p_AsyncThread == NULL) {
  637. error = ERROR_NOT_ENOUGH_MEMORY;
  638. } else {
  639. error = p_AsyncThread->GetError();
  640. if (error == ERROR_SUCCESS)
  641. error = InitializeIOCPSupport();
  642. if (error != ERROR_SUCCESS) {
  643. TerminateAsyncSupport(TRUE);
  644. }
  645. }
  646. if (hThreadToken)
  647. {
  648. SetThreadToken(NULL, hThreadToken);
  649. CloseHandle(hThreadToken);
  650. }
  651. } else {
  652. error = ERROR_SUCCESS;
  653. }
  654. }
  655. GeneralInitCritSec.Unlock();
  656. }
  657. quit:
  658. DEBUG_LEAVE(error);
  659. return error;
  660. }
  661. VOID
  662. TerminateAsyncSupport(
  663. BOOL bCleanUp
  664. )
  665. /*++
  666. Routine Description:
  667. Terminates async support
  668. Arguments:
  669. None.
  670. Return Value:
  671. None.
  672. --*/
  673. {
  674. DEBUG_ENTER((DBG_ASYNC,
  675. None,
  676. "TerminateAsyncSupport",
  677. NULL
  678. ));
  679. ICAsyncThread * pThread;
  680. if (GeneralInitCritSec.Lock())
  681. {
  682. TerminateIOCPSupport();
  683. pThread = (ICAsyncThread *)InterlockedExchangePointer((PVOID*)&p_AsyncThread,
  684. (PVOID)NULL
  685. );
  686. if (pThread != NULL)
  687. {
  688. if (bCleanUp)
  689. pThread->SetCleanUp();
  690. delete pThread;
  691. }
  692. GeneralInitCritSec.Unlock();
  693. }
  694. DEBUG_LEAVE(0);
  695. }
  696. BOOL
  697. RemoveFsmFromAsyncList(
  698. IN CFsm * pFsm
  699. )
  700. {
  701. BOOL bSuccess = TRUE;
  702. if (p_AsyncThread != NULL)
  703. {
  704. bSuccess = p_AsyncThread->RemoveFsmFromAsyncList(pFsm);
  705. }
  706. return bSuccess;
  707. }
  708. DWORD
  709. QueueSocketWorkItem(
  710. IN CFsm * pFsm,
  711. IN SOCKET Socket
  712. )
  713. /*++
  714. Routine Description:
  715. Adds a blocked socket operation/work item to the blocked queue
  716. Arguments:
  717. pFsm - in-progress socket operation (FSM)
  718. Socket - socket handle to wait on
  719. Return Value:
  720. DWORD
  721. Success - ERROR_IO_PENDING
  722. Failure - ERROR_WINHTTP_INTERNAL_ERROR
  723. --*/
  724. {
  725. DEBUG_ENTER((DBG_ASYNC,
  726. Dword,
  727. "QueueSocketWorkItem",
  728. "%#x, %#x",
  729. pFsm,
  730. Socket
  731. ));
  732. DWORD error = ERROR_WINHTTP_INTERNAL_ERROR;
  733. if (p_AsyncThread != NULL) {
  734. pFsm->SetSocket(Socket);
  735. error = p_AsyncThread->QueueSocketWorkItem(pFsm);
  736. if (error == ERROR_SUCCESS) {
  737. error = ERROR_IO_PENDING;
  738. }
  739. }
  740. INET_ASSERT(error != ERROR_WINHTTP_INTERNAL_ERROR);
  741. DEBUG_LEAVE(error);
  742. return error;
  743. }
  744. DWORD
  745. BlockWorkItem(
  746. IN CFsm * pFsm,
  747. IN DWORD_PTR dwBlockId,
  748. IN DWORD dwTimeout
  749. )
  750. /*++
  751. Routine Description:
  752. Blocks a work item
  753. Arguments:
  754. pFsm - work item to block
  755. dwBlockId - block on this id
  756. dwTimeout - for this number of milliseconds
  757. Return Value:
  758. DWORD
  759. Error - ERROR_SUCCESS
  760. Failure -
  761. --*/
  762. {
  763. DEBUG_ENTER((DBG_ASYNC,
  764. Dword,
  765. "BlockWorkItem",
  766. "%#x, %#x, %d",
  767. pFsm,
  768. dwBlockId,
  769. dwTimeout
  770. ));
  771. DWORD error = ERROR_WINHTTP_INTERNAL_ERROR;
  772. if (p_AsyncThread != NULL) {
  773. error = p_AsyncThread->BlockWorkItem(pFsm, dwBlockId, dwTimeout);
  774. }
  775. INET_ASSERT(error != ERROR_WINHTTP_INTERNAL_ERROR);
  776. DEBUG_LEAVE(error);
  777. return error;
  778. }
  779. DWORD
  780. UnblockWorkItems(
  781. IN DWORD dwCount,
  782. IN DWORD_PTR dwBlockId,
  783. IN DWORD dwError,
  784. IN LONG lPriority
  785. )
  786. /*++
  787. Routine Description:
  788. Unblocks 1 or more work items
  789. Arguments:
  790. dwCount - unblock this many work items
  791. dwBlockId - that are blocked on this id
  792. dwError - with this error
  793. lPriority - new priority unless default value of TP_NO_PRIORITY_CHANGE
  794. Return Value:
  795. DWORD
  796. Success - ERROR_SUCCESS
  797. Failure -
  798. --*/
  799. {
  800. DEBUG_ENTER((DBG_ASYNC,
  801. Int,
  802. "UnblockWorkItems",
  803. "%d, %#x, %d (%s), %d",
  804. dwCount,
  805. dwBlockId,
  806. dwError,
  807. InternetMapError(dwError),
  808. lPriority
  809. ));
  810. DWORD dwUnblocked = 0;
  811. if (p_AsyncThread != NULL) {
  812. dwUnblocked = p_AsyncThread->UnblockWorkItems(dwCount,
  813. dwBlockId,
  814. dwError,
  815. lPriority
  816. );
  817. }
  818. DEBUG_LEAVE(dwUnblocked);
  819. return dwUnblocked;
  820. }
  821. DWORD
  822. CheckForBlockedWorkItems(
  823. IN DWORD dwCount,
  824. IN DWORD_PTR dwBlockId
  825. )
  826. /*++
  827. Routine Description:
  828. Checks if there are any items blocked on dwBlockId
  829. Arguments:
  830. dwCount - number of items to look for
  831. dwBlockId - blocked on this id
  832. Return Value:
  833. DWORD
  834. Number of blocked items found
  835. --*/
  836. {
  837. DEBUG_ENTER((DBG_ASYNC,
  838. Int,
  839. "CheckForBlockedWorkItems",
  840. "%d, %#x",
  841. dwCount,
  842. dwBlockId
  843. ));
  844. DWORD dwFound = 0;
  845. if (p_AsyncThread != NULL) {
  846. dwFound = p_AsyncThread->CheckForBlockedWorkItems(dwCount, dwBlockId);
  847. }
  848. DEBUG_LEAVE(dwFound);
  849. return dwFound;
  850. }
  851. //
  852. // private functions
  853. //
  854. //
  855. // ICAsyncThread methods
  856. //
  857. ICAsyncThread::~ICAsyncThread(
  858. VOID
  859. )
  860. /*++
  861. Routine Description:
  862. ICAsyncThread destructor. If we are being dynamically unloaded, signal the
  863. selecter thread and allow it to cleanup. Else the thread is already dead and
  864. we just need to reclaim the resources
  865. Arguments:
  866. None.
  867. Return Value:
  868. None.
  869. --*/
  870. {
  871. DEBUG_ENTER((DBG_ASYNC,
  872. None,
  873. "ICAsyncThread::~ICAsyncThread",
  874. NULL
  875. ));
  876. SetTerminating();
  877. if (GlobalDynaUnload || m_bCleanUp) {
  878. InterruptSelect();
  879. //
  880. // Assuming the async thread was successfully created, the above clean-up
  881. // will have put it in a state where it's going to exit. Need to wait
  882. // for it to exit before returning from here so it doesn't get scheduled
  883. // after wininet has been unloaded.
  884. //
  885. if(m_hThread)
  886. {
  887. DWORD dwRes = WaitForSingleObject(m_hThread, 5 * 1000);
  888. INET_ASSERT(dwRes == WAIT_OBJECT_0);
  889. }
  890. }
  891. DestroySelectSocket();
  892. if(m_hThread)
  893. {
  894. CloseHandle(m_hThread);
  895. }
  896. DEBUG_LEAVE(0);
  897. }
  898. DWORD
  899. ICAsyncThread::QueueSocketWorkItem(
  900. IN CFsm * pFsm
  901. )
  902. /*++
  903. Routine Description:
  904. Add the work-item waiting on a blocked socket to the blocked queue.
  905. Interrupt the SelectThread to alert it to new work
  906. Arguments:
  907. pFsm - blocked work-item to queue
  908. Return Value:
  909. DWORD
  910. Success - ERROR_SUCCESS
  911. Failure - ERROR_WINHTTP_INTERNAL_ERROR
  912. Async Issues: VENKATK_BUG
  913. 1. Reduce contention for m_BlockedQueue by:
  914. maintaining sorted queue for timeout-only fsms.
  915. 2. Don't call InterruptSelect() for timeout queueing
  916. 3. check if content can be moved to per-fsm instead of
  917. global queue..
  918. --*/
  919. {
  920. DEBUG_ENTER((DBG_ASYNC,
  921. Dword,
  922. "ICAsyncThread::QueueSocketWorkItem",
  923. "%#x",
  924. pFsm
  925. ));
  926. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  927. DWORD error = ERROR_WINHTTP_INTERNAL_ERROR;
  928. INET_ASSERT(lpThreadInfo != NULL);
  929. if (lpThreadInfo != NULL) {
  930. pFsm->StartTimer();
  931. error = m_BlockedQueue.Insert((CPriorityListEntry *)pFsm->List());
  932. lpThreadInfo->Fsm = NULL;
  933. InterruptSelect();
  934. }
  935. DEBUG_LEAVE(error);
  936. return error;
  937. }
  938. BOOL
  939. ICAsyncThread::RemoveFsmFromAsyncList(
  940. IN CFsm * pFsm
  941. )
  942. {
  943. BOOL bSuccess = FALSE;
  944. if (m_BlockedQueue.Acquire())
  945. {
  946. if (pFsm->IsOnAsyncList())
  947. {
  948. m_BlockedQueue.Remove((CPriorityListEntry *)pFsm);
  949. pFsm->SetOnAsyncList(FALSE);
  950. bSuccess = TRUE;
  951. }
  952. m_BlockedQueue.Release();
  953. }
  954. return bSuccess;
  955. }
  956. DWORD
  957. ICAsyncThread::BlockWorkItem(
  958. IN CFsm * pFsm,
  959. IN DWORD_PTR dwBlockId,
  960. IN DWORD dwTimeout
  961. )
  962. /*++
  963. Routine Description:
  964. Blocks a work item (FSM)
  965. Arguments:
  966. pFsm - work item (FSM) to block
  967. dwBlockId - block on this
  968. dwTimeout - for this amount of time (mSec)
  969. Return Value:
  970. DWORD
  971. Success - ERROR_SUCCESS
  972. Failure -
  973. --*/
  974. {
  975. DEBUG_ENTER((DBG_ASYNC,
  976. Dword,
  977. "ICAsyncThread::BlockWorkItem",
  978. "%#x [%d], %#x, %d",
  979. pFsm,
  980. pFsm->GetPriority(),
  981. dwBlockId,
  982. dwTimeout
  983. ));
  984. DWORD error = error = ERROR_WINHTTP_INTERNAL_ERROR;
  985. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  986. INET_ASSERT(lpThreadInfo != NULL);
  987. if (lpThreadInfo != NULL) {
  988. pFsm->SetBlockId(dwBlockId);
  989. pFsm->SetTimeout(dwTimeout);
  990. RESET_FSM_OWNED(pFsm);
  991. DEBUG_PRINT(ASYNC,
  992. INFO,
  993. ("!!! FSM %#x unowned\n",
  994. pFsm
  995. ));
  996. error = m_BlockedQueue.Insert((CPriorityListEntry *)pFsm->List());
  997. lpThreadInfo->Fsm = NULL;
  998. }
  999. DEBUG_LEAVE(error);
  1000. return error;
  1001. }
  1002. DWORD
  1003. ICAsyncThread::UnblockWorkItems(
  1004. IN DWORD dwCount,
  1005. IN DWORD_PTR dwBlockId,
  1006. IN DWORD dwError,
  1007. IN LONG lPriority
  1008. )
  1009. /*++
  1010. Routine Description:
  1011. Unblock a nunber of work items waiting on a block id
  1012. Arguments:
  1013. dwCount - unblock this many work items
  1014. dwBlockId - unblock work items waiting on this id
  1015. dwError - unblock work items with this error code
  1016. lPriority - if not TP_NO_PRIORITY_CHANGE, change priority to this value
  1017. Return Value:
  1018. DWORD
  1019. Number of work items unblocked
  1020. --*/
  1021. {
  1022. DEBUG_ENTER((DBG_ASYNC,
  1023. Int,
  1024. "ICAsyncThread::UnblockWorkItems",
  1025. "%d, %#x, %d (%s), %d",
  1026. dwCount,
  1027. dwBlockId,
  1028. dwError,
  1029. InternetMapError(dwError),
  1030. lPriority
  1031. ));
  1032. DWORD dwUnblocked = 0;
  1033. if (!m_BlockedQueue.Acquire())
  1034. goto quit;
  1035. CPriorityListEntry * pCur = (CPriorityListEntry *)m_BlockedQueue.Head();
  1036. CPriorityListEntry * pPrev = (CPriorityListEntry *)m_BlockedQueue.Self();
  1037. while ((dwCount != 0) && (pCur != (CPriorityListEntry *)m_BlockedQueue.Self())) {
  1038. CFsm * pFsm = ContainingFsm((LPVOID)pCur);
  1039. //CHECK_FSM_UNOWNED(pFsm);
  1040. if (pFsm->IsBlockedOn(dwBlockId)) {
  1041. m_BlockedQueue.Remove((CPriorityListEntry *)pFsm);
  1042. pFsm->SetError(dwError);
  1043. if (lPriority != TP_NO_PRIORITY_CHANGE) {
  1044. pFsm->SetPriority(lPriority);
  1045. }
  1046. //dprintf("UNBLOCKED %s FSM %#x state %s socket %#x\n", pFsm->MapType(), pFsm, pFsm->MapState(), pFsm->GetSocket());
  1047. pFsm->QueueWorkItem();
  1048. ++dwUnblocked;
  1049. --dwCount;
  1050. } else {
  1051. pPrev = pCur;
  1052. }
  1053. pCur = (CPriorityListEntry *)pPrev->Next();
  1054. }
  1055. m_BlockedQueue.Release();
  1056. quit:
  1057. DEBUG_LEAVE(dwUnblocked);
  1058. return dwUnblocked;
  1059. }
  1060. DWORD
  1061. ICAsyncThread::CheckForBlockedWorkItems(
  1062. IN DWORD dwCount,
  1063. IN DWORD_PTR dwBlockId
  1064. )
  1065. /*++
  1066. Routine Description:
  1067. Examines to see if a blocked FSM is still blocked in order to prevent
  1068. wasted processing if it isn't.
  1069. Arguments:
  1070. dwCount - unblock this many work items
  1071. dwBlockId - unblock work items waiting on this id
  1072. Return Value:
  1073. DWORD
  1074. Number of work items that are currently blocked
  1075. --*/
  1076. {
  1077. DEBUG_ENTER((DBG_ASYNC,
  1078. Int,
  1079. "ICAsyncThread::CheckForBlockedWorkItems",
  1080. "%d, %#x",
  1081. dwCount,
  1082. dwBlockId
  1083. ));
  1084. DWORD dwFound = 0;
  1085. if (!m_BlockedQueue.Acquire())
  1086. goto quit;
  1087. CPriorityListEntry * pCur = (CPriorityListEntry *)m_BlockedQueue.Head();
  1088. CPriorityListEntry * pPrev = (CPriorityListEntry *)m_BlockedQueue.Self();
  1089. while ((dwCount != 0) && (pCur != (CPriorityListEntry *)m_BlockedQueue.Self())) {
  1090. CFsm * pFsm = ContainingFsm((LPVOID)pCur);
  1091. if (pFsm->IsBlockedOn(dwBlockId)) {
  1092. ++dwFound;
  1093. --dwCount;
  1094. }
  1095. pCur = (CPriorityListEntry *)pCur->Next();
  1096. }
  1097. m_BlockedQueue.Release();
  1098. quit:
  1099. DEBUG_LEAVE(dwFound);
  1100. return dwFound;
  1101. }
  1102. DWORD
  1103. ICAsyncThread::SelectThreadWrapper(
  1104. IN ICAsyncThread * pThread
  1105. )
  1106. /*++
  1107. Routine Description:
  1108. Kicks off select thread as member function of pThread object
  1109. Arguments:
  1110. pThread - pointer to thread object
  1111. Return Value:
  1112. DWORD
  1113. return code from SelectThread (not used)
  1114. --*/
  1115. {
  1116. DEBUG_ENTER((DBG_ASYNC,
  1117. Dword,
  1118. "ICAsyncThread::SelectThreadWrapper",
  1119. "%#x",
  1120. pThread
  1121. ));
  1122. DWORD error = pThread->SelectThread();
  1123. DEBUG_LEAVE(error);
  1124. return error;
  1125. }
  1126. DWORD
  1127. ICAsyncThread::SelectThread(
  1128. VOID
  1129. )
  1130. /*++
  1131. Routine Description:
  1132. Waits for completed items on blocked queue to finish, either due to timeout,
  1133. invalidated request handle or successful or error completion of the socket
  1134. operation.
  1135. Completed items are put on the work queue and a worker signalled to process
  1136. it
  1137. Arguments:
  1138. None.
  1139. Return Value:
  1140. DWORD
  1141. Success - ERROR_SUCCESS
  1142. Failure -
  1143. --*/
  1144. {
  1145. //
  1146. // we need thread info for debug output
  1147. //
  1148. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  1149. if (lpThreadInfo == NULL) {
  1150. DEBUG_PRINT(ASYNC,
  1151. FATAL,
  1152. ("Can't get thread info block\n"
  1153. ));
  1154. INET_ASSERT(FALSE);
  1155. return ERROR_WINHTTP_INTERNAL_ERROR;
  1156. }
  1157. DEBUG_ENTER((DBG_ASYNC,
  1158. Dword,
  1159. "ICAsyncThread::SelectThread",
  1160. NULL
  1161. ));
  1162. //
  1163. // have to create select socket in this thread or winsock blocks main thread
  1164. // on Win95 when autodial enabled
  1165. //
  1166. DWORD error = CreateSelectSocket();
  1167. if (error != ERROR_SUCCESS) {
  1168. DEBUG_LEAVE(error);
  1169. return error;
  1170. }
  1171. DWORD ticks = GetTickCountWrap();
  1172. while (!IsTerminating()) {
  1173. //
  1174. // run through the blocked items finding sockets to wait on and minimum
  1175. // time to wait. If we find any items already timed out or invalidated
  1176. // then remove them and put on the work queue
  1177. //
  1178. if (!m_BlockedQueue.Acquire())
  1179. {
  1180. // wait and try again when more memory might be available
  1181. goto wait_again;
  1182. }
  1183. PLIST_ENTRY pEntry;
  1184. PLIST_ENTRY pPrev;
  1185. pPrev = m_BlockedQueue.Self();
  1186. //
  1187. // BUGBUG - queue limited by size of FD_SET
  1188. //
  1189. struct fd_set read_fds;
  1190. int nTimeouts = 0;
  1191. struct fd_set write_fds;
  1192. struct fd_set except_fds;
  1193. int n = 0;
  1194. BOOL bLazy = FALSE;
  1195. DWORD timeout = 0xffffffff;
  1196. DWORD timeNow = GetTickCountWrap();
  1197. FD_ZERO(&read_fds);
  1198. FD_ZERO(&write_fds);
  1199. FD_ZERO(&except_fds);
  1200. CFsm * pFsm;
  1201. for (pEntry = m_BlockedQueue.Head();
  1202. pEntry != m_BlockedQueue.Self();
  1203. pEntry = ((CPriorityListEntry *)pPrev)->Next()) {
  1204. pFsm = ContainingFsm((LPVOID)pEntry);
  1205. if (pFsm->IsInvalid() || pFsm->IsTimedOut(timeNow)) {
  1206. DEBUG_PRINT(ASYNC,
  1207. INFO,
  1208. ("%s FSM %#x %s\n",
  1209. pFsm->MapType(),
  1210. pFsm,
  1211. pFsm->IsInvalid() ? "invalid" : "timed out"
  1212. ));
  1213. m_BlockedQueue.Remove((CPriorityListEntry *)pEntry);
  1214. if (pFsm->IsOnAsyncList())
  1215. {
  1216. INET_ASSERT( (pFsm->GetAction() == FSM_ACTION_SEND) ||
  1217. (pFsm->GetAction() == FSM_ACTION_RECEIVE) );
  1218. ((INTERNET_HANDLE_BASE *)pFsm->GetMappedHandleObject())->AbortSocket();
  1219. pFsm->SetErrorState(pFsm->IsInvalid()
  1220. ? ERROR_WINHTTP_OPERATION_CANCELLED
  1221. : ERROR_WINHTTP_TIMEOUT
  1222. );
  1223. pFsm->SetOnAsyncList(FALSE);
  1224. continue;
  1225. }
  1226. pFsm->SetErrorState(pFsm->IsInvalid()
  1227. ? ERROR_WINHTTP_OPERATION_CANCELLED
  1228. : ERROR_WINHTTP_TIMEOUT
  1229. );
  1230. pFsm->ResetSocket();
  1231. pFsm->QueueWorkItem();
  1232. continue;
  1233. }
  1234. else if (pFsm->IsOnAsyncList())
  1235. {
  1236. INET_ASSERT (pFsm->IsActive());
  1237. ++nTimeouts;
  1238. }
  1239. else if (pFsm->IsActive())
  1240. {
  1241. SOCKET sock = pFsm->GetSocket();
  1242. if (pFsm->GetAction() == FSM_ACTION_RECEIVE)
  1243. {
  1244. DEBUG_PRINT(ASYNC,
  1245. INFO,
  1246. ("FSM %#x READ waiting on socket %#x\n",
  1247. pFsm,
  1248. sock
  1249. ));
  1250. FD_SET(sock, &read_fds);
  1251. }
  1252. else
  1253. {
  1254. //
  1255. // connect() & send()
  1256. //
  1257. DEBUG_PRINT(ASYNC,
  1258. INFO,
  1259. ("%s FSM %#x WRITE waiting on socket %#x\n",
  1260. pFsm->MapType(),
  1261. pFsm,
  1262. sock
  1263. ));
  1264. FD_SET(sock, &write_fds);
  1265. }
  1266. //
  1267. // all sockets are checked for exception
  1268. //
  1269. FD_SET(sock, &except_fds);
  1270. ++n;
  1271. //DWORD t;
  1272. //if ((t = pFsm->GetElapsedTime()) > 10) {
  1273. // dprintf("%s FSM %#x socket %#x on queue %d mSec times-out in %d\n",
  1274. // pFsm->MapType(),
  1275. // pFsm,
  1276. // sock,
  1277. // t,
  1278. // pFsm->GetTimeout() - GetTickCount());
  1279. //}
  1280. }// if pFsm->IsActive()
  1281. DWORD interval = pFsm->GetTimeout() - timeNow;
  1282. //VENKATKBUG - check negative interval issue?
  1283. if (interval < timeout) {
  1284. timeout = interval;
  1285. //dprintf("min timeout = %d\n", timeout);
  1286. }
  1287. pPrev = pEntry;
  1288. }
  1289. m_BlockedQueue.Release();
  1290. wait_again:
  1291. //
  1292. // BUGBUG - wait for default (5 secs) timeout if nothing currently on
  1293. // list
  1294. //
  1295. if ((n == 0) && (nTimeouts == 0))
  1296. {
  1297. timeout = 5000;
  1298. bLazy = TRUE;
  1299. }
  1300. INET_ASSERT(n < FD_SETSIZE);
  1301. FD_SET(m_SelectSocket, &read_fds);
  1302. ++n;
  1303. struct timeval to;
  1304. to.tv_sec = timeout / 1000;
  1305. to.tv_usec = (timeout % 1000) * 1000;
  1306. DEBUG_PRINT(ASYNC,
  1307. INFO,
  1308. ("waiting %d mSec (%d.%06d) for select(). %d sockets\n",
  1309. timeout,
  1310. to.tv_sec,
  1311. to.tv_usec,
  1312. n
  1313. ));
  1314. //SuspendCAP();
  1315. if (IsTerminating()) {
  1316. break;
  1317. }
  1318. n = PERF_Select(n, &read_fds, &write_fds, &except_fds, &to);
  1319. if (IsTerminating()) {
  1320. break;
  1321. }
  1322. //ResumeCAP();
  1323. DEBUG_PRINT(ASYNC,
  1324. INFO,
  1325. ("select() returns %d\n",
  1326. n
  1327. ));
  1328. //
  1329. // if the only thing that's happened is that a new request has been
  1330. // added to the list then rebuild the list and re-select
  1331. //
  1332. if ((n == 1) && FD_ISSET(m_SelectSocket, &read_fds)) {
  1333. if (!DrainSelectSocket() && !IsTerminating()) {
  1334. RecreateSelectSocket();
  1335. }
  1336. continue;
  1337. }
  1338. //
  1339. // if any items are completed (either successfully or with an error)
  1340. // or timed out or invalidated then put them on the work queue
  1341. //
  1342. if ((n>=0) || (nTimeouts >= 0))
  1343. {
  1344. if (m_BlockedQueue.Acquire())
  1345. {
  1346. pPrev = m_BlockedQueue.Self();
  1347. timeNow = GetTickCountWrap();
  1348. for (pEntry = m_BlockedQueue.Head();
  1349. pEntry != m_BlockedQueue.Self();
  1350. pEntry = ((CPriorityListEntry *)pPrev)->Next())
  1351. {
  1352. DWORD dwEntryError;
  1353. BOOL bComplete = FALSE;
  1354. LONG lPriority = TP_NO_PRIORITY_CHANGE;
  1355. pFsm = ContainingFsm((LPVOID)pEntry);
  1356. if (pFsm->IsInvalid()) {
  1357. DEBUG_PRINT(ASYNC,
  1358. INFO,
  1359. ("%s FSM %#x invalid\n",
  1360. pFsm->MapType(),
  1361. pFsm
  1362. ));
  1363. dwEntryError = ERROR_WINHTTP_OPERATION_CANCELLED;
  1364. bComplete = TRUE;
  1365. } else if (pFsm->IsTimedOut(timeNow)) {
  1366. DEBUG_PRINT(ASYNC,
  1367. INFO,
  1368. ("%s FSM %#x timed out\n",
  1369. pFsm->MapType(),
  1370. pFsm
  1371. ));
  1372. dwEntryError = ERROR_WINHTTP_TIMEOUT;
  1373. bComplete = TRUE;
  1374. } else if (pFsm->IsActive()) {
  1375. SOCKET sock = pFsm->GetSocket();
  1376. if (FD_ISSET(sock, &except_fds)) {
  1377. DEBUG_PRINT(ASYNC,
  1378. INFO,
  1379. ("%s FSM %#x socket %#x exception\n",
  1380. pFsm->MapType(),
  1381. pFsm,
  1382. sock
  1383. ));
  1384. switch (pFsm->GetAction()) {
  1385. case FSM_ACTION_CONNECT:
  1386. dwEntryError = ERROR_WINHTTP_CANNOT_CONNECT;
  1387. break;
  1388. case FSM_ACTION_SEND:
  1389. case FSM_ACTION_RECEIVE:
  1390. INET_ASSERT (! pFsm->IsOnAsyncList());
  1391. dwEntryError = ERROR_WINHTTP_CONNECTION_ERROR;
  1392. break;
  1393. default:
  1394. INET_ASSERT(FALSE);
  1395. break;
  1396. }
  1397. bComplete = TRUE;
  1398. } else if (FD_ISSET(sock, &read_fds)
  1399. || FD_ISSET(sock, &write_fds)) {
  1400. DEBUG_PRINT(ASYNC,
  1401. INFO,
  1402. ("%s FSM %#x socket %#x completed\n",
  1403. pFsm->MapType(),
  1404. pFsm,
  1405. sock
  1406. ));
  1407. dwEntryError = ERROR_SUCCESS;
  1408. bComplete = TRUE;
  1409. //
  1410. // BUGBUG - the priority needs to be boosted
  1411. //
  1412. }
  1413. }
  1414. if (bComplete)
  1415. {
  1416. m_BlockedQueue.Remove((CPriorityListEntry *)pFsm);
  1417. if (dwEntryError != ERROR_SUCCESS)
  1418. {
  1419. if (pFsm->IsOnAsyncList())
  1420. {
  1421. INET_ASSERT( (pFsm->GetAction() == FSM_ACTION_SEND) ||
  1422. (pFsm->GetAction() == FSM_ACTION_RECEIVE) );
  1423. INET_ASSERT( (dwEntryError == ERROR_WINHTTP_TIMEOUT) ||
  1424. (dwEntryError == ERROR_WINHTTP_OPERATION_CANCELLED) );
  1425. ((INTERNET_HANDLE_BASE *)pFsm->GetMappedHandleObject())->AbortSocket();
  1426. //INET_ASSERT (FALSE && "ICASYNC aborting FSM!");
  1427. pFsm->SetErrorState(dwEntryError);//VENKATKBUG - ?? to do?
  1428. pFsm->SetOnAsyncList(FALSE);
  1429. continue;
  1430. }
  1431. pFsm->SetErrorState(dwEntryError);
  1432. }
  1433. else
  1434. {
  1435. INET_ASSERT (! (pFsm->IsOnAsyncList()) );
  1436. pFsm->SetError(ERROR_SUCCESS);
  1437. pFsm->SetState(pFsm->GetNextState());
  1438. }
  1439. pFsm->SetPriority(lPriority);
  1440. //dprintf("%s FSM %#x socket %#x signalled, time on queue = %d\n", pFsm->MapType(), pFsm, pFsm->GetSocket(), pFsm->StopTimer());
  1441. //
  1442. // no longer waiting on this socket handle
  1443. //
  1444. pFsm->ResetSocket();
  1445. //
  1446. // BUGBUG - if the next operation will complete quickly
  1447. // (FSM_HINT_QUICK) then we should run it here
  1448. // instead of queuing to another thread
  1449. //
  1450. pFsm->QueueWorkItem();
  1451. }
  1452. else
  1453. {
  1454. pPrev = pEntry;
  1455. }
  1456. }//loop: for (pEntry = m_BlockedQueue.Head();pEntry != m_BlockedQueue.Self();pEntry = ((CPriorityListEntry *)pPrev)->Next())
  1457. m_BlockedQueue.Release();
  1458. }// if (m_BlockedQueue.Acquire())
  1459. else
  1460. {
  1461. error = ERROR_NOT_ENOUGH_MEMORY;
  1462. }
  1463. }
  1464. else // if! n >= 0
  1465. {
  1466. error = _I_WSAGetLastError();
  1467. DEBUG_PRINT(ASYNC,
  1468. ERROR,
  1469. ("select() returns %d (%s)\n",
  1470. error,
  1471. InternetMapError(error)
  1472. ));
  1473. //
  1474. // WSAENOTSOCK can happen if the socket was cancelled just
  1475. // before we waited on it. We can also get WSAEINTR if
  1476. // select() is terminated early (by APC)
  1477. //
  1478. INET_ASSERT((error == WSAENOTSOCK) || (error == WSAEINTR) || (error == WSAEBADF));
  1479. if (error == WSAEINTR) {
  1480. continue;
  1481. }
  1482. //
  1483. // when running on a portable (& probably desktops also), if we
  1484. // suspend & resume, the select socket can be invalidated. We
  1485. // need to recognize this situation and handle it
  1486. //
  1487. FD_ZERO(&read_fds);
  1488. FD_ZERO(&write_fds);
  1489. FD_ZERO(&except_fds);
  1490. FD_SET(m_SelectSocket, &read_fds);
  1491. to.tv_sec = 0;
  1492. to.tv_usec = 0;
  1493. n = _I_select(1, &read_fds, &write_fds, &except_fds, &to);
  1494. if (n < 0) {
  1495. //
  1496. // the select socket is dead. Throw it away & create a new
  1497. // one. We should pick up any blocked requests that tried
  1498. // unsuccessfully to interrupt the old select socket
  1499. //
  1500. RecreateSelectSocket();
  1501. } else {
  1502. //
  1503. // some socket(s) other than the select socket has become
  1504. // invalid. Cancel the corresponding request(s)
  1505. //
  1506. }
  1507. } // if! n >= 0
  1508. //
  1509. // perform timed events
  1510. //
  1511. // BUGBUG - need variable for 5000
  1512. //
  1513. if ((GetTickCountWrap() - ticks) >= 5000) {
  1514. if( bLazy == TRUE && !InDllCleanup && !IsTerminating())
  1515. {
  1516. #ifndef WININET_SERVER_CORE
  1517. //
  1518. // wake background task mgr
  1519. // this may involve one of the background workitem
  1520. // to be queued and get executed
  1521. //
  1522. NotifyBackgroundTaskMgr();
  1523. #endif
  1524. }
  1525. #ifndef WININET_SERVER_CORE //now per-session
  1526. PurgeServerInfoList(FALSE);
  1527. #endif
  1528. ticks = GetTickCountWrap();
  1529. }
  1530. }
  1531. //REDUNDANT: TerminateAsyncSupport();
  1532. DEBUG_LEAVE(error);
  1533. //dprintf("!!! Waiter FSM is done\n");
  1534. return error;
  1535. }
  1536. DWORD
  1537. ICAsyncThread::CreateSelectSocket(
  1538. VOID
  1539. )
  1540. /*++
  1541. Routine Description:
  1542. In order to not have to keep inefficiently polling select() with a short
  1543. time-out, we create a 'trick' datagram socket that we can use to interrupt
  1544. select() with: this is a local socket, and if we send something to ourself
  1545. then select() will complete (assuming one of the sockets we are waiting on
  1546. is the one we create here)
  1547. N.B. Sockets support must be initialized by the time we get here
  1548. Arguments:
  1549. None.
  1550. Return Value:
  1551. DWORD
  1552. Success - ERROR_SUCCESS
  1553. Failure - mapped socket error
  1554. --*/
  1555. {
  1556. DEBUG_ENTER((DBG_ASYNC,
  1557. Dword,
  1558. "ICAsyncThread::CreateSelectSocket",
  1559. NULL
  1560. ));
  1561. INET_ASSERT(m_SelectSocket == INVALID_SOCKET);
  1562. DWORD error;
  1563. SOCKET sock;
  1564. sock = _I_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  1565. if (sock == INVALID_SOCKET) {
  1566. DEBUG_PRINT(ASYNC,
  1567. ERROR,
  1568. ("socket() failed\n"
  1569. ));
  1570. goto socket_error;
  1571. }
  1572. SOCKADDR_IN sockAddr;
  1573. sockAddr.sin_family = AF_INET;
  1574. sockAddr.sin_port = 0;
  1575. *(LPDWORD)&sockAddr.sin_addr = _I_htonl(INADDR_LOOPBACK);
  1576. memset(&sockAddr.sin_zero, 0, sizeof(sockAddr.sin_zero));
  1577. int rc;
  1578. DEBUG_PRINT(ASYNC,
  1579. INFO,
  1580. ("binding socket %#x to address %d.%d.%d.%d\n",
  1581. sock,
  1582. ((LPBYTE)&sockAddr.sin_addr)[0] & 0xff,
  1583. ((LPBYTE)&sockAddr.sin_addr)[1] & 0xff,
  1584. ((LPBYTE)&sockAddr.sin_addr)[2] & 0xff,
  1585. ((LPBYTE)&sockAddr.sin_addr)[3] & 0xff
  1586. ));
  1587. rc = _I_bind(sock, (LPSOCKADDR)&sockAddr, sizeof(sockAddr));
  1588. if (rc == SOCKET_ERROR) {
  1589. DEBUG_PRINT(ASYNC,
  1590. ERROR,
  1591. ("bind() failed\n"
  1592. ));
  1593. goto socket_error;
  1594. }
  1595. int namelen;
  1596. SOCKADDR sockname;
  1597. namelen = sizeof(sockname);
  1598. rc = _I_getsockname(sock, &sockname, &namelen);
  1599. if (rc == SOCKET_ERROR) {
  1600. DEBUG_PRINT(ASYNC,
  1601. ERROR,
  1602. ("getsockname() failed\n"
  1603. ));
  1604. goto socket_error;
  1605. }
  1606. DEBUG_PRINT(ASYNC,
  1607. INFO,
  1608. ("connecting to address %d.%d.%d.%d\n",
  1609. ((LPBYTE)&sockname.sa_data)[2] & 0xff,
  1610. ((LPBYTE)&sockname.sa_data)[3] & 0xff,
  1611. ((LPBYTE)&sockname.sa_data)[4] & 0xff,
  1612. ((LPBYTE)&sockname.sa_data)[5] & 0xff
  1613. ));
  1614. rc = _I_connect(sock, &sockname, namelen);
  1615. if (rc == SOCKET_ERROR) {
  1616. DEBUG_PRINT(ASYNC,
  1617. ERROR,
  1618. ("connect() failed\n"
  1619. ));
  1620. goto socket_error;
  1621. }
  1622. m_SelectSocket = sock;
  1623. error = ERROR_SUCCESS;
  1624. quit:
  1625. DEBUG_LEAVE(error);
  1626. return error;
  1627. socket_error:
  1628. error = MapInternetError(_I_WSAGetLastError());
  1629. DestroySelectSocket();
  1630. goto quit;
  1631. }
  1632. VOID
  1633. ICAsyncThread::DestroySelectSocket(
  1634. VOID
  1635. )
  1636. /*++
  1637. Routine Description:
  1638. Just closes SelectSocket (if we think its open)
  1639. Arguments:
  1640. None.
  1641. Return Value:
  1642. None.
  1643. --*/
  1644. {
  1645. DEBUG_ENTER((DBG_ASYNC,
  1646. None,
  1647. "ICAsyncThread::DestroySelectSocket",
  1648. NULL
  1649. ));
  1650. if (m_SelectSocket != INVALID_SOCKET) {
  1651. _I_closesocket(m_SelectSocket);
  1652. m_SelectSocket = INVALID_SOCKET;
  1653. }
  1654. DEBUG_LEAVE(0);
  1655. }
  1656. VOID
  1657. ICAsyncThread::RecreateSelectSocket(
  1658. VOID
  1659. )
  1660. /*++
  1661. Routine Description:
  1662. Attempt to destroy & recreate select socket. Required when socket is killed
  1663. due to suspend, e.g.
  1664. Since the underlying net components may take a while to restart, we loop up
  1665. to 12 times, waiting up to ~16 secs (~32 secs cumulative)
  1666. Arguments:
  1667. None.
  1668. Return Value:
  1669. None.
  1670. --*/
  1671. {
  1672. DEBUG_ENTER((DBG_ASYNC,
  1673. None,
  1674. "ICAsyncThread::RecreateSelectSocket",
  1675. NULL
  1676. ));
  1677. DestroySelectSocket();
  1678. DEBUG_PRINT(ASYNC,
  1679. INFO,
  1680. ("current interrupt count = %d\n",
  1681. m_lSelectInterrupts
  1682. ));
  1683. m_lSelectInterrupts = -1;
  1684. int iterations = 12;
  1685. DWORD time = 8;
  1686. DWORD error;
  1687. do {
  1688. error = CreateSelectSocket();
  1689. if (error != ERROR_SUCCESS) {
  1690. PERF_Sleep(time);
  1691. time <<= 1;
  1692. }
  1693. } while ((error != ERROR_SUCCESS) && --iterations);
  1694. DEBUG_LEAVE(0);
  1695. }
  1696. VOID
  1697. ICAsyncThread::InterruptSelect(
  1698. VOID
  1699. )
  1700. /*++
  1701. Routine Description:
  1702. We interrupt a waiting select() by sending a small amount of data to ourself
  1703. on the 'trick datagram socket'
  1704. Arguments:
  1705. None.
  1706. Return Value:
  1707. None.
  1708. --*/
  1709. {
  1710. DEBUG_ENTER((DBG_ASYNC,
  1711. None,
  1712. "ICAsyncThread::InterruptSelect",
  1713. NULL
  1714. ));
  1715. //
  1716. // if the async select socket is already created then interrupt it. If it is
  1717. // not yet created then it probably means that the async scheduler thread
  1718. // hasn't gotten around to it yet, ipso facto the async scheduler can't be
  1719. // stuck in a select(), hence its okay to skip
  1720. //
  1721. if (m_SelectSocket != INVALID_SOCKET) {
  1722. if (InterlockedIncrement(&m_lSelectInterrupts) == 0) {
  1723. if (_I_send != NULL) {
  1724. #if INET_DEBUG
  1725. int nSent =
  1726. #endif
  1727. _I_send(m_SelectSocket, gszBang, 1, 0);
  1728. #if INET_DEBUG
  1729. if (nSent < 0) {
  1730. DWORD error = _I_WSAGetLastError();
  1731. DEBUG_PRINT(ASYNC,
  1732. INFO,
  1733. ("send(%#x) returns %s (%d)\n",
  1734. m_SelectSocket,
  1735. InternetMapError(error),
  1736. error
  1737. ));
  1738. }
  1739. INET_ASSERT(!InDllCleanup ? (nSent == 1) : TRUE);
  1740. #endif
  1741. }
  1742. } else {
  1743. InterlockedDecrement(&m_lSelectInterrupts);
  1744. DEBUG_PRINT(ASYNC,
  1745. INFO,
  1746. ("select() already interrupted, count = %d\n",
  1747. m_lSelectInterrupts
  1748. ));
  1749. }
  1750. } else {
  1751. DEBUG_PRINT(ASYNC,
  1752. WARNING,
  1753. ("select socket not yet created\n"
  1754. ));
  1755. }
  1756. DEBUG_LEAVE(0);
  1757. }
  1758. BOOL
  1759. ICAsyncThread::DrainSelectSocket(
  1760. VOID
  1761. )
  1762. /*++
  1763. Routine Description:
  1764. Just reads the data written to the async select socket in order to wake up
  1765. select()
  1766. Arguments:
  1767. None.
  1768. Return Value:
  1769. BOOL
  1770. TRUE - successfully drained
  1771. FALSE - error occurred
  1772. --*/
  1773. {
  1774. DEBUG_ENTER((DBG_ASYNC,
  1775. Bool,
  1776. "ICAsyncThread::DrainSelectSocket",
  1777. NULL
  1778. ));
  1779. BOOL bSuccess = TRUE;
  1780. if (m_SelectSocket != INVALID_SOCKET) {
  1781. //
  1782. // reduce the interrupt count. Threads making async requests will cause
  1783. // the select() to be interrupted again
  1784. //
  1785. InterlockedDecrement(&m_lSelectInterrupts);
  1786. char buf[32];
  1787. int nReceived;
  1788. nReceived = _I_recv(m_SelectSocket, buf, sizeof(buf), 0);
  1789. #ifdef unix
  1790. if(nReceived > -1)
  1791. {
  1792. #endif /* unix */
  1793. //INET_ASSERT(nReceived == 1);
  1794. //INET_ASSERT(buf[0] == '!');
  1795. #ifdef unix
  1796. }
  1797. #endif /* unix */
  1798. if (nReceived < 0) {
  1799. DWORD error = _I_WSAGetLastError();
  1800. INET_ASSERT(error != ERROR_SUCCESS);
  1801. DEBUG_PRINT(ASYNC,
  1802. ERROR,
  1803. ("recv() returns %s [%d]\n",
  1804. InternetMapError(error),
  1805. error
  1806. ));
  1807. bSuccess = FALSE;
  1808. }
  1809. } else {
  1810. DEBUG_PRINT(ASYNC,
  1811. INFO,
  1812. ("m_SelectSocket == INVALID_SOCKET\n"
  1813. ));
  1814. bSuccess = FALSE;
  1815. }
  1816. DEBUG_LEAVE(bSuccess);
  1817. return bSuccess;
  1818. }