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.

890 lines
18 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. safelock.c
  5. Abstract:
  6. Implementation of the safe lock library - a set of
  7. thin wrappers around critical section and resource
  8. routines that ensures proper lock ordering.
  9. Debug spew is generated when locks are acquired out of
  10. order.
  11. --*/
  12. #include <debuglib.h>
  13. #include <safelock.h>
  14. #ifdef DBG
  15. typedef struct _SAFE_LOCK_ENTRY {
  16. DWORD Enum;
  17. DWORD Count;
  18. } SAFE_LOCK_ENTRY;
  19. typedef struct _SAFE_LOCK_STACK {
  20. DWORD Top;
  21. DWORD Size;
  22. SAFE_LOCK_ENTRY Entries[ANYSIZE_ARRAY];
  23. } SAFE_LOCK_STACK, *PSAFE_LOCK_STACK;
  24. typedef struct _SAFE_LOCK_CONTEXT {
  25. DWORD SafeLockThreadState;
  26. DWORD MaxLocks;
  27. BOOL AssertOnErrors;
  28. LONG InstanceCounts[ANYSIZE_ARRAY];
  29. } SAFE_LOCK_CONTEXT, *PSAFE_LOCK_CONTEXT;
  30. PSAFE_LOCK_CONTEXT SafeLockContext;
  31. ///////////////////////////////////////////////////////////////////////////////
  32. //
  33. // Helper routines
  34. //
  35. ///////////////////////////////////////////////////////////////////////////////
  36. NTSTATUS
  37. SafeLockInit(
  38. IN DWORD MaxLocks,
  39. IN BOOL AssertOnErrors
  40. )
  41. /*++
  42. Routine Description:
  43. Called by the user of the safelock code at startup time,
  44. once per process initialization.
  45. Parameters:
  46. MaxLocks - number of locks to be managed
  47. AssertOnErrors - if TRUE, asserts will fire when errors are encountered
  48. Returns:
  49. STATUS_INSUFFICIENT_RESOURCES TlsAlloc failed
  50. STATUS_SUCCESS Otherwise
  51. --*/
  52. {
  53. ASSERT( MaxLocks > 0 );
  54. ASSERT( MaxLocks < 64 ); // must fit in 6 bits
  55. SafeLockContext = LocalAlloc( 0, sizeof( SAFE_LOCK_CONTEXT ) + ( MaxLocks - 1) * sizeof( LONG ));
  56. if ( SafeLockContext == NULL ) {
  57. return STATUS_INSUFFICIENT_RESOURCES;
  58. }
  59. SafeLockContext->SafeLockThreadState = TlsAlloc();
  60. if ( SafeLockContext->SafeLockThreadState == TLS_OUT_OF_INDEXES ) {
  61. LocalFree( SafeLockContext );
  62. return STATUS_INSUFFICIENT_RESOURCES;
  63. }
  64. SafeLockContext->MaxLocks = MaxLocks;
  65. SafeLockContext->AssertOnErrors = AssertOnErrors;
  66. RtlZeroMemory( SafeLockContext->InstanceCounts, MaxLocks * sizeof( LONG ));
  67. return STATUS_SUCCESS;
  68. }
  69. NTSTATUS
  70. SafeLockCleanup(
  71. )
  72. /*++
  73. Routine Description:
  74. Called by the user of the safelock code at cleanup time
  75. Parameters:
  76. None
  77. Returns:
  78. STATUS_SUCCESS
  79. --*/
  80. {
  81. if ( SafeLockContext ) {
  82. if ( SafeLockContext->SafeLockThreadState != TLS_OUT_OF_INDEXES ) {
  83. TlsFree( SafeLockContext->SafeLockThreadState );
  84. }
  85. LocalFree( SafeLockContext );
  86. SafeLockContext = NULL;
  87. }
  88. return STATUS_SUCCESS;
  89. }
  90. VOID
  91. TrackLockEnter(
  92. DWORD Enum
  93. )
  94. /*++
  95. Routine Description:
  96. Used to insert tracking information about a lock into the stack
  97. Parameters:
  98. Enum ordinal number associated with the lock
  99. Returns:
  100. Nothing, but will assert if not happy
  101. --*/
  102. {
  103. PSAFE_LOCK_STACK Stack;
  104. DWORD Index;
  105. ASSERT(( Enum >> 26 ) < SafeLockContext->MaxLocks );
  106. //
  107. // First see if the space for the stack has been allocated
  108. //
  109. Stack = ( PSAFE_LOCK_STACK )TlsGetValue( SafeLockContext->SafeLockThreadState );
  110. if ( Stack == ( PVOID )( -1 )) {
  111. //
  112. // Once the TLS value for stack is -1, we can no longer reliably track
  113. // lock information for this thread, so just give up
  114. //
  115. return;
  116. } else if ( Stack == NULL ) {
  117. Stack = ( PSAFE_LOCK_STACK )LocalAlloc( 0, sizeof( SAFE_LOCK_STACK ) + ( SafeLockContext->MaxLocks - 1 ) * sizeof( SAFE_LOCK_ENTRY ));
  118. if ( Stack == NULL ) {
  119. //
  120. // Got no better way of dealing with this error here
  121. //
  122. DbgPrint( "Out of memory allocating lock tracking stack\n" );
  123. TlsSetValue( SafeLockContext->SafeLockThreadState, ( PVOID )( -1 ));
  124. return;
  125. }
  126. Stack->Top = 0;
  127. Stack->Size = SafeLockContext->MaxLocks;
  128. RtlZeroMemory( Stack->Entries, SafeLockContext->MaxLocks * sizeof( SAFE_LOCK_ENTRY ));
  129. TlsSetValue( SafeLockContext->SafeLockThreadState, Stack );
  130. }
  131. if ( Stack->Top >= Stack->Size ) {
  132. //
  133. // Stack limits exceeded, must grow
  134. //
  135. PSAFE_LOCK_STACK StackT = ( PSAFE_LOCK_STACK )LocalAlloc( 0, sizeof( SAFE_LOCK_STACK ) + ( 2 * Stack->Size - 1 ) * sizeof( SAFE_LOCK_ENTRY ));
  136. if ( StackT == NULL ) {
  137. //
  138. // Got no better way of dealing with this error here
  139. //
  140. DbgPrint( "Out of memory allocating lock tracking stack\n" );
  141. LocalFree( Stack );
  142. TlsSetValue( SafeLockContext->SafeLockThreadState, ( PVOID )( -1 ));
  143. return;
  144. }
  145. StackT->Top = Stack->Top;
  146. StackT->Size = 2 * Stack->Size;
  147. RtlCopyMemory( StackT->Entries, Stack->Entries, Stack->Size * sizeof( SAFE_LOCK_ENTRY ));
  148. RtlZeroMemory( &StackT->Entries[Stack->Size], Stack->Size * sizeof( SAFE_LOCK_ENTRY ));
  149. LocalFree( Stack );
  150. Stack = StackT;
  151. TlsSetValue( SafeLockContext->SafeLockThreadState, Stack );
  152. }
  153. if ( Stack->Top == 0 ||
  154. Enum > Stack->Entries[Stack->Top-1].Enum ) {
  155. //
  156. // Lock acquired in order; no further checks are necessary
  157. //
  158. Stack->Entries[Stack->Top].Enum = Enum;
  159. Stack->Entries[Stack->Top].Count = 1;
  160. Stack->Top += 1;
  161. } else {
  162. //
  163. // Locks with an enum of '0' are presumed to have no dependencies;
  164. // they must be acquired and released independently
  165. //
  166. if (( Enum >> 26 ) == 0 ) {
  167. CHAR Buffer[128] = {0};
  168. _snprintf(Buffer, sizeof(Buffer) - 1, "Unplaced lock acquired together with other locks: dt %p _SAFE_LOCK_STACK\n", Stack );
  169. DbgPrint( Buffer );
  170. if ( SafeLockContext->AssertOnErrors ) {
  171. ASSERT( FALSE );
  172. }
  173. } else if (( Stack->Entries[0].Enum >> 26 ) == 0 ) {
  174. CHAR Buffer[128] = {0};
  175. _snprintf( Buffer, sizeof(Buffer) - 1, "Lock %d acquired together with an unplaced lock\n", ( Enum >> 26 ));
  176. DbgPrint( Buffer );
  177. if ( SafeLockContext->AssertOnErrors ) {
  178. ASSERT( FALSE );
  179. }
  180. }
  181. //
  182. // See if this lock has been acquired already
  183. //
  184. for ( Index = 0; Index < Stack->Top; Index++ ) {
  185. if ( Stack->Entries[Index].Enum == Enum ) {
  186. Stack->Entries[Index].Count += 1;
  187. break;
  188. }
  189. }
  190. if ( Index == Stack->Top ) {
  191. CHAR Buffer[128] = {0};
  192. _snprintf( Buffer, sizeof(Buffer) - 1, "Lock %d acquired out of order: dt %p _SAFE_LOCK_STACK\n", ( Enum >> 26 ), Stack );
  193. DbgPrint( Buffer );
  194. if ( SafeLockContext->AssertOnErrors ) {
  195. ASSERT( FALSE );
  196. }
  197. //
  198. // To keep the stack consistent, insert the new item
  199. // as if it was acquired in proper order
  200. //
  201. for ( Index = 0; Index < Stack->Top; Index++ ) {
  202. if ( Enum < Stack->Entries[Index].Enum ) {
  203. MoveMemory( &Stack->Entries[Index+1],
  204. &Stack->Entries[Index],
  205. sizeof( SAFE_LOCK_ENTRY ) * ( Stack->Top - Index ));
  206. break;
  207. }
  208. }
  209. Stack->Entries[Index].Enum = Enum;
  210. Stack->Entries[Index].Count = 1;
  211. Stack->Top += 1;
  212. }
  213. }
  214. return;
  215. }
  216. VOID
  217. TrackLockLeave(
  218. DWORD Enum
  219. )
  220. /*++
  221. Routine Description:
  222. Used to remove tracking information about a lock from the stack
  223. Parameters:
  224. Enum ordinal number associated with the lock
  225. Returns:
  226. Nothing, but will assert if not happy
  227. --*/
  228. {
  229. PSAFE_LOCK_STACK Stack;
  230. DWORD Index;
  231. ASSERT(( Enum >> 26 ) < SafeLockContext->MaxLocks );
  232. Stack = ( PSAFE_LOCK_STACK )TlsGetValue( SafeLockContext->SafeLockThreadState );
  233. if ( Stack == ( PVOID )( -1 )) {
  234. //
  235. // No lock tracking information available for this thread
  236. //
  237. return;
  238. } else if ( Stack == NULL || Stack->Top == 0 ) {
  239. CHAR Buffer[128] = {0};
  240. _snprintf( Buffer, sizeof(Buffer) - 1, "Leaving a lock %d that has not been acquired\n", ( Enum >> 26 ));
  241. DbgPrint( Buffer );
  242. if ( SafeLockContext->AssertOnErrors ) {
  243. ASSERT( FALSE );
  244. }
  245. return;
  246. }
  247. //
  248. // See if this lock has been acquired already
  249. //
  250. for ( Index = 0; Index < Stack->Top; Index++ ) {
  251. if ( Stack->Entries[Index].Enum == Enum ) {
  252. Stack->Entries[Index].Count -= 1;
  253. break;
  254. }
  255. }
  256. if ( Index == Stack->Top ) {
  257. CHAR Buffer[128] = {0};
  258. _snprintf( Buffer, sizeof(Buffer) - 1, "Leaving a lock %d that has not been acquired: dt %p _SAFE_LOCK_STACK\n", ( Enum >> 26 ), Stack );
  259. DbgPrint( Buffer );
  260. if ( SafeLockContext->AssertOnErrors ) {
  261. ASSERT( FALSE );
  262. }
  263. } else if ( Stack->Entries[Index].Count == 0 ) {
  264. //
  265. // Compact the stack
  266. //
  267. Stack->Top -= 1;
  268. MoveMemory( &Stack->Entries[Index],
  269. &Stack->Entries[Index+1],
  270. sizeof( SAFE_LOCK_ENTRY ) * ( Stack->Top - Index ));
  271. }
  272. if ( Stack->Top == 0 ) {
  273. LocalFree( Stack );
  274. TlsSetValue( SafeLockContext->SafeLockThreadState, NULL );
  275. }
  276. return;
  277. }
  278. ///////////////////////////////////////////////////////////////////////////////
  279. //
  280. // RTL_CRITICAL_SECTION wrappers
  281. //
  282. ///////////////////////////////////////////////////////////////////////////////
  283. NTSTATUS
  284. SafeEnterCriticalSection(
  285. PSAFE_CRITICAL_SECTION CriticalSection
  286. )
  287. /*++
  288. Routine Description:
  289. Debug wrapper around RtlEnterCriticalSection.
  290. Asserts if it is not happy.
  291. Arguments:
  292. CriticalSection address of a SAFE_CRITICAL_SECTION to enter
  293. Returns:
  294. See RtlEnterCriticalSection
  295. --*/
  296. {
  297. NTSTATUS Status;
  298. TrackLockEnter( CriticalSection->Enum );
  299. Status = RtlEnterCriticalSection( &CriticalSection->CriticalSection );
  300. return Status;
  301. }
  302. NTSTATUS
  303. SafeLeaveCriticalSection(
  304. PSAFE_CRITICAL_SECTION CriticalSection
  305. )
  306. /*++
  307. Routine Description:
  308. Debug wrapper around RtlLeaveCriticalSection that ensures
  309. proper ordering of locks.
  310. Asserts if it is not happy.
  311. Arguments:
  312. CriticalSection address of a SAFE_CRITICAL_SECTION to leave
  313. Returns:
  314. See RtlLeaveCriticalSection
  315. --*/
  316. {
  317. NTSTATUS Status;
  318. TrackLockLeave( CriticalSection->Enum );
  319. Status = RtlLeaveCriticalSection( &CriticalSection->CriticalSection );
  320. return Status;
  321. }
  322. BOOLEAN
  323. SafeTryEnterCriticalSection(
  324. PSAFE_CRITICAL_SECTION CriticalSection
  325. )
  326. /*++
  327. Routine Description:
  328. Debug wrapper around RtlTryEnterCriticalSection that ensures
  329. proper ordering of locks.
  330. Asserts if it is not happy.
  331. Arguments:
  332. CriticalSection address of a SAFE_CRITICAL_SECTION to enter
  333. Returns:
  334. See RtlTryEnterCriticalSection
  335. --*/
  336. {
  337. BOOLEAN Result;
  338. TrackLockEnter( CriticalSection->Enum );
  339. Result = RtlTryEnterCriticalSection( &CriticalSection->CriticalSection );
  340. if ( !Result ) {
  341. TrackLockLeave( CriticalSection->Enum );
  342. }
  343. return Result;
  344. }
  345. NTSTATUS
  346. SafeInitializeCriticalSection(
  347. PSAFE_CRITICAL_SECTION CriticalSection,
  348. DWORD Enum
  349. )
  350. /*++
  351. Routine Description:
  352. Debug wrapper around RtlInitializeCriticalSection.
  353. Arguments:
  354. CriticalSection address of a SAFE_CRITICAL_SECTION to initialize
  355. Enum ordinal number associated with the critical section
  356. Returns:
  357. See RtlInitializeCriticalSection
  358. --*/
  359. {
  360. NTSTATUS Status;
  361. ASSERT( Enum < SafeLockContext->MaxLocks );
  362. CriticalSection->Enum = ( LONG )InterlockedIncrement( &SafeLockContext->InstanceCounts[Enum] );
  363. CriticalSection->Enum |= ( Enum << 26 );
  364. ASSERT(( CriticalSection->Enum >> 26 ) == Enum );
  365. Status = RtlInitializeCriticalSection( &CriticalSection->CriticalSection );
  366. return Status;
  367. }
  368. NTSTATUS
  369. SafeInitializeCriticalSectionAndSpinCount(
  370. PSAFE_CRITICAL_SECTION CriticalSection,
  371. ULONG SpinCount,
  372. DWORD Enum
  373. )
  374. /*++
  375. Routine Description:
  376. Debug wrapper around RtlInitializeCriticalSectionAndSpinCount.
  377. Arguments:
  378. CriticalSection address of a SAFE_CRITICAL_SECTION to initialize
  379. SpinCount spin count
  380. Enum ordinal number associated with the critical section
  381. Returns:
  382. See RtlInitializeCriticalSectionAndSpinCount
  383. --*/
  384. {
  385. NTSTATUS Status;
  386. ASSERT( Enum < SafeLockContext->MaxLocks );
  387. CriticalSection->Enum = ( LONG )InterlockedIncrement( &SafeLockContext->InstanceCounts[Enum] );
  388. CriticalSection->Enum |= ( Enum << 26 );
  389. ASSERT(( CriticalSection->Enum >> 26 ) == Enum );
  390. Status = RtlInitializeCriticalSectionAndSpinCount( &CriticalSection->CriticalSection, SpinCount );
  391. return Status;
  392. }
  393. ULONG
  394. SafeSetCriticalSectionSpinCount(
  395. PSAFE_CRITICAL_SECTION CriticalSection,
  396. ULONG SpinCount
  397. )
  398. /*++
  399. Routine Description:
  400. Debug wrapper around RtlSetCriticalSectionSpinCount.
  401. Arguments:
  402. CriticalSection address of a SAFE_CRITICAL_SECTION to modify
  403. SpinCount see the definition of RtlSetCriticalSectionSpinCount
  404. Returns:
  405. See RtlSetCriticalSectionSpinCount
  406. --*/
  407. {
  408. ULONG Result;
  409. Result = RtlSetCriticalSectionSpinCount( &CriticalSection->CriticalSection, SpinCount );
  410. return Result;
  411. }
  412. NTSTATUS
  413. SafeDeleteCriticalSection(
  414. PSAFE_CRITICAL_SECTION CriticalSection
  415. )
  416. /*++
  417. Routine Description:
  418. Debug wrapper around RtlDeleteCriticalSection.
  419. Arguments:
  420. CriticalSection address of a SAFE_CRITICAL_SECTION to delete
  421. Returns:
  422. See RtlDeleteCriticalSection
  423. --*/
  424. {
  425. NTSTATUS Status;
  426. Status = RtlDeleteCriticalSection( &CriticalSection->CriticalSection );
  427. return Status;
  428. }
  429. ///////////////////////////////////////////////////////////////////////////////
  430. //
  431. // RTL_RESOURCE wrappers
  432. //
  433. ///////////////////////////////////////////////////////////////////////////////
  434. VOID
  435. SafeInitializeResource(
  436. PSAFE_RESOURCE Resource,
  437. DWORD Enum
  438. )
  439. /*++
  440. Routine Description:
  441. Debug wrapper around RtlInitializeResource.
  442. Arguments:
  443. Resource address of a SAFE_RESOURCE to initialize
  444. Enum ordinal number associated with the resource
  445. Returns:
  446. See RtlInitializeResource
  447. --*/
  448. {
  449. ASSERT( Enum < SafeLockContext->MaxLocks );
  450. Resource->Enum = ( LONG )InterlockedIncrement( &SafeLockContext->InstanceCounts[Enum] );
  451. Resource->Enum |= ( Enum << 26 );
  452. ASSERT(( Resource->Enum >> 26 ) == Enum );
  453. RtlInitializeResource( &Resource->Resource );
  454. return;
  455. }
  456. BOOLEAN
  457. SafeAcquireResourceShared(
  458. PSAFE_RESOURCE Resource,
  459. BOOLEAN Wait
  460. )
  461. /*++
  462. Routine Description:
  463. Debug wrapper around RtlAcquireResourceShared that ensures
  464. proper ordering of locks.
  465. Asserts if it is not happy.
  466. Arguments:
  467. Resource address of a SAFE_RESOURCE to enter
  468. Wait see definition of RtlAcquireResourceShared
  469. Returns:
  470. See RtlAcquireResourceShared
  471. --*/
  472. {
  473. BOOLEAN Result;
  474. TrackLockEnter( Resource->Enum );
  475. Result = RtlAcquireResourceShared( &Resource->Resource, Wait );
  476. if ( !Result ) {
  477. TrackLockLeave( Resource->Enum );
  478. }
  479. return Result;
  480. }
  481. BOOLEAN
  482. SafeAcquireResourceExclusive(
  483. PSAFE_RESOURCE Resource,
  484. BOOLEAN Wait
  485. )
  486. /*++
  487. Routine Description:
  488. Debug wrapper around RtlAcquireResourceExclusive that ensures
  489. proper ordering of locks.
  490. Asserts if it is not happy.
  491. Arguments:
  492. Resource address of a SAFE_RESOURCE to enter
  493. Wait see definition of RtlAcquireResourceExclusive
  494. Returns:
  495. See RtlAcquireResourceExclusive
  496. --*/
  497. {
  498. BOOLEAN Result;
  499. TrackLockEnter( Resource->Enum );
  500. Result = RtlAcquireResourceExclusive( &Resource->Resource, Wait );
  501. if ( !Result ) {
  502. TrackLockLeave( Resource->Enum );
  503. }
  504. return Result;
  505. }
  506. VOID
  507. SafeReleaseResource(
  508. PSAFE_RESOURCE Resource
  509. )
  510. /*++
  511. Routine Description:
  512. Debug wrapper around RtlReleaseResource that ensures
  513. proper ordering of locks.
  514. Asserts if it is not happy.
  515. Arguments:
  516. Resource address of a SAFE_RESOURCE to release
  517. Returns:
  518. See RtlReleaseResource
  519. --*/
  520. {
  521. TrackLockLeave( Resource->Enum );
  522. RtlReleaseResource( &Resource->Resource );
  523. return;
  524. }
  525. VOID
  526. SafeConvertSharedToExclusive(
  527. PSAFE_RESOURCE Resource
  528. )
  529. /*++
  530. Routine Description:
  531. Debug wrapper around RtlConvertSharedToExclusive.
  532. Arguments:
  533. Resource address of a SAFE_RESOURCE to convert
  534. Returns:
  535. See RtlConvertSharedToExclusive
  536. --*/
  537. {
  538. RtlConvertSharedToExclusive( &Resource->Resource );
  539. return;
  540. }
  541. VOID
  542. SafeConvertExclusiveToShared(
  543. PSAFE_RESOURCE Resource
  544. )
  545. /*++
  546. Routine Description:
  547. Debug wrapper around RtlConvertExclusiveToShared.
  548. Arguments:
  549. Resource address of a SAFE_RESOURCE to convert
  550. Returns:
  551. See RtlConvertExclusiveToShared
  552. --*/
  553. {
  554. RtlConvertExclusiveToShared( &Resource->Resource );
  555. return;
  556. }
  557. VOID
  558. SafeDeleteResource (
  559. PSAFE_RESOURCE Resource
  560. )
  561. /*++
  562. Routine Description:
  563. Debug wrapper around RtlDeleteResource.
  564. Arguments:
  565. Resource address of a SAFE_RESOURCE to delete
  566. Returns:
  567. See RtlDeleteResource
  568. --*/
  569. {
  570. RtlDeleteResource( &Resource->Resource );
  571. return;
  572. }
  573. #endif