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.

772 lines
19 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. EnterCriticalSection( &BrAsyncIOCriticalSection );
  142. while ( BrNumberOfCreatedWorkerThreads < (NetworkCount+1)/2 &&
  143. BrNumberOfCreatedWorkerThreads < BR_MAX_NUMBER_OF_WORKER_THREADS ) {
  144. BrThreadArray[BrNumberOfCreatedWorkerThreads] = chBEGINTHREADEX(NULL, // CreateThread
  145. 0,
  146. (LPTHREAD_START_ROUTINE)BrWorkerThread,
  147. ULongToPtr(BrNumberOfCreatedWorkerThreads),
  148. 0,
  149. &ThreadId
  150. );
  151. if (BrThreadArray[BrNumberOfCreatedWorkerThreads] == NULL) {
  152. break;
  153. }
  154. //
  155. // Set the browser threads to time critical priority.
  156. //
  157. SetThreadPriority(BrThreadArray[BrNumberOfCreatedWorkerThreads], THREAD_PRIORITY_ABOVE_NORMAL);
  158. //
  159. // Indicate we now have another thread.
  160. //
  161. BrNumberOfCreatedWorkerThreads++;
  162. BrDgWorkerThreadsOutstanding++;
  163. }
  164. LeaveCriticalSection( &BrAsyncIOCriticalSection );
  165. UNLOCK_WORK_QUEUE();
  166. }
  167. VOID
  168. BrWorkerKillThreads(
  169. VOID
  170. )
  171. /*++
  172. Routine Description:
  173. Terminate all worker threads.
  174. Arguments:
  175. None.
  176. Return Value:
  177. None.
  178. --*/
  179. {
  180. ULONG Index;
  181. HANDLE ThreadHandle;
  182. //
  183. // Make sure the terminate now event is in the signalled state to unwind
  184. // all our threads.
  185. //
  186. SetEvent( BrGlobalData.TerminateNowEvent );
  187. //
  188. // Loop waiting for all the threads to stop.
  189. //
  190. LOCK_WORK_QUEUE();
  191. for ( Index = 0 ; Index < BrNumberOfCreatedWorkerThreads ; Index += 1 ) {
  192. if ( BrThreadArray[Index] != NULL ) {
  193. ThreadHandle = BrThreadArray[Index];
  194. UNLOCK_WORK_QUEUE();
  195. WaitForSingleObject( ThreadHandle, 0xffffffff );
  196. CloseHandle( ThreadHandle );
  197. LOCK_WORK_QUEUE();
  198. BrThreadArray[Index] = NULL;
  199. }
  200. }
  201. UNLOCK_WORK_QUEUE();
  202. return;
  203. }
  204. NET_API_STATUS
  205. BrWorkerTermination(
  206. VOID
  207. )
  208. /*++
  209. Routine Description:
  210. Undo initialization of the worker threads.
  211. Arguments:
  212. None.
  213. Return Value:
  214. Status value -
  215. --*/
  216. {
  217. //
  218. // Ensure the threads have been terminated.
  219. //
  220. BrWorkerKillThreads();
  221. if ( BrWorkerSemaphore != NULL ) {
  222. CloseHandle( BrWorkerSemaphore );
  223. BrWorkerSemaphore = NULL;
  224. }
  225. BrNumberOfActiveWorkerThreads = 0;
  226. BrNumberOfCreatedWorkerThreads = 0;
  227. //
  228. // BrWorkerCSInit is set upon successfull CS initialization.
  229. // (see BrWorkerInitialization)
  230. //
  231. if ( BrWorkerCSInitialized ) {
  232. DeleteCriticalSection( &BrWorkerCritSect );
  233. }
  234. return NERR_Success;
  235. }
  236. VOID
  237. BrQueueWorkItem(
  238. IN PWORKER_ITEM WorkItem
  239. )
  240. /*++
  241. Routine Description:
  242. This function queues a work item to a queue that is processed by
  243. a worker thread. This thread runs at low priority, at IRQL 0
  244. Arguments:
  245. WorkItem - Supplies a pointer to the work item to add the the queue.
  246. This structure must be located in NonPagedPool. The work item
  247. structure contains a doubly linked list entry, the address of a
  248. routine to call and a parameter to pass to that routine. It is
  249. the routine's responsibility to reclaim the storage occupied by
  250. the WorkItem structure.
  251. Return Value:
  252. Status value -
  253. --*/
  254. {
  255. //
  256. // Acquire the worker thread spinlock and insert the work item in the
  257. // list and release the worker thread semaphore if the work item is
  258. // not already in the list.
  259. //
  260. LOCK_WORK_QUEUE();
  261. if (WorkItem->Inserted == FALSE) {
  262. BrPrint(( BR_QUEUE, "Inserting work item %lx (%lx)\n",WorkItem, WorkItem->WorkerRoutine));
  263. InsertTailList( &BrWorkerQueueHead, &WorkItem->List );
  264. WorkItem->Inserted = TRUE;
  265. ReleaseSemaphore( BrWorkerSemaphore,
  266. 1,
  267. NULL
  268. );
  269. }
  270. UNLOCK_WORK_QUEUE();
  271. return;
  272. }
  273. VOID
  274. BrWorkerThread(
  275. IN PVOID StartContext
  276. )
  277. {
  278. NET_API_STATUS NetStatus;
  279. #define WORKER_SIGNALED 0
  280. #define TERMINATION_SIGNALED 1
  281. #define REG_CHANGE_SIGNALED 2
  282. #define NUMBER_OF_EVENTS 3
  283. HANDLE WaitList[NUMBER_OF_EVENTS];
  284. ULONG WaitCount = 0;
  285. PWORKER_ITEM WorkItem;
  286. ULONG ThreadIndex = PtrToUlong(StartContext);
  287. HKEY RegistryHandle = NULL;
  288. HANDLE EventHandle = NULL;
  289. WaitList[WORKER_SIGNALED] = BrWorkerSemaphore;
  290. WaitCount ++;
  291. WaitList[TERMINATION_SIGNALED] = BrGlobalData.TerminateNowEvent;
  292. WaitCount ++;
  293. //
  294. // Primary thread waits on registry changes, too.
  295. //
  296. if ( ThreadIndex == 0xFFFFFFFF ) {
  297. DWORD RegStatus;
  298. NET_API_STATUS NetStatus;
  299. //
  300. // Register for notifications of changes to Parameters
  301. //
  302. // Failure doesn't affect normal operation of the browser.
  303. //
  304. RegStatus = RegOpenKeyExA( HKEY_LOCAL_MACHINE,
  305. "System\\CurrentControlSet\\Services\\Browser\\Parameters",
  306. 0,
  307. KEY_NOTIFY,
  308. &RegistryHandle );
  309. if ( RegStatus != ERROR_SUCCESS ) {
  310. BrPrint(( BR_CRITICAL, "BrWorkerThead: Can't RegOpenKey %ld\n", RegStatus ));
  311. } else {
  312. EventHandle = CreateEvent(
  313. NULL, // No security attributes
  314. TRUE, // Automatically reset
  315. FALSE, // Initially not signaled
  316. NULL ); // No name
  317. if ( EventHandle == NULL ) {
  318. BrPrint(( BR_CRITICAL, "BrWorkerThead: Can't CreateEvent %ld\n", GetLastError() ));
  319. } else {
  320. NetStatus = RegNotifyChangeKeyValue(
  321. RegistryHandle,
  322. FALSE, // Ignore subkeys
  323. REG_NOTIFY_CHANGE_LAST_SET, // Notify of value changes
  324. EventHandle,
  325. TRUE ); // Signal event upon change
  326. if ( NetStatus != NERR_Success ) {
  327. BrPrint(( BR_CRITICAL, "BrWorkerThead: Can't RegNotifyChangeKeyValue %ld\n", NetStatus ));
  328. } else {
  329. WaitList[REG_CHANGE_SIGNALED] = EventHandle;
  330. WaitCount ++;
  331. }
  332. }
  333. }
  334. }
  335. BrPrint(( BR_QUEUE, "Starting new work thread, Context: %lx\n", StartContext));
  336. //
  337. // Set the thread priority to the lowest realtime level.
  338. //
  339. while( TRUE ) {
  340. ULONG WaitItem;
  341. //
  342. // Wait until something is put in the queue (semaphore is
  343. // released), remove the item from the queue, mark it not
  344. // inserted, and execute the specified routine.
  345. //
  346. BrPrint(( BR_QUEUE, "%lx: worker thread waiting\n", StartContext));
  347. do {
  348. WaitItem = WaitForMultipleObjectsEx( WaitCount, WaitList, FALSE, 0xffffffff, TRUE );
  349. } while ( WaitItem == WAIT_IO_COMPLETION );
  350. if (WaitItem == 0xffffffff) {
  351. BrPrint(( BR_CRITICAL, "WaitForMultipleObjects in browser queue returned %ld\n", GetLastError()));
  352. break;
  353. }
  354. if (WaitItem == TERMINATION_SIGNALED) {
  355. break;
  356. //
  357. // If the registry has changed,
  358. // process the changes.
  359. //
  360. } else if ( WaitItem == REG_CHANGE_SIGNALED ) {
  361. //
  362. // Setup for future notifications.
  363. //
  364. NetStatus = RegNotifyChangeKeyValue(
  365. RegistryHandle,
  366. FALSE, // Ignore subkeys
  367. REG_NOTIFY_CHANGE_LAST_SET, // Notify of value changes
  368. EventHandle,
  369. TRUE ); // Signal event upon change
  370. if ( NetStatus != NERR_Success ) {
  371. BrPrint(( BR_CRITICAL, "BrWorkerThead: Can't RegNotifyChangeKeyValue %ld\n", NetStatus ));
  372. }
  373. NetStatus = BrReadBrowserConfigFields( FALSE );
  374. if ( NetStatus != NERR_Success) {
  375. BrPrint(( BR_CRITICAL, "BrWorkerThead: Can't BrReadConfigFields %ld\n", NetStatus ));
  376. }
  377. continue;
  378. }
  379. BrPrint(( BR_QUEUE, "%lx: Worker thread waking up\n", StartContext));
  380. LOCK_WORK_QUEUE();
  381. BrWorkerThreadCount[BrNumberOfActiveWorkerThreads++] += 1;
  382. if (!IsListEmpty(&BrWorkerQueueHead)) {
  383. WorkItem = (PWORKER_ITEM)RemoveHeadList( &BrWorkerQueueHead );
  384. ASSERT (WorkItem->Inserted);
  385. WorkItem->Inserted = FALSE;
  386. } else {
  387. WorkItem = NULL;
  388. }
  389. UNLOCK_WORK_QUEUE();
  390. //
  391. // Execute the specified routine.
  392. //
  393. if (WorkItem != NULL) {
  394. (WorkItem->WorkerRoutine)( WorkItem->Parameter );
  395. }
  396. LOCK_WORK_QUEUE();
  397. BrNumberOfActiveWorkerThreads--;
  398. UNLOCK_WORK_QUEUE();
  399. }
  400. BrPrint(( BR_QUEUE, "%lx: worker thread exitting\n", StartContext));
  401. if ( ThreadIndex != 0xFFFFFFFF ) {
  402. IO_STATUS_BLOCK IoSb;
  403. DWORD waitResult;
  404. BOOL SetThreadEvent = FALSE;
  405. //
  406. // Cancel the I/O operations outstanding on the browser.
  407. // Then wait for the shutdown event to be signalled, but allow
  408. // APC's to be called to call our completion routine.
  409. //
  410. NtCancelIoFile(BrDgReceiverDeviceHandle, &IoSb);
  411. do {
  412. waitResult = WaitForSingleObjectEx(BrDgAsyncIOShutDownEvent,0xffffffff, TRUE);
  413. }
  414. while( waitResult == WAIT_IO_COMPLETION );
  415. EnterCriticalSection( &BrAsyncIOCriticalSection );
  416. BrDgWorkerThreadsOutstanding--;
  417. if( BrDgWorkerThreadsOutstanding == 0 )
  418. {
  419. SetThreadEvent = TRUE;
  420. }
  421. LeaveCriticalSection( &BrAsyncIOCriticalSection );
  422. if( SetThreadEvent )
  423. {
  424. SetEvent( BrDgAsyncIOThreadShutDownEvent );
  425. }
  426. } else {
  427. if( RegistryHandle ) CloseHandle( RegistryHandle );
  428. if( EventHandle ) CloseHandle( EventHandle );
  429. }
  430. }
  431. NET_API_STATUS
  432. BrCreateTimer(
  433. IN PBROWSER_TIMER Timer
  434. )
  435. {
  436. OBJECT_ATTRIBUTES ObjA;
  437. NTSTATUS Status;
  438. InitializeObjectAttributes(&ObjA, NULL, 0, NULL, NULL);
  439. Status = NtCreateTimer(&Timer->TimerHandle,
  440. TIMER_ALL_ACCESS,
  441. &ObjA,
  442. NotificationTimer);
  443. if (!NT_SUCCESS(Status)) {
  444. BrPrint(( BR_CRITICAL, "Failed to create timer %lx: %X\n", Timer, Status));
  445. return(BrMapStatus(Status));
  446. }
  447. BrPrint(( BR_TIMER, "Creating timer %lx: Handle: %lx\n", Timer, Timer->TimerHandle));
  448. return(NERR_Success);
  449. }
  450. NET_API_STATUS
  451. BrDestroyTimer(
  452. IN PBROWSER_TIMER Timer
  453. )
  454. {
  455. HANDLE Handle;
  456. //
  457. // Avoid destroying a timer twice.
  458. //
  459. if ( Timer->TimerHandle == NULL ) {
  460. return NERR_Success;
  461. }
  462. // Closing doesn't automatically cancel the timer.
  463. (VOID) BrCancelTimer( Timer );
  464. //
  465. // Close the handle and prevent future uses.
  466. //
  467. Handle = Timer->TimerHandle;
  468. Timer->TimerHandle = NULL;
  469. BrPrint(( BR_TIMER, "Destroying timer %lx\n", Timer));
  470. return BrMapStatus(NtClose(Handle));
  471. }
  472. NET_API_STATUS
  473. BrCancelTimer(
  474. IN PBROWSER_TIMER Timer
  475. )
  476. {
  477. //
  478. // Avoid cancelling a destroyed timer.
  479. //
  480. if ( Timer->TimerHandle == NULL ) {
  481. BrPrint(( BR_TIMER, "Canceling destroyed timer %lx\n", Timer));
  482. return NERR_Success;
  483. }
  484. BrPrint(( BR_TIMER, "Canceling timer %lx\n", Timer));
  485. return BrMapStatus(NtCancelTimer(Timer->TimerHandle, NULL));
  486. }
  487. NET_API_STATUS
  488. BrSetTimer(
  489. IN PBROWSER_TIMER Timer,
  490. IN ULONG MillisecondsToExpire,
  491. IN PBROWSER_WORKER_ROUTINE WorkerFunction,
  492. IN PVOID Context
  493. )
  494. {
  495. LARGE_INTEGER TimerDueTime;
  496. NTSTATUS NtStatus;
  497. //
  498. // Avoid setting a destroyed timer.
  499. //
  500. if ( Timer->TimerHandle == NULL ) {
  501. BrPrint(( BR_TIMER, "Setting a destroyed timer %lx\n", Timer));
  502. return NERR_Success;
  503. }
  504. BrPrint(( BR_TIMER, "Setting timer %lx to %ld milliseconds, WorkerFounction %lx, Context: %lx\n", Timer, MillisecondsToExpire, WorkerFunction, Context));
  505. //
  506. // Figure out the timeout.
  507. //
  508. TimerDueTime.QuadPart = Int32x32To64( MillisecondsToExpire, -10000 );
  509. BrInitializeWorkItem(&Timer->WorkItem, WorkerFunction, Context);
  510. //
  511. // Set the timer to go off when it expires.
  512. //
  513. NtStatus = NtSetTimer(Timer->TimerHandle,
  514. &TimerDueTime,
  515. BrTimerRoutine,
  516. Timer,
  517. FALSE,
  518. 0,
  519. NULL
  520. );
  521. if (!NT_SUCCESS(NtStatus)) {
  522. #if DBG
  523. BrPrint(( BR_CRITICAL, "Unable to set browser timer expiration: %X (%lx)\n", NtStatus, Timer));
  524. DbgBreakPoint();
  525. #endif
  526. return(BrMapStatus(NtStatus));
  527. }
  528. return NERR_Success;
  529. }
  530. VOID
  531. BrTimerRoutine(
  532. IN PVOID TimerContext,
  533. IN ULONG TImerLowValue,
  534. IN LONG TimerHighValue
  535. )
  536. {
  537. PBROWSER_TIMER Timer = TimerContext;
  538. BrPrint(( BR_TIMER, "Timer %lx fired\n", Timer));
  539. BrQueueWorkItem(&Timer->WorkItem);
  540. }
  541. VOID
  542. BrInitializeWorkItem(
  543. IN PWORKER_ITEM Item,
  544. IN PBROWSER_WORKER_ROUTINE Routine,
  545. IN PVOID Context)
  546. /*++
  547. Routine Description:
  548. Initializes fields in Item under queue lock
  549. Arguments:
  550. Item -- worker item to init
  551. Routine -- routine to set
  552. Context -- work context to set
  553. Return Value:
  554. none.
  555. --*/
  556. {
  557. LOCK_WORK_QUEUE();
  558. Item->WorkerRoutine = Routine;
  559. Item->Parameter = Context;
  560. Item->Inserted = FALSE;
  561. UNLOCK_WORK_QUEUE();
  562. }