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.

655 lines
17 KiB

  1. /*++
  2. Copyright (c) 1992-1996 Microsoft Corporation
  3. Module Name:
  4. worker.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. //
  13. // Common include files.
  14. //
  15. #include "logonsrv.h" // Include files common to entire service
  16. #pragma hdrstop
  17. //
  18. // Number of worker threads to create and the usage count array.
  19. //
  20. #define NL_MAX_WORKER_THREADS 5
  21. ULONG NlNumberOfCreatedWorkerThreads = 0;
  22. ULONG NlWorkerThreadStats[NL_MAX_WORKER_THREADS];
  23. PHANDLE NlThreadArray[NL_MAX_WORKER_THREADS];
  24. BOOLEAN NlThreadExitted[NL_MAX_WORKER_THREADS];
  25. //
  26. // CritSect guard the WorkQueue list.
  27. //
  28. BOOLEAN NlWorkerInitialized = FALSE;
  29. CRITICAL_SECTION NlWorkerCritSect;
  30. #define LOCK_WORK_QUEUE() EnterCriticalSection(&NlWorkerCritSect);
  31. #define UNLOCK_WORK_QUEUE() LeaveCriticalSection(&NlWorkerCritSect);
  32. //
  33. // Head of singly linked list of work items queued to the worker thread.
  34. //
  35. LIST_ENTRY NlWorkerQueueHead = {0};
  36. LIST_ENTRY NlWorkerHighQueueHead = {0};
  37. VOID
  38. NlWorkerThread(
  39. IN PVOID StartContext
  40. )
  41. {
  42. NET_API_STATUS NetStatus;
  43. ULONG ThreadIndex = (ULONG)((ULONG_PTR)StartContext);
  44. ULONG Index;
  45. PWORKER_ITEM WorkItem;
  46. HANDLE EventHandle = NULL;
  47. //
  48. // Every thread should loop until the queue is empty.
  49. //
  50. // This loop completes even though Netlogon has been asked to terminate.
  51. // The individual worker routines are designed to terminate quickly when netlogon
  52. // is terminating. This philosophy allows the worker routines to do there own
  53. // cleanup.
  54. //
  55. while( TRUE ) {
  56. //
  57. // Pull an entry off the queue
  58. //
  59. // Prefer a workitem from the high priority queue
  60. //
  61. LOCK_WORK_QUEUE();
  62. if (!IsListEmpty(&NlWorkerHighQueueHead)) {
  63. WorkItem = (PWORKER_ITEM)RemoveHeadList( &NlWorkerHighQueueHead );
  64. } else if (!IsListEmpty(&NlWorkerQueueHead)) {
  65. WorkItem = (PWORKER_ITEM)RemoveHeadList( &NlWorkerQueueHead );
  66. } else {
  67. UNLOCK_WORK_QUEUE();
  68. break;
  69. }
  70. NlAssert(WorkItem->Inserted);
  71. WorkItem->Inserted = FALSE;
  72. NlWorkerThreadStats[NlNumberOfCreatedWorkerThreads-1] += 1;
  73. UNLOCK_WORK_QUEUE();
  74. NlPrint(( NL_WORKER, "%lx: Pulling off work item %lx (%lx)\n", ThreadIndex, WorkItem, WorkItem->WorkerRoutine));
  75. //
  76. // Execute the specified routine.
  77. //
  78. (WorkItem->WorkerRoutine)( WorkItem->Parameter );
  79. //
  80. // A thread can ditch dangling thread handles for the other threads.
  81. //
  82. // This will ensure there is at most one dangling thread handle.
  83. //
  84. LOCK_WORK_QUEUE();
  85. for (Index = 0; Index < NL_MAX_WORKER_THREADS; Index++ ) {
  86. if ( ThreadIndex != Index && NlThreadArray[Index] != NULL && NlThreadExitted[Index] ) {
  87. DWORD WaitStatus;
  88. NlPrint(( NL_WORKER, "%lx: %lx: Ditching worker thread\n", Index, NlThreadArray[Index]));
  89. // Always wait for the thread to exit before closing the handle to
  90. // ensure the thread has left netlogon.dll before we unload the dll.
  91. WaitStatus = WaitForSingleObject( NlThreadArray[Index], 0xffffffff );
  92. if ( WaitStatus != 0 ) {
  93. NlPrint(( NL_CRITICAL, "%lx: worker thread handle cannot be awaited. %ld 0x%lX\n", Index, WaitStatus, NlThreadArray[Index] ));
  94. }
  95. if (!CloseHandle( NlThreadArray[Index] ) ) {
  96. NlPrint(( NL_CRITICAL, "%lx: worker thread handle cannot be closed. %ld 0x%lX\n", Index, GetLastError(), NlThreadArray[Index] ));
  97. }
  98. NlThreadArray[Index] = NULL;
  99. NlThreadExitted[Index] = FALSE;
  100. }
  101. }
  102. UNLOCK_WORK_QUEUE();
  103. }
  104. NlPrint(( NL_WORKER, "%lx: worker thread exitting\n", ThreadIndex ));
  105. LOCK_WORK_QUEUE();
  106. NlThreadExitted[ThreadIndex] = TRUE;
  107. NlNumberOfCreatedWorkerThreads--;
  108. UNLOCK_WORK_QUEUE();
  109. }
  110. NET_API_STATUS
  111. NlWorkerInitialization(
  112. VOID
  113. )
  114. {
  115. ULONG ThreadId;
  116. NET_API_STATUS NetStatus;
  117. //
  118. // Perform initialization that allows us to call NlWorkerTermination
  119. //
  120. try {
  121. InitializeCriticalSection( &NlWorkerCritSect );
  122. } except( EXCEPTION_EXECUTE_HANDLER ) {
  123. return ERROR_NOT_ENOUGH_MEMORY;
  124. }
  125. InitializeListHead( &NlWorkerQueueHead );
  126. InitializeListHead( &NlWorkerHighQueueHead );
  127. NlNumberOfCreatedWorkerThreads = 0;
  128. RtlZeroMemory( NlThreadArray, sizeof(NlThreadArray) );
  129. RtlZeroMemory( NlThreadExitted, sizeof(NlThreadExitted) );
  130. RtlZeroMemory( NlWorkerThreadStats, sizeof(NlWorkerThreadStats) );
  131. NlWorkerInitialized = TRUE;
  132. return NERR_Success;
  133. }
  134. VOID
  135. NlWorkerKillThreads(
  136. VOID
  137. )
  138. /*++
  139. Routine Description:
  140. Terminate all worker threads.
  141. Arguments:
  142. None.
  143. Return Value:
  144. None.
  145. --*/
  146. {
  147. ULONG Index;
  148. //
  149. // Wait for all the threads to exit.
  150. //
  151. for ( Index = 0; Index < NL_MAX_WORKER_THREADS; Index ++ ) {
  152. if ( NlThreadArray[Index] != NULL ) {
  153. DWORD WaitStatus;
  154. NlPrint(( NL_WORKER, "%lx: %lx: Ditching worker thread\n", Index, NlThreadArray[Index]));
  155. // Always wait for the thread to exit before closing the handle to
  156. // ensure the thread has left netlogon.dll before we unload the dll.
  157. WaitStatus = WaitForSingleObject( NlThreadArray[Index], 0xffffffff );
  158. if ( WaitStatus != 0 ) {
  159. NlPrint(( NL_CRITICAL, "%lx: worker thread handle cannot be awaited. %ld 0x%lX\n", Index, WaitStatus, NlThreadArray[Index] ));
  160. }
  161. if (!CloseHandle( NlThreadArray[Index] ) ) {
  162. NlPrint(( NL_CRITICAL, "%lx: worker thread handle cannot be closed. %ld 0x%lX\n", Index, GetLastError(), NlThreadArray[Index] ));
  163. }
  164. NlThreadArray[Index] = NULL;
  165. NlThreadExitted[Index] = FALSE;
  166. }
  167. }
  168. return;
  169. }
  170. VOID
  171. NlWorkerTermination(
  172. VOID
  173. )
  174. /*++
  175. Routine Description:
  176. Undo initialization of the worker threads.
  177. Arguments:
  178. None.
  179. Return Value:
  180. Status value -
  181. --*/
  182. {
  183. //
  184. // Only cleanup if we've successfully initialized.
  185. //
  186. if ( NlWorkerInitialized ) {
  187. //
  188. //
  189. // Ensure the threads have been terminated.
  190. //
  191. NlWorkerKillThreads();
  192. DeleteCriticalSection( &NlWorkerCritSect );
  193. NlWorkerInitialized = FALSE;
  194. }
  195. return;
  196. }
  197. BOOL
  198. NlQueueWorkItem(
  199. IN PWORKER_ITEM WorkItem,
  200. IN BOOL InsertNewItem,
  201. IN BOOL HighPriority
  202. )
  203. /*++
  204. Routine Description:
  205. This function modifies the work item queue by either inserting
  206. a new specified work item to a specified queue or increasing the
  207. priority of the already inserted item to the highest priority
  208. in the specified queue.
  209. Arguments:
  210. WorkItem - Supplies a pointer to the work item to add the the queue.
  211. It is the caller's responsibility to reclaim the storage occupied by
  212. the WorkItem structure.
  213. InsertNewItem - If TRUE, we are to insert the new item into the queue
  214. specified by the value of the HighPriority parameter; the new item
  215. will be inserted at the end of the queue. Otherwise, we are to
  216. modify (boost) the priority of already inserted work item by moving
  217. it to the front of the queue specified by the HighPriority value.
  218. If TRUE and the item is already inserted (as determined by this
  219. routine), this routine is no-op except for some possible cleanup.
  220. If FALSE and the item is not already inserted (as determined by
  221. this routine), this routine is no-op except for some possible cleanup.
  222. HighPriority - The queue entry should be processed at a higher priority than
  223. normal.
  224. Return Value:
  225. TRUE if item got queued or modified
  226. --*/
  227. {
  228. ULONG Index;
  229. //
  230. // Ignore this attempt if the worker threads aren't initialized.
  231. //
  232. if ( !NlWorkerInitialized ) {
  233. NlPrint(( NL_CRITICAL, "NlQueueWorkItem when worker not initialized\n"));
  234. return FALSE;
  235. }
  236. //
  237. // Ditch any dangling thread handles
  238. //
  239. LOCK_WORK_QUEUE();
  240. for (Index = 0; Index < NL_MAX_WORKER_THREADS; Index++ ) {
  241. if ( NlThreadArray[Index] != NULL && NlThreadExitted[Index] ) {
  242. DWORD WaitStatus;
  243. NlPrint(( NL_WORKER, "%lx: %lx: Ditching worker thread\n", Index, NlThreadArray[Index]));
  244. // Always wait for the thread to exit before closing the handle to
  245. // ensure the thread has left netlogon.dll before we unload the dll.
  246. WaitStatus = WaitForSingleObject( NlThreadArray[Index], 0xffffffff );
  247. if ( WaitStatus != 0 ) {
  248. NlPrint(( NL_CRITICAL, "%lx: worker thread handle cannot be awaited. %ld 0x%lX\n", Index, WaitStatus, NlThreadArray[Index] ));
  249. }
  250. if (!CloseHandle( NlThreadArray[Index] ) ) {
  251. NlPrint(( NL_CRITICAL, "%lx: worker thread handle cannot be closed. %ld 0x%lX\n", Index, GetLastError(), NlThreadArray[Index] ));
  252. }
  253. NlThreadArray[Index] = NULL;
  254. NlThreadExitted[Index] = FALSE;
  255. }
  256. }
  257. //
  258. // If we are to insert a new work item,
  259. // do so
  260. //
  261. if ( InsertNewItem ) {
  262. //
  263. // If the work item is already inserted,
  264. // we are done expect for some possible
  265. // cleanup
  266. //
  267. if ( WorkItem->Inserted ) {
  268. //
  269. // If there is no worker thread processing this
  270. // work item (i.e. we failed to create a thread
  271. // at the time this work item was inserted),
  272. // fall through and retry to create a thread
  273. // below. Otherwise, we are done.
  274. //
  275. if ( NlNumberOfCreatedWorkerThreads > 0 ) {
  276. UNLOCK_WORK_QUEUE();
  277. return TRUE;
  278. }
  279. //
  280. // Otherwise, insert this work item
  281. //
  282. } else {
  283. NlPrint(( NL_WORKER, "Inserting work item %lx (%lx)\n",WorkItem, WorkItem->WorkerRoutine));
  284. if ( HighPriority ) {
  285. InsertTailList( &NlWorkerHighQueueHead, &WorkItem->List );
  286. } else {
  287. InsertTailList( &NlWorkerQueueHead, &WorkItem->List );
  288. }
  289. WorkItem->Inserted = TRUE;
  290. }
  291. //
  292. // Otherwise, we are to boost the priority
  293. // of an already inserted work item
  294. //
  295. } else {
  296. //
  297. // If the work item isn't already inserted,
  298. // we are done
  299. //
  300. if ( !WorkItem->Inserted ) {
  301. UNLOCK_WORK_QUEUE();
  302. return TRUE;
  303. //
  304. // Otherwise, boost the priority
  305. //
  306. } else {
  307. NlPrint(( NL_WORKER,
  308. "Boosting %s priority work item %lx (%lx)\n",
  309. (HighPriority ? "high" : "low"),
  310. WorkItem,
  311. WorkItem->WorkerRoutine ));
  312. RemoveEntryList( &WorkItem->List );
  313. if ( HighPriority ) {
  314. InsertHeadList( &NlWorkerHighQueueHead, &WorkItem->List );
  315. } else {
  316. InsertHeadList( &NlWorkerQueueHead, &WorkItem->List );
  317. }
  318. //
  319. // If there is no worker thread processing this
  320. // work item (i.e. we failed to create a thread
  321. // at the time this work item was inserted),
  322. // fall through and retry to create a thread
  323. // below. Otherwise, we are done.
  324. //
  325. if ( NlNumberOfCreatedWorkerThreads > 0 ) {
  326. UNLOCK_WORK_QUEUE();
  327. return TRUE;
  328. }
  329. }
  330. }
  331. //
  332. // If there isn't a worker thread to handle the request,
  333. // create one now.
  334. //
  335. if ( NlNumberOfCreatedWorkerThreads < NL_MAX_WORKER_THREADS ) {
  336. //
  337. // Find a spot for the thread handle.
  338. //
  339. for (Index = 0; Index < NL_MAX_WORKER_THREADS; Index++ ) {
  340. if ( NlThreadArray[Index] == NULL ) {
  341. break;
  342. }
  343. }
  344. if ( Index >= NL_MAX_WORKER_THREADS ) {
  345. NlPrint(( NL_CRITICAL, "NlQueueWorkItem: Internal Error\n" ));
  346. UNLOCK_WORK_QUEUE();
  347. return FALSE;
  348. } else {
  349. DWORD ThreadId;
  350. NlThreadArray[Index] = CreateThread(
  351. NULL, // No security attributes
  352. 0,
  353. (LPTHREAD_START_ROUTINE)NlWorkerThread,
  354. (PVOID) ULongToPtr( Index ),
  355. 0, // No special creation flags
  356. &ThreadId );
  357. NlPrint(( NL_WORKER, "%lx: %lx: %lx: Starting worker thread\n", Index, NlThreadArray[Index], ThreadId ));
  358. //
  359. // Note that if we fail to create a thread,
  360. // the work item remains queued and possibly not processed.
  361. // This is not critical because the item will be processed
  362. // next time a work item gets queued which will happen at
  363. // the next scavenging time at latest.
  364. //
  365. if (NlThreadArray[Index] == NULL) {
  366. NlPrint((NL_CRITICAL,
  367. "NlQueueWorkItem: Cannot create thread %ld\n", GetLastError() ));
  368. } else {
  369. NlNumberOfCreatedWorkerThreads++;
  370. }
  371. }
  372. }
  373. UNLOCK_WORK_QUEUE();
  374. return TRUE;
  375. }
  376. #ifdef notdef // Don't need timers yet
  377. NET_API_STATUS
  378. NlCreateTimer(
  379. IN PNlOWSER_TIMER Timer
  380. )
  381. {
  382. OBJECT_ATTRIBUTES ObjA;
  383. NTSTATUS Status;
  384. InitializeObjectAttributes(&ObjA, NULL, 0, NULL, NULL);
  385. Status = NtCreateTimer(&Timer->TimerHandle,
  386. TIMER_ALL_ACCESS,
  387. &ObjA,
  388. NotificationTimer);
  389. if (!NT_SUCCESS(Status)) {
  390. NlPrint(( NL_CRITICAL, "Failed to create timer %lx: %X\n", Timer, Status));
  391. return(NlMapStatus(Status));
  392. }
  393. NlPrint(( Nl_TIMER, "Creating timer %lx: Handle: %lx\n", Timer, Timer->TimerHandle));
  394. return(NERR_Success);
  395. }
  396. NET_API_STATUS
  397. NlDestroyTimer(
  398. IN PNlOWSER_TIMER Timer
  399. )
  400. {
  401. HANDLE Handle;
  402. //
  403. // Avoid destroying a timer twice.
  404. //
  405. if ( Timer->TimerHandle == NULL ) {
  406. return NERR_Success;
  407. }
  408. // Closing doesn't automatically cancel the timer.
  409. (VOID) NlCancelTimer( Timer );
  410. //
  411. // Close the handle and prevent future uses.
  412. //
  413. Handle = Timer->TimerHandle;
  414. Timer->TimerHandle = NULL;
  415. NlPrint(( Nl_TIMER, "Destroying timer %lx\n", Timer));
  416. return NlMapStatus(NtClose(Handle));
  417. }
  418. NET_API_STATUS
  419. NlCancelTimer(
  420. IN PNlOWSER_TIMER Timer
  421. )
  422. {
  423. //
  424. // Avoid cancelling a destroyed timer.
  425. //
  426. if ( Timer->TimerHandle == NULL ) {
  427. NlPrint(( Nl_TIMER, "Canceling destroyed timer %lx\n", Timer));
  428. return NERR_Success;
  429. }
  430. NlPrint(( Nl_TIMER, "Canceling timer %lx\n", Timer));
  431. return NlMapStatus(NtCancelTimer(Timer->TimerHandle, NULL));
  432. }
  433. NET_API_STATUS
  434. NlSetTimer(
  435. IN PNlOWSER_TIMER Timer,
  436. IN ULONG MillisecondsToExpire,
  437. IN PNlOWSER_WORKER_ROUTINE WorkerFunction,
  438. IN PVOID Context
  439. )
  440. {
  441. LARGE_INTEGER TimerDueTime;
  442. NTSTATUS NtStatus;
  443. //
  444. // Avoid setting a destroyed timer.
  445. //
  446. if ( Timer->TimerHandle == NULL ) {
  447. NlPrint(( Nl_TIMER, "Setting a destroyed timer %lx\n", Timer));
  448. return NERR_Success;
  449. }
  450. NlPrint(( Nl_TIMER, "Setting timer %lx to %ld milliseconds, WorkerFounction %lx, Context: %lx\n", Timer, MillisecondsToExpire, WorkerFunction, Context));
  451. //
  452. // Figure out the timeout.
  453. //
  454. TimerDueTime.QuadPart = Int32x32To64( MillisecondsToExpire, -10000 );
  455. NlInitializeWorkItem(&Timer->WorkItem, WorkerFunction, Context);
  456. //
  457. // Set the timer to go off when it expires.
  458. //
  459. NtStatus = NtSetTimer(Timer->TimerHandle,
  460. &TimerDueTime,
  461. NlTimerRoutine,
  462. Timer,
  463. FALSE,
  464. 0,
  465. NULL
  466. );
  467. if (!NT_SUCCESS(NtStatus)) {
  468. #if DBG
  469. NlPrint(( NL_CRITICAL, "Unable to set Netlogon timer expiration: %X (%lx)\n", NtStatus, Timer));
  470. DbgbreakPoint();
  471. #endif
  472. return(NlMapStatus(NtStatus));
  473. }
  474. return NERR_Success;
  475. }
  476. VOID
  477. NlTimerRoutine(
  478. IN PVOID TimerContext,
  479. IN ULONG TImerLowValue,
  480. IN LONG TimerHighValue
  481. )
  482. {
  483. PNlOWSER_TIMER Timer = TimerContext;
  484. NlPrint(( Nl_TIMER, "Timer %lx fired\n", Timer));
  485. NlQueueWorkItem(&Timer->WorkItem);
  486. }
  487. #endif // notdef // Don't need timers yet