Windows NT 4.0 source code leak
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.

1615 lines
42 KiB

4 years ago
  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 <ldrp.h>
  17. #include <ntimage.h>
  18. #if defined(_MIPS_) || defined(_ALPHA_) || defined(_PPC_)
  19. #define InterlockedDecrement _InterlockedDecrement
  20. LONG
  21. InterlockedDecrement(
  22. PLONG Addend
  23. );
  24. #pragma intrinsic(_InterlockedDecrement)
  25. #else
  26. __inline
  27. VOID
  28. _fastcall
  29. InterlockedDecrement(
  30. IN PLONG Addend
  31. )
  32. {
  33. __asm {
  34. mov eax, -1
  35. mov ecx, Addend
  36. lock xadd [ecx], eax
  37. dec eax
  38. }
  39. }
  40. #endif
  41. //
  42. // Define the desired access for semaphores.
  43. //
  44. #define DESIRED_SEMAPHORE_ACCESS \
  45. (SEMAPHORE_QUERY_STATE | SEMAPHORE_MODIFY_STATE | SYNCHRONIZE)
  46. VOID RtlDumpResource( IN PRTL_RESOURCE Resource );
  47. extern BOOLEAN LdrpShutdownInProgress;
  48. extern HANDLE LdrpShutdownThreadId;
  49. VOID
  50. RtlpInitDeferedCriticalSection( VOID );
  51. RTL_CRITICAL_SECTION DeferedCriticalSection;
  52. #if DBG
  53. BOOLEAN
  54. ProtectHandle(
  55. HANDLE hObject
  56. )
  57. {
  58. NTSTATUS Status;
  59. OBJECT_HANDLE_FLAG_INFORMATION HandleInfo;
  60. Status = NtQueryObject( hObject,
  61. ObjectHandleFlagInformation,
  62. &HandleInfo,
  63. sizeof( HandleInfo ),
  64. NULL
  65. );
  66. if (NT_SUCCESS( Status )) {
  67. HandleInfo.ProtectFromClose = TRUE;
  68. Status = NtSetInformationObject( hObject,
  69. ObjectHandleFlagInformation,
  70. &HandleInfo,
  71. sizeof( HandleInfo )
  72. );
  73. if (NT_SUCCESS( Status )) {
  74. return TRUE;
  75. }
  76. }
  77. return FALSE;
  78. }
  79. BOOLEAN
  80. UnProtectHandle(
  81. HANDLE hObject
  82. )
  83. {
  84. NTSTATUS Status;
  85. OBJECT_HANDLE_FLAG_INFORMATION HandleInfo;
  86. Status = NtQueryObject( hObject,
  87. ObjectHandleFlagInformation,
  88. &HandleInfo,
  89. sizeof( HandleInfo ),
  90. NULL
  91. );
  92. if (NT_SUCCESS( Status )) {
  93. HandleInfo.ProtectFromClose = FALSE;
  94. Status = NtSetInformationObject( hObject,
  95. ObjectHandleFlagInformation,
  96. &HandleInfo,
  97. sizeof( HandleInfo )
  98. );
  99. if (NT_SUCCESS( Status )) {
  100. return TRUE;
  101. }
  102. }
  103. return FALSE;
  104. }
  105. #endif // DBG
  106. RTL_CRITICAL_SECTION_DEBUG RtlpStaticDebugInfo[ 16 ];
  107. PRTL_CRITICAL_SECTION_DEBUG RtlpDebugInfoFreeList;
  108. BOOLEAN RtlpCritSectInitialized;
  109. PRTL_CRITICAL_SECTION_DEBUG
  110. RtlpChainDebugInfo(
  111. IN PVOID BaseAddress,
  112. IN ULONG Size
  113. )
  114. {
  115. PRTL_CRITICAL_SECTION_DEBUG p, p1;
  116. if (Size = Size / sizeof( RTL_CRITICAL_SECTION_DEBUG )) {
  117. p = (PRTL_CRITICAL_SECTION_DEBUG)BaseAddress + Size - 1;
  118. *(PRTL_CRITICAL_SECTION_DEBUG *)p = NULL;
  119. while (--Size) {
  120. p1 = p - 1;
  121. *(PRTL_CRITICAL_SECTION_DEBUG *)p1 = p;
  122. p = p1;
  123. }
  124. }
  125. return p;
  126. }
  127. PVOID
  128. RtlpAllocateDebugInfo( VOID );
  129. VOID
  130. RtlpFreeDebugInfo(
  131. IN PVOID DebugInfo
  132. );
  133. PVOID
  134. RtlpAllocateDebugInfo( VOID )
  135. {
  136. PRTL_CRITICAL_SECTION_DEBUG p;
  137. NTSTATUS Status;
  138. ULONG Size;
  139. if (RtlpCritSectInitialized) {
  140. RtlEnterCriticalSection(&DeferedCriticalSection);
  141. }
  142. try {
  143. p = RtlpDebugInfoFreeList;
  144. if (p == NULL) {
  145. Size = sizeof( RTL_CRITICAL_SECTION_DEBUG );
  146. Status = NtAllocateVirtualMemory( NtCurrentProcess(),
  147. (PVOID *)&p,
  148. 0,
  149. &Size,
  150. MEM_COMMIT | MEM_RESERVE,
  151. PAGE_READWRITE
  152. );
  153. if (!NT_SUCCESS( Status )) {
  154. KdPrint(( "NTDLL: Unable to allocate debug information - Status == %x\n", Status ));
  155. }
  156. else {
  157. p = RtlpChainDebugInfo( p, Size );
  158. }
  159. }
  160. if (p != NULL) {
  161. RtlpDebugInfoFreeList = *(PRTL_CRITICAL_SECTION_DEBUG *)p;
  162. }
  163. }
  164. finally {
  165. if (RtlpCritSectInitialized) {
  166. RtlLeaveCriticalSection(&DeferedCriticalSection);
  167. }
  168. }
  169. return p;
  170. }
  171. VOID
  172. RtlpFreeDebugInfo(
  173. IN PVOID DebugInfo
  174. )
  175. {
  176. RtlEnterCriticalSection(&DeferedCriticalSection);
  177. try {
  178. RtlZeroMemory( DebugInfo, sizeof( RTL_CRITICAL_SECTION_DEBUG ) );
  179. *(PRTL_CRITICAL_SECTION_DEBUG *)DebugInfo = RtlpDebugInfoFreeList;
  180. RtlpDebugInfoFreeList = (PRTL_CRITICAL_SECTION_DEBUG)DebugInfo;
  181. }
  182. finally {
  183. RtlLeaveCriticalSection(&DeferedCriticalSection);
  184. }
  185. return;
  186. }
  187. VOID
  188. RtlpCreateCriticalSectionSem(
  189. IN PRTL_CRITICAL_SECTION CriticalSection
  190. );
  191. VOID
  192. RtlpInitDeferedCriticalSection( VOID )
  193. {
  194. if (sizeof( RTL_CRITICAL_SECTION_DEBUG ) != sizeof( RTL_RESOURCE_DEBUG )) {
  195. DbgPrint( "NTDLL: Critical Section & Resource Debug Info length mismatch.\n" );
  196. return;
  197. }
  198. RtlpDebugInfoFreeList = RtlpChainDebugInfo( RtlpStaticDebugInfo,
  199. sizeof( RtlpStaticDebugInfo )
  200. );
  201. RtlInitializeCriticalSection(&DeferedCriticalSection);
  202. RtlpCreateCriticalSectionSem(&DeferedCriticalSection);
  203. RtlpCritSectInitialized = TRUE;
  204. return;
  205. }
  206. BOOLEAN
  207. NtdllOkayToLockRoutine(
  208. IN PVOID Lock
  209. )
  210. {
  211. return TRUE;
  212. }
  213. VOID
  214. RtlInitializeResource(
  215. IN PRTL_RESOURCE Resource
  216. )
  217. /*++
  218. Routine Description:
  219. This routine initializes the input resource variable
  220. Arguments:
  221. Resource - Supplies the resource variable being initialized
  222. Return Value:
  223. None
  224. --*/
  225. {
  226. NTSTATUS Status;
  227. PRTL_RESOURCE_DEBUG ResourceDebugInfo;
  228. //
  229. // Initialize the lock fields, the count indicates how many are waiting
  230. // to enter or are in the critical section, LockSemaphore is the object
  231. // to wait on when entering the critical section. SpinLock is used
  232. // for the add interlock instruction.
  233. //
  234. Status = RtlInitializeCriticalSection( &Resource->CriticalSection );
  235. if ( !NT_SUCCESS(Status) ){
  236. RtlRaiseStatus(Status);
  237. }
  238. Resource->CriticalSection.DebugInfo->Type = RTL_RESOURCE_TYPE;
  239. ResourceDebugInfo = (PRTL_RESOURCE_DEBUG)
  240. RtlpAllocateDebugInfo();
  241. if (ResourceDebugInfo == NULL) {
  242. RtlRaiseStatus(STATUS_NO_MEMORY);
  243. }
  244. ResourceDebugInfo->ContentionCount = 0;
  245. Resource->DebugInfo = ResourceDebugInfo;
  246. //
  247. // Initialize flags so there is a default value.
  248. // (Some apps may set RTL_RESOURCE_FLAGS_LONG_TERM to affect timeouts.)
  249. //
  250. Resource->Flags = 0;
  251. //
  252. // Initialize the shared and exclusive waiting counters and semaphore.
  253. // The counters indicate how many are waiting for access to the resource
  254. // and the semaphores are used to wait on the resource. Note that
  255. // the semaphores can also indicate the number waiting for a resource
  256. // however there is a race condition in the alogrithm on the acquire
  257. // side if count if not updated before the critical section is exited.
  258. //
  259. Status = NtCreateSemaphore(
  260. &Resource->SharedSemaphore,
  261. DESIRED_SEMAPHORE_ACCESS,
  262. NULL,
  263. 0,
  264. MAXLONG
  265. );
  266. if ( !NT_SUCCESS(Status) ){
  267. RtlRaiseStatus(Status);
  268. }
  269. Resource->NumberOfWaitingShared = 0;
  270. Status = NtCreateSemaphore(
  271. &Resource->ExclusiveSemaphore,
  272. DESIRED_SEMAPHORE_ACCESS,
  273. NULL,
  274. 0,
  275. MAXLONG
  276. );
  277. if ( !NT_SUCCESS(Status) ){
  278. RtlRaiseStatus(Status);
  279. }
  280. Resource->NumberOfWaitingExclusive = 0;
  281. //
  282. // Initialize the current state of the resource
  283. //
  284. Resource->NumberOfActive = 0;
  285. Resource->ExclusiveOwnerThread = NULL;
  286. return;
  287. }
  288. BOOLEAN
  289. RtlAcquireResourceShared(
  290. IN PRTL_RESOURCE Resource,
  291. IN BOOLEAN Wait
  292. )
  293. /*++
  294. Routine Description:
  295. The routine acquires the resource for shared access. Upon return from
  296. the procedure the resource is acquired for shared access.
  297. Arguments:
  298. Resource - Supplies the resource to acquire
  299. Wait - Indicates if the call is allowed to wait for the resource
  300. to become available for must return immediately
  301. Return Value:
  302. BOOLEAN - TRUE if the resource is acquired and FALSE otherwise
  303. --*/
  304. {
  305. NTSTATUS Status;
  306. ULONG TimeoutCount = 0;
  307. PLARGE_INTEGER TimeoutTime = &RtlpTimeout;
  308. //
  309. // Enter the critical section
  310. //
  311. RtlEnterCriticalSection(&Resource->CriticalSection);
  312. //
  313. // If it is not currently acquired for exclusive use then we can acquire
  314. // the resource for shared access. Note that this can potentially
  315. // starve an exclusive waiter however, this is necessary given the
  316. // ability to recursively acquire the resource shared. Otherwise we
  317. // might/will reach a deadlock situation where a thread tries to acquire
  318. // the resource recusively shared but is blocked by an exclusive waiter.
  319. //
  320. // The test to reanable not starving an exclusive waiter is:
  321. //
  322. // if ((Resource->NumberOfWaitingExclusive == 0) &&
  323. // (Resource->NumberOfActive >= 0)) {
  324. //
  325. if (Resource->NumberOfActive >= 0) {
  326. //
  327. // The resource is ours, so indicate that we have it and
  328. // exit the critical section
  329. //
  330. Resource->NumberOfActive += 1;
  331. RtlLeaveCriticalSection(&Resource->CriticalSection);
  332. //
  333. // Otherwise check to see if this thread is the one currently holding
  334. // exclusive access to the resource. And if it is then we change
  335. // this shared request to an exclusive recusive request and grant
  336. // access to the resource.
  337. //
  338. } else if (Resource->ExclusiveOwnerThread == NtCurrentTeb()->ClientId.UniqueThread) {
  339. //
  340. // The resource is ours (recusively) so indicate that we have it
  341. // and exit the critial section
  342. //
  343. Resource->NumberOfActive -= 1;
  344. RtlLeaveCriticalSection(&Resource->CriticalSection);
  345. //
  346. // Otherwise we'll have to wait for access.
  347. //
  348. } else {
  349. //
  350. // Check if we are allowed to wait or must return immedately, and
  351. // indicate that we didn't acquire the resource
  352. //
  353. if (!Wait) {
  354. RtlLeaveCriticalSection(&Resource->CriticalSection);
  355. return FALSE;
  356. }
  357. //
  358. // Otherwise we need to wait to acquire the resource.
  359. // To wait we will increment the number of waiting shared,
  360. // release the lock, and wait on the shared semaphore
  361. //
  362. Resource->NumberOfWaitingShared += 1;
  363. Resource->DebugInfo->ContentionCount++;
  364. RtlLeaveCriticalSection(&Resource->CriticalSection);
  365. rewait:
  366. if ( Resource->Flags & RTL_RESOURCE_FLAG_LONG_TERM ) {
  367. TimeoutTime = NULL;
  368. }
  369. Status = NtWaitForSingleObject(
  370. Resource->SharedSemaphore,
  371. FALSE,
  372. TimeoutTime
  373. );
  374. if ( Status == STATUS_TIMEOUT ) {
  375. DbgPrint("RTL: Acquire Shared Sem Timeout %d(2 minutes)\n",TimeoutCount);
  376. DbgPrint("RTL: Resource at %lx\n",Resource);
  377. TimeoutCount++;
  378. if ( TimeoutCount > 2 ) {
  379. PIMAGE_NT_HEADERS NtHeaders;
  380. //
  381. // If the image is a Win32 image, then raise an exception and try to get to the
  382. // uae popup
  383. //
  384. NtHeaders = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
  385. if (NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI ||
  386. NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) {
  387. EXCEPTION_RECORD ExceptionRecord;
  388. ExceptionRecord.ExceptionCode = STATUS_POSSIBLE_DEADLOCK;
  389. ExceptionRecord.ExceptionFlags = 0;
  390. ExceptionRecord.ExceptionRecord = NULL;
  391. ExceptionRecord.ExceptionAddress = (PVOID)RtlRaiseException;
  392. ExceptionRecord.NumberParameters = 1;
  393. ExceptionRecord.ExceptionInformation[0] = (ULONG)Resource;
  394. RtlRaiseException(&ExceptionRecord);
  395. }
  396. else {
  397. DbgBreakPoint();
  398. }
  399. }
  400. DbgPrint("RTL: Re-Waiting\n");
  401. goto rewait;
  402. }
  403. if ( !NT_SUCCESS(Status) ) {
  404. RtlRaiseStatus(Status);
  405. }
  406. }
  407. //
  408. // Now the resource is ours, for shared access
  409. //
  410. return TRUE;
  411. }
  412. BOOLEAN
  413. RtlAcquireResourceExclusive(
  414. IN PRTL_RESOURCE Resource,
  415. IN BOOLEAN Wait
  416. )
  417. /*++
  418. Routine Description:
  419. The routine acquires the resource for exclusive access. Upon return from
  420. the procedure the resource is acquired for exclusive access.
  421. Arguments:
  422. Resource - Supplies the resource to acquire
  423. Wait - Indicates if the call is allowed to wait for the resource
  424. to become available for must return immediately
  425. Return Value:
  426. BOOLEAN - TRUE if the resource is acquired and FALSE otherwise
  427. --*/
  428. {
  429. NTSTATUS Status;
  430. ULONG TimeoutCount = 0;
  431. PLARGE_INTEGER TimeoutTime = &RtlpTimeout;
  432. //
  433. // Loop until the resource is ours or exit if we cannot wait.
  434. //
  435. while (TRUE) {
  436. //
  437. // Enter the critical section
  438. //
  439. RtlEnterCriticalSection(&Resource->CriticalSection);
  440. //
  441. // If there are no shared users and it is not currently acquired for
  442. // exclusive use then we can acquire the resource for exclusive
  443. // access. We also can acquire it if the resource indicates exclusive
  444. // access but there isn't currently an owner.
  445. //
  446. if ((Resource->NumberOfActive == 0)
  447. ||
  448. ((Resource->NumberOfActive == -1) &&
  449. (Resource->ExclusiveOwnerThread == NULL))) {
  450. //
  451. // The resource is ours, so indicate that we have it and
  452. // exit the critical section
  453. //
  454. Resource->NumberOfActive = -1;
  455. Resource->ExclusiveOwnerThread = NtCurrentTeb()->ClientId.UniqueThread;
  456. RtlLeaveCriticalSection(&Resource->CriticalSection);
  457. return TRUE;
  458. }
  459. //
  460. // Otherwise check to see if we already have exclusive access to the
  461. // resource and can simply recusively acquire it again.
  462. //
  463. if (Resource->ExclusiveOwnerThread == NtCurrentTeb()->ClientId.UniqueThread) {
  464. //
  465. // The resource is ours (recusively) so indicate that we have it
  466. // and exit the critial section
  467. //
  468. Resource->NumberOfActive -= 1;
  469. RtlLeaveCriticalSection(&Resource->CriticalSection);
  470. return TRUE;
  471. }
  472. //
  473. // Check if we are allowed to wait or must return immedately, and
  474. // indicate that we didn't acquire the resource
  475. //
  476. if (!Wait) {
  477. RtlLeaveCriticalSection(&Resource->CriticalSection);
  478. return FALSE;
  479. }
  480. //
  481. // Otherwise we need to wait to acquire the resource.
  482. // To wait we will increment the number of waiting exclusive,
  483. // release the lock, and wait on the exclusive semaphore
  484. //
  485. Resource->NumberOfWaitingExclusive += 1;
  486. Resource->DebugInfo->ContentionCount++;
  487. RtlLeaveCriticalSection(&Resource->CriticalSection);
  488. rewait:
  489. if ( Resource->Flags & RTL_RESOURCE_FLAG_LONG_TERM ) {
  490. TimeoutTime = NULL;
  491. }
  492. Status = NtWaitForSingleObject(
  493. Resource->ExclusiveSemaphore,
  494. FALSE,
  495. TimeoutTime
  496. );
  497. if ( Status == STATUS_TIMEOUT ) {
  498. DbgPrint("RTL: Acquire Exclusive Sem Timeout %d (2 minutes)\n",TimeoutCount);
  499. DbgPrint("RTL: Resource at %lx\n",Resource);
  500. TimeoutCount++;
  501. if ( TimeoutCount > 2 ) {
  502. PIMAGE_NT_HEADERS NtHeaders;
  503. //
  504. // If the image is a Win32 image, then raise an exception and try to get to the
  505. // uae popup
  506. //
  507. NtHeaders = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
  508. if (NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI ||
  509. NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) {
  510. EXCEPTION_RECORD ExceptionRecord;
  511. ExceptionRecord.ExceptionCode = STATUS_POSSIBLE_DEADLOCK;
  512. ExceptionRecord.ExceptionFlags = 0;
  513. ExceptionRecord.ExceptionRecord = NULL;
  514. ExceptionRecord.ExceptionAddress = (PVOID)RtlRaiseException;
  515. ExceptionRecord.NumberParameters = 1;
  516. ExceptionRecord.ExceptionInformation[0] = (ULONG)Resource;
  517. RtlRaiseException(&ExceptionRecord);
  518. }
  519. else {
  520. DbgBreakPoint();
  521. }
  522. }
  523. DbgPrint("RTL: Re-Waiting\n");
  524. goto rewait;
  525. }
  526. if ( !NT_SUCCESS(Status) ) {
  527. RtlRaiseStatus(Status);
  528. }
  529. }
  530. }
  531. VOID
  532. RtlReleaseResource(
  533. IN PRTL_RESOURCE Resource
  534. )
  535. /*++
  536. Routine Description:
  537. This routine release the input resource. The resource can have been
  538. acquired for either shared or exclusive access.
  539. Arguments:
  540. Resource - Supplies the resource to release
  541. Return Value:
  542. None.
  543. --*/
  544. {
  545. NTSTATUS Status;
  546. LONG PreviousCount;
  547. //
  548. // Enter the critical section
  549. //
  550. RtlEnterCriticalSection(&Resource->CriticalSection);
  551. //
  552. // Test if the resource is acquired for shared or exclusive access
  553. //
  554. if (Resource->NumberOfActive > 0) {
  555. //
  556. // Releasing shared access to the resource, so decrement
  557. // the number of shared users
  558. //
  559. Resource->NumberOfActive -= 1;
  560. //
  561. // If the resource is now available and there is a waiting
  562. // exclusive user then give the resource to the waiting thread
  563. //
  564. if ((Resource->NumberOfActive == 0) &&
  565. (Resource->NumberOfWaitingExclusive > 0)) {
  566. //
  567. // Set the resource state to exclusive (but not owned),
  568. // decrement the number of waiting exclusive, and release
  569. // one exclusive waiter
  570. //
  571. Resource->NumberOfActive = -1;
  572. Resource->ExclusiveOwnerThread = NULL;
  573. Resource->NumberOfWaitingExclusive -= 1;
  574. Status = NtReleaseSemaphore(
  575. Resource->ExclusiveSemaphore,
  576. 1,
  577. &PreviousCount
  578. );
  579. if ( !NT_SUCCESS(Status) ) {
  580. RtlRaiseStatus(Status);
  581. }
  582. }
  583. } else if (Resource->NumberOfActive < 0) {
  584. //
  585. // Releasing exclusive access to the resource, so increment the
  586. // number of active by one. And continue testing only
  587. // if the resource is now available.
  588. //
  589. Resource->NumberOfActive += 1;
  590. if (Resource->NumberOfActive == 0) {
  591. //
  592. // The resource is now available. Remove ourselves as the
  593. // owner thread
  594. //
  595. Resource->ExclusiveOwnerThread = NULL;
  596. //
  597. // If there is another waiting exclusive then give the resource
  598. // to it.
  599. //
  600. if (Resource->NumberOfWaitingExclusive > 0) {
  601. //
  602. // Set the resource to exclusive, and its owner undefined.
  603. // Decrement the number of waiting exclusive and release one
  604. // exclusive waiter
  605. //
  606. Resource->NumberOfActive = -1;
  607. Resource->NumberOfWaitingExclusive -= 1;
  608. Status = NtReleaseSemaphore(
  609. Resource->ExclusiveSemaphore,
  610. 1,
  611. &PreviousCount
  612. );
  613. if ( !NT_SUCCESS(Status) ) {
  614. RtlRaiseStatus(Status);
  615. }
  616. //
  617. // Check to see if there are waiting shared, who should now get
  618. // the resource
  619. //
  620. } else if (Resource->NumberOfWaitingShared > 0) {
  621. //
  622. // Set the new state to indicate that all of the shared
  623. // requesters have access and there are no more waiting
  624. // shared requesters, and then release all of the shared
  625. // requsters
  626. //
  627. Resource->NumberOfActive = Resource->NumberOfWaitingShared;
  628. Resource->NumberOfWaitingShared = 0;
  629. Status = NtReleaseSemaphore(
  630. Resource->SharedSemaphore,
  631. Resource->NumberOfActive,
  632. &PreviousCount
  633. );
  634. if ( !NT_SUCCESS(Status) ) {
  635. RtlRaiseStatus(Status);
  636. }
  637. }
  638. }
  639. #if DBG
  640. } else {
  641. //
  642. // The resource isn't current acquired, there is nothing to release
  643. // so tell the user the mistake
  644. //
  645. DbgPrint("NTDLL - Resource released too many times %lx\n", Resource);
  646. DbgBreakPoint();
  647. #endif
  648. }
  649. //
  650. // Exit the critical section, and return to the caller
  651. //
  652. RtlLeaveCriticalSection(&Resource->CriticalSection);
  653. return;
  654. }
  655. VOID
  656. RtlConvertSharedToExclusive(
  657. IN PRTL_RESOURCE Resource
  658. )
  659. /*++
  660. Routine Description:
  661. This routine converts a resource acquired for shared access into
  662. one acquired for exclusive access. Upon return from the procedure
  663. the resource is acquired for exclusive access
  664. Arguments:
  665. Resource - Supplies the resource to acquire for shared access, it
  666. must already be acquired for shared access
  667. Return Value:
  668. None
  669. --*/
  670. {
  671. NTSTATUS Status;
  672. ULONG TimeoutCount = 0;
  673. //
  674. // Enter the critical section
  675. //
  676. RtlEnterCriticalSection(&Resource->CriticalSection);
  677. //
  678. // If there is only one shared user (it's us) and we can acquire the
  679. // resource for exclusive access.
  680. //
  681. if (Resource->NumberOfActive == 1) {
  682. //
  683. // The resource is ours, so indicate that we have it and
  684. // exit the critical section, and return
  685. //
  686. Resource->NumberOfActive = -1;
  687. Resource->ExclusiveOwnerThread = NtCurrentTeb()->ClientId.UniqueThread;
  688. RtlLeaveCriticalSection(&Resource->CriticalSection);
  689. return;
  690. }
  691. //
  692. // If the resource is currently acquired exclusive and it's us then
  693. // we already have exclusive access
  694. //
  695. if ((Resource->NumberOfActive < 0) &&
  696. (Resource->ExclusiveOwnerThread == NtCurrentTeb()->ClientId.UniqueThread)) {
  697. //
  698. // We already have exclusive access to the resource so we'll just
  699. // exit the critical section and return
  700. //
  701. RtlLeaveCriticalSection(&Resource->CriticalSection);
  702. return;
  703. }
  704. //
  705. // If the resource is acquired by more than one shared then we need
  706. // to wait to get exclusive access to the resource
  707. //
  708. if (Resource->NumberOfActive > 1) {
  709. //
  710. // To wait we will decrement the fact that we have the resource for
  711. // shared, and then loop waiting on the exclusive lock, and then
  712. // testing to see if we can get exclusive access to the resource
  713. //
  714. Resource->NumberOfActive -= 1;
  715. while (TRUE) {
  716. //
  717. // Increment the number of waiting exclusive, exit and critical
  718. // section and wait on the exclusive semaphore
  719. //
  720. Resource->NumberOfWaitingExclusive += 1;
  721. Resource->DebugInfo->ContentionCount++;
  722. RtlLeaveCriticalSection(&Resource->CriticalSection);
  723. rewait:
  724. Status = NtWaitForSingleObject(
  725. Resource->ExclusiveSemaphore,
  726. FALSE,
  727. &RtlpTimeout
  728. );
  729. if ( Status == STATUS_TIMEOUT ) {
  730. DbgPrint("RTL: Convert Exclusive Sem Timeout %d (2 minutes)\n",TimeoutCount);
  731. DbgPrint("RTL: Resource at %lx\n",Resource);
  732. TimeoutCount++;
  733. if ( TimeoutCount > 2 ) {
  734. PIMAGE_NT_HEADERS NtHeaders;
  735. //
  736. // If the image is a Win32 image, then raise an exception and try to get to the
  737. // uae popup
  738. //
  739. NtHeaders = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
  740. if (NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI ||
  741. NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) {
  742. EXCEPTION_RECORD ExceptionRecord;
  743. ExceptionRecord.ExceptionCode = STATUS_POSSIBLE_DEADLOCK;
  744. ExceptionRecord.ExceptionFlags = 0;
  745. ExceptionRecord.ExceptionRecord = NULL;
  746. ExceptionRecord.ExceptionAddress = (PVOID)RtlRaiseException;
  747. ExceptionRecord.NumberParameters = 1;
  748. ExceptionRecord.ExceptionInformation[0] = (ULONG)Resource;
  749. RtlRaiseException(&ExceptionRecord);
  750. }
  751. else {
  752. DbgBreakPoint();
  753. }
  754. }
  755. DbgPrint("RTL: Re-Waiting\n");
  756. goto rewait;
  757. }
  758. if ( !NT_SUCCESS(Status) ) {
  759. RtlRaiseStatus(Status);
  760. }
  761. //
  762. // Enter the critical section
  763. //
  764. RtlEnterCriticalSection(&Resource->CriticalSection);
  765. //
  766. // If there are no shared users and it is not currently acquired
  767. // for exclusive use then we can acquire the resource for
  768. // exclusive access. We can also acquire it if the resource
  769. // indicates exclusive access but there isn't currently an owner
  770. //
  771. if ((Resource->NumberOfActive == 0)
  772. ||
  773. ((Resource->NumberOfActive == -1) &&
  774. (Resource->ExclusiveOwnerThread == NULL))) {
  775. //
  776. // The resource is ours, so indicate that we have it and
  777. // exit the critical section and return.
  778. //
  779. Resource->NumberOfActive = -1;
  780. Resource->ExclusiveOwnerThread = NtCurrentTeb()->ClientId.UniqueThread;
  781. RtlLeaveCriticalSection(&Resource->CriticalSection);
  782. return;
  783. }
  784. //
  785. // Otherwise check to see if we already have exclusive access to
  786. // the resource and can simply recusively acquire it again.
  787. //
  788. if (Resource->ExclusiveOwnerThread == NtCurrentTeb()->ClientId.UniqueThread) {
  789. //
  790. // The resource is ours (recusively) so indicate that we have
  791. // it and exit the critical section and return.
  792. //
  793. Resource->NumberOfActive -= 1;
  794. RtlLeaveCriticalSection(&Resource->CriticalSection);
  795. return;
  796. }
  797. }
  798. }
  799. //
  800. // The resource is not currently acquired for shared so this is a
  801. // spurious call
  802. //
  803. #if DBG
  804. DbgPrint("NTDLL: Failed error - SHARED_RESOURCE_CONV_ERROR\n");
  805. DbgBreakPoint();
  806. #endif
  807. }
  808. VOID
  809. RtlConvertExclusiveToShared(
  810. IN PRTL_RESOURCE Resource
  811. )
  812. /*++
  813. Routine Description:
  814. This routine converts a resource acquired for exclusive access into
  815. one acquired for shared access. Upon return from the procedure
  816. the resource is acquired for shared access
  817. Arguments:
  818. Resource - Supplies the resource to acquire for shared access, it
  819. must already be acquired for exclusive access
  820. Return Value:
  821. None
  822. --*/
  823. {
  824. LONG PreviousCount;
  825. NTSTATUS Status;
  826. //
  827. // Enter the critical section
  828. //
  829. RtlEnterCriticalSection(&Resource->CriticalSection);
  830. //
  831. // If there is only one shared user (it's us) and we can acquire the
  832. // resource for exclusive access.
  833. //
  834. if (Resource->NumberOfActive == -1) {
  835. Resource->ExclusiveOwnerThread = NULL;
  836. //
  837. // Check to see if there are waiting shared, who should now get the
  838. // resource along with us
  839. //
  840. if (Resource->NumberOfWaitingShared > 0) {
  841. //
  842. // Set the new state to indicate that all of the shared requesters
  843. // have access including us, and there are no more waiting shared
  844. // requesters, and then release all of the shared requsters
  845. //
  846. Resource->NumberOfActive = Resource->NumberOfWaitingShared + 1;
  847. Resource->NumberOfWaitingShared = 0;
  848. Status = NtReleaseSemaphore(
  849. Resource->SharedSemaphore,
  850. Resource->NumberOfActive - 1,
  851. &PreviousCount
  852. );
  853. if ( !NT_SUCCESS(Status) ) {
  854. RtlRaiseStatus(Status);
  855. }
  856. } else {
  857. //
  858. // There is no one waiting for shared access so it's only ours
  859. //
  860. Resource->NumberOfActive = 1;
  861. }
  862. RtlLeaveCriticalSection(&Resource->CriticalSection);
  863. return;
  864. }
  865. //
  866. // The resource is not currently acquired for exclusive, or we've
  867. // recursively acquired it, so this must be a spurious call
  868. //
  869. #if DBG
  870. DbgPrint("NTDLL: Failed error - SHARED_RESOURCE_CONV_ERROR\n");
  871. DbgBreakPoint();
  872. #endif
  873. }
  874. VOID
  875. RtlDeleteResource (
  876. IN PRTL_RESOURCE Resource
  877. )
  878. /*++
  879. Routine Description:
  880. This routine deletes (i.e., uninitializes) the input resource variable
  881. Arguments:
  882. Resource - Supplies the resource variable being deleted
  883. Return Value:
  884. None
  885. --*/
  886. {
  887. RtlDeleteCriticalSection( &Resource->CriticalSection );
  888. NtClose(Resource->SharedSemaphore);
  889. NtClose(Resource->ExclusiveSemaphore);
  890. RtlpFreeDebugInfo( Resource->DebugInfo );
  891. RtlZeroMemory( Resource, sizeof( *Resource ) );
  892. return;
  893. }
  894. VOID
  895. RtlDumpResource(
  896. IN PRTL_RESOURCE Resource
  897. )
  898. {
  899. DbgPrint("Resource @ %lx\n", Resource);
  900. DbgPrint(" NumberOfWaitingShared = %lx\n", Resource->NumberOfWaitingShared);
  901. DbgPrint(" NumberOfWaitingExclusive = %lx\n", Resource->NumberOfWaitingExclusive);
  902. DbgPrint(" NumberOfActive = %lx\n", Resource->NumberOfActive);
  903. return;
  904. }
  905. NTSTATUS
  906. RtlInitializeCriticalSection(
  907. IN PRTL_CRITICAL_SECTION CriticalSection
  908. )
  909. /*++
  910. Routine Description:
  911. This routine initializes the input critial section variable
  912. Arguments:
  913. CriticalSection - Supplies the resource variable being initialized
  914. Return Value:
  915. TBD - Status of semaphore creation.
  916. --*/
  917. {
  918. PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
  919. //
  920. // Initialize the lock fields, the count indicates how many are waiting
  921. // to enter or are in the critical section, LockSemaphore is the object
  922. // to wait on when entering the critical section. SpinLock is used
  923. // for the add interlock instruction. Recursion count is the number of
  924. // times the critical section has been recursively entered.
  925. //
  926. CriticalSection->LockCount = -1;
  927. CriticalSection->RecursionCount = 0;
  928. CriticalSection->OwningThread = 0;
  929. CriticalSection->LockSemaphore = 0;
  930. //
  931. // Initialize debugging information.
  932. //
  933. DebugInfo = (PRTL_CRITICAL_SECTION_DEBUG)RtlpAllocateDebugInfo();
  934. if (DebugInfo == NULL) {
  935. return STATUS_NO_MEMORY;
  936. }
  937. DebugInfo->Type = RTL_CRITSECT_TYPE;
  938. DebugInfo->ContentionCount = 0;
  939. DebugInfo->EntryCount = 0;
  940. //
  941. // If the critical section lock itself is not being initialized, then
  942. // synchronize the insert of the critical section in the process locks
  943. // list. Otherwise, insert the critical section with no synchronization.
  944. //
  945. if ((CriticalSection != &RtlCriticalSectionLock) &&
  946. (RtlpCritSectInitialized != FALSE)) {
  947. RtlEnterCriticalSection(&RtlCriticalSectionLock);
  948. InsertTailList(&RtlCriticalSectionList, &DebugInfo->ProcessLocksList);
  949. RtlLeaveCriticalSection(&RtlCriticalSectionLock );
  950. } else {
  951. InsertTailList(&RtlCriticalSectionList, &DebugInfo->ProcessLocksList);
  952. }
  953. DebugInfo->CriticalSection = CriticalSection;
  954. CriticalSection->DebugInfo = DebugInfo;
  955. #if i386
  956. DebugInfo->CreatorBackTraceIndex = (USHORT)RtlLogStackBackTrace();
  957. #endif // i386
  958. return STATUS_SUCCESS;
  959. }
  960. VOID
  961. RtlpCreateCriticalSectionSem(
  962. IN PRTL_CRITICAL_SECTION CriticalSection
  963. )
  964. {
  965. NTSTATUS Status;
  966. Status = NtCreateSemaphore(
  967. &CriticalSection->LockSemaphore,
  968. DESIRED_SEMAPHORE_ACCESS,
  969. NULL,
  970. 0,
  971. MAXLONG
  972. );
  973. if ( !NT_SUCCESS(Status) ) {
  974. InterlockedDecrement(&CriticalSection->LockCount);
  975. KdPrint(( "NTDLL: Warning. Unable to allocate lock semaphore for Cs %x. Undoing lock and raising %x\n", CriticalSection,Status ));
  976. RtlRaiseStatus(Status);
  977. }
  978. #if DBG
  979. ProtectHandle(CriticalSection->LockSemaphore);
  980. #endif // DBG
  981. }
  982. VOID
  983. RtlpCheckDeferedCriticalSection(
  984. IN PRTL_CRITICAL_SECTION CriticalSection
  985. )
  986. {
  987. RtlEnterCriticalSection(&DeferedCriticalSection);
  988. try {
  989. if ( !CriticalSection->LockSemaphore ) {
  990. RtlpCreateCriticalSectionSem(CriticalSection);
  991. }
  992. }
  993. finally {
  994. RtlLeaveCriticalSection(&DeferedCriticalSection);
  995. }
  996. return;
  997. }
  998. NTSTATUS
  999. RtlDeleteCriticalSection(
  1000. IN PRTL_CRITICAL_SECTION CriticalSection
  1001. )
  1002. /*++
  1003. Routine Description:
  1004. This routine deletes (i.e., uninitializes) the input critical
  1005. section variable
  1006. Arguments:
  1007. CriticalSection - Supplies the resource variable being deleted
  1008. Return Value:
  1009. TBD - Status of semaphore close.
  1010. --*/
  1011. {
  1012. NTSTATUS Status;
  1013. PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
  1014. if ( CriticalSection->LockSemaphore ) {
  1015. #if DBG
  1016. UnProtectHandle( CriticalSection->LockSemaphore );
  1017. #endif // DBG
  1018. Status = NtClose( CriticalSection->LockSemaphore );
  1019. }
  1020. else {
  1021. Status = STATUS_SUCCESS;
  1022. }
  1023. //
  1024. // Remove critical section from the list
  1025. //
  1026. RtlEnterCriticalSection( &RtlCriticalSectionLock );
  1027. DebugInfo = CriticalSection->DebugInfo;
  1028. if (DebugInfo != NULL) {
  1029. RemoveEntryList( &DebugInfo->ProcessLocksList );
  1030. RtlZeroMemory( DebugInfo, sizeof( *DebugInfo ) );
  1031. }
  1032. RtlLeaveCriticalSection( &RtlCriticalSectionLock );
  1033. if (DebugInfo != NULL) {
  1034. RtlpFreeDebugInfo( DebugInfo );
  1035. }
  1036. RtlZeroMemory( CriticalSection, sizeof( *CriticalSection ) );
  1037. return Status;
  1038. }
  1039. //
  1040. // The following support routines are called from the machine language
  1041. // implementations of RtlEnterCriticalSection and RtlLeaveCriticalSection
  1042. // to execute the slow path logic of either waiting for a critical section
  1043. // or releasing a critical section to a waiting thread.
  1044. //
  1045. void
  1046. RtlpWaitForCriticalSection(
  1047. IN PRTL_CRITICAL_SECTION CriticalSection
  1048. );
  1049. void
  1050. RtlpUnWaitCriticalSection(
  1051. IN PRTL_CRITICAL_SECTION CriticalSection
  1052. );
  1053. void
  1054. RtlpWaitForCriticalSection(
  1055. IN PRTL_CRITICAL_SECTION CriticalSection
  1056. )
  1057. {
  1058. NTSTATUS st;
  1059. ULONG TimeoutCount = 0;
  1060. PLARGE_INTEGER TimeoutTime;
  1061. BOOLEAN CsIsLoaderLock;
  1062. //
  1063. // critical sections are disabled during exit process so that
  1064. // apps that are not carefull during shutdown don't hang
  1065. //
  1066. CsIsLoaderLock = (CriticalSection == NtCurrentPeb()->LoaderLock);
  1067. NtCurrentTeb()->WaitingOnLoaderLock = (ULONG)CsIsLoaderLock;
  1068. if ( LdrpShutdownInProgress &&
  1069. ((!CsIsLoaderLock) ||
  1070. (CsIsLoaderLock && LdrpShutdownThreadId == NtCurrentTeb()->ClientId.UniqueThread) ) ) {
  1071. //
  1072. // slimey reinitialization of the critical section with the count biased by one
  1073. // this is how the critical section would normally look to the thread coming out
  1074. // of this function. Note that the semaphore handle is leaked, but since the
  1075. // app is exiting, it's ok
  1076. //
  1077. CriticalSection->LockCount = 0;
  1078. CriticalSection->RecursionCount = 0;
  1079. CriticalSection->OwningThread = 0;
  1080. CriticalSection->LockSemaphore = 0;
  1081. NtCurrentTeb()->WaitingOnLoaderLock = 0;
  1082. return;
  1083. }
  1084. if (RtlpTimoutDisable) {
  1085. TimeoutTime = NULL;
  1086. }
  1087. else {
  1088. TimeoutTime = &RtlpTimeout;
  1089. }
  1090. if ( !CriticalSection->LockSemaphore ) {
  1091. RtlpCheckDeferedCriticalSection(CriticalSection);
  1092. }
  1093. CriticalSection->DebugInfo->EntryCount++;
  1094. while( TRUE ) {
  1095. CriticalSection->DebugInfo->ContentionCount++;
  1096. #if 0
  1097. DbgPrint( "NTDLL: Waiting for CritSect: %X owned by ThreadId: %X Count: %u Level: %u\n",
  1098. CriticalSection,
  1099. CriticalSection->OwningThread,
  1100. CriticalSection->LockCount,
  1101. CriticalSection->RecursionCount
  1102. );
  1103. #endif
  1104. st = NtWaitForSingleObject( CriticalSection->LockSemaphore,
  1105. FALSE,
  1106. TimeoutTime
  1107. );
  1108. if ( st == STATUS_TIMEOUT ) {
  1109. DbgPrint( "RTL: Enter Critical Section Timeout (2 minutes) %d\n",
  1110. TimeoutCount
  1111. );
  1112. DbgPrint( "RTL: Pid.Tid %x.%x, owner tid %x Critical Section %lx - ContentionCount == %lu\n",
  1113. NtCurrentTeb()->ClientId.UniqueProcess,
  1114. NtCurrentTeb()->ClientId.UniqueThread,
  1115. CriticalSection->OwningThread,
  1116. CriticalSection, CriticalSection->DebugInfo->ContentionCount
  1117. );
  1118. TimeoutCount++;
  1119. if ( TimeoutCount > 2 && CriticalSection != NtCurrentPeb()->LoaderLock ) {
  1120. PIMAGE_NT_HEADERS NtHeaders;
  1121. //
  1122. // If the image is a Win32 image, then raise an exception and try to get to the
  1123. // uae popup
  1124. //
  1125. NtHeaders = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
  1126. if (NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI ||
  1127. NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) {
  1128. EXCEPTION_RECORD ExceptionRecord;
  1129. ExceptionRecord.ExceptionCode = STATUS_POSSIBLE_DEADLOCK;
  1130. ExceptionRecord.ExceptionFlags = 0;
  1131. ExceptionRecord.ExceptionRecord = NULL;
  1132. ExceptionRecord.ExceptionAddress = (PVOID)RtlRaiseException;
  1133. ExceptionRecord.NumberParameters = 1;
  1134. ExceptionRecord.ExceptionInformation[0] = (ULONG)CriticalSection;
  1135. RtlRaiseException(&ExceptionRecord);
  1136. }
  1137. else {
  1138. DbgBreakPoint();
  1139. }
  1140. }
  1141. DbgPrint("RTL: Re-Waiting\n");
  1142. }
  1143. else {
  1144. if ( NT_SUCCESS(st) ) {
  1145. if ( CsIsLoaderLock ) {
  1146. CriticalSection->OwningThread = NtCurrentTeb()->ClientId.UniqueThread;
  1147. NtCurrentTeb()->WaitingOnLoaderLock = 0;
  1148. }
  1149. return;
  1150. }
  1151. else {
  1152. RtlRaiseStatus(st);
  1153. }
  1154. }
  1155. }
  1156. }
  1157. void
  1158. RtlpUnWaitCriticalSection(
  1159. IN PRTL_CRITICAL_SECTION CriticalSection
  1160. )
  1161. {
  1162. NTSTATUS st;
  1163. #if 0
  1164. DbgPrint( "NTDLL: Releasing CritSect: %X ThreadId: %X\n",
  1165. CriticalSection, CriticalSection->OwningThread
  1166. );
  1167. #endif
  1168. if ( !CriticalSection->LockSemaphore ) {
  1169. RtlpCheckDeferedCriticalSection(CriticalSection);
  1170. }
  1171. st = NtReleaseSemaphore( CriticalSection->LockSemaphore,
  1172. 1,
  1173. NULL
  1174. );
  1175. if ( NT_SUCCESS(st) ) {
  1176. return;
  1177. }
  1178. else {
  1179. RtlRaiseStatus(st);
  1180. }
  1181. }
  1182. void
  1183. RtlpNotOwnerCriticalSection(
  1184. IN PRTL_CRITICAL_SECTION CriticalSection
  1185. );
  1186. void
  1187. RtlpNotOwnerCriticalSection(
  1188. IN PRTL_CRITICAL_SECTION CriticalSection
  1189. )
  1190. {
  1191. BOOLEAN CsIsLoaderLock;
  1192. //
  1193. // critical sections are disabled during exit process so that
  1194. // apps that are not carefull during shutdown don't hang
  1195. //
  1196. CsIsLoaderLock = (CriticalSection == NtCurrentPeb()->LoaderLock);
  1197. if ( LdrpShutdownInProgress &&
  1198. ((!CsIsLoaderLock) ||
  1199. (CsIsLoaderLock && LdrpShutdownThreadId == NtCurrentTeb()->ClientId.UniqueThread) ) ) {
  1200. return;
  1201. }
  1202. if (NtCurrentPeb()->BeingDebugged) {
  1203. DbgPrint( "NTDLL: Calling thread (%X) not owner of CritSect: %X Owner ThreadId: %X\n",
  1204. NtCurrentTeb()->ClientId.UniqueThread,
  1205. CriticalSection,
  1206. CriticalSection->OwningThread
  1207. );
  1208. #if i386
  1209. _asm { int 3 }
  1210. #else
  1211. DbgBreakPoint();
  1212. #endif
  1213. }
  1214. RtlRaiseStatus( STATUS_RESOURCE_NOT_OWNED );
  1215. }