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.

654 lines
15 KiB

  1. //depot/Lab03_N/Base/ntos/rtl/threads.c#15 - integrate change 19911 (text)
  2. /*++
  3. Copyright (c) 1989-1998 Microsoft Corporation
  4. Module Name:
  5. threads.c
  6. Abstract:
  7. This module defines support functions for the worker, waiter, and
  8. timer thread pools.
  9. Author:
  10. Gurdeep Singh Pall (gurdeep) Nov 13, 1997
  11. Revision History:
  12. lokeshs - extended/modified threadpool.
  13. Rob Earhart (earhart) September 28, 2000
  14. Moved globals from threads.h to threads.c
  15. Split into independant modules
  16. Event cache cleanup
  17. Environment:
  18. These routines are statically linked in the caller's executable and
  19. are callable only from user mode. They make use of Nt system services.
  20. --*/
  21. #include <ntos.h>
  22. #include <ntrtl.h>
  23. #include <nturtl.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 Token = NULL;
  138. HANDLE ThreadHandle;
  139. if (ThreadHandleReturn) {
  140. *ThreadHandleReturn = NULL;
  141. }
  142. if (LdrpShutdownInProgress) {
  143. return STATUS_UNSUCCESSFUL;
  144. }
  145. try {
  146. if (NtCurrentTeb()->IsImpersonating) {
  147. // We don't want to create the thread while impersonating;
  148. // this was nt raid #278770
  149. HANDLE NewToken = NULL;
  150. Status = NtOpenThreadToken(NtCurrentThread(),
  151. MAXIMUM_ALLOWED,
  152. TRUE,
  153. &Token);
  154. if (! NT_SUCCESS(Status)) {
  155. leave;
  156. }
  157. Status = NtSetInformationThread(NtCurrentThread(),
  158. ThreadImpersonationToken,
  159. (PVOID)&NewToken,
  160. (ULONG)sizeof(HANDLE));
  161. if (! NT_SUCCESS(Status)) {
  162. leave;
  163. }
  164. }
  165. // Create the thread.
  166. #if DBG
  167. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  168. RTLP_THREADPOOL_TRACE_MASK,
  169. "StartThread: Starting worker thread %p(%p)\n",
  170. Function,
  171. Parameter);
  172. #endif
  173. Status = RtlpStartThreadFunc(Function,
  174. Parameter,
  175. &ThreadHandle);
  176. #if DBG
  177. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  178. RTLP_THREADPOOL_TRACE_MASK,
  179. "StartThread: Started worker thread: status %p, handle %x\n",
  180. Status,
  181. ThreadHandle);
  182. #endif
  183. if (! NT_SUCCESS(Status)) {
  184. leave;
  185. }
  186. if (ThreadHandleReturn) {
  187. // Set the thread handle return before we resume the
  188. // thread -- in case the thread proceeds to use the
  189. // returned handle.
  190. *ThreadHandleReturn = ThreadHandle;
  191. }
  192. Status = NtResumeThread(ThreadHandle, NULL);
  193. if (! NT_SUCCESS(Status)) {
  194. if (ThreadHandleReturn) {
  195. *ThreadHandleReturn = NULL;
  196. }
  197. NtClose(ThreadHandle);
  198. leave;
  199. }
  200. } finally {
  201. if (Token) {
  202. // We can't do anything useful if this fails (which it
  203. // never should); no need to check the status.
  204. NtSetInformationThread(NtCurrentThread(),
  205. ThreadImpersonationToken,
  206. &Token,
  207. sizeof(HANDLE));
  208. NtClose(Token);
  209. }
  210. }
  211. return Status ;
  212. }
  213. NTSTATUS
  214. NTAPI
  215. RtlpExitThread(
  216. NTSTATUS Status
  217. )
  218. {
  219. return NtTerminateThread( NtCurrentThread(), Status );
  220. }
  221. VOID
  222. RtlpDoNothing (
  223. PVOID NotUsed1,
  224. PVOID NotUsed2,
  225. PVOID NotUsed3
  226. )
  227. /*++
  228. Routine Description:
  229. This routine is used to see if the thread is alive
  230. Arguments:
  231. NotUsed1, NotUsed2 and NotUsed 3 - not used
  232. Return Value:
  233. None
  234. --*/
  235. {
  236. }
  237. VOID
  238. RtlpThreadCleanup (
  239. )
  240. /*++
  241. Routine Description:
  242. This routine is used for exiting timer, wait and IOworker threads.
  243. Arguments:
  244. Return Value:
  245. --*/
  246. {
  247. NtTerminateThread( NtCurrentThread(), 0) ;
  248. }
  249. NTSTATUS
  250. RtlpWaitForEvent (
  251. HANDLE Event,
  252. HANDLE ThreadHandle
  253. )
  254. /*++
  255. Routine Description:
  256. Waits for the event to be signalled. If the event is not signalled within
  257. one second, then checks to see that the thread is alive
  258. Arguments:
  259. Event : Event handle used for signalling completion of request
  260. ThreadHandle: Thread to check whether still alive
  261. Return Value:
  262. STATUS_SUCCESS if event was signalled
  263. else return NTSTATUS
  264. --*/
  265. {
  266. NTSTATUS Status;
  267. HANDLE Handles[2];
  268. Handles[0] = Event;
  269. Handles[1] = ThreadHandle;
  270. Status = NtWaitForMultipleObjects(2, Handles, WaitAny, FALSE, NULL);
  271. if (Status == STATUS_WAIT_0) {
  272. //
  273. // The event has been signalled
  274. //
  275. Status = STATUS_SUCCESS;
  276. } else if (Status == STATUS_WAIT_1) {
  277. //
  278. // The target thread has died.
  279. //
  280. #if DBG
  281. DbgPrintEx(DPFLTR_RTLTHREADPOOL_ID,
  282. RTLP_THREADPOOL_ERROR_MASK,
  283. "Threadpool thread died before event could be signalled\n");
  284. #endif
  285. Status = STATUS_UNSUCCESSFUL;
  286. } else if (NT_SUCCESS(Status)) {
  287. //
  288. // Something else has happened; make sure we fail.
  289. //
  290. Status = STATUS_UNSUCCESSFUL;
  291. }
  292. return Status;
  293. }
  294. PRTLP_EVENT
  295. RtlpGetWaitEvent (
  296. VOID
  297. )
  298. /*++
  299. Routine Description:
  300. Returns an event from the event cache.
  301. Arguments:
  302. None
  303. Return Value:
  304. Pointer to event structure
  305. --*/
  306. {
  307. PSINGLE_LIST_ENTRY Entry;
  308. PRTLP_EVENT Event;
  309. NTSTATUS Status;
  310. Entry = RtlInterlockedPopEntrySList(&EventCache);
  311. if (Entry) {
  312. Event = CONTAINING_RECORD(Entry, RTLP_EVENT, Link);
  313. } else {
  314. Event = RtlpAllocateTPHeap(sizeof(RTLP_EVENT), 0);
  315. if (Event) {
  316. Status = NtCreateEvent(&Event->Handle,
  317. EVENT_ALL_ACCESS,
  318. NULL,
  319. SynchronizationEvent,
  320. FALSE);
  321. if (! NT_SUCCESS(Status)) {
  322. RtlpFreeTPHeap(Event);
  323. Event = NULL;
  324. } else {
  325. OBJECT_HANDLE_FLAG_INFORMATION HandleInfo;
  326. HandleInfo.Inherit = FALSE;
  327. HandleInfo.ProtectFromClose = TRUE;
  328. NtSetInformationObject(Event->Handle,
  329. ObjectHandleFlagInformation,
  330. &HandleInfo,
  331. sizeof(HandleInfo));
  332. }
  333. }
  334. }
  335. return Event;
  336. }
  337. VOID
  338. RtlpFreeWaitEvent (
  339. PRTLP_EVENT Event
  340. )
  341. /*++
  342. Routine Description:
  343. Frees the event to the event cache
  344. Arguments:
  345. Event - the event struct to put back into the cache
  346. Return Value:
  347. Nothing
  348. --*/
  349. {
  350. ASSERT(Event != NULL);
  351. //
  352. // Note: There's a race between checking the depth and pushing the
  353. // event. This doesn't hurt anything.
  354. //
  355. if (RtlQueryDepthSList(&EventCache) >= MAX_UNUSED_EVENTS) {
  356. OBJECT_HANDLE_FLAG_INFORMATION HandleInfo;
  357. HandleInfo.Inherit = FALSE;
  358. HandleInfo.ProtectFromClose = FALSE;
  359. NtSetInformationObject(Event->Handle,
  360. ObjectHandleFlagInformation,
  361. &HandleInfo,
  362. sizeof(HandleInfo));
  363. NtClose(Event->Handle);
  364. RtlpFreeTPHeap(Event);
  365. } else {
  366. RtlInterlockedPushEntrySList(&EventCache,
  367. &Event->Link);
  368. }
  369. }
  370. VOID
  371. RtlpWaitOrTimerCallout(WAITORTIMERCALLBACKFUNC Function,
  372. PVOID Context,
  373. BOOLEAN TimedOut,
  374. PACTIVATION_CONTEXT ActivationContext,
  375. HANDLE ImpersonationToken)
  376. /*++
  377. Routine Description:
  378. Perform a safe callout to the supplied wait or timer callback
  379. Arguments:
  380. Function -- the function to call
  381. Context -- the context parameter for the function
  382. TimedOut -- whether this callback occurred because of a timer
  383. expiration
  384. ActivationContext -- the request's originating activation context
  385. ImpersonationToken -- the request's impersonation token
  386. Return Value:
  387. None
  388. --*/
  389. {
  390. RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME ActivationFrame = { sizeof(ActivationFrame), RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER };
  391. #if (DBG1)
  392. DBG_SET_FUNCTION( Function, Context );
  393. #endif
  394. if (ImpersonationToken) {
  395. NtSetInformationThread(NtCurrentThread(),
  396. ThreadImpersonationToken,
  397. (PVOID)&ImpersonationToken,
  398. (ULONG)sizeof(HANDLE));
  399. }
  400. RtlActivateActivationContextUnsafeFast(&ActivationFrame, ActivationContext);
  401. __try {
  402. // ISSUE-2000/10/10-earhart: Once the top level exception
  403. // handling code's moved to RTL, we need to catch any
  404. // exceptions here, and recover the thread.
  405. Function(Context, TimedOut);
  406. } __finally {
  407. RtlDeactivateActivationContextUnsafeFast(&ActivationFrame);
  408. if (NtCurrentTeb()->IsImpersonating) {
  409. HANDLE NewToken = NULL;
  410. NtSetInformationThread(NtCurrentThread(),
  411. ThreadImpersonationToken,
  412. (PVOID)&NewToken,
  413. (ULONG)sizeof(HANDLE));
  414. }
  415. }
  416. }
  417. VOID
  418. RtlpApcCallout(APC_CALLBACK_FUNCTION Function,
  419. NTSTATUS Status,
  420. PVOID Context1,
  421. PVOID Context2)
  422. /*++
  423. Routine Description:
  424. Perform a safe callout to the supplied APC callback
  425. Arguments:
  426. Function -- the function to call
  427. Status -- the status parameter for the function
  428. Context1 -- the first context parameter for the function
  429. Context2 -- the second context parameter for the function
  430. Return Value:
  431. None
  432. --*/
  433. {
  434. // ISSUE-2000/10/10-earhart: Once the top level exception handling
  435. // code's moved to RTL, we need to catch any exceptions here, and
  436. // recover the thread.
  437. Function(Status, Context1, Context2) ;
  438. }
  439. VOID
  440. RtlpWorkerCallout(WORKERCALLBACKFUNC Function,
  441. PVOID Context,
  442. PACTIVATION_CONTEXT ActivationContext,
  443. HANDLE ImpersonationToken)
  444. /*++
  445. Routine Description:
  446. Perform a safe callout to the supplied worker callback
  447. Arguments:
  448. Function -- the function to call
  449. Context -- the context parameter for the function
  450. ActivationContext -- the request's originating activation context
  451. ImpersonationToken -- the request's impersonation token
  452. Return Value:
  453. None
  454. --*/
  455. {
  456. RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME ActivationFrame = { sizeof(ActivationFrame), RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER };
  457. #if (DBG1)
  458. DBG_SET_FUNCTION( Function, Context ) ;
  459. #endif
  460. if (ImpersonationToken) {
  461. NtSetInformationThread(NtCurrentThread(),
  462. ThreadImpersonationToken,
  463. (PVOID)&ImpersonationToken,
  464. (ULONG)sizeof(HANDLE));
  465. }
  466. RtlActivateActivationContextUnsafeFast(&ActivationFrame, ActivationContext);
  467. __try {
  468. // ISSUE-2000/10/10-earhart: Once the top level exception
  469. // handling code's moved to RTL, we need to catch any
  470. // exceptions here, and recover the thread.
  471. Function(Context) ;
  472. } __finally {
  473. RtlDeactivateActivationContextUnsafeFast(&ActivationFrame);
  474. if (NtCurrentTeb()->IsImpersonating) {
  475. HANDLE NewToken = NULL;
  476. NtSetInformationThread(NtCurrentThread(),
  477. ThreadImpersonationToken,
  478. (PVOID)&NewToken,
  479. (ULONG)sizeof(HANDLE));
  480. }
  481. }
  482. }