Source code of Windows XP (NT5)
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.

779 lines
18 KiB

  1. /*++
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. bowqueue.c
  5. Abstract:
  6. This module implements a worker thread and a set of functions for
  7. passing work to it.
  8. Author:
  9. Larry Osterman (LarryO) 13-Jul-1992
  10. Revision History:
  11. --*/
  12. #include "precomp.h"
  13. #pragma hdrstop
  14. // defines
  15. // Thread start definition helpers. Taken from article in URL below.
  16. // mk:@MSITStore:\\INFOSRV2\MSDN_OCT99\MSDN\period99.chm::/html/msft/msj/0799/win32/win320799.htm
  17. //
  18. typedef unsigned (__stdcall *PTHREAD_START) (void *);
  19. #define chBEGINTHREADEX(psa, cbStack, pfnStartAddr, \
  20. pvParam, fdwCreate, pdwThreadID) \
  21. ((HANDLE) _beginthreadex( \
  22. (void *) (psa), \
  23. (unsigned) (cbStack), \
  24. (PTHREAD_START) (pfnStartAddr), \
  25. (void *) (pvParam), \
  26. (unsigned) (fdwCreate), \
  27. (unsigned *) (pdwThreadID)))
  28. //
  29. // Limit the number of created worker threads.
  30. //
  31. // This count doesn't include the main thread.
  32. //
  33. #define BR_MAX_NUMBER_OF_WORKER_THREADS 10
  34. ULONG BrNumberOfCreatedWorkerThreads = 0;
  35. ULONG BrNumberOfActiveWorkerThreads = 0;
  36. //
  37. // Usage count array for determining how often each thread is used.
  38. //
  39. // Allow for the main thread.
  40. //
  41. ULONG BrWorkerThreadCount[BR_MAX_NUMBER_OF_WORKER_THREADS+1];
  42. //
  43. // Handles of created worker threads.
  44. //
  45. PHANDLE BrThreadArray[BR_MAX_NUMBER_OF_WORKER_THREADS];
  46. //
  47. // CritSect guard the WorkQueue list.
  48. //
  49. CRITICAL_SECTION BrWorkerCritSect;
  50. BOOL BrWorkerCSInitialized = FALSE;
  51. #define LOCK_WORK_QUEUE() EnterCriticalSection(&BrWorkerCritSect);
  52. #define UNLOCK_WORK_QUEUE() LeaveCriticalSection(&BrWorkerCritSect);
  53. //
  54. // Head of singly linked list of work items queued to the worker thread.
  55. //
  56. LIST_ENTRY
  57. BrWorkerQueueHead = {0};
  58. //
  59. // Event that is signal whenever a work item is put in the queue. The
  60. // worker thread waits on this event.
  61. //
  62. HANDLE
  63. BrWorkerSemaphore = NULL;
  64. //
  65. // Synchronization mechanisms for shutdown
  66. //
  67. extern HANDLE BrDgAsyncIOShutDownEvent;
  68. extern HANDLE BrDgAsyncIOThreadShutDownEvent;
  69. extern BOOL BrDgShutDownInitiated;
  70. extern DWORD BrDgAsyncIOsOutstanding;
  71. extern DWORD BrDgWorkerThreadsOutstanding;
  72. extern CRITICAL_SECTION BrAsyncIOCriticalSection;
  73. VOID
  74. BrTimerRoutine(
  75. IN PVOID TimerContext,
  76. IN ULONG TImerLowValue,
  77. IN LONG TimerHighValue
  78. );
  79. NET_API_STATUS
  80. BrWorkerInitialization(
  81. VOID
  82. )
  83. {
  84. ULONG Index;
  85. NET_API_STATUS NetStatus;
  86. try {
  87. //
  88. // Perform initialization that allows us to call BrWorkerTermination
  89. //
  90. try{
  91. InitializeCriticalSection( &BrWorkerCritSect );
  92. }
  93. except ( EXCEPTION_EXECUTE_HANDLER ) {
  94. return NERR_NoNetworkResource;
  95. }
  96. BrWorkerCSInitialized = TRUE;
  97. InitializeListHead( &BrWorkerQueueHead );
  98. BrNumberOfCreatedWorkerThreads = 0;
  99. BrNumberOfActiveWorkerThreads = 0;
  100. //
  101. // Initialize the work queue semaphore.
  102. //
  103. BrWorkerSemaphore = CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
  104. if (BrWorkerSemaphore == NULL) {
  105. try_return ( NetStatus = GetLastError() );
  106. }
  107. NetStatus = NERR_Success;
  108. //
  109. // Done
  110. //
  111. try_exit:NOTHING;
  112. } finally {
  113. if (NetStatus != NERR_Success) {
  114. (VOID) BrWorkerTermination();
  115. }
  116. }
  117. return NetStatus;
  118. }
  119. VOID
  120. BrWorkerCreateThread(
  121. ULONG NetworkCount
  122. )
  123. /*++
  124. Routine Description:
  125. Ensure there are enough worker threads to handle the current number of
  126. networks.
  127. Worker threads are created but are never deleted until the browser terminates.
  128. Each worker thread has pending I/O. We don't keep track of which thread has
  129. which I/O pending. Thus, we can't delete any threads.
  130. Arguments:
  131. NetworkCount - Current number of networks.
  132. Return Value:
  133. None.
  134. --*/
  135. {
  136. ULONG ThreadId;
  137. //
  138. // Create 1 thread for every 2 networks.
  139. // (round up)
  140. LOCK_WORK_QUEUE();
  141. while ( BrNumberOfCreatedWorkerThreads < (NetworkCount+1)/2 &&
  142. BrNumberOfCreatedWorkerThreads < BR_MAX_NUMBER_OF_WORKER_THREADS ) {
  143. BrThreadArray[BrNumberOfCreatedWorkerThreads] = chBEGINTHREADEX(NULL, // CreateThread
  144. 0,
  145. (LPTHREAD_START_ROUTINE)BrWorkerThread,
  146. ULongToPtr(BrNumberOfCreatedWorkerThreads),
  147. 0,
  148. &ThreadId
  149. );
  150. if (BrThreadArray[BrNumberOfCreatedWorkerThreads] == NULL) {
  151. break;
  152. }
  153. //
  154. // Set the browser threads to time critical priority.
  155. //
  156. SetThreadPriority(BrThreadArray[BrNumberOfCreatedWorkerThreads], THREAD_PRIORITY_ABOVE_NORMAL);
  157. //
  158. // Indicate we now have another thread.
  159. //
  160. BrNumberOfCreatedWorkerThreads++;
  161. }
  162. UNLOCK_WORK_QUEUE();
  163. }
  164. VOID
  165. BrWorkerKillThreads(
  166. VOID
  167. )
  168. /*++
  169. Routine Description:
  170. Terminate all worker threads.
  171. Arguments:
  172. None.
  173. Return Value:
  174. None.
  175. --*/
  176. {
  177. ULONG Index;
  178. HANDLE ThreadHandle;
  179. //
  180. // Make sure the terminate now event is in the signalled state to unwind
  181. // all our threads.
  182. //
  183. SetEvent( BrGlobalData.TerminateNowEvent );
  184. //
  185. // Loop waiting for all the threads to stop.
  186. //
  187. LOCK_WORK_QUEUE();
  188. for ( Index = 0 ; Index < BrNumberOfCreatedWorkerThreads ; Index += 1 ) {
  189. if ( BrThreadArray[Index] != NULL ) {
  190. ThreadHandle = BrThreadArray[Index];
  191. UNLOCK_WORK_QUEUE();
  192. WaitForSingleObject( ThreadHandle, 0xffffffff );
  193. CloseHandle( ThreadHandle );
  194. LOCK_WORK_QUEUE();
  195. BrThreadArray[Index] = NULL;
  196. }
  197. }
  198. UNLOCK_WORK_QUEUE();
  199. return;
  200. }
  201. NET_API_STATUS
  202. BrWorkerTermination(
  203. VOID
  204. )
  205. /*++
  206. Routine Description:
  207. Undo initialization of the worker threads.
  208. Arguments:
  209. None.
  210. Return Value:
  211. Status value -
  212. --*/
  213. {
  214. //
  215. // Ensure the threads have been terminated.
  216. //
  217. BrWorkerKillThreads();
  218. if ( BrWorkerSemaphore != NULL ) {
  219. CloseHandle( BrWorkerSemaphore );
  220. BrWorkerSemaphore = NULL;
  221. }
  222. BrNumberOfActiveWorkerThreads = 0;
  223. BrNumberOfCreatedWorkerThreads = 0;
  224. //
  225. // BrWorkerCSInit is set upon successfull CS initialization.
  226. // (see BrWorkerInitialization)
  227. //
  228. if ( BrWorkerCSInitialized ) {
  229. DeleteCriticalSection( &BrWorkerCritSect );
  230. }
  231. return NERR_Success;
  232. }
  233. VOID
  234. BrQueueWorkItem(
  235. IN PWORKER_ITEM WorkItem
  236. )
  237. /*++
  238. Routine Description:
  239. This function queues a work item to a queue that is processed by
  240. a worker thread. This thread runs at low priority, at IRQL 0
  241. Arguments:
  242. WorkItem - Supplies a pointer to the work item to add the the queue.
  243. This structure must be located in NonPagedPool. The work item
  244. structure contains a doubly linked list entry, the address of a
  245. routine to call and a parameter to pass to that routine. It is
  246. the routine's responsibility to reclaim the storage occupied by
  247. the WorkItem structure.
  248. Return Value:
  249. Status value -
  250. --*/
  251. {
  252. //
  253. // Acquire the worker thread spinlock and insert the work item in the
  254. // list and release the worker thread semaphore if the work item is
  255. // not already in the list.
  256. //
  257. LOCK_WORK_QUEUE();
  258. if (WorkItem->Inserted == FALSE) {
  259. BrPrint(( BR_QUEUE, "Inserting work item %lx (%lx)\n",WorkItem, WorkItem->WorkerRoutine));
  260. InsertTailList( &BrWorkerQueueHead, &WorkItem->List );
  261. WorkItem->Inserted = TRUE;
  262. ReleaseSemaphore( BrWorkerSemaphore,
  263. 1,
  264. NULL
  265. );
  266. }
  267. UNLOCK_WORK_QUEUE();
  268. return;
  269. }
  270. VOID
  271. BrWorkerThread(
  272. IN PVOID StartContext
  273. )
  274. {
  275. NET_API_STATUS NetStatus;
  276. #define WORKER_SIGNALED 0
  277. #define TERMINATION_SIGNALED 1
  278. #define REG_CHANGE_SIGNALED 2
  279. #define NUMBER_OF_EVENTS 3
  280. HANDLE WaitList[NUMBER_OF_EVENTS];
  281. ULONG WaitCount = 0;
  282. PWORKER_ITEM WorkItem;
  283. ULONG ThreadIndex = PtrToUlong(StartContext);
  284. HKEY RegistryHandle = NULL;
  285. HANDLE EventHandle = NULL;
  286. WaitList[WORKER_SIGNALED] = BrWorkerSemaphore;
  287. WaitCount ++;
  288. WaitList[TERMINATION_SIGNALED] = BrGlobalData.TerminateNowEvent;
  289. WaitCount ++;
  290. //
  291. // Primary thread waits on registry changes, too.
  292. //
  293. if ( ThreadIndex == 0xFFFFFFFF ) {
  294. DWORD RegStatus;
  295. NET_API_STATUS NetStatus;
  296. //
  297. // Register for notifications of changes to Parameters
  298. //
  299. // Failure doesn't affect normal operation of the browser.
  300. //
  301. RegStatus = RegOpenKeyExA( HKEY_LOCAL_MACHINE,
  302. "System\\CurrentControlSet\\Services\\Browser\\Parameters",
  303. 0,
  304. KEY_NOTIFY,
  305. &RegistryHandle );
  306. if ( RegStatus != ERROR_SUCCESS ) {
  307. BrPrint(( BR_CRITICAL, "BrWorkerThead: Can't RegOpenKey %ld\n", RegStatus ));
  308. } else {
  309. EventHandle = CreateEvent(
  310. NULL, // No security attributes
  311. TRUE, // Automatically reset
  312. FALSE, // Initially not signaled
  313. NULL ); // No name
  314. if ( EventHandle == NULL ) {
  315. BrPrint(( BR_CRITICAL, "BrWorkerThead: Can't CreateEvent %ld\n", GetLastError() ));
  316. } else {
  317. NetStatus = RegNotifyChangeKeyValue(
  318. RegistryHandle,
  319. FALSE, // Ignore subkeys
  320. REG_NOTIFY_CHANGE_LAST_SET, // Notify of value changes
  321. EventHandle,
  322. TRUE ); // Signal event upon change
  323. if ( NetStatus != NERR_Success ) {
  324. BrPrint(( BR_CRITICAL, "BrWorkerThead: Can't RegNotifyChangeKeyValue %ld\n", NetStatus ));
  325. } else {
  326. WaitList[REG_CHANGE_SIGNALED] = EventHandle;
  327. WaitCount ++;
  328. }
  329. }
  330. }
  331. }
  332. else
  333. {
  334. EnterCriticalSection( &BrAsyncIOCriticalSection );
  335. BrDgWorkerThreadsOutstanding++;
  336. LeaveCriticalSection( &BrAsyncIOCriticalSection );
  337. }
  338. BrPrint(( BR_QUEUE, "Starting new work thread, Context: %lx\n", StartContext));
  339. //
  340. // Set the thread priority to the lowest realtime level.
  341. //
  342. while( TRUE ) {
  343. ULONG WaitItem;
  344. //
  345. // Wait until something is put in the queue (semaphore is
  346. // released), remove the item from the queue, mark it not
  347. // inserted, and execute the specified routine.
  348. //
  349. BrPrint(( BR_QUEUE, "%lx: worker thread waiting\n", StartContext));
  350. do {
  351. WaitItem = WaitForMultipleObjectsEx( WaitCount, WaitList, FALSE, 0xffffffff, TRUE );
  352. } while ( WaitItem == WAIT_IO_COMPLETION );
  353. if (WaitItem == 0xffffffff) {
  354. BrPrint(( BR_CRITICAL, "WaitForMultipleObjects in browser queue returned %ld\n", GetLastError()));
  355. break;
  356. }
  357. if (WaitItem == TERMINATION_SIGNALED) {
  358. break;
  359. //
  360. // If the registry has changed,
  361. // process the changes.
  362. //
  363. } else if ( WaitItem == REG_CHANGE_SIGNALED ) {
  364. //
  365. // Setup for future notifications.
  366. //
  367. NetStatus = RegNotifyChangeKeyValue(
  368. RegistryHandle,
  369. FALSE, // Ignore subkeys
  370. REG_NOTIFY_CHANGE_LAST_SET, // Notify of value changes
  371. EventHandle,
  372. TRUE ); // Signal event upon change
  373. if ( NetStatus != NERR_Success ) {
  374. BrPrint(( BR_CRITICAL, "BrWorkerThead: Can't RegNotifyChangeKeyValue %ld\n", NetStatus ));
  375. }
  376. NetStatus = BrReadBrowserConfigFields( FALSE );
  377. if ( NetStatus != NERR_Success) {
  378. BrPrint(( BR_CRITICAL, "BrWorkerThead: Can't BrReadConfigFields %ld\n", NetStatus ));
  379. }
  380. continue;
  381. }
  382. BrPrint(( BR_QUEUE, "%lx: Worker thread waking up\n", StartContext));
  383. LOCK_WORK_QUEUE();
  384. BrWorkerThreadCount[BrNumberOfActiveWorkerThreads++] += 1;
  385. ASSERT (!IsListEmpty(&BrWorkerQueueHead));
  386. if (!IsListEmpty(&BrWorkerQueueHead)) {
  387. WorkItem = (PWORKER_ITEM)RemoveHeadList( &BrWorkerQueueHead );
  388. ASSERT (WorkItem->Inserted);
  389. WorkItem->Inserted = FALSE;
  390. } else {
  391. WorkItem = NULL;
  392. }
  393. UNLOCK_WORK_QUEUE();
  394. BrPrint(( BR_QUEUE, "%lx: Pulling off work item %lx (%lx)\n", StartContext, WorkItem, WorkItem->WorkerRoutine));
  395. //
  396. // Execute the specified routine.
  397. //
  398. if (WorkItem != NULL) {
  399. (WorkItem->WorkerRoutine)( WorkItem->Parameter );
  400. }
  401. LOCK_WORK_QUEUE();
  402. BrNumberOfActiveWorkerThreads--;
  403. UNLOCK_WORK_QUEUE();
  404. }
  405. BrPrint(( BR_QUEUE, "%lx: worker thread exitting\n", StartContext));
  406. if ( ThreadIndex != 0xFFFFFFFF ) {
  407. IO_STATUS_BLOCK IoSb;
  408. DWORD waitResult;
  409. BOOL SetThreadEvent = FALSE;
  410. //
  411. // Cancel the I/O operations outstanding on the browser.
  412. // Then wait for the shutdown event to be signalled, but allow
  413. // APC's to be called to call our completion routine.
  414. //
  415. NtCancelIoFile(BrDgReceiverDeviceHandle, &IoSb);
  416. do {
  417. waitResult = WaitForSingleObjectEx(BrDgAsyncIOShutDownEvent,0xffffffff, TRUE);
  418. }
  419. while( waitResult == WAIT_IO_COMPLETION );
  420. EnterCriticalSection( &BrAsyncIOCriticalSection );
  421. BrDgWorkerThreadsOutstanding--;
  422. if( BrDgWorkerThreadsOutstanding == 0 )
  423. {
  424. SetThreadEvent = TRUE;
  425. }
  426. LeaveCriticalSection( &BrAsyncIOCriticalSection );
  427. if( SetThreadEvent )
  428. {
  429. SetEvent( BrDgAsyncIOThreadShutDownEvent );
  430. }
  431. } else {
  432. if( RegistryHandle ) CloseHandle( RegistryHandle );
  433. if( EventHandle ) CloseHandle( EventHandle );
  434. }
  435. }
  436. NET_API_STATUS
  437. BrCreateTimer(
  438. IN PBROWSER_TIMER Timer
  439. )
  440. {
  441. OBJECT_ATTRIBUTES ObjA;
  442. NTSTATUS Status;
  443. InitializeObjectAttributes(&ObjA, NULL, 0, NULL, NULL);
  444. Status = NtCreateTimer(&Timer->TimerHandle,
  445. TIMER_ALL_ACCESS,
  446. &ObjA,
  447. NotificationTimer);
  448. if (!NT_SUCCESS(Status)) {
  449. BrPrint(( BR_CRITICAL, "Failed to create timer %lx: %X\n", Timer, Status));
  450. return(BrMapStatus(Status));
  451. }
  452. BrPrint(( BR_TIMER, "Creating timer %lx: Handle: %lx\n", Timer, Timer->TimerHandle));
  453. return(NERR_Success);
  454. }
  455. NET_API_STATUS
  456. BrDestroyTimer(
  457. IN PBROWSER_TIMER Timer
  458. )
  459. {
  460. HANDLE Handle;
  461. //
  462. // Avoid destroying a timer twice.
  463. //
  464. if ( Timer->TimerHandle == NULL ) {
  465. return NERR_Success;
  466. }
  467. // Closing doesn't automatically cancel the timer.
  468. (VOID) BrCancelTimer( Timer );
  469. //
  470. // Close the handle and prevent future uses.
  471. //
  472. Handle = Timer->TimerHandle;
  473. Timer->TimerHandle = NULL;
  474. BrPrint(( BR_TIMER, "Destroying timer %lx\n", Timer));
  475. return BrMapStatus(NtClose(Handle));
  476. }
  477. NET_API_STATUS
  478. BrCancelTimer(
  479. IN PBROWSER_TIMER Timer
  480. )
  481. {
  482. //
  483. // Avoid cancelling a destroyed timer.
  484. //
  485. if ( Timer->TimerHandle == NULL ) {
  486. BrPrint(( BR_TIMER, "Canceling destroyed timer %lx\n", Timer));
  487. return NERR_Success;
  488. }
  489. BrPrint(( BR_TIMER, "Canceling timer %lx\n", Timer));
  490. return BrMapStatus(NtCancelTimer(Timer->TimerHandle, NULL));
  491. }
  492. NET_API_STATUS
  493. BrSetTimer(
  494. IN PBROWSER_TIMER Timer,
  495. IN ULONG MillisecondsToExpire,
  496. IN PBROWSER_WORKER_ROUTINE WorkerFunction,
  497. IN PVOID Context
  498. )
  499. {
  500. LARGE_INTEGER TimerDueTime;
  501. NTSTATUS NtStatus;
  502. //
  503. // Avoid setting a destroyed timer.
  504. //
  505. if ( Timer->TimerHandle == NULL ) {
  506. BrPrint(( BR_TIMER, "Setting a destroyed timer %lx\n", Timer));
  507. return NERR_Success;
  508. }
  509. BrPrint(( BR_TIMER, "Setting timer %lx to %ld milliseconds, WorkerFounction %lx, Context: %lx\n", Timer, MillisecondsToExpire, WorkerFunction, Context));
  510. //
  511. // Figure out the timeout.
  512. //
  513. TimerDueTime.QuadPart = Int32x32To64( MillisecondsToExpire, -10000 );
  514. BrInitializeWorkItem(&Timer->WorkItem, WorkerFunction, Context);
  515. //
  516. // Set the timer to go off when it expires.
  517. //
  518. NtStatus = NtSetTimer(Timer->TimerHandle,
  519. &TimerDueTime,
  520. BrTimerRoutine,
  521. Timer,
  522. FALSE,
  523. 0,
  524. NULL
  525. );
  526. if (!NT_SUCCESS(NtStatus)) {
  527. #if DBG
  528. BrPrint(( BR_CRITICAL, "Unable to set browser timer expiration: %X (%lx)\n", NtStatus, Timer));
  529. DbgBreakPoint();
  530. #endif
  531. return(BrMapStatus(NtStatus));
  532. }
  533. return NERR_Success;
  534. }
  535. VOID
  536. BrTimerRoutine(
  537. IN PVOID TimerContext,
  538. IN ULONG TImerLowValue,
  539. IN LONG TimerHighValue
  540. )
  541. {
  542. PBROWSER_TIMER Timer = TimerContext;
  543. BrPrint(( BR_TIMER, "Timer %lx fired\n", Timer));
  544. BrQueueWorkItem(&Timer->WorkItem);
  545. }
  546. VOID
  547. BrInitializeWorkItem(
  548. IN PWORKER_ITEM Item,
  549. IN PBROWSER_WORKER_ROUTINE Routine,
  550. IN PVOID Context)
  551. /*++
  552. Routine Description:
  553. Initializes fields in Item under queue lock
  554. Arguments:
  555. Item -- worker item to init
  556. Routine -- routine to set
  557. Context -- work context to set
  558. Return Value:
  559. none.
  560. --*/
  561. {
  562. LOCK_WORK_QUEUE();
  563. Item->WorkerRoutine = Routine;
  564. Item->Parameter = Context;
  565. Item->Inserted = FALSE;
  566. UNLOCK_WORK_QUEUE();
  567. }