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.

2208 lines
60 KiB

  1. /*++
  2. Copyright (c) 1989-1993 Microsoft Corporation
  3. Module Name:
  4. resource.c
  5. Abstract:
  6. This module implements the executive functions to acquire and release
  7. a shared resource.
  8. Author:
  9. Mark Lucovsky (markl) 04-Aug-1989
  10. Environment:
  11. These routines are statically linked in the caller's executable and
  12. are callable only from user mode. They make use of Nt system
  13. services.
  14. Revision History:
  15. --*/
  16. #include "ldrp.h"
  17. #include <ntos.h>
  18. #include <ntrtl.h>
  19. #include <nturtl.h>
  20. #include <heap.h>
  21. #include "wmiumkm.h"
  22. #include "NtdllTrc.h"
  23. //
  24. // Define the desired access for semaphores.
  25. //
  26. #define DESIRED_EVENT_ACCESS \
  27. (EVENT_QUERY_STATE | EVENT_MODIFY_STATE | SYNCHRONIZE)
  28. #define DESIRED_SEMAPHORE_ACCESS \
  29. (SEMAPHORE_QUERY_STATE | SEMAPHORE_MODIFY_STATE | SYNCHRONIZE)
  30. VOID RtlDumpResource( IN PRTL_RESOURCE Resource );
  31. extern BOOLEAN LdrpShutdownInProgress;
  32. extern HANDLE LdrpShutdownThreadId;
  33. RTL_CRITICAL_SECTION DeferredCriticalSection;
  34. HANDLE GlobalKeyedEventHandle=NULL;
  35. //#define RTLP_USE_GLOBAL_KEYED_EVENT 1
  36. #define RtlpIsKeyedEvent(xxHandle) ((((ULONG_PTR)xxHandle)&1) != 0)
  37. #define RtlpSetKeyedEventHandle(xxHandle) ((HANDLE)(((ULONG_PTR)xxHandle)|1))
  38. #define MAX_SLIST_DEPTH 10
  39. BOOLEAN
  40. ProtectHandle (
  41. IN HANDLE hObject
  42. )
  43. {
  44. NTSTATUS Status;
  45. OBJECT_HANDLE_FLAG_INFORMATION HandleInfo;
  46. Status = NtQueryObject (hObject,
  47. ObjectHandleFlagInformation,
  48. &HandleInfo,
  49. sizeof (HandleInfo),
  50. NULL);
  51. if (NT_SUCCESS(Status)) {
  52. HandleInfo.ProtectFromClose = TRUE;
  53. Status = NtSetInformationObject (hObject,
  54. ObjectHandleFlagInformation,
  55. &HandleInfo,
  56. sizeof (HandleInfo));
  57. if (NT_SUCCESS( Status )) {
  58. return TRUE;
  59. }
  60. }
  61. return FALSE;
  62. }
  63. BOOLEAN
  64. UnProtectHandle (
  65. IN HANDLE hObject
  66. )
  67. {
  68. NTSTATUS Status;
  69. OBJECT_HANDLE_FLAG_INFORMATION HandleInfo;
  70. Status = NtQueryObject (hObject,
  71. ObjectHandleFlagInformation,
  72. &HandleInfo,
  73. sizeof (HandleInfo),
  74. NULL);
  75. if (NT_SUCCESS(Status)) {
  76. HandleInfo.ProtectFromClose = FALSE;
  77. Status = NtSetInformationObject (hObject,
  78. ObjectHandleFlagInformation,
  79. &HandleInfo,
  80. sizeof (HandleInfo));
  81. if (NT_SUCCESS(Status)) {
  82. return TRUE;
  83. }
  84. }
  85. return FALSE;
  86. }
  87. SLIST_HEADER RtlCriticalSectionDebugSList;
  88. RTL_CRITICAL_SECTION_DEBUG RtlpStaticDebugInfo[64];
  89. PRTL_CRITICAL_SECTION_DEBUG RtlpStaticDebugInfoEnd;
  90. PVOID
  91. RtlpAllocateDebugInfo (
  92. VOID
  93. )
  94. {
  95. PVOID p;
  96. p = RtlInterlockedPopEntrySList (&RtlCriticalSectionDebugSList);
  97. if (p == NULL) {
  98. p = RtlAllocateHeap (NtCurrentPeb()->ProcessHeap,
  99. 0,
  100. sizeof(RTL_CRITICAL_SECTION_DEBUG));
  101. if (p == NULL) {
  102. KdPrint(( "NTDLL: Unable to allocate debug information from heap\n"));
  103. }
  104. }
  105. return p;
  106. }
  107. VOID
  108. RtlpFreeDebugInfo (
  109. IN PRTL_CRITICAL_SECTION_DEBUG DebugInfo
  110. )
  111. {
  112. ASSERT (RtlpStaticDebugInfoEnd != NULL);
  113. if ((RtlQueryDepthSList (&RtlCriticalSectionDebugSList) < MAX_SLIST_DEPTH) ||
  114. ((DebugInfo >= RtlpStaticDebugInfo) &&
  115. (DebugInfo < RtlpStaticDebugInfoEnd))) {
  116. RtlInterlockedPushEntrySList (&RtlCriticalSectionDebugSList,
  117. (PSLIST_ENTRY) DebugInfo);
  118. } else {
  119. //
  120. // Free it to the heap.
  121. //
  122. RtlFreeHeap(NtCurrentPeb()->ProcessHeap, 0, DebugInfo);
  123. }
  124. return;
  125. }
  126. NTSTATUS
  127. RtlpInitDeferredCriticalSection (
  128. VOID
  129. )
  130. {
  131. ULONG Size;
  132. NTSTATUS Status;
  133. PRTL_CRITICAL_SECTION_DEBUG p;
  134. OBJECT_ATTRIBUTES oa;
  135. UNICODE_STRING Name;
  136. HANDLE Handle;
  137. //
  138. // Open the global out of memory keyed event if it's not already set up.
  139. //
  140. Name.Buffer = L"\\KernelObjects\\CritSecOutOfMemoryEvent";
  141. Name.Length = sizeof (L"\\KernelObjects\\CritSecOutOfMemoryEvent") - sizeof (WCHAR);
  142. InitializeObjectAttributes (&oa, &Name, 0, NULL, NULL);
  143. Status = NtOpenKeyedEvent (&Handle,
  144. KEYEDEVENT_WAKE|KEYEDEVENT_WAIT,
  145. &oa);
  146. if (!NT_SUCCESS (Status)) {
  147. return Status;
  148. }
  149. #if DBG
  150. ProtectHandle (Handle);
  151. #endif // DBG
  152. GlobalKeyedEventHandle = RtlpSetKeyedEventHandle (Handle);
  153. InitializeListHead (&RtlCriticalSectionList);
  154. //
  155. // Create the critical section SLIST. Populate it with initial entries
  156. // so it can be used by ntdll routines before the first heap is created.
  157. //
  158. RtlInitializeSListHead (&RtlCriticalSectionDebugSList);
  159. Size = sizeof (RtlpStaticDebugInfo) / sizeof (RtlpStaticDebugInfo[0]);
  160. RtlpStaticDebugInfoEnd = RtlpStaticDebugInfo + Size;
  161. //
  162. // Chain all the free blocks together and then push them as a set.
  163. //
  164. for (p = RtlpStaticDebugInfo; p <= RtlpStaticDebugInfo + Size - 2; p++) {
  165. ((PSLIST_ENTRY) p)->Next = (PSLIST_ENTRY)(p + 1);
  166. }
  167. ((PSLIST_ENTRY) p)->Next = NULL;
  168. InterlockedPushListSList (&RtlCriticalSectionDebugSList,
  169. (PSLIST_ENTRY) &RtlpStaticDebugInfo[0],
  170. (PSLIST_ENTRY) &RtlpStaticDebugInfo[Size - 1],
  171. Size);
  172. //
  173. // Initialize the critical section lock & the deferred critical section.
  174. //
  175. ASSERT (sizeof (RTL_CRITICAL_SECTION_DEBUG) == sizeof (RTL_RESOURCE_DEBUG));
  176. Status = RtlInitializeCriticalSectionAndSpinCount (&RtlCriticalSectionLock,
  177. 1000);
  178. if (NT_SUCCESS (Status)) {
  179. Status = RtlInitializeCriticalSectionAndSpinCount (&DeferredCriticalSection,
  180. 1000);
  181. }
  182. return Status;
  183. }
  184. BOOLEAN
  185. NtdllOkayToLockRoutine(
  186. IN PVOID Lock
  187. )
  188. {
  189. UNREFERENCED_PARAMETER (Lock);
  190. return TRUE;
  191. }
  192. VOID
  193. RtlInitializeResource (
  194. IN PRTL_RESOURCE Resource
  195. )
  196. /*++
  197. Routine Description:
  198. This routine initializes the input resource variable
  199. Arguments:
  200. Resource - Supplies the resource variable being initialized
  201. Return Value:
  202. None
  203. --*/
  204. {
  205. NTSTATUS Status;
  206. PRTL_RESOURCE_DEBUG ResourceDebugInfo;
  207. ULONG SpinCount;
  208. //
  209. // Initialize the lock fields, the count indicates how many are waiting
  210. // to enter or are in the critical section, LockSemaphore is the object
  211. // to wait on when entering the critical section. SpinLock is used
  212. // for the add interlock instruction.
  213. //
  214. SpinCount = 1024 * (NtCurrentPeb()->NumberOfProcessors - 1);
  215. if (SpinCount > 12000) {
  216. SpinCount = 12000;
  217. }
  218. Status = RtlInitializeCriticalSectionAndSpinCount (&Resource->CriticalSection, SpinCount);
  219. if (!NT_SUCCESS (Status)) {
  220. RtlRaiseStatus(Status);
  221. }
  222. Resource->CriticalSection.DebugInfo->Type = RTL_RESOURCE_TYPE;
  223. ResourceDebugInfo = (PRTL_RESOURCE_DEBUG) RtlpAllocateDebugInfo();
  224. if (ResourceDebugInfo == NULL) {
  225. RtlDeleteCriticalSection (&Resource->CriticalSection);
  226. RtlRaiseStatus(STATUS_NO_MEMORY);
  227. }
  228. //
  229. // NOTICE-2002/03/21-ELi
  230. // Only the ResourceDebugInfo->ContentionCount is used by the resource
  231. // code, so the other fields are not initialized
  232. //
  233. ResourceDebugInfo->ContentionCount = 0;
  234. Resource->DebugInfo = ResourceDebugInfo;
  235. //
  236. // Initialize flags so there is a default value.
  237. // (Some apps may set RTL_RESOURCE_FLAGS_LONG_TERM to affect timeouts.)
  238. //
  239. Resource->Flags = 0;
  240. //
  241. // Initialize the shared and exclusive waiting counters and semaphore.
  242. // The counters indicate how many are waiting for access to the resource
  243. // and the semaphores are used to wait on the resource. Note that
  244. // the semaphores can also indicate the number waiting for a resource
  245. // however there is a race condition in the alogrithm on the acquire
  246. // side if count if not updated before the critical section is exited.
  247. //
  248. Status = NtCreateSemaphore (&Resource->SharedSemaphore,
  249. DESIRED_SEMAPHORE_ACCESS,
  250. NULL,
  251. 0,
  252. MAXLONG);
  253. if (!NT_SUCCESS(Status)) {
  254. RtlDeleteCriticalSection (&Resource->CriticalSection);
  255. RtlpFreeDebugInfo (Resource->DebugInfo);
  256. RtlRaiseStatus (Status);
  257. }
  258. Resource->NumberOfWaitingShared = 0;
  259. Status = NtCreateSemaphore (&Resource->ExclusiveSemaphore,
  260. DESIRED_SEMAPHORE_ACCESS,
  261. NULL,
  262. 0,
  263. MAXLONG);
  264. if (!NT_SUCCESS(Status)) {
  265. RtlDeleteCriticalSection (&Resource->CriticalSection);
  266. NtClose (Resource->SharedSemaphore);
  267. RtlpFreeDebugInfo (Resource->DebugInfo);
  268. RtlRaiseStatus (Status);
  269. }
  270. Resource->NumberOfWaitingExclusive = 0;
  271. //
  272. // Initialize the current state of the resource
  273. //
  274. Resource->NumberOfActive = 0;
  275. Resource->ExclusiveOwnerThread = NULL;
  276. return;
  277. }
  278. VOID
  279. RtlpPossibleDeadlock (
  280. IN PVOID ResourceOrCritSect
  281. )
  282. {
  283. PIMAGE_NT_HEADERS NtHeaders;
  284. EXCEPTION_RECORD ExceptionRecord;
  285. NtHeaders = RtlImageNtHeader (NtCurrentPeb()->ImageBaseAddress);
  286. //
  287. // If the image is a Win32 image, then raise an exception
  288. // and try to get to the uae popup.
  289. //
  290. try {
  291. if (NtHeaders != NULL &&
  292. (NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI ||
  293. NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI)) {
  294. ExceptionRecord.ExceptionCode = STATUS_POSSIBLE_DEADLOCK;
  295. ExceptionRecord.ExceptionFlags = 0;
  296. ExceptionRecord.ExceptionRecord = NULL;
  297. ExceptionRecord.ExceptionAddress = (PVOID)(ULONG_PTR) RtlRaiseException;
  298. ExceptionRecord.NumberParameters = 1;
  299. ExceptionRecord.ExceptionInformation[0] = (ULONG_PTR)ResourceOrCritSect;
  300. RtlRaiseException(&ExceptionRecord);
  301. } else {
  302. DbgBreakPoint();
  303. }
  304. } finally {
  305. ASSERT (!AbnormalTermination ());
  306. #if !DBG
  307. if (AbnormalTermination ()) {
  308. //
  309. // Somebody tried to handle this exception.
  310. // This is illegal as it will corrupt the critical section.
  311. //
  312. DbgBreakPoint ();
  313. }
  314. #endif
  315. }
  316. return;
  317. }
  318. BOOLEAN
  319. RtlAcquireResourceShared (
  320. IN PRTL_RESOURCE Resource,
  321. IN BOOLEAN Wait
  322. )
  323. /*++
  324. Routine Description:
  325. The routine acquires the resource for shared access. Upon return from
  326. the procedure the resource is acquired for shared access.
  327. Arguments:
  328. Resource - Supplies the resource to acquire
  329. Wait - Indicates if the call is allowed to wait for the resource
  330. to become available or must return immediately
  331. Return Value:
  332. BOOLEAN - TRUE if the resource is acquired and FALSE otherwise
  333. --*/
  334. {
  335. NTSTATUS Status;
  336. ULONG TimeoutCount;
  337. PLARGE_INTEGER TimeoutTime;
  338. //
  339. // Enter the critical section
  340. //
  341. RtlEnterCriticalSection (&Resource->CriticalSection);
  342. //
  343. // If it is not currently acquired for exclusive use then we can acquire
  344. // the resource for shared access. Note that this can potentially
  345. // starve an exclusive waiter however, this is necessary given the
  346. // ability to recursively acquire the resource shared. Otherwise we
  347. // might/will reach a deadlock situation where a thread tries to acquire
  348. // the resource recursively shared but is blocked by an exclusive waiter.
  349. //
  350. // The test to reenable not starving an exclusive waiter is:
  351. //
  352. // if ((Resource->NumberOfWaitingExclusive == 0) &&
  353. // (Resource->NumberOfActive >= 0)) {
  354. //
  355. if (Resource->NumberOfActive >= 0) {
  356. //
  357. // The resource is ours, so indicate that we have it and
  358. // exit the critical section
  359. //
  360. Resource->NumberOfActive += 1;
  361. RtlLeaveCriticalSection(&Resource->CriticalSection);
  362. //
  363. // Otherwise check to see if this thread is the one currently holding
  364. // exclusive access to the resource. And if it is then we change
  365. // this shared request to an exclusive recursive request and grant
  366. // access to the resource.
  367. //
  368. } else if (Resource->ExclusiveOwnerThread == NtCurrentTeb()->ClientId.UniqueThread) {
  369. //
  370. // The resource is ours (recursively) so indicate that we have it
  371. // and exit the critical section
  372. //
  373. Resource->NumberOfActive -= 1;
  374. RtlLeaveCriticalSection(&Resource->CriticalSection);
  375. //
  376. // Otherwise we'll have to wait for access.
  377. //
  378. } else {
  379. //
  380. // Check if we are allowed to wait or must return immediately, and
  381. // indicate that we didn't acquire the resource
  382. //
  383. if (!Wait) {
  384. RtlLeaveCriticalSection(&Resource->CriticalSection);
  385. return FALSE;
  386. }
  387. //
  388. // Otherwise we need to wait to acquire the resource.
  389. // To wait we will increment the number of waiting shared,
  390. // release the lock, and wait on the shared semaphore
  391. //
  392. Resource->NumberOfWaitingShared += 1;
  393. Resource->DebugInfo->ContentionCount += 1;
  394. RtlLeaveCriticalSection(&Resource->CriticalSection);
  395. TimeoutCount = 0;
  396. rewait:
  397. if (Resource->Flags & RTL_RESOURCE_FLAG_LONG_TERM) {
  398. TimeoutTime = NULL;
  399. }
  400. else {
  401. TimeoutTime = &RtlpTimeout;
  402. }
  403. Status = NtWaitForSingleObject (Resource->SharedSemaphore,
  404. FALSE,
  405. TimeoutTime);
  406. if (Status == STATUS_TIMEOUT) {
  407. DbgPrint("RTL: Acquire Shared Sem Timeout %d(%I64u secs)\n",
  408. TimeoutCount, TimeoutTime->QuadPart / (-10000000));
  409. DbgPrint("RTL: Resource at %p\n",Resource);
  410. TimeoutCount += 1;
  411. if (TimeoutCount > 2) {
  412. //
  413. // If the image is a Win32 image, then raise an exception
  414. // and try to get to the uae popup
  415. //
  416. RtlpPossibleDeadlock ((PVOID) Resource);
  417. }
  418. DbgPrint("RTL: Re-Waiting\n");
  419. goto rewait;
  420. }
  421. if (!NT_SUCCESS(Status)) {
  422. RtlRaiseStatus(Status);
  423. }
  424. }
  425. //
  426. // Now the resource is ours, for shared access
  427. //
  428. return TRUE;
  429. }
  430. BOOLEAN
  431. RtlAcquireResourceExclusive (
  432. IN PRTL_RESOURCE Resource,
  433. IN BOOLEAN Wait
  434. )
  435. /*++
  436. Routine Description:
  437. The routine acquires the resource for exclusive access. Upon return from
  438. the procedure the resource is acquired for exclusive access.
  439. Arguments:
  440. Resource - Supplies the resource to acquire
  441. Wait - Indicates if the call is allowed to wait for the resource
  442. to become available or must return immediately
  443. Return Value:
  444. BOOLEAN - TRUE if the resource is acquired and FALSE otherwise
  445. --*/
  446. {
  447. NTSTATUS Status;
  448. ULONG TimeoutCount;
  449. PLARGE_INTEGER TimeoutTime;
  450. HANDLE Thread;
  451. Thread = NtCurrentTeb()->ClientId.UniqueThread;
  452. //
  453. // Loop until the resource is ours or exit if we cannot wait.
  454. //
  455. do {
  456. //
  457. // Enter the critical section
  458. //
  459. RtlEnterCriticalSection (&Resource->CriticalSection);
  460. //
  461. // If there are no shared users and it is not currently acquired for
  462. // exclusive use then we can acquire the resource for exclusive
  463. // access. We also can acquire it if the resource indicates exclusive
  464. // access but there isn't currently an owner.
  465. //
  466. if ((Resource->NumberOfActive == 0)
  467. ||
  468. ((Resource->NumberOfActive == -1) &&
  469. (Resource->ExclusiveOwnerThread == NULL))) {
  470. //
  471. // The resource is ours, so indicate that we have it and
  472. // exit the critical section
  473. //
  474. Resource->NumberOfActive = -1;
  475. Resource->ExclusiveOwnerThread = Thread;
  476. RtlLeaveCriticalSection(&Resource->CriticalSection);
  477. return TRUE;
  478. }
  479. //
  480. // Otherwise check to see if we already have exclusive access to the
  481. // resource and can simply recursively acquire it again.
  482. //
  483. if (Resource->ExclusiveOwnerThread == Thread) {
  484. //
  485. // The resource is ours (recursively) so indicate that we have it
  486. // and exit the critical section
  487. //
  488. Resource->NumberOfActive -= 1;
  489. RtlLeaveCriticalSection(&Resource->CriticalSection);
  490. return TRUE;
  491. }
  492. //
  493. // Check if we are allowed to wait or must return immediately, and
  494. // indicate that we didn't acquire the resource
  495. //
  496. if (!Wait) {
  497. RtlLeaveCriticalSection(&Resource->CriticalSection);
  498. return FALSE;
  499. }
  500. //
  501. // Otherwise we need to wait to acquire the resource.
  502. // To wait we will increment the number of waiting exclusive,
  503. // release the lock, and wait on the exclusive semaphore
  504. //
  505. Resource->NumberOfWaitingExclusive += 1;
  506. Resource->DebugInfo->ContentionCount += 1;
  507. RtlLeaveCriticalSection (&Resource->CriticalSection);
  508. TimeoutCount = 0;
  509. rewait:
  510. if (Resource->Flags & RTL_RESOURCE_FLAG_LONG_TERM) {
  511. TimeoutTime = NULL;
  512. }
  513. else {
  514. TimeoutTime = &RtlpTimeout;
  515. }
  516. Status = NtWaitForSingleObject (Resource->ExclusiveSemaphore,
  517. FALSE,
  518. TimeoutTime);
  519. if (Status == STATUS_TIMEOUT) {
  520. DbgPrint("RTL: Acquire Exclusive Sem Timeout %d (%I64u secs)\n",
  521. TimeoutCount, TimeoutTime->QuadPart / (-10000000));
  522. DbgPrint("RTL: Resource at %p\n",Resource);
  523. TimeoutCount += 1;
  524. if (TimeoutCount > 2) {
  525. //
  526. // If the image is a Win32 image, then raise an exception
  527. // and try to get to the uae popup.
  528. //
  529. RtlpPossibleDeadlock ((PVOID) Resource);
  530. }
  531. DbgPrint("RTL: Re-Waiting\n");
  532. goto rewait;
  533. }
  534. if (!NT_SUCCESS(Status)) {
  535. RtlRaiseStatus(Status);
  536. }
  537. } while (TRUE);
  538. }
  539. VOID
  540. RtlReleaseResource (
  541. IN PRTL_RESOURCE Resource
  542. )
  543. /*++
  544. Routine Description:
  545. This routine release the input resource. The resource can have been
  546. acquired for either shared or exclusive access.
  547. Arguments:
  548. Resource - Supplies the resource to release
  549. Return Value:
  550. None.
  551. --*/
  552. {
  553. NTSTATUS Status;
  554. LONG PreviousCount;
  555. //
  556. // Enter the critical section
  557. //
  558. RtlEnterCriticalSection (&Resource->CriticalSection);
  559. //
  560. // Test if the resource is acquired for shared or exclusive access
  561. //
  562. if (Resource->NumberOfActive > 0) {
  563. //
  564. // Releasing shared access to the resource, so decrement
  565. // the number of shared users
  566. //
  567. Resource->NumberOfActive -= 1;
  568. //
  569. // If the resource is now available and there is a waiting
  570. // exclusive user then give the resource to the waiting thread
  571. //
  572. if ((Resource->NumberOfActive == 0) &&
  573. (Resource->NumberOfWaitingExclusive > 0)) {
  574. //
  575. // Set the resource state to exclusive (but not owned),
  576. // decrement the number of waiting exclusive, and release
  577. // one exclusive waiter
  578. //
  579. Resource->NumberOfActive = -1;
  580. Resource->ExclusiveOwnerThread = NULL;
  581. Resource->NumberOfWaitingExclusive -= 1;
  582. Status = NtReleaseSemaphore (Resource->ExclusiveSemaphore,
  583. 1,
  584. &PreviousCount);
  585. if (!NT_SUCCESS(Status)) {
  586. RtlLeaveCriticalSection (&Resource->CriticalSection);
  587. RtlRaiseStatus(Status);
  588. }
  589. }
  590. } else if (Resource->NumberOfActive < 0) {
  591. //
  592. // Releasing exclusive access to the resource, so increment the
  593. // number of active by one. And continue testing only
  594. // if the resource is now available.
  595. //
  596. Resource->NumberOfActive += 1;
  597. if (Resource->NumberOfActive == 0) {
  598. //
  599. // The resource is now available. Remove ourselves as the
  600. // owner thread
  601. //
  602. Resource->ExclusiveOwnerThread = NULL;
  603. //
  604. // If there is another waiting exclusive then give the resource
  605. // to it.
  606. //
  607. if (Resource->NumberOfWaitingExclusive > 0) {
  608. //
  609. // Set the resource to exclusive, and its owner undefined.
  610. // Decrement the number of waiting exclusive and release one
  611. // exclusive waiter
  612. //
  613. Resource->NumberOfActive = -1;
  614. Resource->NumberOfWaitingExclusive -= 1;
  615. Status = NtReleaseSemaphore (Resource->ExclusiveSemaphore,
  616. 1,
  617. &PreviousCount);
  618. if ( !NT_SUCCESS(Status) ) {
  619. RtlLeaveCriticalSection (&Resource->CriticalSection);
  620. RtlRaiseStatus(Status);
  621. }
  622. //
  623. // Check to see if there are waiting shared, who should now get
  624. // the resource
  625. //
  626. } else if (Resource->NumberOfWaitingShared > 0) {
  627. //
  628. // Set the new state to indicate that all of the shared
  629. // requesters have access and there are no more waiting
  630. // shared requesters, and then release all of the shared
  631. // requsters
  632. //
  633. Resource->NumberOfActive = Resource->NumberOfWaitingShared;
  634. Resource->NumberOfWaitingShared = 0;
  635. Status = NtReleaseSemaphore (Resource->SharedSemaphore,
  636. Resource->NumberOfActive,
  637. &PreviousCount);
  638. if (!NT_SUCCESS(Status)) {
  639. RtlLeaveCriticalSection (&Resource->CriticalSection);
  640. RtlRaiseStatus(Status);
  641. }
  642. }
  643. }
  644. #if DBG
  645. } else {
  646. //
  647. // The resource isn't currently acquired, there is nothing to release
  648. // so tell the user the mistake
  649. //
  650. DbgPrint("NTDLL - Resource released too many times %lx\n", Resource);
  651. DbgBreakPoint();
  652. #endif
  653. }
  654. //
  655. // Exit the critical section, and return to the caller
  656. //
  657. RtlLeaveCriticalSection (&Resource->CriticalSection);
  658. return;
  659. }
  660. VOID
  661. RtlConvertSharedToExclusive (
  662. IN PRTL_RESOURCE Resource
  663. )
  664. /*++
  665. Routine Description:
  666. This routine converts a resource acquired for shared access into
  667. one acquired for exclusive access. Upon return from the procedure
  668. the resource is acquired for exclusive access
  669. Arguments:
  670. Resource - Supplies the resource to acquire for shared access, it
  671. must already be acquired for shared access
  672. Return Value:
  673. None
  674. --*/
  675. {
  676. NTSTATUS Status;
  677. ULONG TimeoutCount;
  678. HANDLE Thread;
  679. Thread = NtCurrentTeb()->ClientId.UniqueThread;
  680. //
  681. // Enter the critical section
  682. //
  683. RtlEnterCriticalSection(&Resource->CriticalSection);
  684. //
  685. // If there is only one shared user (it's us) and we can acquire the
  686. // resource for exclusive access.
  687. //
  688. if (Resource->NumberOfActive == 1) {
  689. //
  690. // The resource is ours, so indicate that we have it and
  691. // exit the critical section, and return
  692. //
  693. Resource->NumberOfActive = -1;
  694. Resource->ExclusiveOwnerThread = Thread;
  695. RtlLeaveCriticalSection(&Resource->CriticalSection);
  696. return;
  697. }
  698. //
  699. // If the resource is currently acquired exclusive and it's us then
  700. // we already have exclusive access
  701. //
  702. if ((Resource->NumberOfActive < 0) &&
  703. (Resource->ExclusiveOwnerThread == Thread)) {
  704. //
  705. // We already have exclusive access to the resource so we'll just
  706. // exit the critical section and return
  707. //
  708. RtlLeaveCriticalSection(&Resource->CriticalSection);
  709. return;
  710. }
  711. //
  712. // If the resource is acquired by more than one shared then we need
  713. // to wait to get exclusive access to the resource
  714. //
  715. if (Resource->NumberOfActive > 1) {
  716. TimeoutCount = 0;
  717. //
  718. // To wait we will decrement the fact that we have the resource for
  719. // shared, and then loop waiting on the exclusive lock, and then
  720. // testing to see if we can get exclusive access to the resource
  721. //
  722. Resource->NumberOfActive -= 1;
  723. while (TRUE) {
  724. //
  725. // Increment the number of waiting exclusive, exit and critical
  726. // section and wait on the exclusive semaphore
  727. //
  728. Resource->NumberOfWaitingExclusive += 1;
  729. Resource->DebugInfo->ContentionCount += 1;
  730. RtlLeaveCriticalSection(&Resource->CriticalSection);
  731. do {
  732. Status = NtWaitForSingleObject (Resource->ExclusiveSemaphore,
  733. FALSE,
  734. &RtlpTimeout);
  735. if (NT_SUCCESS(Status)) {
  736. if (Status != STATUS_TIMEOUT) {
  737. //
  738. // Success !
  739. //
  740. break;
  741. }
  742. DbgPrint("RTL: Convert Exclusive Sem Timeout %d (%I64u secs)\n",
  743. TimeoutCount, RtlpTimeout.QuadPart / (-10000000));
  744. DbgPrint("RTL: Resource at %p\n",Resource);
  745. TimeoutCount += 1;
  746. if (TimeoutCount > 2) {
  747. //
  748. // If the image is a Win32 image, then raise an
  749. // exception and try to get to the uae popup.
  750. //
  751. RtlpPossibleDeadlock ((PVOID) Resource);
  752. }
  753. DbgPrint("RTL: Re-Waiting\n");
  754. }
  755. else {
  756. RtlRaiseStatus (Status);
  757. }
  758. } while (TRUE);
  759. //
  760. // Enter the critical section
  761. //
  762. RtlEnterCriticalSection(&Resource->CriticalSection);
  763. //
  764. // If there are no shared users and it is not currently acquired
  765. // for exclusive use then we can acquire the resource for
  766. // exclusive access. We can also acquire it if the resource
  767. // indicates exclusive access but there isn't currently an owner
  768. //
  769. if ((Resource->NumberOfActive == 0)
  770. ||
  771. ((Resource->NumberOfActive == -1) &&
  772. (Resource->ExclusiveOwnerThread == NULL))) {
  773. //
  774. // The resource is ours, so indicate that we have it and
  775. // exit the critical section and return.
  776. //
  777. Resource->NumberOfActive = -1;
  778. Resource->ExclusiveOwnerThread = NtCurrentTeb()->ClientId.UniqueThread;
  779. RtlLeaveCriticalSection(&Resource->CriticalSection);
  780. return;
  781. }
  782. //
  783. // Otherwise check to see if we already have exclusive access to
  784. // the resource and can simply recursively acquire it again.
  785. //
  786. if (Resource->ExclusiveOwnerThread == NtCurrentTeb()->ClientId.UniqueThread) {
  787. //
  788. // The resource is ours (recursively) so indicate that we have
  789. // it and exit the critical section and return.
  790. //
  791. Resource->NumberOfActive -= 1;
  792. RtlLeaveCriticalSection(&Resource->CriticalSection);
  793. return;
  794. }
  795. }
  796. }
  797. //
  798. // The resource is not currently acquired for shared so this is a
  799. // spurious call
  800. //
  801. #if DBG
  802. DbgPrint("NTDLL: Failed error - SHARED_RESOURCE_CONV_ERROR\n");
  803. DbgBreakPoint();
  804. #endif
  805. RtlLeaveCriticalSection(&Resource->CriticalSection);
  806. }
  807. VOID
  808. RtlConvertExclusiveToShared (
  809. IN PRTL_RESOURCE Resource
  810. )
  811. /*++
  812. Routine Description:
  813. This routine converts a resource acquired for exclusive access into
  814. one acquired for shared access. Upon return from the procedure
  815. the resource is acquired for shared access
  816. Arguments:
  817. Resource - Supplies the resource to acquire for shared access, it
  818. must already be acquired for exclusive access
  819. Return Value:
  820. None
  821. --*/
  822. {
  823. LONG PreviousCount;
  824. NTSTATUS Status;
  825. //
  826. // Enter the critical section
  827. //
  828. RtlEnterCriticalSection(&Resource->CriticalSection);
  829. //
  830. // Release our exclusive access.
  831. //
  832. if (Resource->NumberOfActive == -1) {
  833. Resource->ExclusiveOwnerThread = NULL;
  834. //
  835. // Check to see if there are waiting shared, who should now get the
  836. // resource along with us
  837. //
  838. if (Resource->NumberOfWaitingShared > 0) {
  839. //
  840. // Set the new state to indicate that all of the shared requesters
  841. // have access including us, and there are no more waiting shared
  842. // requesters, and then release all of the shared requsters
  843. //
  844. Resource->NumberOfActive = Resource->NumberOfWaitingShared + 1;
  845. Resource->NumberOfWaitingShared = 0;
  846. Status = NtReleaseSemaphore (Resource->SharedSemaphore,
  847. Resource->NumberOfActive - 1,
  848. &PreviousCount);
  849. if (!NT_SUCCESS(Status)) {
  850. RtlLeaveCriticalSection(&Resource->CriticalSection);
  851. RtlRaiseStatus(Status);
  852. }
  853. } else {
  854. //
  855. // There is no one waiting for shared access so it's only ours
  856. //
  857. Resource->NumberOfActive = 1;
  858. }
  859. }
  860. else {
  861. //
  862. // The resource is not currently acquired for exclusive, or we've
  863. // recursively acquired it, so this must be a spurious call
  864. //
  865. #if DBG
  866. DbgPrint("NTDLL: Failed error - SHARED_RESOURCE_CONV_ERROR\n");
  867. DbgBreakPoint();
  868. #endif
  869. }
  870. RtlLeaveCriticalSection(&Resource->CriticalSection);
  871. return;
  872. }
  873. VOID
  874. RtlDeleteResource (
  875. IN PRTL_RESOURCE Resource
  876. )
  877. /*++
  878. Routine Description:
  879. This routine deletes (i.e., uninitializes) the input resource variable
  880. Arguments:
  881. Resource - Supplies the resource variable being deleted
  882. Return Value:
  883. None
  884. --*/
  885. {
  886. RtlDeleteCriticalSection (&Resource->CriticalSection);
  887. NtClose (Resource->SharedSemaphore);
  888. NtClose (Resource->ExclusiveSemaphore);
  889. RtlpFreeDebugInfo (Resource->DebugInfo);
  890. RtlZeroMemory (Resource, sizeof(*Resource));
  891. return;
  892. }
  893. VOID
  894. RtlDumpResource (
  895. IN PRTL_RESOURCE Resource
  896. )
  897. {
  898. DbgPrint("Resource @ %lx\n", Resource);
  899. DbgPrint(" NumberOfWaitingShared = %lx\n", Resource->NumberOfWaitingShared);
  900. DbgPrint(" NumberOfWaitingExclusive = %lx\n", Resource->NumberOfWaitingExclusive);
  901. DbgPrint(" NumberOfActive = %lx\n", Resource->NumberOfActive);
  902. return;
  903. }
  904. NTSTATUS
  905. RtlInitializeCriticalSection (
  906. IN PRTL_CRITICAL_SECTION CriticalSection
  907. )
  908. /*++
  909. Routine Description:
  910. This routine initializes the input critial section variable
  911. Arguments:
  912. CriticalSection - Supplies the resource variable being initialized
  913. Return Value:
  914. NTSTATUS.
  915. --*/
  916. {
  917. return RtlInitializeCriticalSectionAndSpinCount (CriticalSection, 0);
  918. }
  919. #define MAX_SPIN_COUNT 0x00ffffff
  920. #define PREALLOCATE_EVENT_MASK 0x80000000
  921. VOID
  922. RtlEnableEarlyCriticalSectionEventCreation (
  923. VOID
  924. )
  925. /*++
  926. Routine Description:
  927. This routine marks the PEB of the calling process so critical section events
  928. are created at critical section creation time rather than at contention time.
  929. This allows critical processes not to have to worry about error paths later
  930. on at the expense of extra pool consumed.
  931. Arguments:
  932. None
  933. Return Value:
  934. None
  935. --*/
  936. {
  937. NtCurrentPeb ()->NtGlobalFlag |= FLG_CRITSEC_EVENT_CREATION;
  938. }
  939. NTSTATUS
  940. RtlInitializeCriticalSectionAndSpinCount (
  941. IN PRTL_CRITICAL_SECTION CriticalSection,
  942. ULONG SpinCount
  943. )
  944. /*++
  945. Routine Description:
  946. This routine initializes the input critial section variable
  947. Arguments:
  948. CriticalSection - Supplies the resource variable being initialized
  949. SpinCount - Supplies the spincount to use for MP collisions.
  950. Return Value:
  951. NTSTATUS - Status of semaphore creation.
  952. --*/
  953. {
  954. PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
  955. //
  956. // Initialize the lock fields, the count indicates how many are waiting
  957. // to enter or are in the critical section, LockSemaphore is the object
  958. // to wait on when entering the critical section. SpinLock is used
  959. // for the add interlock instruction. Recursion count is the number of
  960. // times the critical section has been recursively entered.
  961. //
  962. CriticalSection->LockCount = -1;
  963. CriticalSection->RecursionCount = 0;
  964. CriticalSection->OwningThread = 0;
  965. CriticalSection->LockSemaphore = 0;
  966. if ( NtCurrentPeb()->NumberOfProcessors > 1 ) {
  967. CriticalSection->SpinCount = SpinCount & MAX_SPIN_COUNT;
  968. } else {
  969. CriticalSection->SpinCount = 0;
  970. }
  971. ASSERT (GlobalKeyedEventHandle != NULL);
  972. //
  973. // Initialize debugging information.
  974. //
  975. DebugInfo = (PRTL_CRITICAL_SECTION_DEBUG) RtlpAllocateDebugInfo ();
  976. if (DebugInfo == NULL) {
  977. return STATUS_NO_MEMORY;
  978. }
  979. DebugInfo->Type = RTL_CRITSECT_TYPE;
  980. DebugInfo->ContentionCount = 0;
  981. DebugInfo->EntryCount = 0;
  982. //
  983. // It is important to set critical section pointers and potential
  984. // stack trace before we insert the resource in the process'
  985. // resource list because the list can be randomly traversed from
  986. // other threads that check for orphaned resources.
  987. //
  988. DebugInfo->CriticalSection = CriticalSection;
  989. CriticalSection->DebugInfo = DebugInfo;
  990. //
  991. // Try to get a stack trace. If no trace database was created
  992. // then the log() function is a no op.
  993. //
  994. DebugInfo->CreatorBackTraceIndex = (USHORT) RtlLogStackBackTrace();
  995. //
  996. // If the critical section lock itself is not being initialized, then
  997. // synchronize the insert of the critical section in the process locks
  998. // list. Otherwise, insert the critical section with no synchronization.
  999. //
  1000. if (CriticalSection != &RtlCriticalSectionLock) {
  1001. RtlEnterCriticalSection(&RtlCriticalSectionLock);
  1002. InsertTailList(&RtlCriticalSectionList, &DebugInfo->ProcessLocksList);
  1003. RtlLeaveCriticalSection(&RtlCriticalSectionLock );
  1004. } else {
  1005. InsertTailList(&RtlCriticalSectionList, &DebugInfo->ProcessLocksList);
  1006. }
  1007. if (IsCritSecLogging(CriticalSection)) {
  1008. PTHREAD_LOCAL_DATA pThreadLocalData = NULL;
  1009. PPERFINFO_TRACE_HEADER pEventHeader = NULL;
  1010. USHORT ReqSize = sizeof(CRIT_SEC_INITIALIZE_EVENT_DATA) + FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data);
  1011. AcquireBufferLocation (&pEventHeader, &pThreadLocalData, &ReqSize);
  1012. if(pEventHeader && pThreadLocalData) {
  1013. PCRIT_SEC_INITIALIZE_EVENT_DATA pCritSecInitializeEvent = (PCRIT_SEC_INITIALIZE_EVENT_DATA)
  1014. ((SIZE_T)pEventHeader + (SIZE_T)FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data ));
  1015. pEventHeader->Packet.Size = ReqSize;
  1016. pEventHeader->Packet.HookId= (USHORT) PERFINFO_LOG_TYPE_CRITSEC_INITIALIZE;
  1017. pCritSecInitializeEvent->Address = (PVOID)CriticalSection;
  1018. pCritSecInitializeEvent->SpinCount = (PVOID)CriticalSection->SpinCount;
  1019. ReleaseBufferLocation(pThreadLocalData);
  1020. }
  1021. }
  1022. return STATUS_SUCCESS;
  1023. }
  1024. ULONG
  1025. RtlSetCriticalSectionSpinCount (
  1026. IN PRTL_CRITICAL_SECTION CriticalSection,
  1027. ULONG SpinCount
  1028. )
  1029. /*++
  1030. Routine Description:
  1031. This routine initializes the input critial section variable
  1032. Arguments:
  1033. CriticalSection - Supplies the resource variable being initialized
  1034. Return Value:
  1035. Returns the previous critical section spin count
  1036. --*/
  1037. {
  1038. ULONG OldSpinCount;
  1039. OldSpinCount = (ULONG)CriticalSection->SpinCount;
  1040. if ( NtCurrentPeb()->NumberOfProcessors > 1 ) {
  1041. CriticalSection->SpinCount = SpinCount & MAX_SPIN_COUNT;
  1042. } else {
  1043. CriticalSection->SpinCount = 0;
  1044. }
  1045. return OldSpinCount;
  1046. }
  1047. BOOLEAN
  1048. RtlpCreateCriticalSectionSem (
  1049. IN PRTL_CRITICAL_SECTION CriticalSection
  1050. )
  1051. {
  1052. NTSTATUS Status, Status1;
  1053. HANDLE SemHandle;
  1054. #if defined (RTLP_USE_GLOBAL_KEYED_EVENT)
  1055. Status = STATUS_INSUFFICIENT_RESOURCES;
  1056. SemHandle = NULL;
  1057. #else
  1058. Status = NtCreateEvent (&SemHandle,
  1059. DESIRED_EVENT_ACCESS,
  1060. NULL,
  1061. SynchronizationEvent,
  1062. FALSE);
  1063. #endif
  1064. if (NT_SUCCESS (Status)) {
  1065. if (InterlockedCompareExchangePointer (&CriticalSection->LockSemaphore, SemHandle, NULL) != NULL) {
  1066. Status1 = NtClose (SemHandle);
  1067. ASSERT (NT_SUCCESS (Status1));
  1068. } else {
  1069. #if DBG
  1070. ProtectHandle(SemHandle);
  1071. #endif // DBG
  1072. }
  1073. } else {
  1074. ASSERT (GlobalKeyedEventHandle != NULL);
  1075. InterlockedCompareExchangePointer (&CriticalSection->LockSemaphore,
  1076. GlobalKeyedEventHandle,
  1077. NULL);
  1078. }
  1079. return TRUE;
  1080. }
  1081. VOID
  1082. RtlpCheckDeferredCriticalSection(
  1083. IN PRTL_CRITICAL_SECTION CriticalSection
  1084. )
  1085. {
  1086. if (CriticalSection->LockSemaphore == NULL) {
  1087. RtlpCreateCriticalSectionSem(CriticalSection);
  1088. }
  1089. return;
  1090. }
  1091. NTSTATUS
  1092. RtlDeleteCriticalSection (
  1093. IN PRTL_CRITICAL_SECTION CriticalSection
  1094. )
  1095. /*++
  1096. Routine Description:
  1097. This routine deletes (i.e., uninitializes) the input critical
  1098. section variable
  1099. Arguments:
  1100. CriticalSection - Supplies the resource variable being deleted
  1101. Return Value:
  1102. NTSTATUS.
  1103. --*/
  1104. {
  1105. NTSTATUS Status;
  1106. PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
  1107. HANDLE LockSemaphore;
  1108. LockSemaphore = CriticalSection->LockSemaphore;
  1109. if (LockSemaphore != NULL && !RtlpIsKeyedEvent (LockSemaphore)) {
  1110. #if DBG
  1111. UnProtectHandle (LockSemaphore);
  1112. #endif // DBG
  1113. Status = NtClose (LockSemaphore);
  1114. } else {
  1115. Status = STATUS_SUCCESS;
  1116. }
  1117. //
  1118. // Remove critical section from the list
  1119. //
  1120. DebugInfo = NULL;
  1121. RtlEnterCriticalSection (&RtlCriticalSectionLock);
  1122. try {
  1123. DebugInfo = CriticalSection->DebugInfo;
  1124. if (DebugInfo != NULL) {
  1125. RemoveEntryList (&DebugInfo->ProcessLocksList);
  1126. RtlZeroMemory (DebugInfo, sizeof (*DebugInfo));
  1127. }
  1128. } finally {
  1129. RtlLeaveCriticalSection (&RtlCriticalSectionLock);
  1130. }
  1131. if (DebugInfo != NULL) {
  1132. RtlpFreeDebugInfo( DebugInfo );
  1133. }
  1134. RtlZeroMemory (CriticalSection,
  1135. sizeof(RTL_CRITICAL_SECTION));
  1136. return Status;
  1137. }
  1138. //
  1139. // The following support routines are called from the machine language
  1140. // implementations of RtlEnterCriticalSection and RtlLeaveCriticalSection
  1141. // to execute the slow path logic of either waiting for a critical section
  1142. // or releasing a critical section to a waiting thread.
  1143. //
  1144. void
  1145. RtlpWaitForCriticalSection (
  1146. IN PRTL_CRITICAL_SECTION CriticalSection
  1147. )
  1148. {
  1149. NTSTATUS st;
  1150. ULONG TimeoutCount = 0;
  1151. PLARGE_INTEGER TimeoutTime;
  1152. LOGICAL CsIsLoaderLock;
  1153. HANDLE LockSemaphore;
  1154. PTEB Teb;
  1155. //
  1156. // Critical sections are disabled during exit process so that
  1157. // apps that are not careful during shutdown don't hang
  1158. //
  1159. CsIsLoaderLock = (CriticalSection == &LdrpLoaderLock);
  1160. Teb = NtCurrentTeb ();
  1161. Teb->WaitingOnLoaderLock = (ULONG)CsIsLoaderLock;
  1162. if ( LdrpShutdownInProgress &&
  1163. ((!CsIsLoaderLock) ||
  1164. (CsIsLoaderLock && LdrpShutdownThreadId == Teb->ClientId.UniqueThread) ) ) {
  1165. //
  1166. // Slimy reinitialization of the critical section with the count
  1167. // biased by one. This is how the critical section would normally
  1168. // look to the thread coming out of this function. Note that the
  1169. // semaphore handle is leaked, but since the app is exiting, it's ok
  1170. //
  1171. CriticalSection->LockCount = 0;
  1172. CriticalSection->RecursionCount = 0;
  1173. CriticalSection->OwningThread = 0;
  1174. CriticalSection->LockSemaphore = 0;
  1175. Teb->WaitingOnLoaderLock = 0;
  1176. return;
  1177. }
  1178. if (RtlpTimoutDisable) {
  1179. TimeoutTime = NULL;
  1180. } else {
  1181. TimeoutTime = &RtlpTimeout;
  1182. }
  1183. LockSemaphore = CriticalSection->LockSemaphore;
  1184. if (LockSemaphore == NULL) {
  1185. RtlpCheckDeferredCriticalSection (CriticalSection);
  1186. LockSemaphore = CriticalSection->LockSemaphore;
  1187. }
  1188. CriticalSection->DebugInfo->EntryCount++;
  1189. do {
  1190. CriticalSection->DebugInfo->ContentionCount++;
  1191. #if 0
  1192. DbgPrint( "NTDLL: Waiting for CritSect: %p owned by ThreadId: %X Count: %u Level: %u\n",
  1193. CriticalSection,
  1194. CriticalSection->OwningThread,
  1195. CriticalSection->LockCount,
  1196. CriticalSection->RecursionCount
  1197. );
  1198. #endif
  1199. if( IsCritSecLogging(CriticalSection)){
  1200. PTHREAD_LOCAL_DATA pThreadLocalData = NULL;
  1201. PPERFINFO_TRACE_HEADER pEventHeader = NULL;
  1202. USHORT ReqSize = sizeof(CRIT_SEC_COLLISION_EVENT_DATA) + FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data);
  1203. AcquireBufferLocation(&pEventHeader, &pThreadLocalData, &ReqSize );
  1204. if(pEventHeader && pThreadLocalData) {
  1205. PCRIT_SEC_COLLISION_EVENT_DATA pCritSecCollEvent = (PCRIT_SEC_COLLISION_EVENT_DATA)( (SIZE_T)pEventHeader
  1206. +(SIZE_T)FIELD_OFFSET(PERFINFO_TRACE_HEADER, Data ));
  1207. pEventHeader->Packet.Size = ReqSize;
  1208. pEventHeader->Packet.HookId= (USHORT) PERFINFO_LOG_TYPE_CRITSEC_COLLISION;
  1209. pCritSecCollEvent->Address = (PVOID)CriticalSection;
  1210. pCritSecCollEvent->SpinCount = (PVOID)CriticalSection->SpinCount;
  1211. pCritSecCollEvent->LockCount = CriticalSection->LockCount;
  1212. pCritSecCollEvent->OwningThread = (PVOID)CriticalSection->OwningThread;
  1213. ReleaseBufferLocation(pThreadLocalData);
  1214. }
  1215. }
  1216. if (!RtlpIsKeyedEvent (LockSemaphore)) {
  1217. st = NtWaitForSingleObject (LockSemaphore,
  1218. FALSE,
  1219. TimeoutTime);
  1220. } else {
  1221. st = NtWaitForKeyedEvent (LockSemaphore,
  1222. CriticalSection,
  1223. FALSE,
  1224. TimeoutTime);
  1225. }
  1226. if ( st == STATUS_TIMEOUT ) {
  1227. //
  1228. // This code path will be taken only if the TimeoutTime parameter for
  1229. // Wait() was not null.
  1230. //
  1231. DbgPrint( "RTL: Enter Critical Section Timeout (%I64u secs) %d\n",
  1232. TimeoutTime->QuadPart / (-10000000), TimeoutCount
  1233. );
  1234. DbgPrint( "RTL: Pid.Tid %x.%x, owner tid %x Critical Section %p - ContentionCount == %lu\n",
  1235. Teb->ClientId.UniqueProcess,
  1236. Teb->ClientId.UniqueThread,
  1237. CriticalSection->OwningThread,
  1238. CriticalSection, CriticalSection->DebugInfo->ContentionCount
  1239. );
  1240. TimeoutCount += 1;
  1241. if ((TimeoutCount > 2) && (CriticalSection != &LdrpLoaderLock)) {
  1242. //
  1243. // If the image is a Win32 image, then raise an exception
  1244. // and try to get to the uae popup
  1245. //
  1246. RtlpPossibleDeadlock ((PVOID) CriticalSection);
  1247. }
  1248. DbgPrint("RTL: Re-Waiting\n");
  1249. } else {
  1250. if (NT_SUCCESS(st)) {
  1251. //
  1252. // If some errant thread calls SetEvent on a bogus handle
  1253. // which happens to match the handle we are using in the
  1254. // critical section, everything gets really messed up since
  1255. // two threads now own the lock at the same time. ASSERT
  1256. // that no other thread owns the lock if we have been
  1257. // granted ownership.
  1258. //
  1259. ASSERT(CriticalSection->OwningThread == 0);
  1260. if (CsIsLoaderLock) {
  1261. CriticalSection->OwningThread = Teb->ClientId.UniqueThread;
  1262. Teb->WaitingOnLoaderLock = 0;
  1263. }
  1264. return;
  1265. }
  1266. RtlRaiseStatus(st);
  1267. }
  1268. } while (TRUE);
  1269. }
  1270. VOID
  1271. RtlpUnWaitCriticalSection (
  1272. IN PRTL_CRITICAL_SECTION CriticalSection
  1273. )
  1274. {
  1275. NTSTATUS st;
  1276. HANDLE LockSemaphore;
  1277. LockSemaphore = CriticalSection->LockSemaphore;
  1278. if (LockSemaphore == NULL) {
  1279. RtlpCheckDeferredCriticalSection (CriticalSection);
  1280. LockSemaphore = CriticalSection->LockSemaphore;
  1281. }
  1282. if (!RtlpIsKeyedEvent (LockSemaphore)) {
  1283. st = NtSetEventBoostPriority (LockSemaphore);
  1284. } else {
  1285. st = NtReleaseKeyedEvent (LockSemaphore,
  1286. CriticalSection,
  1287. FALSE,
  1288. 0);
  1289. }
  1290. if (!NT_SUCCESS (st)) {
  1291. RtlRaiseStatus(st);
  1292. }
  1293. }
  1294. VOID
  1295. RtlpNotOwnerCriticalSection (
  1296. IN PRTL_CRITICAL_SECTION CriticalSection
  1297. )
  1298. {
  1299. LOGICAL CsIsLoaderLock;
  1300. //
  1301. // Critical sections are disabled during exit process so that
  1302. // apps that are not careful during shutdown don't hang
  1303. //
  1304. CsIsLoaderLock = (CriticalSection == &LdrpLoaderLock);
  1305. if ( LdrpShutdownInProgress &&
  1306. ((!CsIsLoaderLock) ||
  1307. (CsIsLoaderLock && LdrpShutdownThreadId == NtCurrentTeb()->ClientId.UniqueThread) ) ) {
  1308. return;
  1309. }
  1310. if (NtCurrentPeb()->BeingDebugged) {
  1311. DbgPrint( "NTDLL: Calling thread (%X) not owner of CritSect: %p Owner ThreadId: %X\n",
  1312. NtCurrentTeb()->ClientId.UniqueThread,
  1313. CriticalSection,
  1314. CriticalSection->OwningThread
  1315. );
  1316. DbgBreakPoint();
  1317. }
  1318. RtlRaiseStatus (STATUS_RESOURCE_NOT_OWNED);
  1319. }
  1320. #if DBG
  1321. void
  1322. RtlpCriticalSectionIsOwned (
  1323. IN PRTL_CRITICAL_SECTION CriticalSection
  1324. )
  1325. {
  1326. //
  1327. // The loader lock gets handled differently, so don't assert on it
  1328. //
  1329. if ((CriticalSection == &LdrpLoaderLock) &&
  1330. (CriticalSection->OwningThread == NtCurrentTeb()->ClientId.UniqueThread))
  1331. return;
  1332. //
  1333. // If we're being debugged, throw up a warning
  1334. //
  1335. if (NtCurrentPeb()->BeingDebugged) {
  1336. DbgPrint( "NTDLL: Calling thread (%X) shouldn't enter CritSect: %p Owner ThreadId: %X\n",
  1337. NtCurrentTeb()->ClientId.UniqueThread,
  1338. CriticalSection,
  1339. CriticalSection->OwningThread
  1340. );
  1341. DbgBreakPoint();
  1342. }
  1343. }
  1344. #endif
  1345. /////////////////////////////////////////////////////////////////////
  1346. /////////////////////////////////////////// Critical section verifier
  1347. /////////////////////////////////////////////////////////////////////
  1348. //
  1349. // This variable enables the critical section verifier (abandoned locks,
  1350. // terminatethread() while holding locks, etc.).
  1351. //
  1352. BOOLEAN RtlpCriticalSectionVerifier = FALSE;
  1353. //
  1354. // Settable from debugger to avoid a flurry of similar failures.
  1355. //
  1356. BOOLEAN RtlpCsVerifyDoNotBreak = FALSE;
  1357. VOID
  1358. RtlCheckHeldCriticalSections (
  1359. IN HANDLE hThread,
  1360. IN PRTL_CRITICAL_SECTION const *LocksHeld
  1361. )
  1362. /*++
  1363. Routine Description:
  1364. This routine is called to ensure that the given thread does not
  1365. hold any locks other than the ones in a specified list of
  1366. known-held locks.
  1367. Arguments:
  1368. hThread -- the thread to check
  1369. LocksHeld -- Locks known to be held by the thread
  1370. Return Value:
  1371. None.
  1372. --*/
  1373. {
  1374. NTSTATUS Status;
  1375. THREAD_BASIC_INFORMATION ThreadInfo;
  1376. PLIST_ENTRY Entry;
  1377. PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
  1378. RTL_CRITICAL_SECTION_DEBUG ExtraDebugInfoCopy = {0}; // initialized because of W4 warning
  1379. PRTL_CRITICAL_SECTION CriticalSection;
  1380. RTL_CRITICAL_SECTION CritSectCopy = {0}; // initialized because of W4 warning
  1381. PRTL_CRITICAL_SECTION const *LockHeld;
  1382. BOOLEAN Copied;
  1383. //
  1384. // We do not check anything if critical section verifier is not on.
  1385. //
  1386. if (RtlpCriticalSectionVerifier == FALSE || RtlpCsVerifyDoNotBreak == TRUE ) {
  1387. return;
  1388. }
  1389. //
  1390. // We do not do anything if we are shutting down the process.
  1391. //
  1392. if (LdrpShutdownInProgress) {
  1393. return;
  1394. }
  1395. //
  1396. // Query the thread ID and TEB address.
  1397. //
  1398. Status = NtQueryInformationThread (hThread,
  1399. ThreadBasicInformation,
  1400. &ThreadInfo,
  1401. sizeof(ThreadInfo),
  1402. NULL);
  1403. if (! NT_SUCCESS(Status)) {
  1404. return;
  1405. }
  1406. //
  1407. // Don't do anything else if number of owned critical sections
  1408. // stored in the TEB is zero. The app verifier keeps this TEB
  1409. // counter updated on all platforms instead of just x86 chk
  1410. // without the verifier, at least for critical sections that were not
  1411. // entered from ntdll.
  1412. //
  1413. if (ThreadInfo.TebBaseAddress->CountOfOwnedCriticalSections < 1) {
  1414. return;
  1415. }
  1416. //
  1417. // Iterate the global list of critical sections
  1418. //
  1419. RtlEnterCriticalSection (&RtlCriticalSectionLock);
  1420. try {
  1421. for (Entry = RtlCriticalSectionList.Flink;
  1422. Entry != &RtlCriticalSectionList;
  1423. Entry = Entry->Flink) {
  1424. DebugInfo = CONTAINING_RECORD (Entry,
  1425. RTL_CRITICAL_SECTION_DEBUG,
  1426. ProcessLocksList);
  1427. CriticalSection = DebugInfo->CriticalSection;
  1428. if (CriticalSection == &RtlCriticalSectionLock) {
  1429. //
  1430. // We know that we're holding this one, so there's no
  1431. // problem -- continue on.
  1432. //
  1433. continue;
  1434. }
  1435. if (LocksHeld) {
  1436. //
  1437. // We have a list of okay-to-hold critical sections;
  1438. // scan through it, looking for this critical section.
  1439. // If we find it, we'll skip it and continue walking
  1440. // ProcessLocksList.
  1441. //
  1442. for (LockHeld = LocksHeld;
  1443. *LockHeld;
  1444. LockHeld++) {
  1445. if (CriticalSection == *LockHeld) {
  1446. //
  1447. // We've found this critical section in the
  1448. // LocksHeld array.
  1449. //
  1450. break;
  1451. }
  1452. }
  1453. if (*LockHeld) {
  1454. //
  1455. // Our caller expected the thread to be holding
  1456. // this critical section; skip it, and continue
  1457. // walking through ProcessLocksList.
  1458. //
  1459. continue;
  1460. }
  1461. }
  1462. Copied = TRUE;
  1463. try {
  1464. CritSectCopy = *CriticalSection;
  1465. }
  1466. except (EXCEPTION_EXECUTE_HANDLER) {
  1467. Copied = FALSE;
  1468. }
  1469. if (Copied == FALSE) {
  1470. //
  1471. // Exception while reading the contents of the critsect.
  1472. // The critsect has probably been decommitted without a call to
  1473. // RtlDeleteCriticalSection.
  1474. //
  1475. // You might think the entry could be deleted from the list,
  1476. // but it can't... there may be another RTL_CRITICAL_SECTION out
  1477. // there that is truly allocated, and whose DebugInfo pointer
  1478. // points at this DebugInfo. In that case, when that critsect
  1479. // is deleted, the RtlCriticalSectionList is corrupted.
  1480. //
  1481. VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_IN_FREED_MEMORY,
  1482. "undeleted critical section in freed memory",
  1483. CriticalSection, "Critical section address",
  1484. DebugInfo, "Critical section debug info address",
  1485. RtlpGetStackTraceAddress (DebugInfo->CreatorBackTraceIndex),
  1486. "Initialization stack trace. Use dds to dump it if non-NULL.",
  1487. NULL, "" );
  1488. }
  1489. else if(CritSectCopy.DebugInfo != DebugInfo) {
  1490. //
  1491. // Successfully read the critical section structure but
  1492. // the current debug info field of this critical section
  1493. // doesn't point to the current DebugInfo - it was probably
  1494. // initialized more than one time or simply corrupted.
  1495. //
  1496. // Try to make a copy of the DebugInfo currently pointed
  1497. // by our critical section. This might fail if the critical
  1498. // section is corrupted.
  1499. //
  1500. Copied = TRUE;
  1501. try {
  1502. ExtraDebugInfoCopy = *(CritSectCopy.DebugInfo);
  1503. }
  1504. except (EXCEPTION_EXECUTE_HANDLER) {
  1505. Copied = FALSE;
  1506. }
  1507. if (Copied == FALSE) {
  1508. //
  1509. // Exception while reading the contents of the debug info.
  1510. // The current critical section structure is corrupted.
  1511. //
  1512. VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_CORRUPTED,
  1513. "corrupted critical section",
  1514. CriticalSection,
  1515. "Critical section address",
  1516. CritSectCopy.DebugInfo,
  1517. "Invalid debug info address of this critical section",
  1518. DebugInfo,
  1519. "Address of the debug info found in the active list.",
  1520. RtlpGetStackTraceAddress (DebugInfo->CreatorBackTraceIndex),
  1521. "Initialization stack trace. Use dds to dump it if non-NULL." );
  1522. }
  1523. else {
  1524. //
  1525. // Successfully read this second debug info
  1526. // of the same critical section.
  1527. //
  1528. VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_DOUBLE_INITIALIZE,
  1529. "double initialized or corrupted critical section",
  1530. CriticalSection,
  1531. "Critical section address.",
  1532. DebugInfo,
  1533. "Address of the debug info found in the active list.",
  1534. RtlpGetStackTraceAddress (DebugInfo->CreatorBackTraceIndex),
  1535. "First initialization stack trace. Use dds to dump it if non-NULL.",
  1536. RtlpGetStackTraceAddress (ExtraDebugInfoCopy.CreatorBackTraceIndex),
  1537. "Second initialization stack trace. Use dds to dump it if non-NULL.");
  1538. }
  1539. }
  1540. else if (CritSectCopy.OwningThread == ThreadInfo.ClientId.UniqueThread
  1541. && CritSectCopy.LockCount != -1) {
  1542. //
  1543. // The thread has a critical section locked. Since this API is called
  1544. // whenever the thread is in a context that does not allow this
  1545. // we will have to report the issue (e.g. thread exits or is
  1546. // terminated, a thread pool work item finished, etc.).
  1547. //
  1548. VERIFIER_STOP (APPLICATION_VERIFIER_EXIT_THREAD_OWNS_LOCK,
  1549. "Thread is in a state in which it cannot own a critical section",
  1550. ThreadInfo.ClientId.UniqueThread, "Thread identifier",
  1551. CriticalSection, "Critical section address",
  1552. DebugInfo, "Critical section debug info address",
  1553. RtlpGetStackTraceAddress (DebugInfo->CreatorBackTraceIndex), "Initialization stack trace. Use dds to dump it if non-NULL." );
  1554. }
  1555. }
  1556. }
  1557. finally {
  1558. //
  1559. // Release the CS list lock.
  1560. //
  1561. RtlLeaveCriticalSection (&RtlCriticalSectionLock);
  1562. }
  1563. }
  1564. VOID
  1565. RtlCheckForOrphanedCriticalSections (
  1566. IN HANDLE hThread
  1567. )
  1568. /*++
  1569. Routine Description:
  1570. This routine is called from kernel32's ExitThread, TerminateThread
  1571. and SuspendThread in an effort to track calls that kill threads while
  1572. they own critical sections.
  1573. Arguments:
  1574. hThread -- thread to be killed
  1575. Return Value:
  1576. None.
  1577. --*/
  1578. {
  1579. // The work is performed by RtlCheckHeldCriticalSections, which we
  1580. // call with the following okay-to-be-held critical section.
  1581. PRTL_CRITICAL_SECTION const LocksHeld[] = {
  1582. &LdrpLoaderLock,
  1583. NULL
  1584. };
  1585. RtlCheckHeldCriticalSections(hThread, LocksHeld);
  1586. }