Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3033 lines
82 KiB

  1. /*++
  2. Copyright (c) 1994 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. Gary D. Kimura [GaryKi] 25-Jun-1989
  10. David N. Cutler (davec) 20-Mar-1994
  11. Substantially rewritten to make fastlock optimizations portable
  12. across all platforms and to improve the algorithms used to be
  13. perfectly synchronized.
  14. Environment:
  15. Kernel mode only.
  16. Revision History:
  17. --*/
  18. //#define _COLLECT_RESOURCE_DATA_ 1
  19. #include "exp.h"
  20. #pragma hdrstop
  21. #include "nturtl.h"
  22. //
  23. // Define local macros to test resource state.
  24. //
  25. #define IsExclusiveWaiting(a) ((a)->NumberOfExclusiveWaiters != 0)
  26. #define IsSharedWaiting(a) ((a)->NumberOfSharedWaiters != 0)
  27. #define IsOwnedExclusive(a) (((a)->Flag & ResourceOwnedExclusive) != 0)
  28. #define IsBoostAllowed(a) (((a)->Flag & DisablePriorityBoost) == 0)
  29. //
  30. // Define priority boost flags.
  31. //
  32. #define DisablePriorityBoost 0x08
  33. LARGE_INTEGER ExShortTime = {(ULONG)(-10 * 1000 * 10), -1}; // 10 milliseconds
  34. #define EX_RESOURCE_CHECK_FREES 0x1
  35. #define EX_RESOURCE_CHECK_ORPHANS 0x2
  36. ULONG ExResourceCheckFlags = EX_RESOURCE_CHECK_FREES|EX_RESOURCE_CHECK_ORPHANS;
  37. //
  38. // Define resource assertion macro.
  39. //
  40. #if DBG
  41. VOID
  42. ExpAssertResource(
  43. IN PERESOURCE Resource
  44. );
  45. #define ASSERT_RESOURCE(_Resource) ExpAssertResource(_Resource)
  46. #else
  47. #define ASSERT_RESOURCE(_Resource)
  48. #endif
  49. //
  50. // Define locking primitives.
  51. // On UP systems, fastlocks are used.
  52. // On MP systems, a queued spinlock is used.
  53. //
  54. #if defined(NT_UP)
  55. #define EXP_LOCK_HANDLE KIRQL
  56. #define PEXP_LOCK_HANDLE PKIRQL
  57. #define EXP_LOCK_RESOURCE(_resource_, _plockhandle_) UNREFERENCED_PARAMETER(_plockhandle_); ExAcquireFastLock(&(_resource_)->SpinLock, (_plockhandle_))
  58. #define EXP_UNLOCK_RESOURCE(_resource_, _plockhandle_) ExReleaseFastLock(&(_resource_)->SpinLock, *(_plockhandle_))
  59. #else
  60. #define EXP_LOCK_HANDLE KLOCK_QUEUE_HANDLE
  61. #define PEXP_LOCK_HANDLE PKLOCK_QUEUE_HANDLE
  62. #define EXP_LOCK_RESOURCE(_resource_, _plockhandle_) KeAcquireInStackQueuedSpinLock(&(_resource_)->SpinLock, (_plockhandle_))
  63. #define EXP_UNLOCK_RESOURCE(_resource_, _plockhandle_) KeReleaseInStackQueuedSpinLock(_plockhandle_)
  64. #endif
  65. //
  66. // Define private function prototypes.
  67. //
  68. VOID
  69. FASTCALL
  70. ExpWaitForResource (
  71. IN PERESOURCE Resource,
  72. IN PVOID Object
  73. );
  74. POWNER_ENTRY
  75. FASTCALL
  76. ExpFindCurrentThread(
  77. IN PERESOURCE Resource,
  78. IN ERESOURCE_THREAD CurrentThread,
  79. IN PEXP_LOCK_HANDLE LockHandle OPTIONAL
  80. );
  81. //
  82. // Resource wait time out value.
  83. //
  84. LARGE_INTEGER ExpTimeout;
  85. //
  86. // Consecutive time outs before message. Note this is registry-settable.
  87. //
  88. ULONG ExResourceTimeoutCount = 648000;
  89. //
  90. // Global spinlock to guard access to resource lists.
  91. //
  92. KSPIN_LOCK ExpResourceSpinLock;
  93. //
  94. // Resource list used to record all resources in the system.
  95. //
  96. LIST_ENTRY ExpSystemResourcesList;
  97. //
  98. // Define executive resource performance data.
  99. //
  100. #if defined(_COLLECT_RESOURCE_DATA_)
  101. #define ExpIncrementCounter(Member) ExpResourcePerformanceData.Member += 1
  102. RESOURCE_PERFORMANCE_DATA ExpResourcePerformanceData;
  103. #else
  104. #define ExpIncrementCounter(Member)
  105. #endif
  106. #ifdef ALLOC_PRAGMA
  107. #pragma alloc_text(INIT, ExpResourceInitialization)
  108. #pragma alloc_text(PAGELK, ExQuerySystemLockInformation)
  109. #endif
  110. //
  111. // Resource strict verification (checked builds only)
  112. //
  113. // When acquiring a resource while running in a thread that is not a system
  114. // thread and runs at passive level we need to disable kernel APCs first
  115. // (KeEnterCriticalRegion()). Otherwise any user mode code can call
  116. // NtSuspendThread() which is implemented using kernel APCs and can
  117. // suspend the thread while having a resource acquired.
  118. // This will potentially deadlock the whole system.
  119. //
  120. #if DBG
  121. ULONG ExResourceStrict = 1;
  122. VOID
  123. ExCheckIfKernelApcsShouldBeDisabled (
  124. IN KIRQL Irql,
  125. IN PVOID Resource,
  126. IN PKTHREAD Thread)
  127. {
  128. if ((ExResourceStrict == 0) ||
  129. (Irql >= APC_LEVEL) ||
  130. (IS_SYSTEM_THREAD((PETHREAD)Thread)) ||
  131. (Thread->CombinedApcDisable != 0)) {
  132. return;
  133. }
  134. DbgPrint ("EX: resource: APCs still enabled before resource %p acquire !!!\n", Resource);
  135. DbgBreakPoint ();
  136. }
  137. #define EX_ENSURE_APCS_DISABLED(Irql, Resource, Thread) \
  138. ExCheckIfKernelApcsShouldBeDisabled (Irql, Resource, Thread);
  139. #else
  140. #define EX_ENSURE_APCS_DISABLED(Irql, Resource, Thread)
  141. #endif // DBG
  142. BOOLEAN
  143. ExpResourceInitialization(
  144. VOID
  145. )
  146. /*++
  147. Routine Description:
  148. This function initializes global data during system initialization.
  149. Arguments:
  150. None.
  151. Return Value:
  152. BOOLEAN - TRUE
  153. --*/
  154. {
  155. #if defined(_COLLECT_RESOURCE_DATA_)
  156. ULONG Index;
  157. #endif
  158. //
  159. // Initialize resource timeout value, the system resource listhead,
  160. // and the resource spinlock.
  161. //
  162. ExpTimeout.QuadPart = Int32x32To64(4 * 1000, -10000);
  163. InitializeListHead(&ExpSystemResourcesList);
  164. KeInitializeSpinLock(&ExpResourceSpinLock);
  165. //
  166. // Initialize resource performance data.
  167. //
  168. #if defined(_COLLECT_RESOURCE_DATA_)
  169. ExpResourcePerformanceData.ActiveResourceCount = 0;
  170. ExpResourcePerformanceData.TotalResourceCount = 0;
  171. ExpResourcePerformanceData.ExclusiveAcquire = 0;
  172. ExpResourcePerformanceData.SharedFirstLevel = 0;
  173. ExpResourcePerformanceData.SharedSecondLevel = 0;
  174. ExpResourcePerformanceData.StarveFirstLevel = 0;
  175. ExpResourcePerformanceData.StarveSecondLevel = 0;
  176. ExpResourcePerformanceData.WaitForExclusive = 0;
  177. ExpResourcePerformanceData.OwnerTableExpands = 0;
  178. ExpResourcePerformanceData.MaximumTableExpand = 0;
  179. for (Index = 0; Index < RESOURCE_HASH_TABLE_SIZE; Index += 1) {
  180. InitializeListHead(&ExpResourcePerformanceData.HashTable[Index]);
  181. }
  182. #endif
  183. return TRUE;
  184. }
  185. VOID
  186. ExpAllocateExclusiveWaiterEvent (
  187. IN PERESOURCE Resource,
  188. IN PEXP_LOCK_HANDLE LockHandle
  189. )
  190. /*++
  191. Routine Description:
  192. This function allocates and initializes the exclusive waiter event
  193. for a resource.
  194. N.B. The resource spin lock is held on entry and exit of this routine.
  195. Arguments:
  196. Resource - Supplies a pointer to the resource.
  197. LockHandle - Supplies a pointer to a lock handle.
  198. Return Value:
  199. None.
  200. --*/
  201. {
  202. PKEVENT Event;
  203. //
  204. // Allocate an exclusive wait event and retry the acquire operation.
  205. //
  206. EXP_UNLOCK_RESOURCE(Resource, LockHandle);
  207. do {
  208. Event = ExAllocatePoolWithTag(NonPagedPool,
  209. sizeof(KEVENT),
  210. 'vEeR');
  211. if (Event != NULL) {
  212. KeInitializeEvent(Event, SynchronizationEvent, FALSE);
  213. if (InterlockedCompareExchangePointer(&Resource->ExclusiveWaiters,
  214. Event,
  215. NULL) != NULL) {
  216. ExFreePool(Event);
  217. }
  218. break;
  219. }
  220. KeDelayExecutionThread(KernelMode, FALSE, &ExShortTime);
  221. } while (TRUE);
  222. EXP_LOCK_RESOURCE(Resource, LockHandle);
  223. return;
  224. }
  225. VOID
  226. ExpAllocateSharedWaiterSemaphore (
  227. IN PERESOURCE Resource,
  228. IN PEXP_LOCK_HANDLE LockHandle
  229. )
  230. /*++
  231. Routine Description:
  232. This function allocates and initializes the shared waiter semaphore
  233. for a resource.
  234. N.B. The resource spin lock is held on entry and exit of this routine.
  235. Arguments:
  236. Resource - Supplies a pointer to the resource.
  237. LockHandle - Supplies a pointer to a lock handle.
  238. Return Value:
  239. None.
  240. --*/
  241. {
  242. PKSEMAPHORE Semaphore;
  243. //
  244. // Allocate and initialize a shared wait semaphore for the specified
  245. // resource.
  246. //
  247. EXP_UNLOCK_RESOURCE(Resource, LockHandle);
  248. do {
  249. Semaphore = ExAllocatePoolWithTag(NonPagedPool,
  250. sizeof(KSEMAPHORE),
  251. 'eSeR');
  252. if (Semaphore != NULL) {
  253. KeInitializeSemaphore(Semaphore, 0, MAXLONG);
  254. if (InterlockedCompareExchangePointer(&Resource->SharedWaiters,
  255. Semaphore,
  256. NULL) != NULL) {
  257. ExFreePool(Semaphore);
  258. }
  259. break;
  260. }
  261. KeDelayExecutionThread(KernelMode, FALSE, &ExShortTime);
  262. } while (TRUE);
  263. EXP_LOCK_RESOURCE(Resource, LockHandle);
  264. return;
  265. }
  266. NTSTATUS
  267. ExInitializeResourceLite(
  268. IN PERESOURCE Resource
  269. )
  270. /*++
  271. Routine Description:
  272. This routine initializes the specified resource.
  273. Arguments:
  274. Resource - Supplies a pointer to the resource to initialize.
  275. Return Value:
  276. STATUS_SUCCESS.
  277. --*/
  278. {
  279. #if defined(_COLLECT_RESOURCE_DATA_)
  280. PVOID CallersCaller;
  281. #endif
  282. KLOCK_QUEUE_HANDLE LockHandle;
  283. ASSERT(MmDeterminePoolType(Resource) == NonPagedPool);
  284. //
  285. // Initialize the specified resource.
  286. //
  287. // N.B. All fields are initialized to zero (NULL pointers) except
  288. // the list entry and spinlock.
  289. //
  290. RtlZeroMemory(Resource, sizeof(ERESOURCE));
  291. KeInitializeSpinLock(&Resource->SpinLock);
  292. if (NtGlobalFlag & FLG_KERNEL_STACK_TRACE_DB) {
  293. Resource->CreatorBackTraceIndex = RtlLogStackBackTrace();
  294. }else {
  295. Resource->CreatorBackTraceIndex = 0;
  296. }
  297. KeAcquireInStackQueuedSpinLock (&ExpResourceSpinLock, &LockHandle);
  298. InsertTailList (&ExpSystemResourcesList, &Resource->SystemResourcesList);
  299. KeReleaseInStackQueuedSpinLock (&LockHandle);
  300. //
  301. // Initialize performance data entry for the resource.
  302. //
  303. #if defined(_COLLECT_RESOURCE_DATA_)
  304. RtlGetCallersAddress(&Resource->Address, &CallersCaller);
  305. ExpResourcePerformanceData.TotalResourceCount += 1;
  306. ExpResourcePerformanceData.ActiveResourceCount += 1;
  307. #endif
  308. return STATUS_SUCCESS;
  309. }
  310. NTSTATUS
  311. ExReinitializeResourceLite(
  312. IN PERESOURCE Resource
  313. )
  314. /*++
  315. Routine Description:
  316. This routine reinitializes the specified resource.
  317. Arguments:
  318. Resource - Supplies a pointer to the resource to initialize.
  319. Return Value:
  320. STATUS_SUCCESS.
  321. --*/
  322. {
  323. PKEVENT Event;
  324. ULONG Index;
  325. POWNER_ENTRY OwnerTable;
  326. PKSEMAPHORE Semaphore;
  327. ULONG TableSize;
  328. ASSERT(MmDeterminePoolType(Resource) == NonPagedPool);
  329. //
  330. // If the resource has an owner table, then zero the owner table.
  331. //
  332. OwnerTable = Resource->OwnerTable;
  333. if (OwnerTable != NULL) {
  334. TableSize = OwnerTable->TableSize;
  335. for (Index = 1; Index < TableSize; Index += 1) {
  336. OwnerTable[Index].OwnerThread = 0;
  337. OwnerTable[Index].OwnerCount = 0;
  338. }
  339. }
  340. //
  341. // Set the active count and flags to zero.
  342. //
  343. Resource->ActiveCount = 0;
  344. Resource->Flag = 0;
  345. //
  346. // If the resource has a shared waiter semaphore, then reinitialize
  347. // it.
  348. //
  349. Semaphore = Resource->SharedWaiters;
  350. if (Semaphore != NULL) {
  351. KeInitializeSemaphore(Semaphore, 0, MAXLONG);
  352. }
  353. //
  354. // If the resource has a exclusive waiter event, then reinitialize
  355. // it.
  356. //
  357. Event = Resource->ExclusiveWaiters;
  358. if (Event != NULL) {
  359. KeInitializeEvent(Event, SynchronizationEvent, FALSE);
  360. }
  361. //
  362. // Initialize the builtin owner table.
  363. //
  364. Resource->OwnerThreads[0].OwnerThread = 0;
  365. Resource->OwnerThreads[0].OwnerCount = 0;
  366. Resource->OwnerThreads[1].OwnerThread = 0;
  367. Resource->OwnerThreads[1].OwnerCount = 0;
  368. //
  369. // Set the contention count, number of shared waiters, and number
  370. // of exclusive waiters to zero.
  371. //
  372. Resource->ContentionCount = 0;
  373. Resource->NumberOfSharedWaiters = 0;
  374. Resource->NumberOfExclusiveWaiters = 0;
  375. return STATUS_SUCCESS;
  376. }
  377. VOID
  378. ExDisableResourceBoostLite(
  379. IN PERESOURCE Resource
  380. )
  381. /*++
  382. Routine Description:
  383. This routine disables priority inversion boosting for the specified
  384. resource.
  385. Arguments:
  386. Resource - Supplies a pointer to the resource for which priority
  387. boosting is disabled.
  388. Return Value:
  389. None.
  390. --*/
  391. {
  392. EXP_LOCK_HANDLE LockHandle;
  393. ASSERT_RESOURCE(Resource);
  394. //
  395. // Disable priority boosts for the specified resource.
  396. //
  397. EXP_LOCK_RESOURCE(Resource, &LockHandle);
  398. Resource->Flag |= DisablePriorityBoost;
  399. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  400. return;
  401. }
  402. BOOLEAN
  403. ExAcquireResourceExclusiveLite(
  404. IN PERESOURCE Resource,
  405. IN BOOLEAN Wait
  406. )
  407. /*++
  408. Routine Description:
  409. The routine acquires the specified resource for exclusive access.
  410. Arguments:
  411. Resource - Supplies a pointer to the resource that is acquired
  412. for exclusive access.
  413. Wait - A boolean value that specifies whether to wait for the
  414. resource to become available if access cannot be granted
  415. immediately.
  416. Return Value:
  417. BOOLEAN - TRUE if the resource is acquired and FALSE otherwise.
  418. --*/
  419. {
  420. ERESOURCE_THREAD CurrentThread;
  421. EXP_LOCK_HANDLE LockHandle;
  422. BOOLEAN Result;
  423. ASSERT((Resource->Flag & ResourceNeverExclusive) == 0);
  424. //
  425. // Acquire exclusive access to the specified resource.
  426. //
  427. CurrentThread = (ERESOURCE_THREAD)PsGetCurrentThread();
  428. ASSERT(KeIsExecutingDpc() == FALSE);
  429. ASSERT_RESOURCE(Resource);
  430. EXP_LOCK_RESOURCE(Resource, &LockHandle);
  431. //
  432. // Resource acquisition must be protected from thread suspends.
  433. //
  434. EX_ENSURE_APCS_DISABLED (LockHandle.OldIrql,
  435. Resource,
  436. KeGetCurrentThread());
  437. ExpIncrementCounter(ExclusiveAcquire);
  438. //
  439. // If the active count of the resource is zero, then there is neither
  440. // an exclusive owner nor a shared owner and access to the resource can
  441. // be immediately granted. Otherwise, there is either a shared owner or
  442. // an exclusive owner.
  443. //
  444. retry:
  445. if (Resource->ActiveCount != 0) {
  446. //
  447. // The resource is either owned exclusive or shared.
  448. //
  449. // If the resource is owned exclusive and the current thread is the
  450. // owner, then increment the recursion count.
  451. //
  452. if (IsOwnedExclusive(Resource) &&
  453. (Resource->OwnerThreads[0].OwnerThread == CurrentThread)) {
  454. Resource->OwnerThreads[0].OwnerCount += 1;
  455. Result = TRUE;
  456. } else {
  457. //
  458. // The resource is either owned exclusive by some other thread,
  459. // or owned shared.
  460. //
  461. // If wait is not specified, then return that the resource was
  462. // not acquired. Otherwise, wait for exclusive access to the
  463. // resource to be granted.
  464. //
  465. if (Wait == FALSE) {
  466. Result = FALSE;
  467. } else {
  468. //
  469. // If the exclusive wait event has not yet been allocated,
  470. // then the long path code must be taken.
  471. //
  472. if (Resource->ExclusiveWaiters == NULL) {
  473. ExpAllocateExclusiveWaiterEvent(Resource, &LockHandle);
  474. goto retry;
  475. }
  476. //
  477. // Wait for exclusive access to the resource to be granted
  478. // and set the owner thread.
  479. //
  480. Resource->NumberOfExclusiveWaiters += 1;
  481. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  482. ExpWaitForResource(Resource, Resource->ExclusiveWaiters);
  483. //
  484. // N.B. It is "safe" to store the owner thread without
  485. // obtaining any locks since the thread has already
  486. // been granted exclusive ownership.
  487. //
  488. Resource->OwnerThreads[0].OwnerThread = (ERESOURCE_THREAD)PsGetCurrentThread();
  489. return TRUE;
  490. }
  491. }
  492. } else {
  493. //
  494. // The resource is not owned.
  495. //
  496. Resource->Flag |= ResourceOwnedExclusive;
  497. Resource->OwnerThreads[0].OwnerThread = CurrentThread;
  498. Resource->OwnerThreads[0].OwnerCount = 1;
  499. Resource->ActiveCount = 1;
  500. Result = TRUE;
  501. }
  502. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  503. return Result;
  504. }
  505. BOOLEAN
  506. ExTryToAcquireResourceExclusiveLite(
  507. IN PERESOURCE Resource
  508. )
  509. /*++
  510. Routine Description:
  511. The routine attempts to acquire the specified resource for exclusive
  512. access.
  513. Arguments:
  514. Resource - Supplies a pointer to the resource that is acquired
  515. for exclusive access.
  516. Return Value:
  517. BOOLEAN - TRUE if the resource is acquired and FALSE otherwise.
  518. --*/
  519. {
  520. ERESOURCE_THREAD CurrentThread;
  521. EXP_LOCK_HANDLE LockHandle;
  522. BOOLEAN Result;
  523. ASSERT((Resource->Flag & ResourceNeverExclusive) == 0);
  524. //
  525. // Attempt to acquire exclusive access to the specified resource.
  526. //
  527. CurrentThread = (ERESOURCE_THREAD)PsGetCurrentThread();
  528. ASSERT(KeIsExecutingDpc() == FALSE);
  529. ASSERT_RESOURCE(Resource);
  530. EXP_LOCK_RESOURCE(Resource, &LockHandle);
  531. //
  532. // If the active count of the resource is zero, then there is neither
  533. // an exclusive owner nor a shared owner and access to the resource can
  534. // be immediately granted. Otherwise, if the resource is owned exclusive
  535. // and the current thread is the owner, then access to the resource can
  536. // be immediately granted. Otherwise, access cannot be granted.
  537. //
  538. Result = FALSE;
  539. if (Resource->ActiveCount == 0) {
  540. ExpIncrementCounter(ExclusiveAcquire);
  541. Resource->Flag |= ResourceOwnedExclusive;
  542. Resource->OwnerThreads[0].OwnerThread = CurrentThread;
  543. Resource->OwnerThreads[0].OwnerCount = 1;
  544. Resource->ActiveCount = 1;
  545. Result = TRUE;
  546. } else if (IsOwnedExclusive(Resource) &&
  547. (Resource->OwnerThreads[0].OwnerThread == CurrentThread)) {
  548. ExpIncrementCounter(ExclusiveAcquire);
  549. Resource->OwnerThreads[0].OwnerCount += 1;
  550. Result = TRUE;
  551. }
  552. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  553. return Result;
  554. }
  555. BOOLEAN
  556. ExAcquireResourceSharedLite(
  557. IN PERESOURCE Resource,
  558. IN BOOLEAN Wait
  559. )
  560. /*++
  561. Routine Description:
  562. The routine acquires the specified resource for shared access.
  563. Arguments:
  564. Resource - Supplies a pointer to the resource that is acquired
  565. for shared access.
  566. Wait - A boolean value that specifies whether to wait for the
  567. resource to become available if access cannot be granted
  568. immediately.
  569. Return Value:
  570. BOOLEAN - TRUE if the resource is acquired and FALSE otherwise.
  571. --*/
  572. {
  573. ERESOURCE_THREAD CurrentThread;
  574. EXP_LOCK_HANDLE LockHandle;
  575. POWNER_ENTRY OwnerEntry;
  576. //
  577. // Acquire exclusive access to the specified resource.
  578. //
  579. CurrentThread = (ERESOURCE_THREAD)PsGetCurrentThread();
  580. ASSERT(KeIsExecutingDpc() == FALSE);
  581. ASSERT_RESOURCE(Resource);
  582. EXP_LOCK_RESOURCE(Resource, &LockHandle);
  583. //
  584. // Resource acquisition must be protected from thread suspends.
  585. //
  586. EX_ENSURE_APCS_DISABLED (LockHandle.OldIrql,
  587. Resource,
  588. KeGetCurrentThread());
  589. ExpIncrementCounter(SharedFirstLevel);
  590. //
  591. // If the active count of the resource is zero, then there is neither
  592. // an exclusive owner nor a shared owner and access to the resource can
  593. // be immediately granted.
  594. //
  595. retry:
  596. if (Resource->ActiveCount == 0) {
  597. Resource->OwnerThreads[1].OwnerThread = CurrentThread;
  598. Resource->OwnerThreads[1].OwnerCount = 1;
  599. Resource->ActiveCount = 1;
  600. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  601. return TRUE;
  602. }
  603. //
  604. // The resource is either owned exclusive or shared.
  605. //
  606. // If the resource is owned exclusive and the current thread is the
  607. // owner, then treat the shared request as an exclusive request and
  608. // increment the recursion count. Otherwise, it is owned shared.
  609. //
  610. if (IsOwnedExclusive(Resource)) {
  611. if (Resource->OwnerThreads[0].OwnerThread == CurrentThread) {
  612. Resource->OwnerThreads[0].OwnerCount += 1;
  613. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  614. return TRUE;
  615. }
  616. //
  617. // Find an empty entry in the thread array.
  618. //
  619. OwnerEntry = ExpFindCurrentThread(Resource, 0, &LockHandle);
  620. if (OwnerEntry == NULL) {
  621. goto retry;
  622. }
  623. } else {
  624. //
  625. // The resource is owned shared.
  626. //
  627. // If the current thread already has acquired the resource for
  628. // shared access, then increment the recursion count. Otherwise
  629. // grant shared access if there are no exclusive waiters.
  630. //
  631. OwnerEntry = ExpFindCurrentThread(Resource, CurrentThread, &LockHandle);
  632. if (OwnerEntry == NULL) {
  633. goto retry;
  634. }
  635. if (OwnerEntry->OwnerThread == CurrentThread) {
  636. OwnerEntry->OwnerCount += 1;
  637. ASSERT(OwnerEntry->OwnerCount != 0);
  638. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  639. return TRUE;
  640. }
  641. //
  642. // If there are no exclusive waiters, then grant shared access
  643. // to the resource. Otherwise, wait for the resource to become
  644. // available.
  645. //
  646. if (IsExclusiveWaiting(Resource) == FALSE) {
  647. OwnerEntry->OwnerThread = CurrentThread;
  648. OwnerEntry->OwnerCount = 1;
  649. Resource->ActiveCount += 1;
  650. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  651. return TRUE;
  652. }
  653. }
  654. //
  655. // The resource is either owned exclusive by some other thread, or
  656. // owned shared by some other threads, but there is an exclusive
  657. // waiter and the current thread does not already have shared access
  658. // to the resource.
  659. //
  660. // If wait is not specified, then return that the resource was
  661. // not acquired.
  662. //
  663. if (Wait == FALSE) {
  664. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  665. return FALSE;
  666. }
  667. //
  668. // If the shared wait semaphore has not yet been allocated, then the
  669. // long path must be taken.
  670. //
  671. if (Resource->SharedWaiters == NULL) {
  672. ExpAllocateSharedWaiterSemaphore(Resource, &LockHandle);
  673. goto retry;
  674. }
  675. //
  676. // Wait for shared access to the resource to be granted and increment
  677. // the recursion count.
  678. //
  679. OwnerEntry->OwnerThread = CurrentThread;
  680. OwnerEntry->OwnerCount = 1;
  681. Resource->NumberOfSharedWaiters += 1;
  682. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  683. ExpWaitForResource(Resource, Resource->SharedWaiters);
  684. return TRUE;
  685. }
  686. BOOLEAN
  687. ExAcquireSharedStarveExclusive(
  688. IN PERESOURCE Resource,
  689. IN BOOLEAN Wait
  690. )
  691. /*++
  692. Routine Description:
  693. This routine acquires the specified resource for shared access and
  694. does not wait for any pending exclusive owners.
  695. Arguments:
  696. Resource - Supplies a pointer to the resource that is acquired
  697. for shared access.
  698. Wait - A boolean value that specifies whether to wait for the
  699. resource to become available if access cannot be granted
  700. immediately.
  701. Return Value:
  702. BOOLEAN - TRUE if the resource is acquired and FALSE otherwise.
  703. --*/
  704. {
  705. ERESOURCE_THREAD CurrentThread;
  706. EXP_LOCK_HANDLE LockHandle;
  707. POWNER_ENTRY OwnerEntry;
  708. //
  709. // Acquire exclusive access to the specified resource.
  710. //
  711. CurrentThread = (ERESOURCE_THREAD)PsGetCurrentThread();
  712. ASSERT(KeIsExecutingDpc() == FALSE);
  713. ASSERT_RESOURCE(Resource);
  714. EXP_LOCK_RESOURCE(Resource, &LockHandle);
  715. ExpIncrementCounter(StarveFirstLevel);
  716. //
  717. // If the active count of the resource is zero, then there is neither
  718. // an exclusive owner nor a shared owner and access to the resource can
  719. // be immediately granted.
  720. //
  721. retry:
  722. if (Resource->ActiveCount == 0) {
  723. Resource->OwnerThreads[1].OwnerThread = CurrentThread;
  724. Resource->OwnerThreads[1].OwnerCount = 1;
  725. Resource->ActiveCount = 1;
  726. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  727. return TRUE;
  728. }
  729. //
  730. // The resource is either owned exclusive or shared.
  731. //
  732. // If the resource is owned exclusive and the current thread is the
  733. // owner, then treat the shared request as an exclusive request and
  734. // increment the recursion count. Otherwise, it is owned shared.
  735. //
  736. if (IsOwnedExclusive(Resource)) {
  737. if (Resource->OwnerThreads[0].OwnerThread == CurrentThread) {
  738. Resource->OwnerThreads[0].OwnerCount += 1;
  739. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  740. return TRUE;
  741. }
  742. //
  743. // Find an empty entry in the thread array.
  744. //
  745. OwnerEntry = ExpFindCurrentThread(Resource, 0, &LockHandle);
  746. if (OwnerEntry == NULL) {
  747. goto retry;
  748. }
  749. } else {
  750. //
  751. // The resource is owned shared.
  752. //
  753. // If the current thread already has acquired the resource for
  754. // shared access, then increment the recursion count. Otherwise
  755. // grant shared access to the current thread.
  756. //
  757. OwnerEntry = ExpFindCurrentThread(Resource, CurrentThread, &LockHandle);
  758. if (OwnerEntry == NULL) {
  759. goto retry;
  760. }
  761. if (OwnerEntry->OwnerThread == CurrentThread) {
  762. OwnerEntry->OwnerCount += 1;
  763. ASSERT(OwnerEntry->OwnerCount != 0);
  764. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  765. return TRUE;
  766. }
  767. //
  768. // Grant the current thread shared access to the resource.
  769. //
  770. OwnerEntry->OwnerThread = CurrentThread;
  771. OwnerEntry->OwnerCount = 1;
  772. Resource->ActiveCount += 1;
  773. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  774. return TRUE;
  775. }
  776. //
  777. // The resource is owned exclusive by some other thread.
  778. //
  779. // If wait is not specified, then return that the resource was
  780. // not acquired.
  781. //
  782. if (Wait == FALSE) {
  783. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  784. return FALSE;
  785. }
  786. //
  787. // If the shared wait semaphore has not yet been allocated, then the
  788. // long path must be taken.
  789. //
  790. if (Resource->SharedWaiters == NULL) {
  791. ExpAllocateSharedWaiterSemaphore(Resource, &LockHandle);
  792. goto retry;
  793. }
  794. //
  795. // Wait for shared access to the resource to be granted and increment
  796. // the recursion count.
  797. //
  798. OwnerEntry->OwnerThread = CurrentThread;
  799. OwnerEntry->OwnerCount = 1;
  800. Resource->NumberOfSharedWaiters += 1;
  801. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  802. ExpWaitForResource(Resource, Resource->SharedWaiters);
  803. return TRUE;
  804. }
  805. BOOLEAN
  806. ExAcquireSharedWaitForExclusive(
  807. IN PERESOURCE Resource,
  808. IN BOOLEAN Wait
  809. )
  810. /*++
  811. Routine Description:
  812. This routine acquires the specified resource for shared access, but
  813. waits for any pending exclusive owners.
  814. Arguments:
  815. Resource - Supplies a pointer to the resource that is acquired
  816. for shared access.
  817. Wait - A boolean value that specifies whether to wait for the
  818. resource to become available if access cannot be granted
  819. immediately.
  820. Return Value:
  821. BOOLEAN - TRUE if the resource is acquired and FALSE otherwise.
  822. --*/
  823. {
  824. ERESOURCE_THREAD CurrentThread;
  825. EXP_LOCK_HANDLE LockHandle;
  826. POWNER_ENTRY OwnerEntry;
  827. //
  828. // Acquire exclusive access to the specified resource.
  829. //
  830. CurrentThread = (ERESOURCE_THREAD)PsGetCurrentThread();
  831. ASSERT(KeIsExecutingDpc() == FALSE);
  832. ASSERT_RESOURCE(Resource);
  833. EXP_LOCK_RESOURCE(Resource, &LockHandle);
  834. ExpIncrementCounter(WaitForExclusive);
  835. //
  836. // If the active count of the resource is zero, then there is neither
  837. // an exclusive owner nor a shared owner and access to the resource can
  838. // be immediately granted.
  839. //
  840. retry:
  841. if (Resource->ActiveCount == 0) {
  842. Resource->OwnerThreads[1].OwnerThread = CurrentThread;
  843. Resource->OwnerThreads[1].OwnerCount = 1;
  844. Resource->ActiveCount = 1;
  845. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  846. return TRUE;
  847. }
  848. //
  849. // The resource is either owned exclusive or shared.
  850. //
  851. // If the resource is owned exclusive and the current thread is the
  852. // owner, then treat the shared request as an exclusive request and
  853. // increment the recursion count. Otherwise, it is owned shared.
  854. //
  855. if (IsOwnedExclusive(Resource)) {
  856. if (Resource->OwnerThreads[0].OwnerThread == CurrentThread) {
  857. Resource->OwnerThreads[0].OwnerCount += 1;
  858. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  859. return TRUE;
  860. }
  861. //
  862. // Find an empty entry in the thread array.
  863. //
  864. OwnerEntry = ExpFindCurrentThread(Resource, 0, &LockHandle);
  865. if (OwnerEntry == NULL) {
  866. goto retry;
  867. }
  868. } else {
  869. //
  870. // The resource is owned shared.
  871. //
  872. // If there is an exclusive waiter, then wait for the exclusive
  873. // waiter to gain access to the resource, then acquire the resource
  874. // shared without regard to exclusive waiters. Otherwise, if the
  875. // current thread already has acquired the resource for shared access,
  876. // then increment the recursion count. Otherwise grant shared access
  877. // to the current thread.
  878. //
  879. if (IsExclusiveWaiting(Resource)) {
  880. //
  881. // The resource is shared, but there is an exclusive waiter.
  882. //
  883. // It doesn't matter if this thread is already one of the shared
  884. // owner(s) - if TRUE is specified, this thread must block - an APC
  885. // will release the resource to unjam things and callers count on
  886. // this behavior.
  887. //
  888. #if 0
  889. //
  890. // This code must NOT be enabled as per the comment above.
  891. //
  892. OwnerEntry = ExpFindCurrentThread(Resource, CurrentThread, NULL);
  893. if ((OwnerEntry != NULL) &&
  894. (OwnerEntry->OwnerThread == CurrentThread)) {
  895. ASSERT(OwnerEntry->OwnerCount != 0);
  896. OwnerEntry->OwnerCount += 1;
  897. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  898. return TRUE;
  899. }
  900. #endif
  901. //
  902. // If wait is not specified, then return that the resource was
  903. // not acquired.
  904. //
  905. if (Wait == FALSE) {
  906. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  907. return FALSE;
  908. }
  909. //
  910. // If the shared wait semaphore has not yet been allocated, then
  911. // allocate and initialize it.
  912. //
  913. if (Resource->SharedWaiters == NULL) {
  914. ExpAllocateSharedWaiterSemaphore(Resource, &LockHandle);
  915. goto retry;
  916. }
  917. //
  918. // Increment the number of shared waiters and wait for shared
  919. // access to the resource to be granted to some other set of
  920. // threads, and then acquire the resource shared without regard
  921. // to exclusive access.
  922. //
  923. // N.B. The resource is left in a state such that the calling
  924. // thread does not have a reference in the owner table
  925. // for the requested access even though the active count
  926. // is incremented when control is returned. However, the
  927. // resource is owned shared at this point, so an owner
  928. // entry can simply be allocated and the owner count set
  929. // to one.
  930. //
  931. Resource->NumberOfSharedWaiters += 1;
  932. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  933. ExpWaitForResource(Resource, Resource->SharedWaiters);
  934. //
  935. // Reacquire the resource spin lock, allocate an owner entry,
  936. // and initialize the owner count to one. The active count
  937. // was already incremented when shared access was granted.
  938. //
  939. EXP_LOCK_RESOURCE(Resource, &LockHandle);
  940. do {
  941. } while ((OwnerEntry = ExpFindCurrentThread(Resource,
  942. CurrentThread,
  943. &LockHandle)) == NULL);
  944. ASSERT(IsOwnedExclusive(Resource) == FALSE);
  945. ASSERT(Resource->ActiveCount > 0);
  946. ASSERT(OwnerEntry->OwnerThread != CurrentThread);
  947. OwnerEntry->OwnerThread = CurrentThread;
  948. OwnerEntry->OwnerCount = 1;
  949. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  950. return TRUE;
  951. } else {
  952. OwnerEntry = ExpFindCurrentThread(Resource, CurrentThread, &LockHandle);
  953. if (OwnerEntry == NULL) {
  954. goto retry;
  955. }
  956. if (OwnerEntry->OwnerThread == CurrentThread) {
  957. OwnerEntry->OwnerCount += 1;
  958. ASSERT(OwnerEntry->OwnerCount != 0);
  959. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  960. return TRUE;
  961. }
  962. //
  963. // Grant the current thread shared access to the resource.
  964. //
  965. OwnerEntry->OwnerThread = CurrentThread;
  966. OwnerEntry->OwnerCount = 1;
  967. Resource->ActiveCount += 1;
  968. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  969. return TRUE;
  970. }
  971. }
  972. //
  973. // The resource is owned exclusive by some other thread.
  974. //
  975. // If wait is not specified, then return that the resource was
  976. // not acquired.
  977. //
  978. if (Wait == FALSE) {
  979. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  980. return FALSE;
  981. }
  982. //
  983. // If the shared wait semaphore has not yet been allocated, then allocate
  984. // and initialize it.
  985. //
  986. if (Resource->SharedWaiters == NULL) {
  987. ExpAllocateSharedWaiterSemaphore(Resource, &LockHandle);
  988. goto retry;
  989. }
  990. //
  991. // Wait for shared access to the resource to be granted and increment
  992. // the recursion count.
  993. //
  994. OwnerEntry->OwnerThread = CurrentThread;
  995. OwnerEntry->OwnerCount = 1;
  996. Resource->NumberOfSharedWaiters += 1;
  997. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  998. ExpWaitForResource(Resource, Resource->SharedWaiters);
  999. return TRUE;
  1000. }
  1001. VOID
  1002. FASTCALL
  1003. ExReleaseResourceLite(
  1004. IN PERESOURCE Resource
  1005. )
  1006. /*++
  1007. Routine Description:
  1008. This routine releases the specified resource for the current thread
  1009. and decrements the recursion count. If the count reaches zero, then
  1010. the resource may also be released.
  1011. Arguments:
  1012. Resource - Supplies a pointer to the resource to release.
  1013. Return Value:
  1014. None.
  1015. --*/
  1016. {
  1017. ERESOURCE_THREAD CurrentThread;
  1018. ULONG Index;
  1019. ULONG Number;
  1020. EXP_LOCK_HANDLE LockHandle;
  1021. POWNER_ENTRY OwnerEntry, OwnerEnd;
  1022. CurrentThread = (ERESOURCE_THREAD)PsGetCurrentThread();
  1023. ASSERT_RESOURCE(Resource);
  1024. //
  1025. // Acquire exclusive access to the specified resource.
  1026. //
  1027. EXP_LOCK_RESOURCE(Resource, &LockHandle);
  1028. //
  1029. // Resource release must be protected from thread suspends.
  1030. //
  1031. EX_ENSURE_APCS_DISABLED (LockHandle.OldIrql,
  1032. Resource,
  1033. KeGetCurrentThread());
  1034. //
  1035. // If the resource is exclusively owned, then release exclusive
  1036. // ownership. Otherwise, release shared ownership.
  1037. //
  1038. // N.B. The two release paths are split since this is such a high
  1039. // frequency function.
  1040. //
  1041. if (IsOwnedExclusive(Resource)) {
  1042. #if DBG
  1043. //
  1044. // This can only be enabled in checked builds because this (unusual)
  1045. // behavior might have worked in earlier releases of NT. However,
  1046. // in the checked builds, this can be enabled because callers really
  1047. // should convert to using ExReleaseResourceForThreadLite instead.
  1048. //
  1049. if (Resource->OwnerThreads[0].OwnerThread != CurrentThread) {
  1050. KeBugCheckEx(RESOURCE_NOT_OWNED,
  1051. (ULONG_PTR)Resource,
  1052. (ULONG_PTR)CurrentThread,
  1053. (ULONG_PTR)Resource->OwnerTable,
  1054. 0x1);
  1055. }
  1056. #endif
  1057. //
  1058. // Decrement the recursion count and check if ownership can be
  1059. // released.
  1060. //
  1061. ASSERT(Resource->OwnerThreads[0].OwnerCount > 0);
  1062. if (--Resource->OwnerThreads[0].OwnerCount != 0) {
  1063. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  1064. return;
  1065. }
  1066. //
  1067. // Clear the owner thread.
  1068. //
  1069. Resource->OwnerThreads[0].OwnerThread = 0;
  1070. //
  1071. // The thread recursion count reached zero so decrement the resource
  1072. // active count. If the active count reaches zero, then the resource
  1073. // is no longer owned and an attempt should be made to grant access to
  1074. // another thread.
  1075. //
  1076. ASSERT(Resource->ActiveCount > 0);
  1077. if (--Resource->ActiveCount == 0) {
  1078. //
  1079. // If there are shared waiters, then grant shared access to the
  1080. // resource. Otherwise, grant exclusive ownership if there are
  1081. // exclusive waiters.
  1082. //
  1083. if (IsSharedWaiting(Resource)) {
  1084. Resource->Flag &= ~ResourceOwnedExclusive;
  1085. Number = Resource->NumberOfSharedWaiters;
  1086. Resource->ActiveCount = (SHORT)Number;
  1087. Resource->NumberOfSharedWaiters = 0;
  1088. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  1089. KeReleaseSemaphore(Resource->SharedWaiters, 0, Number, FALSE);
  1090. return;
  1091. } else if (IsExclusiveWaiting(Resource)) {
  1092. Resource->OwnerThreads[0].OwnerThread = 1;
  1093. Resource->OwnerThreads[0].OwnerCount = 1;
  1094. Resource->ActiveCount = 1;
  1095. Resource->NumberOfExclusiveWaiters -= 1;
  1096. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  1097. KeSetEventBoostPriority(Resource->ExclusiveWaiters,
  1098. (PRKTHREAD *)&Resource->OwnerThreads[0].OwnerThread);
  1099. return;
  1100. }
  1101. Resource->Flag &= ~ResourceOwnedExclusive;
  1102. }
  1103. } else {
  1104. if (Resource->OwnerThreads[1].OwnerThread == CurrentThread) {
  1105. OwnerEntry = &Resource->OwnerThreads[1];
  1106. } else if (Resource->OwnerThreads[0].OwnerThread == CurrentThread) {
  1107. OwnerEntry = &Resource->OwnerThreads[0];
  1108. } else {
  1109. Index = ((PKTHREAD)(CurrentThread))->ResourceIndex;
  1110. OwnerEntry = Resource->OwnerTable;
  1111. if (OwnerEntry == NULL) {
  1112. KeBugCheckEx(RESOURCE_NOT_OWNED,
  1113. (ULONG_PTR)Resource,
  1114. (ULONG_PTR)CurrentThread,
  1115. (ULONG_PTR)Resource->OwnerTable,
  1116. 0x2);
  1117. }
  1118. //
  1119. // If the resource hint is not within range or the resource
  1120. // table entry does match the current thread, then search
  1121. // the owner table for a match.
  1122. //
  1123. if ((Index >= OwnerEntry->TableSize) ||
  1124. (OwnerEntry[Index].OwnerThread != CurrentThread)) {
  1125. OwnerEnd = &OwnerEntry[OwnerEntry->TableSize];
  1126. while (1) {
  1127. OwnerEntry += 1;
  1128. if (OwnerEntry >= OwnerEnd) {
  1129. KeBugCheckEx(RESOURCE_NOT_OWNED,
  1130. (ULONG_PTR)Resource,
  1131. (ULONG_PTR)CurrentThread,
  1132. (ULONG_PTR)Resource->OwnerTable,
  1133. 0x3);
  1134. }
  1135. if (OwnerEntry->OwnerThread == CurrentThread) {
  1136. break;
  1137. }
  1138. }
  1139. } else {
  1140. OwnerEntry = &OwnerEntry[Index];
  1141. }
  1142. }
  1143. //
  1144. // Decrement the recursion count and check if ownership can be
  1145. // released.
  1146. //
  1147. ASSERT(OwnerEntry->OwnerThread == CurrentThread);
  1148. ASSERT(OwnerEntry->OwnerCount > 0);
  1149. if (--OwnerEntry->OwnerCount != 0) {
  1150. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  1151. return;
  1152. }
  1153. //
  1154. // Clear the owner thread.
  1155. //
  1156. OwnerEntry->OwnerThread = 0;
  1157. //
  1158. // The thread recursion count reached zero so decrement the resource
  1159. // active count. If the active count reaches zero, then the resource
  1160. // is no longer owned and an attempt should be made to grant access to
  1161. // another thread.
  1162. //
  1163. ASSERT(Resource->ActiveCount > 0);
  1164. if (--Resource->ActiveCount == 0) {
  1165. //
  1166. // If there are exclusive waiters, then grant exclusive access
  1167. // to the resource.
  1168. //
  1169. if (IsExclusiveWaiting(Resource)) {
  1170. Resource->Flag |= ResourceOwnedExclusive;
  1171. Resource->OwnerThreads[0].OwnerThread = 1;
  1172. Resource->OwnerThreads[0].OwnerCount = 1;
  1173. Resource->ActiveCount = 1;
  1174. Resource->NumberOfExclusiveWaiters -= 1;
  1175. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  1176. KeSetEventBoostPriority(Resource->ExclusiveWaiters,
  1177. (PRKTHREAD *)&Resource->OwnerThreads[0].OwnerThread);
  1178. return;
  1179. }
  1180. }
  1181. }
  1182. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  1183. return;
  1184. }
  1185. VOID
  1186. ExReleaseResourceForThreadLite(
  1187. IN PERESOURCE Resource,
  1188. IN ERESOURCE_THREAD CurrentThread
  1189. )
  1190. /*++
  1191. Routine Description:
  1192. This routine release the specified resource for the specified thread
  1193. and decrements the recursion count. If the count reaches zero, then
  1194. the resource may also be released.
  1195. Arguments:
  1196. Resource - Supplies a pointer to the resource to release.
  1197. Thread - Supplies the thread that originally acquired the resource.
  1198. Return Value:
  1199. None.
  1200. --*/
  1201. {
  1202. ULONG Index;
  1203. ULONG Number;
  1204. EXP_LOCK_HANDLE LockHandle;
  1205. POWNER_ENTRY OwnerEntry, OwnerEnd;
  1206. ASSERT(CurrentThread != 0);
  1207. ASSERT_RESOURCE(Resource);
  1208. //
  1209. // Acquire exclusive access to the specified resource.
  1210. //
  1211. EXP_LOCK_RESOURCE(Resource, &LockHandle);
  1212. //
  1213. // Resource release must be protected from thread suspends.
  1214. //
  1215. EX_ENSURE_APCS_DISABLED (LockHandle.OldIrql,
  1216. Resource,
  1217. KeGetCurrentThread());
  1218. //
  1219. // If the resource is exclusively owned, then release exclusive
  1220. // ownership. Otherwise, release shared ownership.
  1221. //
  1222. // N.B. The two release paths are split since this is such a high
  1223. // frequency function.
  1224. //
  1225. if (IsOwnedExclusive(Resource)) {
  1226. ASSERT(Resource->OwnerThreads[0].OwnerThread == CurrentThread);
  1227. //
  1228. // Decrement the recursion count and check if ownership can be
  1229. // released.
  1230. //
  1231. ASSERT(Resource->OwnerThreads[0].OwnerCount > 0);
  1232. if (--Resource->OwnerThreads[0].OwnerCount != 0) {
  1233. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  1234. return;
  1235. }
  1236. //
  1237. // Clear the owner thread.
  1238. //
  1239. Resource->OwnerThreads[0].OwnerThread = 0;
  1240. //
  1241. // The thread recursion count reached zero so decrement the resource
  1242. // active count. If the active count reaches zero, then the resource
  1243. // is no longer owned and an attempt should be made to grant access to
  1244. // another thread.
  1245. //
  1246. ASSERT(Resource->ActiveCount > 0);
  1247. if (--Resource->ActiveCount == 0) {
  1248. //
  1249. // If there are shared waiters, then grant shared access to the
  1250. // resource. Otherwise, grant exclusive ownership if there are
  1251. // exclusive waiters.
  1252. //
  1253. if (IsSharedWaiting(Resource)) {
  1254. Resource->Flag &= ~ResourceOwnedExclusive;
  1255. Number = Resource->NumberOfSharedWaiters;
  1256. Resource->ActiveCount = (SHORT)Number;
  1257. Resource->NumberOfSharedWaiters = 0;
  1258. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  1259. KeReleaseSemaphore(Resource->SharedWaiters, 0, Number, FALSE);
  1260. return;
  1261. } else if (IsExclusiveWaiting(Resource)) {
  1262. Resource->OwnerThreads[0].OwnerThread = 1;
  1263. Resource->OwnerThreads[0].OwnerCount = 1;
  1264. Resource->ActiveCount = 1;
  1265. Resource->NumberOfExclusiveWaiters -= 1;
  1266. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  1267. KeSetEventBoostPriority(Resource->ExclusiveWaiters,
  1268. (PRKTHREAD *)&Resource->OwnerThreads[0].OwnerThread);
  1269. return;
  1270. }
  1271. Resource->Flag &= ~ResourceOwnedExclusive;
  1272. }
  1273. } else {
  1274. if (Resource->OwnerThreads[1].OwnerThread == CurrentThread) {
  1275. OwnerEntry = &Resource->OwnerThreads[1];
  1276. } else if (Resource->OwnerThreads[0].OwnerThread == CurrentThread) {
  1277. OwnerEntry = &Resource->OwnerThreads[0];
  1278. } else {
  1279. //
  1280. // If the specified current thread is an owner address (low
  1281. // bits are nonzero), then set the hint index to the first
  1282. // entry. Otherwise, set the hint index from the owner thread.
  1283. //
  1284. Index = 1;
  1285. if (((ULONG)CurrentThread & 3) == 0) {
  1286. Index = ((PKTHREAD)(CurrentThread))->ResourceIndex;
  1287. }
  1288. OwnerEntry = Resource->OwnerTable;
  1289. ASSERT(OwnerEntry != NULL);
  1290. //
  1291. // If the resource hint is not within range or the resource
  1292. // table entry does match the current thread, then search
  1293. // the owner table for a match.
  1294. //
  1295. if ((Index >= OwnerEntry->TableSize) ||
  1296. (OwnerEntry[Index].OwnerThread != CurrentThread)) {
  1297. OwnerEnd = &OwnerEntry[OwnerEntry->TableSize];
  1298. while (1) {
  1299. OwnerEntry += 1;
  1300. if (OwnerEntry >= OwnerEnd) {
  1301. KeBugCheckEx(RESOURCE_NOT_OWNED,
  1302. (ULONG_PTR)Resource,
  1303. (ULONG_PTR)CurrentThread,
  1304. (ULONG_PTR)Resource->OwnerTable,
  1305. 0x3);
  1306. }
  1307. if (OwnerEntry->OwnerThread == CurrentThread) {
  1308. break;
  1309. }
  1310. }
  1311. } else {
  1312. OwnerEntry = &OwnerEntry[Index];
  1313. }
  1314. }
  1315. //
  1316. // Decrement the recursion count and check if ownership can be
  1317. // released.
  1318. //
  1319. ASSERT(OwnerEntry->OwnerThread == CurrentThread);
  1320. ASSERT(OwnerEntry->OwnerCount > 0);
  1321. if (--OwnerEntry->OwnerCount != 0) {
  1322. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  1323. return;
  1324. }
  1325. //
  1326. // Clear the owner thread.
  1327. //
  1328. OwnerEntry->OwnerThread = 0;
  1329. //
  1330. // The thread recursion count reached zero so decrement the resource
  1331. // active count. If the active count reaches zero, then the resource
  1332. // is no longer owned and an attempt should be made to grant access to
  1333. // another thread.
  1334. //
  1335. ASSERT(Resource->ActiveCount > 0);
  1336. if (--Resource->ActiveCount == 0) {
  1337. //
  1338. // If there are exclusive waiters, then grant exclusive access
  1339. // to the resource.
  1340. //
  1341. if (IsExclusiveWaiting(Resource)) {
  1342. Resource->Flag |= ResourceOwnedExclusive;
  1343. Resource->OwnerThreads[0].OwnerThread = 1;
  1344. Resource->OwnerThreads[0].OwnerCount = 1;
  1345. Resource->ActiveCount = 1;
  1346. Resource->NumberOfExclusiveWaiters -= 1;
  1347. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  1348. KeSetEventBoostPriority(Resource->ExclusiveWaiters,
  1349. (PRKTHREAD *)&Resource->OwnerThreads[0].OwnerThread);
  1350. return;
  1351. }
  1352. }
  1353. }
  1354. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  1355. return;
  1356. }
  1357. VOID
  1358. ExSetResourceOwnerPointer(
  1359. IN PERESOURCE Resource,
  1360. IN PVOID OwnerPointer
  1361. )
  1362. /*++
  1363. Routine Description:
  1364. This routine locates the owner entry for the current thread and stores
  1365. the specified owner address as the owner thread. Subsequent to calling
  1366. this routine, the only routine which may be called for this resource is
  1367. ExReleaseResourceForThreadLite, supplying the owner address as the "thread".
  1368. Owner addresses must obey the following rules:
  1369. They must be a unique pointer to a structure allocated in system space,
  1370. and they must point to a structure which remains allocated until after
  1371. the call to ExReleaseResourceForThreadLite. This is to eliminate aliasing
  1372. with a thread or other owner address.
  1373. The low order two bits of the owner address must be set by the caller,
  1374. so that other routines in the resource package can distinguish owner
  1375. address from thread addresses.
  1376. Arguments:
  1377. Resource - Supplies a pointer to the resource to release.
  1378. OwnerPointer - Supplies a pointer to an allocated structure with the low
  1379. order two bits set.
  1380. Return Value:
  1381. None.
  1382. --*/
  1383. {
  1384. ERESOURCE_THREAD CurrentThread;
  1385. EXP_LOCK_HANDLE LockHandle;
  1386. POWNER_ENTRY OwnerEntry, ExistingOwnerEntry;
  1387. ASSERT((OwnerPointer != 0) && (((ULONG_PTR)OwnerPointer & 3) == 3));
  1388. CurrentThread = (ERESOURCE_THREAD)PsGetCurrentThread();
  1389. ASSERT_RESOURCE(Resource);
  1390. //
  1391. // Acquire exclusive access to the specified resource.
  1392. //
  1393. EXP_LOCK_RESOURCE(Resource, &LockHandle);
  1394. //
  1395. // If the resource is exclusively owned, then it is the first owner entry.
  1396. //
  1397. if (IsOwnedExclusive(Resource)) {
  1398. ASSERT(Resource->OwnerThreads[0].OwnerThread == CurrentThread);
  1399. //
  1400. // Set the owner address.
  1401. //
  1402. ASSERT(Resource->OwnerThreads[0].OwnerCount > 0);
  1403. Resource->OwnerThreads[0].OwnerThread = (ULONG_PTR)OwnerPointer;
  1404. //
  1405. // For shared access we have to search for the current thread to set
  1406. // the owner address.
  1407. //
  1408. } else {
  1409. ExistingOwnerEntry = ExpFindCurrentThread(Resource, (ERESOURCE_THREAD)OwnerPointer, NULL);
  1410. OwnerEntry = ExpFindCurrentThread(Resource, CurrentThread, NULL);
  1411. if (OwnerEntry == NULL) {
  1412. KeBugCheckEx(RESOURCE_NOT_OWNED,
  1413. (ULONG_PTR)Resource,
  1414. (ULONG_PTR)CurrentThread,
  1415. (ULONG_PTR)Resource->OwnerTable,
  1416. 0x3);
  1417. }
  1418. if (ExistingOwnerEntry != NULL) {
  1419. ExistingOwnerEntry->OwnerCount += OwnerEntry->OwnerCount;
  1420. OwnerEntry->OwnerCount = 0;
  1421. OwnerEntry->OwnerThread = 0;
  1422. ASSERT (Resource->ActiveCount >= 2);
  1423. Resource->ActiveCount -= 1;
  1424. } else {
  1425. OwnerEntry->OwnerThread = (ERESOURCE_THREAD)OwnerPointer;
  1426. }
  1427. }
  1428. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  1429. return;
  1430. }
  1431. VOID
  1432. ExConvertExclusiveToSharedLite(
  1433. IN PERESOURCE Resource
  1434. )
  1435. /*++
  1436. Routine Description:
  1437. This routine converts the specified resource from acquired for exclusive
  1438. access to acquired for shared access.
  1439. Arguments:
  1440. Resource - Supplies a pointer to the resource to acquire for shared access. it
  1441. Return Value:
  1442. None.
  1443. --*/
  1444. {
  1445. ULONG Number;
  1446. EXP_LOCK_HANDLE LockHandle;
  1447. ASSERT(KeIsExecutingDpc() == FALSE);
  1448. ASSERT_RESOURCE(Resource);
  1449. ASSERT(IsOwnedExclusive(Resource));
  1450. ASSERT(Resource->OwnerThreads[0].OwnerThread == (ERESOURCE_THREAD)PsGetCurrentThread());
  1451. //
  1452. // Acquire exclusive access to the specified resource.
  1453. //
  1454. EXP_LOCK_RESOURCE(Resource, &LockHandle);
  1455. //
  1456. // Convert the granted access from exclusive to shared.
  1457. //
  1458. Resource->Flag &= ~ResourceOwnedExclusive;
  1459. //
  1460. // If there are any shared waiters, then grant them shared access.
  1461. //
  1462. if (IsSharedWaiting(Resource)) {
  1463. Number = Resource->NumberOfSharedWaiters;
  1464. Resource->ActiveCount = (SHORT)(Resource->ActiveCount + Number);
  1465. Resource->NumberOfSharedWaiters = 0;
  1466. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  1467. KeReleaseSemaphore(Resource->SharedWaiters, 0, Number, FALSE);
  1468. return;
  1469. }
  1470. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  1471. return;
  1472. }
  1473. NTSTATUS
  1474. ExDeleteResourceLite(
  1475. IN PERESOURCE Resource
  1476. )
  1477. /*++
  1478. Routine Description:
  1479. This routine deallocates any pool allocated to support the specified
  1480. resource.
  1481. Arguments:
  1482. Resource - Supplies a pointer to the resource whose allocated pool
  1483. is freed.
  1484. Return Value:
  1485. STATUS_SUCCESS.
  1486. --*/
  1487. {
  1488. #if defined(_COLLECT_RESOURCE_DATA_)
  1489. ULONG Hash;
  1490. PLIST_ENTRY NextEntry;
  1491. PRESOURCE_HASH_ENTRY MatchEntry;
  1492. PRESOURCE_HASH_ENTRY HashEntry;
  1493. #endif
  1494. KLOCK_QUEUE_HANDLE LockHandle;
  1495. ASSERT(IsSharedWaiting(Resource) == FALSE);
  1496. ASSERT(IsExclusiveWaiting(Resource) == FALSE);
  1497. ASSERT(KeIsExecutingDpc() == FALSE);
  1498. ASSERT_RESOURCE(Resource);
  1499. //
  1500. // Acquire the executive resource spinlock and remove the resource from
  1501. // the system resource list.
  1502. //
  1503. KeAcquireInStackQueuedSpinLock (&ExpResourceSpinLock, &LockHandle);
  1504. RemoveEntryList(&Resource->SystemResourcesList);
  1505. #if defined(_COLLECT_RESOURCE_DATA_)
  1506. //
  1507. // Lookup resource initialization address in resource hash table. If
  1508. // the address does not exist in the table, then create a new entry.
  1509. //
  1510. Hash = (ULONG)Resource->Address;
  1511. Hash = ((Hash >> 24) ^ (Hash >> 16) ^ (Hash >> 8) ^ (Hash)) & (RESOURCE_HASH_TABLE_SIZE - 1);
  1512. MatchEntry = NULL;
  1513. NextEntry = ExpResourcePerformanceData.HashTable[Hash].Flink;
  1514. while (NextEntry != &ExpResourcePerformanceData.HashTable[Hash]) {
  1515. HashEntry = CONTAINING_RECORD(NextEntry,
  1516. RESOURCE_HASH_ENTRY,
  1517. ListEntry);
  1518. if (HashEntry->Address == Resource->Address) {
  1519. MatchEntry = HashEntry;
  1520. break;
  1521. }
  1522. NextEntry = NextEntry->Flink;
  1523. }
  1524. //
  1525. // If a matching initialization address was found, then update the call
  1526. // site statistics. Otherwise, allocate a new hash entry and initialize
  1527. // call site statistics.
  1528. //
  1529. if (MatchEntry != NULL) {
  1530. MatchEntry->ContentionCount += Resource->ContentionCount;
  1531. MatchEntry->Number += 1;
  1532. } else {
  1533. MatchEntry = ExAllocatePoolWithTag(NonPagedPool,
  1534. sizeof(RESOURCE_HASH_ENTRY),
  1535. 'vEpR');
  1536. if (MatchEntry != NULL) {
  1537. MatchEntry->Address = Resource->Address;
  1538. MatchEntry->ContentionCount = Resource->ContentionCount;
  1539. MatchEntry->Number = 1;
  1540. InsertTailList(&ExpResourcePerformanceData.HashTable[Hash],
  1541. &MatchEntry->ListEntry);
  1542. }
  1543. }
  1544. ExpResourcePerformanceData.ActiveResourceCount -= 1;
  1545. #endif
  1546. KeReleaseInStackQueuedSpinLock (&LockHandle);
  1547. //
  1548. // If an owner table was allocated, then free it to pool.
  1549. //
  1550. if (Resource->OwnerTable != NULL) {
  1551. ExFreePool(Resource->OwnerTable);
  1552. }
  1553. //
  1554. // If a semaphore was allocated, then free it to pool.
  1555. //
  1556. if (Resource->SharedWaiters) {
  1557. ExFreePool(Resource->SharedWaiters);
  1558. }
  1559. //
  1560. // If an event was allocated, then free it to pool.
  1561. //
  1562. if (Resource->ExclusiveWaiters) {
  1563. ExFreePool(Resource->ExclusiveWaiters);
  1564. }
  1565. return STATUS_SUCCESS;
  1566. }
  1567. ULONG
  1568. ExGetExclusiveWaiterCount(
  1569. IN PERESOURCE Resource
  1570. )
  1571. /*++
  1572. Routine Description:
  1573. This routine returns the exclusive waiter count.
  1574. Arguments:
  1575. Resource - Supplies a pointer to and executive resource.
  1576. Return Value:
  1577. The current number of exclusive waiters is returned as the function
  1578. value.
  1579. --*/
  1580. {
  1581. return Resource->NumberOfExclusiveWaiters;
  1582. }
  1583. ULONG
  1584. ExGetSharedWaiterCount(
  1585. IN PERESOURCE Resource
  1586. )
  1587. /*++
  1588. Routine Description:
  1589. This routine returns the shared waiter count.
  1590. Arguments:
  1591. Resource - Supplies a pointer to and executive resource.
  1592. Return Value:
  1593. The current number of shared waiters is returned as the function
  1594. value.
  1595. --*/
  1596. {
  1597. return Resource->NumberOfSharedWaiters;
  1598. }
  1599. BOOLEAN
  1600. ExIsResourceAcquiredExclusiveLite(
  1601. IN PERESOURCE Resource
  1602. )
  1603. /*++
  1604. Routine Description:
  1605. This routine determines if a resource is acquired exclusive by the
  1606. calling thread.
  1607. Arguments:
  1608. Resource - Supplies a pointer the resource to query.
  1609. Return Value:
  1610. If the current thread has acquired the resource exclusive, a value of
  1611. TRUE is returned. Otherwise, a value of FALSE is returned.
  1612. --*/
  1613. {
  1614. ERESOURCE_THREAD CurrentThread;
  1615. BOOLEAN Result;
  1616. CurrentThread = (ERESOURCE_THREAD)PsGetCurrentThread();
  1617. ASSERT_RESOURCE(Resource);
  1618. //
  1619. // If the resource is owned exclusive and the current thread is the
  1620. // owner, then set the return value of TRUE. Otherwise, set the return
  1621. // value to FALSE.
  1622. //
  1623. Result = FALSE;
  1624. if ((IsOwnedExclusive(Resource)) &&
  1625. (Resource->OwnerThreads[0].OwnerThread == CurrentThread)) {
  1626. Result = TRUE;
  1627. }
  1628. return Result;
  1629. }
  1630. ULONG
  1631. ExIsResourceAcquiredSharedLite(
  1632. IN PERESOURCE Resource
  1633. )
  1634. /*++
  1635. Routine Description:
  1636. This routine determines if a resource is acquired either shared or
  1637. exclusive by the calling thread.
  1638. Arguments:
  1639. Resource - Supplies a pointer to the resource to query.
  1640. Return Value:
  1641. If the current thread has not acquired the resource a value of zero
  1642. is returned. Otherwise, the thread's acquire count is returned.
  1643. --*/
  1644. {
  1645. ERESOURCE_THREAD CurrentThread;
  1646. ULONG Index;
  1647. ULONG Number;
  1648. POWNER_ENTRY OwnerEntry;
  1649. ULONG Result;
  1650. EXP_LOCK_HANDLE LockHandle;
  1651. ASSERT_RESOURCE(Resource);
  1652. //
  1653. // If nobody owns this resource then exit early.
  1654. //
  1655. if (Resource->ActiveCount == 0) {
  1656. return 0;
  1657. }
  1658. CurrentThread = (ERESOURCE_THREAD)PsGetCurrentThread();
  1659. //
  1660. // Find the current thread in the thread array and return the count.
  1661. //
  1662. // N.B. If the thread is not found a value of zero will be returned.
  1663. //
  1664. if (Resource->OwnerThreads[0].OwnerThread == CurrentThread) {
  1665. Result = Resource->OwnerThreads[0].OwnerCount;
  1666. } else {
  1667. //
  1668. // If we are not in the first slot and the resource is owned exclusive
  1669. // then we can't own it at all
  1670. //
  1671. if (IsOwnedExclusive(Resource)) {
  1672. return 0;
  1673. }
  1674. //
  1675. // Check slot 2 and other slots in the owner table
  1676. //
  1677. if (Resource->OwnerThreads[1].OwnerThread == CurrentThread) {
  1678. Result = Resource->OwnerThreads[1].OwnerCount;
  1679. } else {
  1680. Result = 0;
  1681. Index = ((PKTHREAD)(CurrentThread))->ResourceIndex;
  1682. EXP_LOCK_RESOURCE(Resource, &LockHandle);
  1683. //
  1684. // If the resource hint is not within range or the resource table
  1685. // entry does not match the current thread, then search the owner
  1686. // table for a match.
  1687. //
  1688. OwnerEntry = Resource->OwnerTable;
  1689. if (OwnerEntry != NULL) {
  1690. Number = OwnerEntry->TableSize;
  1691. if ((Index >= Number) ||
  1692. (OwnerEntry[Index].OwnerThread != CurrentThread)) {
  1693. for (Index = 1; Index < Number; Index += 1) {
  1694. OwnerEntry += 1;
  1695. if (OwnerEntry->OwnerThread == CurrentThread) {
  1696. Result = OwnerEntry->OwnerCount;
  1697. break;
  1698. }
  1699. }
  1700. } else {
  1701. Result = OwnerEntry[Index].OwnerCount;
  1702. }
  1703. }
  1704. //
  1705. // Release exclusive access to the specified resource.
  1706. //
  1707. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  1708. }
  1709. }
  1710. return Result;
  1711. }
  1712. NTSTATUS
  1713. ExQuerySystemLockInformation(
  1714. OUT PRTL_PROCESS_LOCKS LockInformation,
  1715. IN ULONG LockInformationLength,
  1716. OUT PULONG ReturnLength OPTIONAL
  1717. )
  1718. {
  1719. NTSTATUS Status;
  1720. KLOCK_QUEUE_HANDLE LockHandle;
  1721. ULONG RequiredLength;
  1722. PLIST_ENTRY Head, Next;
  1723. PRTL_PROCESS_LOCK_INFORMATION LockInfo;
  1724. PERESOURCE Resource;
  1725. PETHREAD OwningThread;
  1726. RequiredLength = FIELD_OFFSET(RTL_PROCESS_LOCKS, Locks);
  1727. if (LockInformationLength < RequiredLength) {
  1728. Status = STATUS_INFO_LENGTH_MISMATCH;
  1729. } else {
  1730. Status = STATUS_SUCCESS;
  1731. LockInformation->NumberOfLocks = 0;
  1732. LockInfo = &LockInformation->Locks[0];
  1733. Head = &ExpSystemResourcesList;
  1734. KeAcquireInStackQueuedSpinLock(&ExpResourceSpinLock, &LockHandle);
  1735. Next = Head->Flink;
  1736. while (Next != Head) {
  1737. Resource = CONTAINING_RECORD(Next,
  1738. ERESOURCE,
  1739. SystemResourcesList);
  1740. LockInformation->NumberOfLocks += 1;
  1741. RequiredLength += sizeof(RTL_PROCESS_LOCK_INFORMATION);
  1742. if (LockInformationLength < RequiredLength) {
  1743. Status = STATUS_INFO_LENGTH_MISMATCH;
  1744. } else {
  1745. LockInfo->Address = Resource;
  1746. LockInfo->Type = RTL_RESOURCE_TYPE;
  1747. LockInfo->CreatorBackTraceIndex = 0;
  1748. #if i386 && !FPO
  1749. LockInfo->CreatorBackTraceIndex = (USHORT)Resource->CreatorBackTraceIndex;
  1750. #endif // i386 && !FPO
  1751. if ((Resource->OwnerThreads[0].OwnerThread != 0) &&
  1752. ((Resource->OwnerThreads[0].OwnerThread & 3) == 0)) {
  1753. OwningThread = (PETHREAD)(Resource->OwnerThreads[0].OwnerThread);
  1754. LockInfo->OwningThread = OwningThread->Cid.UniqueThread;
  1755. } else {
  1756. LockInfo->OwningThread = 0;
  1757. }
  1758. LockInfo->LockCount = Resource->ActiveCount;
  1759. LockInfo->ContentionCount = Resource->ContentionCount;
  1760. LockInfo->NumberOfWaitingShared = Resource->NumberOfSharedWaiters;
  1761. LockInfo->NumberOfWaitingExclusive = Resource->NumberOfExclusiveWaiters;
  1762. LockInfo += 1;
  1763. }
  1764. Next = Next->Flink;
  1765. }
  1766. KeReleaseInStackQueuedSpinLock(&LockHandle);
  1767. }
  1768. if (ARGUMENT_PRESENT(ReturnLength)) {
  1769. *ReturnLength = RequiredLength;
  1770. }
  1771. return Status;
  1772. }
  1773. VOID
  1774. FASTCALL
  1775. ExpBoostOwnerThread (
  1776. IN PKTHREAD CurrentThread,
  1777. IN PKTHREAD OwnerThread
  1778. )
  1779. /*++
  1780. Routine Description:
  1781. This function boots the priority of the specified owner thread iff
  1782. its priority is less than that of the current thread and is also
  1783. less than fourteen.
  1784. N.B. this function is called with the dispatcher database lock held.
  1785. Arguments:
  1786. CurrentThread - Supplies a pointer to the current thread object.
  1787. OwnerThread - Supplies a pointer to the owner thread object.
  1788. Return Value:
  1789. None.
  1790. --*/
  1791. {
  1792. //
  1793. // If the owner thread is lower priority than the current thread, the
  1794. // current thread is running at a priority less than 14, then boost the
  1795. // priority of the owner thread for a quantum.
  1796. //
  1797. // N.B. A thread that has already been boosted may be reboosted to allow
  1798. // it to execute and release resources. When the boost is removed,
  1799. // the thread will return to its priority before any boosting.
  1800. //
  1801. if (((ULONG_PTR)OwnerThread & 0x3) == 0) {
  1802. if ((OwnerThread->Priority < CurrentThread->Priority) &&
  1803. (OwnerThread->Priority < 14)) {
  1804. KiAcquireThreadLock(OwnerThread);
  1805. OwnerThread->PriorityDecrement += 14 - OwnerThread->Priority;
  1806. OwnerThread->Quantum = OwnerThread->Process->ThreadQuantum;
  1807. KiSetPriorityThread(OwnerThread, 14);
  1808. KiReleaseThreadLock(OwnerThread);
  1809. }
  1810. }
  1811. return;
  1812. }
  1813. VOID
  1814. FASTCALL
  1815. ExpWaitForResource (
  1816. IN PERESOURCE Resource,
  1817. IN PVOID Object
  1818. )
  1819. /*++
  1820. Routine Description:
  1821. The routine waits for the specified resource object to be set. If the
  1822. wait is too long the priority of the current owners of the resource
  1823. are boosted.
  1824. Arguments:
  1825. Resource - Supplies a pointer to the resource to wait for.
  1826. Object - Supplies a pointer to an event (exclusive) or semaphore
  1827. (shared) to wait for.
  1828. Return Value:
  1829. None.
  1830. --*/
  1831. {
  1832. ULONG Index;
  1833. ULONG Limit;
  1834. ULONG Number;
  1835. POWNER_ENTRY OwnerEntry;
  1836. PKTHREAD OwnerThread;
  1837. NTSTATUS Status;
  1838. PKTHREAD CurrentThread;
  1839. LARGE_INTEGER Timeout;
  1840. #if DBG
  1841. EXP_LOCK_HANDLE LockHandle;
  1842. #endif
  1843. //
  1844. // Increment the contention count for the resource, set the initial
  1845. // timeout value, and wait for the specified object to be signalled
  1846. // or a timeout to occur.
  1847. //
  1848. Limit = 0;
  1849. Resource->ContentionCount += 1;
  1850. Timeout.QuadPart = 500 * -10000;
  1851. do {
  1852. Status = KeWaitForSingleObject (
  1853. Object,
  1854. WrResource,
  1855. KernelMode,
  1856. FALSE,
  1857. &Timeout );
  1858. if (Status != STATUS_TIMEOUT) {
  1859. break;
  1860. }
  1861. //
  1862. // The limit has been exceeded, then output status information.
  1863. //
  1864. Limit += 1;
  1865. Timeout = ExpTimeout;
  1866. if (Limit > ExResourceTimeoutCount) {
  1867. Limit = 0;
  1868. #if DBG
  1869. //
  1870. // Output information for the specified resource.
  1871. //
  1872. EXP_LOCK_RESOURCE(Resource, &LockHandle);
  1873. DbgPrint("Resource @ %p\n", Resource);
  1874. DbgPrint(" ActiveCount = %04lx Flags = %s%s%s\n",
  1875. Resource->ActiveCount,
  1876. IsOwnedExclusive(Resource) ? "IsOwnedExclusive " : "",
  1877. IsSharedWaiting(Resource) ? "SharedWaiter " : "",
  1878. IsExclusiveWaiting(Resource) ? "ExclusiveWaiter " : "");
  1879. DbgPrint(" NumberOfExclusiveWaiters = %04lx\n", Resource->NumberOfExclusiveWaiters);
  1880. DbgPrint(" Thread = %p, Count = %02x\n",
  1881. Resource->OwnerThreads[0].OwnerThread,
  1882. Resource->OwnerThreads[0].OwnerCount);
  1883. DbgPrint(" Thread = %p, Count = %02x\n",
  1884. Resource->OwnerThreads[1].OwnerThread,
  1885. Resource->OwnerThreads[1].OwnerCount);
  1886. OwnerEntry = Resource->OwnerTable;
  1887. if (OwnerEntry != NULL) {
  1888. Number = OwnerEntry->TableSize;
  1889. for(Index = 1; Index < Number; Index += 1) {
  1890. OwnerEntry += 1;
  1891. DbgPrint(" Thread = %p, Count = %02x\n",
  1892. OwnerEntry->OwnerThread,
  1893. OwnerEntry->OwnerCount);
  1894. }
  1895. }
  1896. DbgBreakPoint();
  1897. DbgPrint("EX - Rewaiting\n");
  1898. EXP_UNLOCK_RESOURCE(Resource, &LockHandle);
  1899. #endif
  1900. }
  1901. //
  1902. // If priority boosts are allowed, then attempt to boost the priority
  1903. // of owner threads.
  1904. //
  1905. if (IsBoostAllowed(Resource)) {
  1906. //
  1907. // Get the current thread address, lock the dispatcher database,
  1908. // and set wait next in the current thread so the dispatcher
  1909. // database lock does not need to be released before waiting
  1910. // for the resource.
  1911. //
  1912. // N.B. Since the dispatcher database lock instead of the resource
  1913. // lock is being used to synchronize access to the resource,
  1914. // it is possible for the information being read from the
  1915. // resource to be stale. However, the important thing that
  1916. // cannot change is a valid thread address. Thus a thread
  1917. // could possibly get boosted that actually has dropped its
  1918. // access to the resource, but it guaranteed that the thread
  1919. // cannot be terminated or otherwise deleted.
  1920. //
  1921. // N.B. The dispatcher lock is released by the wait at the top of
  1922. // loop.
  1923. //
  1924. CurrentThread = KeGetCurrentThread();
  1925. KiLockDispatcherDatabase(&CurrentThread->WaitIrql);
  1926. CurrentThread->WaitNext = TRUE;
  1927. //
  1928. // Attempt to boost the one owner that can be shared or exclusive.
  1929. //
  1930. OwnerThread = (PKTHREAD)Resource->OwnerThreads[0].OwnerThread;
  1931. if (OwnerThread != NULL) {
  1932. ExpBoostOwnerThread(CurrentThread, OwnerThread);
  1933. }
  1934. //
  1935. // If the specified resource is not owned exclusive, then attempt
  1936. // to boost all the owning shared threads priority.
  1937. //
  1938. if (!IsOwnedExclusive(Resource)) {
  1939. OwnerThread = (PKTHREAD)Resource->OwnerThreads[1].OwnerThread;
  1940. if (OwnerThread != NULL) {
  1941. ExpBoostOwnerThread(CurrentThread, OwnerThread);
  1942. }
  1943. OwnerEntry = Resource->OwnerTable;
  1944. if (OwnerEntry != NULL) {
  1945. Number = OwnerEntry->TableSize;
  1946. for(Index = 1; Index < Number; Index += 1) {
  1947. OwnerEntry += 1;
  1948. OwnerThread = (PKTHREAD)OwnerEntry->OwnerThread;
  1949. if (OwnerThread != NULL) {
  1950. ExpBoostOwnerThread(CurrentThread, OwnerThread);
  1951. }
  1952. }
  1953. }
  1954. }
  1955. }
  1956. } while (TRUE);
  1957. return;
  1958. }
  1959. POWNER_ENTRY
  1960. FASTCALL
  1961. ExpFindCurrentThread(
  1962. IN PERESOURCE Resource,
  1963. IN ERESOURCE_THREAD CurrentThread,
  1964. IN PEXP_LOCK_HANDLE LockHandle OPTIONAL
  1965. )
  1966. /*++
  1967. Routine Description:
  1968. This function searches for the specified thread in the resource
  1969. thread array. If the thread is located, then a pointer to the
  1970. array entry is returned as the function value. Otherwise, a pointer
  1971. to a free entry is returned.
  1972. N.B. This routine is entered with the resource lock held and returns
  1973. with the resource lock held. If the resource lock is released
  1974. to expand the owner table, then the return value will be NULL.
  1975. This is a signal to the caller that the complete operation must
  1976. be repeated. This is done to avoid holding the resource lock
  1977. while memory is allocated and freed.
  1978. Arguments:
  1979. Resource - Supplies a pointer to the resource for which the search
  1980. is performed.
  1981. CurrentThread - Supplies the identification of the thread to search
  1982. for.
  1983. LockHandle - Supplies a pointer to a lock handle. If NULL, then the
  1984. caller just wants to know if the requested thread is an owner of
  1985. this resource. No free entry index is returned and no table
  1986. expansion is performed. Instead NULL is returned if the requested
  1987. thread cannot be found in the table.
  1988. Return Value:
  1989. A pointer to an owner entry is returned or NULL if one could not be
  1990. allocated.
  1991. --*/
  1992. {
  1993. POWNER_ENTRY FreeEntry;
  1994. ULONG NewSize;
  1995. ULONG OldSize;
  1996. POWNER_ENTRY OldTable;
  1997. POWNER_ENTRY OwnerEntry;
  1998. POWNER_ENTRY OwnerBound;
  1999. POWNER_ENTRY OwnerTable;
  2000. KIRQL OldIrql;
  2001. //
  2002. // Search the owner threads for the specified thread and return either
  2003. // a pointer to the found thread or a pointer to a free thread table
  2004. // entry.
  2005. //
  2006. if (Resource->OwnerThreads[0].OwnerThread == CurrentThread) {
  2007. return &Resource->OwnerThreads[0];
  2008. } else if (Resource->OwnerThreads[1].OwnerThread == CurrentThread) {
  2009. return &Resource->OwnerThreads[1];
  2010. } else {
  2011. FreeEntry = NULL;
  2012. if (Resource->OwnerThreads[1].OwnerThread == 0) {
  2013. FreeEntry = &Resource->OwnerThreads[1];
  2014. }
  2015. OwnerEntry = Resource->OwnerTable;
  2016. if (OwnerEntry == NULL) {
  2017. OldSize = 0;
  2018. } else {
  2019. OldSize = OwnerEntry->TableSize;
  2020. OwnerBound = &OwnerEntry[OldSize];
  2021. OwnerEntry += 1;
  2022. do {
  2023. if (OwnerEntry->OwnerThread == CurrentThread) {
  2024. KeGetCurrentThread()->ResourceIndex = (UCHAR)(OwnerEntry - Resource->OwnerTable);
  2025. return OwnerEntry;
  2026. }
  2027. if ((FreeEntry == NULL) &&
  2028. (OwnerEntry->OwnerThread == 0)) {
  2029. FreeEntry = OwnerEntry;
  2030. }
  2031. OwnerEntry += 1;
  2032. } while (OwnerEntry != OwnerBound);
  2033. }
  2034. }
  2035. if (!ARGUMENT_PRESENT(LockHandle)) {
  2036. //
  2037. // No argument indicates the caller does not want a free entry or
  2038. // automatic table expansion. The caller just wants to know if the
  2039. // requested thread is a resource owner. And clearly the answer is
  2040. // NO at this point.
  2041. //
  2042. return NULL;
  2043. }
  2044. //
  2045. // If a free entry was found in the table, then return the address of the
  2046. // free entry. Otherwise, expand the size of the owner thread table.
  2047. //
  2048. if (FreeEntry != NULL) {
  2049. KeGetCurrentThread()->ResourceIndex = (UCHAR)(FreeEntry - Resource->OwnerTable);
  2050. return FreeEntry;
  2051. }
  2052. //
  2053. // Save previous owner table address and allocate an expanded owner table.
  2054. //
  2055. ExpIncrementCounter(OwnerTableExpands);
  2056. OldTable = Resource->OwnerTable;
  2057. EXP_UNLOCK_RESOURCE(Resource, LockHandle);
  2058. if (OldSize == 0 ) {
  2059. NewSize = 3;
  2060. } else {
  2061. NewSize = OldSize + 4;
  2062. }
  2063. OwnerTable = ExAllocatePoolWithTag(NonPagedPool,
  2064. NewSize * sizeof(OWNER_ENTRY),
  2065. 'aTeR');
  2066. if (OwnerTable == NULL) {
  2067. KeDelayExecutionThread(KernelMode, FALSE, &ExShortTime);
  2068. } else {
  2069. //
  2070. // Zero the expansion area of the new owner table.
  2071. //
  2072. RtlZeroMemory(OwnerTable + OldSize,
  2073. (NewSize - OldSize) * sizeof(OWNER_ENTRY));
  2074. //
  2075. // Acquire the resource lock and determine if the owner table
  2076. // has been expanded by another thread while the new owner table
  2077. // was being allocated. If the owner table has been expanded by
  2078. // another thread, then release the new owner table. Otherwise,
  2079. // copy the owner table to the new owner table and establish the
  2080. // new owner table as the owner table.
  2081. //
  2082. EXP_LOCK_RESOURCE(Resource, LockHandle);
  2083. if ((OldTable != Resource->OwnerTable) ||
  2084. ((OldTable != NULL) && (OldSize != OldTable->TableSize))) {
  2085. EXP_UNLOCK_RESOURCE(Resource, LockHandle);
  2086. ExFreePool(OwnerTable);
  2087. } else {
  2088. RtlCopyMemory(OwnerTable,
  2089. OldTable,
  2090. OldSize * sizeof(OWNER_ENTRY));
  2091. //
  2092. // Swapping of the owner table must be done while owning the
  2093. // dispatcher lock to prevent a priority boost scan from occuring
  2094. // while the table is being changed. The priority boost scan is
  2095. // done when a time out occurs on a specific resource.
  2096. //
  2097. KiLockDispatcherDatabase(&OldIrql);
  2098. OwnerTable->TableSize = NewSize;
  2099. Resource->OwnerTable = OwnerTable;
  2100. KiUnlockDispatcherDatabase(OldIrql);
  2101. ASSERT_RESOURCE(Resource);
  2102. #if defined(_COLLECT_RESOURCE_DATA_)
  2103. if (NewSize > ExpResourcePerformanceData.MaximumTableExpand) {
  2104. ExpResourcePerformanceData.MaximumTableExpand = NewSize;
  2105. }
  2106. #endif
  2107. //
  2108. // Release the resource lock and free the old owner table.
  2109. //
  2110. EXP_UNLOCK_RESOURCE(Resource, LockHandle);
  2111. if (OldTable != NULL) {
  2112. ExFreePool(OldTable);
  2113. }
  2114. if (OldSize == 0) {
  2115. OldSize = 1;
  2116. }
  2117. }
  2118. }
  2119. //
  2120. // Set the hint index, acquire the resource lock, and return NULL
  2121. // as the function value. This will force a reevaluation of the
  2122. // calling resource function.
  2123. //
  2124. KeGetCurrentThread()->ResourceIndex = (CCHAR)OldSize;
  2125. EXP_LOCK_RESOURCE(Resource, LockHandle);
  2126. return NULL;
  2127. }
  2128. #if DBG
  2129. VOID
  2130. ExpAssertResource (
  2131. IN PERESOURCE Resource
  2132. )
  2133. {
  2134. //
  2135. // Assert that resource structure is correct.
  2136. //
  2137. // N.B. This routine is called with the resource lock held.
  2138. //
  2139. //
  2140. // Check resource is aligned naturally.
  2141. //
  2142. ASSERT((((ULONG_PTR)Resource) & (sizeof(ULONG_PTR) - 1)) == 0);
  2143. ASSERT(!Resource->SharedWaiters ||
  2144. Resource->SharedWaiters->Header.Type == SemaphoreObject);
  2145. ASSERT(!Resource->SharedWaiters ||
  2146. Resource->SharedWaiters->Header.Size == (sizeof(KSEMAPHORE) / sizeof(ULONG)));
  2147. ASSERT(!Resource->ExclusiveWaiters ||
  2148. Resource->ExclusiveWaiters->Header.Type == SynchronizationEvent);
  2149. ASSERT(!Resource->ExclusiveWaiters ||
  2150. Resource->ExclusiveWaiters->Header.Size == (sizeof(KEVENT) / sizeof(ULONG)));
  2151. }
  2152. #endif
  2153. PVOID
  2154. ExpCheckForResource (
  2155. IN PVOID p,
  2156. IN SIZE_T Size
  2157. )
  2158. {
  2159. KLOCK_QUEUE_HANDLE LockHandle;
  2160. PLIST_ENTRY Head, Next;
  2161. volatile PLIST_ENTRY Last=NULL, Last1=NULL;
  2162. PERESOURCE Resource;
  2163. PCHAR BeginBlock;
  2164. PCHAR EndBlock;
  2165. //
  2166. // This can cause a deadlock on MP machines.
  2167. //
  2168. if (KeNumberProcessors > 1) {
  2169. return NULL;
  2170. }
  2171. if ((ExResourceCheckFlags&EX_RESOURCE_CHECK_FREES) == 0) {
  2172. return NULL;
  2173. }
  2174. BeginBlock = (PCHAR)p;
  2175. EndBlock = (PCHAR)p + Size;
  2176. Head = &ExpSystemResourcesList;
  2177. KeAcquireInStackQueuedSpinLock (&ExpResourceSpinLock, &LockHandle);
  2178. Next = Head->Flink;
  2179. while (Next != Head) {
  2180. Resource = CONTAINING_RECORD(Next,
  2181. ERESOURCE,
  2182. SystemResourcesList);
  2183. if ((PCHAR)Resource >= BeginBlock && (PCHAR)Resource < EndBlock) {
  2184. DbgPrint("EX: ExFreePool( %p, %lx ) contains an ERESOURCE structure that has not been ExDeleteResourced\n",
  2185. p,
  2186. Size);
  2187. DbgBreakPoint ();
  2188. KeReleaseInStackQueuedSpinLock (&LockHandle);
  2189. return (PVOID)Resource;
  2190. }
  2191. //
  2192. // Save the last ptr in a volatile variable for debugging when a flink is bad
  2193. //
  2194. Last1 = Last;
  2195. Last = Next;
  2196. Next = Next->Flink;
  2197. }
  2198. KeReleaseInStackQueuedSpinLock (&LockHandle);
  2199. return NULL;
  2200. }
  2201. #if DBG
  2202. VOID
  2203. ExCheckIfResourceOwned (
  2204. VOID
  2205. )
  2206. {
  2207. KLOCK_QUEUE_HANDLE LockHandle;
  2208. PLIST_ENTRY Head, Next;
  2209. volatile PLIST_ENTRY Last=NULL, Last1=NULL;
  2210. PERESOURCE Resource;
  2211. if ((ExResourceCheckFlags&EX_RESOURCE_CHECK_ORPHANS) == 0) {
  2212. return;
  2213. }
  2214. Head = &ExpSystemResourcesList;
  2215. KeAcquireInStackQueuedSpinLock (&ExpResourceSpinLock, &LockHandle);
  2216. Next = Head->Flink;
  2217. while (Next != Head) {
  2218. Resource = CONTAINING_RECORD(Next,
  2219. ERESOURCE,
  2220. SystemResourcesList);
  2221. if (ExIsResourceAcquiredSharedLite (Resource) != 0) {
  2222. DbgPrint("EX: Resource %p held in a position where all resources acquires should have been released\n",
  2223. Resource);
  2224. DbgBreakPoint ();
  2225. KeReleaseInStackQueuedSpinLock (&LockHandle);
  2226. return;
  2227. }
  2228. //
  2229. // Save the last ptr in a volatile variable for debugging when a flink is bad
  2230. //
  2231. Last1 = Last;
  2232. Last = Next;
  2233. Next = Next->Flink;
  2234. }
  2235. KeReleaseInStackQueuedSpinLock (&LockHandle);
  2236. return;
  2237. }
  2238. #endif