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.

698 lines
17 KiB

  1. /*++
  2. Copyright (c) 1989-1998 Microsoft Corporation
  3. Module Name:
  4. threads.c
  5. Abstract:
  6. This module defines support functions for the worker, waiter, and
  7. timer thread pools.
  8. Author:
  9. Gurdeep Singh Pall (gurdeep) Nov 13, 1997
  10. Revision History:
  11. lokeshs - extended/modified threadpool.
  12. Rob Earhart (earhart) September 28, 2000
  13. Moved globals from threads.h to threads.c
  14. Split into independant modules
  15. Event cache cleanup
  16. Environment:
  17. These routines are statically linked in the caller's executable and
  18. are callable only from user mode. They make use of Nt system services.
  19. --*/
  20. #include <ntos.h>
  21. #include <ntrtl.h>
  22. #include <nturtl.h>
  23. #include <wow64t.h>
  24. #include "ntrtlp.h"
  25. #include "threads.h"
  26. // Thread pool globals
  27. // Events used for synchronization
  28. // NTRAID#201102-2000/10/10-earhart -- C requires that statically
  29. // declared structures be initialized to zero, which is correct for an
  30. // slist -- although it would be nice to have some sort of an
  31. // SLIST_HEADER_STATIC_INITIALIZER available to use here.
  32. SLIST_HEADER EventCache;
  33. RTLP_START_THREAD RtlpStartThread ;
  34. PRTLP_START_THREAD RtlpStartThreadFunc = RtlpStartThread ;
  35. RTLP_EXIT_THREAD RtlpExitThread ;
  36. PRTLP_EXIT_THREAD RtlpExitThreadFunc = RtlpExitThread ;
  37. ULONG MaxThreads = 500;
  38. #if DBG1
  39. PVOID CallbackFn1, CallbackFn2, Context1, Context2 ;
  40. #endif
  41. #if DBG
  42. CHAR InvalidSignatureMsg[] = "Invalid threadpool object signature";
  43. CHAR InvalidDelSignatureMsg[] = "Invalid or deleted threadpool object signature";
  44. #endif
  45. NTSTATUS
  46. NTAPI
  47. RtlSetThreadPoolStartFunc(
  48. PRTLP_START_THREAD StartFunc,
  49. PRTLP_EXIT_THREAD ExitFunc
  50. )
  51. /*++
  52. Routine Description:
  53. This routine sets the thread pool's thread creation function. This is not
  54. thread safe, because it is intended solely for kernel32 to call for processes
  55. that aren't csrss/smss.
  56. Arguments:
  57. StartFunc - Function to create a new thread
  58. Return Value:
  59. --*/
  60. {
  61. RtlpStartThreadFunc = StartFunc ;
  62. RtlpExitThreadFunc = ExitFunc ;
  63. return STATUS_SUCCESS ;
  64. }
  65. NTSTATUS
  66. RtlThreadPoolCleanup (
  67. ULONG Flags
  68. )
  69. /*++
  70. Routine Description:
  71. This routine cleans up the thread pool.
  72. Arguments:
  73. None
  74. Return Value:
  75. STATUS_SUCCESS : if none of the components are in use.
  76. STATUS_UNSUCCESSFUL : if some components are still in use.
  77. --*/
  78. {
  79. NTSTATUS Status, NextStatus;
  80. return STATUS_UNSUCCESSFUL;
  81. //
  82. // Attempt to cleanup all modules. Keep, as our final return
  83. // value, the status of the first cleanup routine to error (it's
  84. // pretty arbitrary), but continue on and attempt cleanup of all
  85. // modules.
  86. //
  87. Status = RtlpTimerCleanup();
  88. NextStatus = RtlpWaitCleanup();
  89. if (NT_SUCCESS(Status)) {
  90. Status = NextStatus;
  91. }
  92. NextStatus = RtlpWorkerCleanup();
  93. if (NT_SUCCESS(Status)) {
  94. Status = NextStatus;
  95. }
  96. return Status;
  97. }
  98. NTSTATUS
  99. NTAPI
  100. RtlpStartThread (
  101. PUSER_THREAD_START_ROUTINE Function,
  102. PVOID Parameter,
  103. HANDLE *ThreadHandleReturn
  104. )
  105. {
  106. return RtlCreateUserThread(
  107. NtCurrentProcess(), // process handle
  108. NULL, // security descriptor
  109. TRUE, // Create suspended?
  110. 0L, // ZeroBits: default
  111. 0L, // Max stack size: default
  112. 0L, // Committed stack size: default
  113. Function, // Function to start in
  114. Parameter, // Parameter to start with
  115. ThreadHandleReturn, // Thread handle return
  116. NULL // Thread id
  117. );
  118. }
  119. NTSTATUS
  120. NTAPI
  121. RtlpStartThreadpoolThread (
  122. PUSER_THREAD_START_ROUTINE Function,
  123. PVOID Parameter,
  124. HANDLE *ThreadHandleReturn
  125. )
  126. /*++
  127. Routine Description:
  128. This routine is used start a new wait thread in the pool.
  129. Arguments:
  130. None
  131. Return Value:
  132. STATUS_SUCCESS - Timer Queue created successfully.
  133. STATUS_NO_MEMORY - There was not sufficient heap to perform the requested operation.
  134. --*/
  135. {
  136. NTSTATUS Status;
  137. HANDLE ThreadHandle;
  138. if (ThreadHandleReturn) {
  139. *ThreadHandleReturn = NULL;
  140. }
  141. if (LdrpShutdownInProgress) {
  142. return STATUS_UNSUCCESSFUL;
  143. }
  144. // We don't want to create the thread while impersonating;
  145. // this was nt raid #278770
  146. ASSERT(! RtlIsImpersonating());
  147. // Create the thread.
  148. #if DBG
  149. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  150. RTLP_THREADPOOL_TRACE_MASK,
  151. "StartThread: Starting worker thread %p(%p)\n",
  152. Function,
  153. Parameter);
  154. #endif
  155. Status = RtlpStartThreadFunc(Function,
  156. Parameter,
  157. &ThreadHandle);
  158. #if DBG
  159. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  160. RTLP_THREADPOOL_TRACE_MASK,
  161. "StartThread: Started worker thread: status %p, handle %x\n",
  162. Status,
  163. ThreadHandle);
  164. #endif
  165. if (! NT_SUCCESS(Status)) {
  166. goto cleanup;
  167. }
  168. if (ThreadHandleReturn) {
  169. // Set the thread handle return before we resume the
  170. // thread -- in case the thread proceeds to use the
  171. // returned handle.
  172. *ThreadHandleReturn = ThreadHandle;
  173. }
  174. Status = NtResumeThread(ThreadHandle, NULL);
  175. if (! NT_SUCCESS(Status)) {
  176. NtTerminateThread(ThreadHandle, Status);
  177. if (ThreadHandleReturn) {
  178. *ThreadHandleReturn = NULL;
  179. }
  180. NtClose(ThreadHandle);
  181. }
  182. cleanup:
  183. return Status ;
  184. }
  185. NTSTATUS
  186. NTAPI
  187. RtlpExitThread(
  188. NTSTATUS Status
  189. )
  190. {
  191. return NtTerminateThread( NtCurrentThread(), Status );
  192. }
  193. VOID
  194. RtlpDoNothing (
  195. PVOID NotUsed1,
  196. PVOID NotUsed2,
  197. PVOID NotUsed3
  198. )
  199. /*++
  200. Routine Description:
  201. This routine is used to see if the thread is alive
  202. Arguments:
  203. NotUsed1, NotUsed2 and NotUsed 3 - not used
  204. Return Value:
  205. None
  206. --*/
  207. {
  208. }
  209. VOID
  210. RtlpThreadCleanup (
  211. )
  212. /*++
  213. Routine Description:
  214. This routine is used for exiting timer, wait and IOworker threads.
  215. Arguments:
  216. Return Value:
  217. --*/
  218. {
  219. NtTerminateThread( NtCurrentThread(), 0) ;
  220. }
  221. NTSTATUS
  222. RtlpWaitForEvent (
  223. HANDLE Event,
  224. HANDLE ThreadHandle
  225. )
  226. /*++
  227. Routine Description:
  228. Waits for the event to be signalled. If the event is not signalled within
  229. one second, then checks to see that the thread is alive
  230. Arguments:
  231. Event : Event handle used for signalling completion of request
  232. ThreadHandle: Thread to check whether still alive
  233. Return Value:
  234. STATUS_SUCCESS if event was signalled
  235. else return NTSTATUS
  236. --*/
  237. {
  238. NTSTATUS Status;
  239. HANDLE Handles[2];
  240. Handles[0] = Event;
  241. Handles[1] = ThreadHandle;
  242. Status = NtWaitForMultipleObjects(2, Handles, WaitAny, FALSE, NULL);
  243. if (Status == STATUS_WAIT_0) {
  244. //
  245. // The event has been signalled
  246. //
  247. Status = STATUS_SUCCESS;
  248. } else if (Status == STATUS_WAIT_1) {
  249. //
  250. // The target thread has died.
  251. //
  252. #if DBG
  253. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  254. RTLP_THREADPOOL_ERROR_MASK,
  255. "Threadpool thread died before event could be signalled\n");
  256. #endif
  257. Status = STATUS_UNSUCCESSFUL;
  258. } else if (NT_SUCCESS(Status)) {
  259. //
  260. // Something else has happened; make sure we fail.
  261. //
  262. Status = STATUS_UNSUCCESSFUL;
  263. }
  264. return Status;
  265. }
  266. PRTLP_EVENT
  267. RtlpGetWaitEvent (
  268. VOID
  269. )
  270. /*++
  271. Routine Description:
  272. Returns an event from the event cache.
  273. Arguments:
  274. None
  275. Return Value:
  276. Pointer to event structure
  277. --*/
  278. {
  279. PSLIST_ENTRY Entry;
  280. PRTLP_EVENT Event;
  281. NTSTATUS Status;
  282. ASSERT(! RtlIsImpersonating());
  283. Entry = RtlInterlockedPopEntrySList(&EventCache);
  284. if (Entry) {
  285. Event = CONTAINING_RECORD(Entry, RTLP_EVENT, Link);
  286. } else {
  287. Event = RtlpAllocateTPHeap(sizeof(RTLP_EVENT), 0);
  288. if (Event) {
  289. Status = NtCreateEvent(&Event->Handle,
  290. EVENT_ALL_ACCESS,
  291. NULL,
  292. SynchronizationEvent,
  293. FALSE);
  294. if (! NT_SUCCESS(Status)) {
  295. RtlpFreeTPHeap(Event);
  296. Event = NULL;
  297. } else {
  298. OBJECT_HANDLE_FLAG_INFORMATION HandleInfo;
  299. HandleInfo.Inherit = FALSE;
  300. HandleInfo.ProtectFromClose = TRUE;
  301. NtSetInformationObject(Event->Handle,
  302. ObjectHandleFlagInformation,
  303. &HandleInfo,
  304. sizeof(HandleInfo));
  305. }
  306. }
  307. }
  308. return Event;
  309. }
  310. VOID
  311. RtlpFreeWaitEvent (
  312. PRTLP_EVENT Event
  313. )
  314. /*++
  315. Routine Description:
  316. Frees the event to the event cache
  317. Arguments:
  318. Event - the event struct to put back into the cache
  319. Return Value:
  320. Nothing
  321. --*/
  322. {
  323. ASSERT(Event != NULL);
  324. //
  325. // Note: There's a race between checking the depth and pushing the
  326. // event. This doesn't hurt anything.
  327. //
  328. if (RtlQueryDepthSList(&EventCache) >= MAX_UNUSED_EVENTS) {
  329. OBJECT_HANDLE_FLAG_INFORMATION HandleInfo;
  330. HandleInfo.Inherit = FALSE;
  331. HandleInfo.ProtectFromClose = FALSE;
  332. NtSetInformationObject(Event->Handle,
  333. ObjectHandleFlagInformation,
  334. &HandleInfo,
  335. sizeof(HandleInfo));
  336. NtClose(Event->Handle);
  337. RtlpFreeTPHeap(Event);
  338. } else {
  339. RtlInterlockedPushEntrySList(&EventCache,
  340. (PSLIST_ENTRY)&Event->Link);
  341. }
  342. }
  343. VOID
  344. RtlpWaitOrTimerCallout(WAITORTIMERCALLBACKFUNC Function,
  345. PVOID Context,
  346. BOOLEAN TimedOut,
  347. PACTIVATION_CONTEXT ActivationContext,
  348. HANDLE ImpersonationToken,
  349. PRTL_CRITICAL_SECTION const *LocksHeld)
  350. /*++
  351. Routine Description:
  352. Perform a safe callout to the supplied wait or timer callback
  353. Arguments:
  354. Function -- the function to call
  355. Context -- the context parameter for the function
  356. TimedOut -- whether this callback occurred because of a timer
  357. expiration
  358. ActivationContext -- the request's originating activation context
  359. ImpersonationToken -- the request's impersonation token
  360. Return Value:
  361. None
  362. --*/
  363. {
  364. NTSTATUS Status;
  365. RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME ActivationFrame = { sizeof(ActivationFrame), RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER };
  366. #if (DBG1)
  367. DBG_SET_FUNCTION( Function, Context );
  368. #endif
  369. if (ImpersonationToken) {
  370. RTL_VERIFY(
  371. NT_SUCCESS(
  372. NtSetInformationThread(NtCurrentThread(),
  373. ThreadImpersonationToken,
  374. (PVOID)&ImpersonationToken,
  375. (ULONG)sizeof(HANDLE))));
  376. }
  377. RtlActivateActivationContextUnsafeFast(&ActivationFrame, ActivationContext);
  378. __try {
  379. // ISSUE-2000/10/10-earhart: Once the top level exception
  380. // handling code's moved to RTL, we need to catch any
  381. // exceptions here, and recover the thread.
  382. Function(Context, TimedOut);
  383. } __finally {
  384. RtlCheckHeldCriticalSections(NtCurrentThread(), LocksHeld);
  385. RtlDeactivateActivationContextUnsafeFast(&ActivationFrame);
  386. if (RtlIsImpersonating()) {
  387. HANDLE NewToken = NULL;
  388. RTL_VERIFY(
  389. NT_SUCCESS(
  390. NtSetInformationThread(NtCurrentThread(),
  391. ThreadImpersonationToken,
  392. (PVOID)&NewToken,
  393. (ULONG)sizeof(HANDLE))));
  394. }
  395. }
  396. }
  397. VOID
  398. RtlpApcCallout(APC_CALLBACK_FUNCTION Function,
  399. NTSTATUS Status,
  400. PVOID Context1,
  401. PVOID Context2)
  402. /*++
  403. Routine Description:
  404. Perform a safe callout to the supplied APC callback
  405. Arguments:
  406. Function -- the function to call
  407. Status -- the status parameter for the function
  408. Context1 -- the first context parameter for the function
  409. Context2 -- the second context parameter for the function
  410. Return Value:
  411. None
  412. --*/
  413. {
  414. // ISSUE-2000/10/10-earhart: Once the top level exception handling
  415. // code's moved to RTL, we need to catch any exceptions here, and
  416. // recover the thread.
  417. Function(Status, Context1, Context2) ;
  418. RtlCheckHeldCriticalSections(NtCurrentThread(), NULL);
  419. }
  420. VOID
  421. RtlpWorkerCallout(WORKERCALLBACKFUNC Function,
  422. PVOID Context,
  423. PACTIVATION_CONTEXT ActivationContext,
  424. HANDLE ImpersonationToken)
  425. /*++
  426. Routine Description:
  427. Perform a safe callout to the supplied worker callback
  428. Arguments:
  429. Function -- the function to call
  430. Context -- the context parameter for the function
  431. ActivationContext -- the request's originating activation context
  432. ImpersonationToken -- the request's impersonation token
  433. Return Value:
  434. None
  435. --*/
  436. {
  437. RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME ActivationFrame = { sizeof(ActivationFrame), RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER };
  438. #if (DBG1)
  439. DBG_SET_FUNCTION( Function, Context ) ;
  440. #endif
  441. if (ImpersonationToken) {
  442. RTL_VERIFY(
  443. NT_SUCCESS(
  444. NtSetInformationThread(NtCurrentThread(),
  445. ThreadImpersonationToken,
  446. (PVOID)&ImpersonationToken,
  447. (ULONG)sizeof(HANDLE))));
  448. }
  449. RtlActivateActivationContextUnsafeFast(&ActivationFrame, ActivationContext);
  450. __try {
  451. // ISSUE-2000/10/10-earhart: Once the top level exception
  452. // handling code's moved to RTL, we need to catch any
  453. // exceptions here, and recover the thread.
  454. Function(Context) ;
  455. } __finally {
  456. RtlCheckHeldCriticalSections(NtCurrentThread(), NULL);
  457. RtlDeactivateActivationContextUnsafeFast(&ActivationFrame);
  458. if (RtlIsImpersonating()) {
  459. HANDLE NewToken = NULL;
  460. RTL_VERIFY(
  461. NT_SUCCESS(
  462. NtSetInformationThread(NtCurrentThread(),
  463. ThreadImpersonationToken,
  464. (PVOID)&NewToken,
  465. (ULONG)sizeof(HANDLE))));
  466. }
  467. }
  468. }
  469. NTSTATUS
  470. RtlpCaptureImpersonation(
  471. IN LOGICAL RequestDuplicateAccess,
  472. OUT PHANDLE Token
  473. )
  474. {
  475. NTSTATUS Status;
  476. HANDLE NewToken;
  477. *Token = NULL;
  478. if (RtlIsImpersonating()) {
  479. Status = NtOpenThreadToken(NtCurrentThread(),
  480. TOKEN_IMPERSONATE
  481. | (RequestDuplicateAccess
  482. ? TOKEN_DUPLICATE
  483. : 0),
  484. TRUE,
  485. Token);
  486. if (! NT_SUCCESS(Status)) {
  487. return Status;
  488. }
  489. NewToken = NULL;
  490. Status = NtSetInformationThread(NtCurrentThread(),
  491. ThreadImpersonationToken,
  492. (PVOID) &NewToken,
  493. (ULONG) sizeof(NewToken));
  494. if (! NT_SUCCESS(Status)) {
  495. NtSetInformationThread(NtCurrentThread(),
  496. ThreadImpersonationToken,
  497. (PVOID) Token,
  498. (ULONG) sizeof(*Token));
  499. NtClose(*Token);
  500. *Token = NULL;
  501. return Status;
  502. }
  503. }
  504. return STATUS_SUCCESS;
  505. }
  506. VOID
  507. RtlpRestartImpersonation(
  508. IN HANDLE Token
  509. )
  510. {
  511. NtSetInformationThread(NtCurrentThread(),
  512. ThreadImpersonationToken,
  513. &Token,
  514. sizeof(Token));
  515. }