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.

1743 lines
38 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. //
  38. // private classes
  39. //
  40. class ICAsyncThread {
  41. #define DEFAULT_ASYNC_THREAD_TIMEOUT 5000
  42. private:
  43. CPriorityList m_BlockedQueue;
  44. SOCKET m_SelectSocket;
  45. LONG m_lSelectInterrupts;
  46. BOOL m_bTerminating;
  47. DWORD m_dwError;
  48. HANDLE m_hThread;
  49. public:
  50. ICAsyncThread() {
  51. DEBUG_ENTER((DBG_ASYNC,
  52. None,
  53. "ICAsyncThread::ICAsyncThread",
  54. NULL
  55. ));
  56. m_SelectSocket = INVALID_SOCKET;
  57. m_lSelectInterrupts = -1;
  58. m_bTerminating = FALSE;
  59. m_dwError = ERROR_SUCCESS;
  60. DWORD dwThreadId;
  61. m_hThread = CreateThread(
  62. NULL,
  63. 0,
  64. (LPTHREAD_START_ROUTINE)ICAsyncThread::SelectThreadWrapper,
  65. (LPVOID)this,
  66. 0,
  67. &dwThreadId
  68. );
  69. if (m_hThread == NULL) {
  70. SetError();
  71. }
  72. DEBUG_LEAVE(0);
  73. }
  74. ~ICAsyncThread();
  75. DWORD GetError(VOID) const {
  76. return m_dwError;
  77. }
  78. VOID SetError(DWORD dwError = GetLastError()) {
  79. m_dwError = dwError;
  80. }
  81. BOOL IsTerminating(VOID) const {
  82. return m_bTerminating;
  83. }
  84. VOID SetTerminating(VOID) {
  85. m_bTerminating = TRUE;
  86. }
  87. DWORD
  88. QueueSocketWorkItem(
  89. IN CFsm * pFsm
  90. );
  91. DWORD
  92. BlockWorkItem(
  93. IN CFsm * WorkItem,
  94. IN DWORD_PTR dwBlockId,
  95. IN DWORD dwTimeout = TP_NO_TIMEOUT
  96. );
  97. DWORD
  98. UnblockWorkItems(
  99. IN DWORD dwCount,
  100. IN DWORD_PTR dwBlockId,
  101. IN DWORD dwError,
  102. IN LONG lPriority = TP_NO_PRIORITY_CHANGE
  103. );
  104. DWORD
  105. CheckForBlockedWorkItems(
  106. IN DWORD dwCount,
  107. IN DWORD_PTR dwBlockId
  108. );
  109. static
  110. DWORD
  111. SelectThreadWrapper(
  112. IN ICAsyncThread * pThread
  113. );
  114. DWORD
  115. SelectThread(
  116. VOID
  117. );
  118. DWORD
  119. CreateSelectSocket(
  120. VOID
  121. );
  122. PRIVATE
  123. VOID
  124. DestroySelectSocket(
  125. VOID
  126. );
  127. VOID
  128. RecreateSelectSocket(
  129. VOID
  130. );
  131. VOID
  132. InterruptSelect(
  133. VOID
  134. );
  135. BOOL
  136. DrainSelectSocket(
  137. VOID
  138. );
  139. };
  140. //
  141. // private data
  142. //
  143. PRIVATE ICAsyncThread * p_AsyncThread = NULL;
  144. //
  145. // functions
  146. //
  147. DWORD
  148. InitializeAsyncSupport(
  149. VOID
  150. )
  151. /*++
  152. Routine Description:
  153. Create async select thread object
  154. Arguments:
  155. None.
  156. Return Value:
  157. DWORD
  158. Success - ERROR_SUCCESS
  159. Failure -
  160. --*/
  161. {
  162. DEBUG_ENTER((DBG_ASYNC,
  163. Dword,
  164. "InitializeAsyncSupport",
  165. NULL
  166. ));
  167. DWORD error = ERROR_INTERNET_SHUTDOWN;
  168. if (!InDllCleanup) {
  169. EnterCriticalSection(&GeneralInitCritSec);
  170. if (!InDllCleanup) {
  171. if (p_AsyncThread == NULL) {
  172. p_AsyncThread = new ICAsyncThread();
  173. if (p_AsyncThread == NULL) {
  174. error = ERROR_NOT_ENOUGH_MEMORY;
  175. } else {
  176. error = p_AsyncThread->GetError();
  177. if (error != ERROR_SUCCESS) {
  178. TerminateAsyncSupport();
  179. }
  180. }
  181. } else {
  182. error = ERROR_SUCCESS;
  183. }
  184. }
  185. LeaveCriticalSection(&GeneralInitCritSec);
  186. }
  187. DEBUG_LEAVE(error);
  188. return error;
  189. }
  190. VOID
  191. TerminateAsyncSupport(
  192. VOID
  193. )
  194. /*++
  195. Routine Description:
  196. Terminates async support
  197. Arguments:
  198. None.
  199. Return Value:
  200. None.
  201. --*/
  202. {
  203. DEBUG_ENTER((DBG_ASYNC,
  204. None,
  205. "TerminateAsyncSupport",
  206. NULL
  207. ));
  208. ICAsyncThread * pThread;
  209. pThread = (ICAsyncThread *)InterlockedExchangePointer((PVOID*)&p_AsyncThread,
  210. (PVOID)NULL
  211. );
  212. if (pThread != NULL) {
  213. delete pThread;
  214. }
  215. DEBUG_LEAVE(0);
  216. }
  217. DWORD
  218. QueueSocketWorkItem(
  219. IN CFsm * pFsm,
  220. IN SOCKET Socket
  221. )
  222. /*++
  223. Routine Description:
  224. Adds a blocked socket operation/work item to the blocked queue
  225. Arguments:
  226. pFsm - in-progress socket operation (FSM)
  227. Socket - socket handle to wait on
  228. Return Value:
  229. DWORD
  230. Success - ERROR_IO_PENDING
  231. Failure - ERROR_INTERNET_INTERNAL_ERROR
  232. --*/
  233. {
  234. DEBUG_ENTER((DBG_ASYNC,
  235. Dword,
  236. "QueueSocketWorkItem",
  237. "%#x, %#x",
  238. pFsm,
  239. Socket
  240. ));
  241. DWORD error = ERROR_INTERNET_INTERNAL_ERROR;
  242. if (p_AsyncThread != NULL) {
  243. pFsm->SetSocket(Socket);
  244. error = p_AsyncThread->QueueSocketWorkItem(pFsm);
  245. if (error == ERROR_SUCCESS) {
  246. error = ERROR_IO_PENDING;
  247. }
  248. }
  249. INET_ASSERT(error != ERROR_INTERNET_INTERNAL_ERROR);
  250. DEBUG_LEAVE(error);
  251. return error;
  252. }
  253. DWORD
  254. BlockWorkItem(
  255. IN CFsm * pFsm,
  256. IN DWORD_PTR dwBlockId,
  257. IN DWORD dwTimeout
  258. )
  259. /*++
  260. Routine Description:
  261. Blocks a work item
  262. Arguments:
  263. pFsm - work item to block
  264. dwBlockId - block on this id
  265. dwTimeout - for this number of milliseconds
  266. Return Value:
  267. DWORD
  268. Error - ERROR_SUCCESS
  269. Failure -
  270. --*/
  271. {
  272. DEBUG_ENTER((DBG_ASYNC,
  273. Dword,
  274. "BlockWorkItem",
  275. "%#x, %#x, %d",
  276. pFsm,
  277. dwBlockId,
  278. dwTimeout
  279. ));
  280. DWORD error = ERROR_INTERNET_INTERNAL_ERROR;
  281. if (p_AsyncThread != NULL) {
  282. error = p_AsyncThread->BlockWorkItem(pFsm, dwBlockId, dwTimeout);
  283. }
  284. INET_ASSERT(error != ERROR_INTERNET_INTERNAL_ERROR);
  285. DEBUG_LEAVE(error);
  286. return error;
  287. }
  288. DWORD
  289. UnblockWorkItems(
  290. IN DWORD dwCount,
  291. IN DWORD_PTR dwBlockId,
  292. IN DWORD dwError,
  293. IN LONG lPriority
  294. )
  295. /*++
  296. Routine Description:
  297. Unblocks 1 or more work items
  298. Arguments:
  299. dwCount - unblock this many work items
  300. dwBlockId - that are blocked on this id
  301. dwError - with this error
  302. lPriority - new priority unless default value of TP_NO_PRIORITY_CHANGE
  303. Return Value:
  304. DWORD
  305. Success - ERROR_SUCCESS
  306. Failure -
  307. --*/
  308. {
  309. DEBUG_ENTER((DBG_ASYNC,
  310. Int,
  311. "UnblockWorkItems",
  312. "%d, %#x, %d (%s), %d",
  313. dwCount,
  314. dwBlockId,
  315. dwError,
  316. InternetMapError(dwError),
  317. lPriority
  318. ));
  319. DWORD dwUnblocked = 0;
  320. if (p_AsyncThread != NULL) {
  321. dwUnblocked = p_AsyncThread->UnblockWorkItems(dwCount,
  322. dwBlockId,
  323. dwError,
  324. lPriority
  325. );
  326. }
  327. DEBUG_LEAVE(dwUnblocked);
  328. return dwUnblocked;
  329. }
  330. DWORD
  331. CheckForBlockedWorkItems(
  332. IN DWORD dwCount,
  333. IN DWORD_PTR dwBlockId
  334. )
  335. /*++
  336. Routine Description:
  337. Checks if there are any items blocked on dwBlockId
  338. Arguments:
  339. dwCount - number of items to look for
  340. dwBlockId - blocked on this id
  341. Return Value:
  342. DWORD
  343. Number of blocked items found
  344. --*/
  345. {
  346. DEBUG_ENTER((DBG_ASYNC,
  347. Int,
  348. "CheckForBlockedWorkItems",
  349. "%d, %#x",
  350. dwCount,
  351. dwBlockId
  352. ));
  353. DWORD dwFound = 0;
  354. if (p_AsyncThread != NULL) {
  355. dwFound = p_AsyncThread->CheckForBlockedWorkItems(dwCount, dwBlockId);
  356. }
  357. DEBUG_LEAVE(dwFound);
  358. return dwFound;
  359. }
  360. //
  361. // private functions
  362. //
  363. //
  364. // ICAsyncThread methods
  365. //
  366. ICAsyncThread::~ICAsyncThread(
  367. VOID
  368. )
  369. /*++
  370. Routine Description:
  371. ICAsyncThread destructor. If we are being dynamically unloaded, signal the
  372. selecter thread and allow it to cleanup. Else the thread is already dead and
  373. we just need to reclaim the resources
  374. Arguments:
  375. None.
  376. Return Value:
  377. None.
  378. --*/
  379. {
  380. DEBUG_ENTER((DBG_ASYNC,
  381. None,
  382. "ICAsyncThread::~ICAsyncThread",
  383. NULL
  384. ));
  385. SetTerminating();
  386. if (GlobalDynaUnload) {
  387. InterruptSelect();
  388. //
  389. // Assuming the async thread was successfully created, the above clean-up
  390. // will have put it in a state where it's going to exit. Need to wait
  391. // for it to exit before returning from here so it doesn't get scheduled
  392. // after wininet has been unloaded.
  393. //
  394. if(m_hThread)
  395. {
  396. DWORD dwRes = WaitForSingleObject(m_hThread, 5 * 1000);
  397. INET_ASSERT(dwRes == WAIT_OBJECT_0);
  398. }
  399. }
  400. DestroySelectSocket();
  401. if(m_hThread)
  402. {
  403. CloseHandle(m_hThread);
  404. }
  405. DEBUG_LEAVE(0);
  406. }
  407. DWORD
  408. ICAsyncThread::QueueSocketWorkItem(
  409. IN CFsm * pFsm
  410. )
  411. /*++
  412. Routine Description:
  413. Add the work-item waiting on a blocked socket to the blocked queue.
  414. Interrupt the SelectThread to alert it to new work
  415. Arguments:
  416. pFsm - blocked work-item to queue
  417. Return Value:
  418. DWORD
  419. Success - ERROR_SUCCESS
  420. Failure - ERROR_INTERNET_INTERNAL_ERROR
  421. --*/
  422. {
  423. DEBUG_ENTER((DBG_ASYNC,
  424. Dword,
  425. "ICAsyncThread::QueueSocketWorkItem",
  426. "%#x",
  427. pFsm
  428. ));
  429. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  430. DWORD error = ERROR_INTERNET_INTERNAL_ERROR;
  431. INET_ASSERT(lpThreadInfo != NULL);
  432. if (lpThreadInfo != NULL) {
  433. pFsm->StartTimer();
  434. m_BlockedQueue.Insert((CPriorityListEntry *)pFsm->List());
  435. lpThreadInfo->Fsm = NULL;
  436. InterruptSelect();
  437. error = ERROR_SUCCESS;
  438. }
  439. DEBUG_LEAVE(error);
  440. return error;
  441. }
  442. DWORD
  443. ICAsyncThread::BlockWorkItem(
  444. IN CFsm * pFsm,
  445. IN DWORD_PTR dwBlockId,
  446. IN DWORD dwTimeout
  447. )
  448. /*++
  449. Routine Description:
  450. Blocks a work item (FSM)
  451. Arguments:
  452. pFsm - work item (FSM) to block
  453. dwBlockId - block on this
  454. dwTimeout - for this amount of time (mSec)
  455. Return Value:
  456. DWORD
  457. Success - ERROR_SUCCESS
  458. Failure -
  459. --*/
  460. {
  461. DEBUG_ENTER((DBG_ASYNC,
  462. Dword,
  463. "ICAsyncThread::BlockWorkItem",
  464. "%#x [%d], %#x, %d",
  465. pFsm,
  466. pFsm->GetPriority(),
  467. dwBlockId,
  468. dwTimeout
  469. ));
  470. DWORD error = error = ERROR_INTERNET_INTERNAL_ERROR;
  471. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  472. INET_ASSERT(lpThreadInfo != NULL);
  473. if (lpThreadInfo != NULL) {
  474. pFsm->SetBlockId(dwBlockId);
  475. pFsm->SetTimeout(dwTimeout);
  476. RESET_FSM_OWNED(pFsm);
  477. DEBUG_PRINT(ASYNC,
  478. INFO,
  479. ("!!! FSM %#x unowned\n",
  480. pFsm
  481. ));
  482. m_BlockedQueue.Insert((CPriorityListEntry *)pFsm->List());
  483. lpThreadInfo->Fsm = NULL;
  484. error = ERROR_SUCCESS;
  485. }
  486. DEBUG_LEAVE(error);
  487. return error;
  488. }
  489. DWORD
  490. ICAsyncThread::UnblockWorkItems(
  491. IN DWORD dwCount,
  492. IN DWORD_PTR dwBlockId,
  493. IN DWORD dwError,
  494. IN LONG lPriority
  495. )
  496. /*++
  497. Routine Description:
  498. Unblock a nunber of work items waiting on a block id
  499. Arguments:
  500. dwCount - unblock this many work items
  501. dwBlockId - unblock work items waiting on this id
  502. dwError - unblock work items with this error code
  503. lPriority - if not TP_NO_PRIORITY_CHANGE, change priority to this value
  504. Return Value:
  505. DWORD
  506. Number of work items unblocked
  507. --*/
  508. {
  509. DEBUG_ENTER((DBG_ASYNC,
  510. Int,
  511. "ICAsyncThread::UnblockWorkItems",
  512. "%d, %#x, %d (%s), %d",
  513. dwCount,
  514. dwBlockId,
  515. dwError,
  516. InternetMapError(dwError),
  517. lPriority
  518. ));
  519. DWORD dwUnblocked = 0;
  520. m_BlockedQueue.Acquire();
  521. CPriorityListEntry * pCur = (CPriorityListEntry *)m_BlockedQueue.Head();
  522. CPriorityListEntry * pPrev = (CPriorityListEntry *)m_BlockedQueue.Self();
  523. while ((dwCount != 0) && (pCur != (CPriorityListEntry *)m_BlockedQueue.Self())) {
  524. CFsm * pFsm = ContainingFsm((LPVOID)pCur);
  525. //CHECK_FSM_UNOWNED(pFsm);
  526. if (pFsm->IsBlockedOn(dwBlockId)) {
  527. m_BlockedQueue.Remove((CPriorityListEntry *)pFsm);
  528. pFsm->SetError(dwError);
  529. if (lPriority != TP_NO_PRIORITY_CHANGE) {
  530. pFsm->SetPriority(lPriority);
  531. }
  532. //dprintf("UNBLOCKED %s FSM %#x state %s socket %#x\n", pFsm->MapType(), pFsm, pFsm->MapState(), pFsm->GetSocket());
  533. pFsm->QueueWorkItem();
  534. ++dwUnblocked;
  535. --dwCount;
  536. } else {
  537. pPrev = pCur;
  538. }
  539. pCur = (CPriorityListEntry *)pPrev->Next();
  540. }
  541. m_BlockedQueue.Release();
  542. DEBUG_LEAVE(dwUnblocked);
  543. return dwUnblocked;
  544. }
  545. DWORD
  546. ICAsyncThread::CheckForBlockedWorkItems(
  547. IN DWORD dwCount,
  548. IN DWORD_PTR dwBlockId
  549. )
  550. /*++
  551. Routine Description:
  552. Examines to see if a blocked FSM is still blocked in order to prevent
  553. wasted processing if it isn't.
  554. Arguments:
  555. dwCount - unblock this many work items
  556. dwBlockId - unblock work items waiting on this id
  557. Return Value:
  558. DWORD
  559. Number of work items that are currently blocked
  560. --*/
  561. {
  562. DEBUG_ENTER((DBG_ASYNC,
  563. Int,
  564. "ICAsyncThread::CheckForBlockedWorkItems",
  565. "%d, %#x",
  566. dwCount,
  567. dwBlockId
  568. ));
  569. DWORD dwFound = 0;
  570. m_BlockedQueue.Acquire();
  571. CPriorityListEntry * pCur = (CPriorityListEntry *)m_BlockedQueue.Head();
  572. CPriorityListEntry * pPrev = (CPriorityListEntry *)m_BlockedQueue.Self();
  573. while ((dwCount != 0) && (pCur != (CPriorityListEntry *)m_BlockedQueue.Self())) {
  574. CFsm * pFsm = ContainingFsm((LPVOID)pCur);
  575. if (pFsm->IsBlockedOn(dwBlockId)) {
  576. ++dwFound;
  577. --dwCount;
  578. }
  579. pCur = (CPriorityListEntry *)pCur->Next();
  580. }
  581. m_BlockedQueue.Release();
  582. DEBUG_LEAVE(dwFound);
  583. return dwFound;
  584. }
  585. DWORD
  586. ICAsyncThread::SelectThreadWrapper(
  587. IN ICAsyncThread * pThread
  588. )
  589. /*++
  590. Routine Description:
  591. Kicks off select thread as member function of pThread object
  592. Arguments:
  593. pThread - pointer to thread object
  594. Return Value:
  595. DWORD
  596. return code from SelectThread (not used)
  597. --*/
  598. {
  599. DEBUG_ENTER((DBG_ASYNC,
  600. Dword,
  601. "ICAsyncThread::SelectThreadWrapper",
  602. "%#x",
  603. pThread
  604. ));
  605. DWORD error = pThread->SelectThread();
  606. DEBUG_LEAVE(error);
  607. return error;
  608. }
  609. DWORD
  610. ICAsyncThread::SelectThread(
  611. VOID
  612. )
  613. /*++
  614. Routine Description:
  615. Waits for completed items on blocked queue to finish, either due to timeout,
  616. invalidated request handle or successful or error completion of the socket
  617. operation.
  618. Completed items are put on the work queue and a worker signalled to process
  619. it
  620. Arguments:
  621. None.
  622. Return Value:
  623. DWORD
  624. Success - ERROR_SUCCESS
  625. Failure -
  626. --*/
  627. {
  628. //
  629. // we need thread info for debug output
  630. //
  631. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  632. if (lpThreadInfo == NULL) {
  633. DEBUG_PRINT(ASYNC,
  634. FATAL,
  635. ("Can't get thread info block\n"
  636. ));
  637. INET_ASSERT(FALSE);
  638. return ERROR_INTERNET_INTERNAL_ERROR;
  639. }
  640. DEBUG_ENTER((DBG_ASYNC,
  641. Dword,
  642. "ICAsyncThread::SelectThread",
  643. NULL
  644. ));
  645. //
  646. // have to create select socket in this thread or winsock blocks main thread
  647. // on Win95 when autodial enabled
  648. //
  649. DWORD error = CreateSelectSocket();
  650. if (error != ERROR_SUCCESS) {
  651. DEBUG_LEAVE(error);
  652. return error;
  653. }
  654. DWORD ticks = GetTickCountWrap();
  655. while (!IsTerminating()) {
  656. //
  657. // run through the blocked items finding sockets to wait on and minimum
  658. // time to wait. If we find any items already timed out or invalidated
  659. // then remove them and put on the work queue
  660. //
  661. m_BlockedQueue.Acquire();
  662. PLIST_ENTRY pEntry;
  663. PLIST_ENTRY pPrev;
  664. pPrev = m_BlockedQueue.Self();
  665. //
  666. // BUGBUG - queue limited by size of FD_SET
  667. //
  668. struct fd_set read_fds;
  669. struct fd_set write_fds;
  670. struct fd_set except_fds;
  671. int n = 0;
  672. BOOL bLazy = FALSE;
  673. DWORD timeout = 0xffffffff;
  674. DWORD timeNow = GetTickCountWrap();
  675. FD_ZERO(&read_fds);
  676. FD_ZERO(&write_fds);
  677. FD_ZERO(&except_fds);
  678. FD_SET(m_SelectSocket, &read_fds);
  679. ++n;
  680. CFsm * pFsm;
  681. for (pEntry = m_BlockedQueue.Head();
  682. pEntry != m_BlockedQueue.Self();
  683. pEntry = ((CPriorityListEntry *)pPrev)->Next()) {
  684. pFsm = ContainingFsm((LPVOID)pEntry);
  685. if (pFsm->IsInvalid() || pFsm->IsTimedOut(timeNow)) {
  686. DEBUG_PRINT(ASYNC,
  687. INFO,
  688. ("%s FSM %#x %s\n",
  689. pFsm->MapType(),
  690. pFsm,
  691. pFsm->IsInvalid() ? "invalid" : "timed out"
  692. ));
  693. m_BlockedQueue.Remove((CPriorityListEntry *)pEntry);
  694. pFsm->SetErrorState(pFsm->IsInvalid()
  695. ? ERROR_INTERNET_OPERATION_CANCELLED
  696. : ERROR_INTERNET_TIMEOUT
  697. );
  698. pFsm->ResetSocket();
  699. pFsm->QueueWorkItem();
  700. continue;
  701. } else if (pFsm->IsActive()) {
  702. SOCKET sock = pFsm->GetSocket();
  703. if (pFsm->GetAction() == FSM_ACTION_RECEIVE) {
  704. DEBUG_PRINT(ASYNC,
  705. INFO,
  706. ("FSM %#x READ waiting on socket %#x\n",
  707. pFsm,
  708. sock
  709. ));
  710. FD_SET(sock, &read_fds);
  711. } else {
  712. //
  713. // connect() & send()
  714. //
  715. DEBUG_PRINT(ASYNC,
  716. INFO,
  717. ("%s FSM %#x WRITE waiting on socket %#x\n",
  718. pFsm->MapType(),
  719. pFsm,
  720. sock
  721. ));
  722. FD_SET(sock, &write_fds);
  723. }
  724. //
  725. // all sockets are checked for exception
  726. //
  727. FD_SET(sock, &except_fds);
  728. ++n;
  729. //DWORD t;
  730. //if ((t = pFsm->GetElapsedTime()) > 10) {
  731. // dprintf("%s FSM %#x socket %#x on queue %d mSec times-out in %d\n",
  732. // pFsm->MapType(),
  733. // pFsm,
  734. // sock,
  735. // t,
  736. // pFsm->GetTimeout() - GetTickCount());
  737. //}
  738. }
  739. DWORD interval = pFsm->GetTimeout() - timeNow;
  740. if (interval < timeout) {
  741. timeout = interval;
  742. //dprintf("min timeout = %d\n", timeout);
  743. }
  744. pPrev = pEntry;
  745. }
  746. m_BlockedQueue.Release();
  747. //
  748. // BUGBUG - wait for default (5 secs) timeout if nothing currently on
  749. // list
  750. //
  751. if (n == 1) {
  752. timeout = g_bHibernating ? INFINITE : DEFAULT_ASYNC_THREAD_TIMEOUT;
  753. bLazy = TRUE;
  754. }
  755. INET_ASSERT(n <= FD_SETSIZE);
  756. struct timeval to;
  757. to.tv_sec = timeout / 1000;
  758. to.tv_usec = (timeout % 1000) * 1000;
  759. DEBUG_PRINT(ASYNC,
  760. INFO,
  761. ("waiting %d mSec (%d.%06d) for select(). %d sockets\n",
  762. timeout,
  763. to.tv_sec,
  764. to.tv_usec,
  765. n
  766. ));
  767. //SuspendCAP();
  768. if (IsTerminating()) {
  769. break;
  770. }
  771. n = PERF_Select(n, &read_fds, &write_fds, &except_fds, &to);
  772. if (IsTerminating()) {
  773. break;
  774. }
  775. //ResumeCAP();
  776. DEBUG_PRINT(ASYNC,
  777. INFO,
  778. ("select() returns %d\n",
  779. n
  780. ));
  781. //
  782. // if the only thing that's happened is that a new request has been
  783. // added to the list then rebuild the list and re-select
  784. //
  785. if ((n == 1) && FD_ISSET(m_SelectSocket, &read_fds)) {
  786. if (!DrainSelectSocket() && !IsTerminating()) {
  787. RecreateSelectSocket();
  788. }
  789. continue;
  790. }
  791. //
  792. // if any items are completed (either successfully or with an error)
  793. // or timed out or invalidated then put them on the work queue
  794. //
  795. if (n >= 0) {
  796. m_BlockedQueue.Acquire();
  797. pPrev = m_BlockedQueue.Self();
  798. timeNow = GetTickCountWrap();
  799. for (pEntry = m_BlockedQueue.Head();
  800. pEntry != m_BlockedQueue.Self();
  801. pEntry = ((CPriorityListEntry *)pPrev)->Next()) {
  802. DWORD dwEntryError;
  803. BOOL bComplete = FALSE;
  804. LONG lPriority = TP_NO_PRIORITY_CHANGE;
  805. pFsm = ContainingFsm((LPVOID)pEntry);
  806. if (pFsm->IsInvalid()) {
  807. DEBUG_PRINT(ASYNC,
  808. INFO,
  809. ("%s FSM %#x invalid\n",
  810. pFsm->MapType(),
  811. pFsm
  812. ));
  813. dwEntryError = ERROR_INTERNET_OPERATION_CANCELLED;
  814. bComplete = TRUE;
  815. } else if (pFsm->IsTimedOut(timeNow)) {
  816. DEBUG_PRINT(ASYNC,
  817. INFO,
  818. ("%s FSM %#x timed out\n",
  819. pFsm->MapType(),
  820. pFsm
  821. ));
  822. dwEntryError = ERROR_INTERNET_TIMEOUT;
  823. bComplete = TRUE;
  824. } else if (pFsm->IsActive()) {
  825. SOCKET sock = pFsm->GetSocket();
  826. if (FD_ISSET(sock, &except_fds)) {
  827. DEBUG_PRINT(ASYNC,
  828. INFO,
  829. ("%s FSM %#x socket %#x exception\n",
  830. pFsm->MapType(),
  831. pFsm,
  832. sock
  833. ));
  834. switch (pFsm->GetAction()) {
  835. case FSM_ACTION_CONNECT:
  836. dwEntryError = ERROR_INTERNET_CANNOT_CONNECT;
  837. break;
  838. case FSM_ACTION_SEND:
  839. case FSM_ACTION_RECEIVE:
  840. dwEntryError = ERROR_INTERNET_CONNECTION_RESET;
  841. break;
  842. default:
  843. INET_ASSERT(FALSE);
  844. break;
  845. }
  846. bComplete = TRUE;
  847. } else if (FD_ISSET(sock, &read_fds)
  848. || FD_ISSET(sock, &write_fds)) {
  849. DEBUG_PRINT(ASYNC,
  850. INFO,
  851. ("%s FSM %#x socket %#x completed\n",
  852. pFsm->MapType(),
  853. pFsm,
  854. sock
  855. ));
  856. dwEntryError = ERROR_SUCCESS;
  857. bComplete = TRUE;
  858. //
  859. // BUGBUG - the priority needs to be boosted
  860. //
  861. }
  862. }
  863. if (bComplete) {
  864. m_BlockedQueue.Remove((CPriorityListEntry *)pFsm);
  865. if (dwEntryError != ERROR_SUCCESS) {
  866. pFsm->SetErrorState(dwEntryError);
  867. } else {
  868. pFsm->SetError(ERROR_SUCCESS);
  869. pFsm->SetState(pFsm->GetNextState());
  870. }
  871. pFsm->SetPriority(lPriority);
  872. //dprintf("%s FSM %#x socket %#x signalled, time on queue = %d\n", pFsm->MapType(), pFsm, pFsm->GetSocket(), pFsm->StopTimer());
  873. //
  874. // no longer waiting on this socket handle
  875. //
  876. pFsm->ResetSocket();
  877. //
  878. // BUGBUG - if the next operation will complete quickly
  879. // (FSM_HINT_QUICK) then we should run it here
  880. // instead of queuing to another thread
  881. //
  882. pFsm->QueueWorkItem();
  883. } else {
  884. pPrev = pEntry;
  885. }
  886. }
  887. m_BlockedQueue.Release();
  888. } else {
  889. error = _I_WSAGetLastError();
  890. DEBUG_PRINT(ASYNC,
  891. ERROR,
  892. ("select() returns %d (%s)\n",
  893. error,
  894. InternetMapError(error)
  895. ));
  896. //
  897. // WSAENOTSOCK can happen if the socket was cancelled just
  898. // before we waited on it. We can also get WSAEINTR if
  899. // select() is terminated early (by APC)
  900. //
  901. INET_ASSERT((error == WSAENOTSOCK) || (error == WSAEINTR) || (error == WSAEBADF));
  902. if (error == WSAEINTR) {
  903. continue;
  904. }
  905. //
  906. // when running on a portable (& probably desktops also), if we
  907. // suspend & resume, the select socket can be invalidated. We
  908. // need to recognize this situation and handle it
  909. //
  910. if (error == WSAENOTSOCK) {
  911. //
  912. // the select socket may be dead. Throw it away & create a new
  913. // one. We should pick up any blocked requests that tried
  914. // unsuccessfully to interrupt the old select socket
  915. //
  916. RecreateSelectSocket();
  917. } else {
  918. //
  919. // some socket(s) other than the select socket has become
  920. // invalid. Cancel the corresponding request(s)
  921. //
  922. }
  923. }
  924. //
  925. // perform timed events
  926. //
  927. if ((GetTickCountWrap() - ticks) >= DEFAULT_ASYNC_THREAD_TIMEOUT) {
  928. if( bLazy == TRUE && !InDllCleanup && !IsTerminating())
  929. {
  930. //
  931. // wake background task mgr
  932. // this may involve one of the background workitem
  933. // to be queued and get executed
  934. //
  935. NotifyBackgroundTaskMgr();
  936. }
  937. PurgeServerInfoList(FALSE);
  938. ticks = GetTickCountWrap();
  939. }
  940. }
  941. TerminateAsyncSupport();
  942. DEBUG_LEAVE(error);
  943. //dprintf("!!! Waiter FSM is done\n");
  944. return error;
  945. }
  946. DWORD
  947. ICAsyncThread::CreateSelectSocket(
  948. VOID
  949. )
  950. /*++
  951. Routine Description:
  952. In order to not have to keep inefficiently polling select() with a short
  953. time-out, we create a 'trick' datagram socket that we can use to interrupt
  954. select() with: this is a local socket, and if we send something to ourself
  955. then select() will complete (assuming one of the sockets we are waiting on
  956. is the one we create here)
  957. N.B. Sockets support must be initialized by the time we get here
  958. Arguments:
  959. None.
  960. Return Value:
  961. DWORD
  962. Success - ERROR_SUCCESS
  963. Failure - mapped socket error
  964. --*/
  965. {
  966. DEBUG_ENTER((DBG_ASYNC,
  967. Dword,
  968. "ICAsyncThread::CreateSelectSocket",
  969. NULL
  970. ));
  971. INET_ASSERT(m_SelectSocket == INVALID_SOCKET);
  972. DWORD error;
  973. SOCKET sock;
  974. sock = _I_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  975. if (sock == INVALID_SOCKET) {
  976. DEBUG_PRINT(ASYNC,
  977. ERROR,
  978. ("socket() failed\n"
  979. ));
  980. goto socket_error;
  981. }
  982. SOCKADDR_IN sockAddr;
  983. sockAddr.sin_family = AF_INET;
  984. sockAddr.sin_port = 0;
  985. *(LPDWORD)&sockAddr.sin_addr = _I_htonl(INADDR_LOOPBACK);
  986. memset(&sockAddr.sin_zero, 0, sizeof(sockAddr.sin_zero));
  987. int rc;
  988. DEBUG_PRINT(ASYNC,
  989. INFO,
  990. ("binding socket %#x to address %d.%d.%d.%d\n",
  991. sock,
  992. ((LPBYTE)&sockAddr.sin_addr)[0] & 0xff,
  993. ((LPBYTE)&sockAddr.sin_addr)[1] & 0xff,
  994. ((LPBYTE)&sockAddr.sin_addr)[2] & 0xff,
  995. ((LPBYTE)&sockAddr.sin_addr)[3] & 0xff
  996. ));
  997. rc = _I_bind(sock, (LPSOCKADDR)&sockAddr, sizeof(sockAddr));
  998. if (rc == SOCKET_ERROR) {
  999. DEBUG_PRINT(ASYNC,
  1000. ERROR,
  1001. ("bind() failed\n"
  1002. ));
  1003. goto socket_error;
  1004. }
  1005. int namelen;
  1006. SOCKADDR sockname;
  1007. namelen = sizeof(sockname);
  1008. rc = _I_getsockname(sock, &sockname, &namelen);
  1009. if (rc == SOCKET_ERROR) {
  1010. DEBUG_PRINT(ASYNC,
  1011. ERROR,
  1012. ("getsockname() failed\n"
  1013. ));
  1014. goto socket_error;
  1015. }
  1016. DEBUG_PRINT(ASYNC,
  1017. INFO,
  1018. ("connecting to address %d.%d.%d.%d\n",
  1019. ((LPBYTE)&sockname.sa_data)[2] & 0xff,
  1020. ((LPBYTE)&sockname.sa_data)[3] & 0xff,
  1021. ((LPBYTE)&sockname.sa_data)[4] & 0xff,
  1022. ((LPBYTE)&sockname.sa_data)[5] & 0xff
  1023. ));
  1024. rc = _I_connect(sock, &sockname, namelen);
  1025. if (rc == SOCKET_ERROR) {
  1026. DEBUG_PRINT(ASYNC,
  1027. ERROR,
  1028. ("connect() failed\n"
  1029. ));
  1030. goto socket_error;
  1031. }
  1032. m_SelectSocket = sock;
  1033. error = ERROR_SUCCESS;
  1034. quit:
  1035. DEBUG_LEAVE(error);
  1036. return error;
  1037. socket_error:
  1038. error = MapInternetError(_I_WSAGetLastError());
  1039. DestroySelectSocket();
  1040. goto quit;
  1041. }
  1042. VOID
  1043. ICAsyncThread::DestroySelectSocket(
  1044. VOID
  1045. )
  1046. /*++
  1047. Routine Description:
  1048. Just closes SelectSocket (if we think its open)
  1049. Arguments:
  1050. None.
  1051. Return Value:
  1052. None.
  1053. --*/
  1054. {
  1055. DEBUG_ENTER((DBG_ASYNC,
  1056. None,
  1057. "ICAsyncThread::DestroySelectSocket",
  1058. NULL
  1059. ));
  1060. if (m_SelectSocket != INVALID_SOCKET) {
  1061. _I_closesocket(m_SelectSocket);
  1062. m_SelectSocket = INVALID_SOCKET;
  1063. }
  1064. DEBUG_LEAVE(0);
  1065. }
  1066. VOID
  1067. ICAsyncThread::RecreateSelectSocket(
  1068. VOID
  1069. )
  1070. /*++
  1071. Routine Description:
  1072. Attempt to destroy & recreate select socket. Required when socket is killed
  1073. due to suspend, e.g.
  1074. Since the underlying net components may take a while to restart, we loop up
  1075. to 12 times, waiting up to ~16 secs (~32 secs cumulative)
  1076. Arguments:
  1077. None.
  1078. Return Value:
  1079. None.
  1080. --*/
  1081. {
  1082. DEBUG_ENTER((DBG_ASYNC,
  1083. None,
  1084. "ICAsyncThread::RecreateSelectSocket",
  1085. NULL
  1086. ));
  1087. DestroySelectSocket();
  1088. DEBUG_PRINT(ASYNC,
  1089. INFO,
  1090. ("current interrupt count = %d\n",
  1091. m_lSelectInterrupts
  1092. ));
  1093. m_lSelectInterrupts = -1;
  1094. int iterations = 12;
  1095. DWORD time = 8;
  1096. DWORD error;
  1097. do {
  1098. error = CreateSelectSocket();
  1099. if (error != ERROR_SUCCESS) {
  1100. PERF_Sleep(time);
  1101. time <<= 1;
  1102. }
  1103. } while ((error != ERROR_SUCCESS) && --iterations);
  1104. DEBUG_LEAVE(0);
  1105. }
  1106. VOID
  1107. InterruptSelect(
  1108. VOID
  1109. )
  1110. {
  1111. DEBUG_ENTER((DBG_ASYNC,
  1112. None,
  1113. "InterruptSelect",
  1114. NULL
  1115. ));
  1116. __try
  1117. {
  1118. if (p_AsyncThread != NULL)
  1119. {
  1120. p_AsyncThread->InterruptSelect();
  1121. }
  1122. }
  1123. __except(EXCEPTION_EXECUTE_HANDLER)
  1124. {
  1125. }
  1126. ENDEXCEPT
  1127. DEBUG_LEAVE(0);
  1128. return;
  1129. }
  1130. VOID
  1131. ICAsyncThread::InterruptSelect(
  1132. VOID
  1133. )
  1134. /*++
  1135. Routine Description:
  1136. We interrupt a waiting select() by sending a small amount of data to ourself
  1137. on the 'trick datagram socket'
  1138. Arguments:
  1139. None.
  1140. Return Value:
  1141. None.
  1142. --*/
  1143. {
  1144. DEBUG_ENTER((DBG_ASYNC,
  1145. None,
  1146. "ICAsyncThread::InterruptSelect",
  1147. NULL
  1148. ));
  1149. //
  1150. // if the async select socket is already created then interrupt it. If it is
  1151. // not yet created then it probably means that the async scheduler thread
  1152. // hasn't gotten around to it yet, ipso facto the async scheduler can't be
  1153. // stuck in a select(), hence its okay to skip
  1154. //
  1155. if (m_SelectSocket != INVALID_SOCKET) {
  1156. if (InterlockedIncrement(&m_lSelectInterrupts) == 0) {
  1157. if (_I_send != NULL) {
  1158. #if INET_DEBUG
  1159. int nSent =
  1160. #endif
  1161. _I_send(m_SelectSocket, gszBang, 1, 0);
  1162. #if INET_DEBUG
  1163. if (nSent < 0) {
  1164. DWORD error = _I_WSAGetLastError();
  1165. DEBUG_PRINT(ASYNC,
  1166. INFO,
  1167. ("send(%#x) returns %s (%d)\n",
  1168. m_SelectSocket,
  1169. InternetMapError(error),
  1170. error
  1171. ));
  1172. }
  1173. INET_ASSERT(!InDllCleanup ? (nSent == 1) : TRUE);
  1174. #endif
  1175. }
  1176. } else {
  1177. InterlockedDecrement(&m_lSelectInterrupts);
  1178. DEBUG_PRINT(ASYNC,
  1179. INFO,
  1180. ("select() already interrupted, count = %d\n",
  1181. m_lSelectInterrupts
  1182. ));
  1183. }
  1184. } else {
  1185. DEBUG_PRINT(ASYNC,
  1186. WARNING,
  1187. ("select socket not yet created\n"
  1188. ));
  1189. }
  1190. DEBUG_LEAVE(0);
  1191. }
  1192. BOOL
  1193. ICAsyncThread::DrainSelectSocket(
  1194. VOID
  1195. )
  1196. /*++
  1197. Routine Description:
  1198. Just reads the data written to the async select socket in order to wake up
  1199. select()
  1200. Arguments:
  1201. None.
  1202. Return Value:
  1203. BOOL
  1204. TRUE - successfully drained
  1205. FALSE - error occurred
  1206. --*/
  1207. {
  1208. DEBUG_ENTER((DBG_ASYNC,
  1209. Bool,
  1210. "ICAsyncThread::DrainSelectSocket",
  1211. NULL
  1212. ));
  1213. BOOL bSuccess = TRUE;
  1214. if (m_SelectSocket != INVALID_SOCKET) {
  1215. //
  1216. // reduce the interrupt count. Threads making async requests will cause
  1217. // the select() to be interrupted again
  1218. //
  1219. InterlockedDecrement(&m_lSelectInterrupts);
  1220. char buf[32];
  1221. int nReceived;
  1222. nReceived = _I_recv(m_SelectSocket, buf, sizeof(buf), 0);
  1223. #ifdef unix
  1224. if(nReceived > -1)
  1225. {
  1226. #endif /* unix */
  1227. //INET_ASSERT(nReceived == 1);
  1228. //INET_ASSERT(buf[0] == '!');
  1229. #ifdef unix
  1230. }
  1231. #endif /* unix */
  1232. if (nReceived < 0) {
  1233. DWORD error = _I_WSAGetLastError();
  1234. INET_ASSERT(error != ERROR_SUCCESS);
  1235. DEBUG_PRINT(ASYNC,
  1236. ERROR,
  1237. ("recv() returns %s [%d]\n",
  1238. InternetMapError(error),
  1239. error
  1240. ));
  1241. bSuccess = FALSE;
  1242. }
  1243. } else {
  1244. DEBUG_PRINT(ASYNC,
  1245. INFO,
  1246. ("m_SelectSocket == INVALID_SOCKET\n"
  1247. ));
  1248. bSuccess = FALSE;
  1249. }
  1250. DEBUG_LEAVE(bSuccess);
  1251. return bSuccess;
  1252. }