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.

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