Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

749 lines
14 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. //
  16. // Uncomment the #define below to assert on locks acquired out
  17. // of order. Otherwise, only debug spew is generated.
  18. //
  19. #define SAFE_LOCK_ASSERT
  20. #define MAX_SAFE_LOCK_ENTRIES 64
  21. typedef struct _SAFE_LOCK_ENTRY {
  22. DWORD Enum;
  23. DWORD Count;
  24. } SAFE_LOCK_ENTRY;
  25. typedef struct _SAFE_LOCK_STACK {
  26. DWORD Top;
  27. SAFE_LOCK_ENTRY Entries[MAX_SAFE_LOCK_ENTRIES];
  28. } SAFE_LOCK_STACK, *PSAFE_LOCK_STACK;
  29. DWORD SafeLockThreadState = TLS_OUT_OF_INDEXES;
  30. ///////////////////////////////////////////////////////////////////////////////
  31. //
  32. // Helper routines
  33. //
  34. ///////////////////////////////////////////////////////////////////////////////
  35. NTSTATUS
  36. SafeLockInit()
  37. /*++
  38. Routine Description:
  39. Called by the user of the safelock code at startup time,
  40. once per process initialization.
  41. Parameters:
  42. None
  43. Returns:
  44. STATUS_INSUFFICIENT_RESOURCES TlsAlloc failed
  45. STATUS_SUCCESS Otherwise
  46. --*/
  47. {
  48. if ( SafeLockThreadState == TLS_OUT_OF_INDEXES ) {
  49. SafeLockThreadState = TlsAlloc();
  50. if ( SafeLockThreadState == TLS_OUT_OF_INDEXES ) {
  51. return STATUS_INSUFFICIENT_RESOURCES;
  52. }
  53. }
  54. return STATUS_SUCCESS;
  55. }
  56. VOID
  57. TrackLockEnter(
  58. DWORD Enum
  59. )
  60. /*++
  61. Routine Description:
  62. Used to insert tracking information about a lock into the stack
  63. Parameters:
  64. Enum ordinal number associated with the lock
  65. Returns:
  66. Nothing, but will assert if not happy
  67. --*/
  68. {
  69. PSAFE_LOCK_STACK Stack;
  70. DWORD Index;
  71. //
  72. // First see if the space for the stack has been allocated
  73. //
  74. Stack = ( SAFE_LOCK_STACK * )TlsGetValue( SafeLockThreadState );
  75. if ( Stack == NULL ) {
  76. Stack = ( PSAFE_LOCK_STACK )LocalAlloc( 0, sizeof( SAFE_LOCK_STACK ));
  77. if ( Stack == NULL ) {
  78. //
  79. // Got no better way of dealing with this error here
  80. //
  81. DbgPrint( "Out of memory allocating lock tracking stack\n" );
  82. #ifdef SAFE_LOCK_ASSERT
  83. ASSERT( FALSE );
  84. #endif
  85. return;
  86. }
  87. ZeroMemory( Stack, sizeof( SAFE_LOCK_STACK ));
  88. TlsSetValue( SafeLockThreadState, Stack );
  89. }
  90. if ( Stack->Top >= MAX_SAFE_LOCK_ENTRIES ) {
  91. DbgPrint( "Lock tracking stack is full\n" );
  92. #ifdef SAFE_LOCK_ASSERT
  93. ASSERT( FALSE );
  94. #endif
  95. return;
  96. }
  97. if ( Stack->Top == 0 ||
  98. Enum > Stack->Entries[Stack->Top-1].Enum ) {
  99. //
  100. // Lock acquired in order; no further checks are necessary
  101. //
  102. Stack->Entries[Stack->Top].Enum = Enum;
  103. Stack->Entries[Stack->Top].Count = 1;
  104. Stack->Top += 1;
  105. } else {
  106. //
  107. // See if this lock has been acquired already
  108. //
  109. for ( Index = 0; Index < Stack->Top; Index++ ) {
  110. if ( Stack->Entries[Index].Enum == Enum ) {
  111. Stack->Entries[Index].Count += 1;
  112. break;
  113. }
  114. }
  115. if ( Index == Stack->Top ) {
  116. CHAR Buffer[128];
  117. sprintf( Buffer, "Lock %d acquired out of order: dt %p SAFE_LOCK_STACK\n", Enum, Stack );
  118. DbgPrint( Buffer );
  119. #ifdef SAFE_LOCK_ASSERT
  120. ASSERT( FALSE );
  121. #endif
  122. //
  123. // To keep the stack consistent, insert the new item
  124. // as if it was acquired in proper order
  125. //
  126. for ( Index = 0; Index < Stack->Top; Index++ ) {
  127. if ( Enum < Stack->Entries[Index].Enum ) {
  128. MoveMemory( &Stack->Entries[Index+1],
  129. &Stack->Entries[Index],
  130. sizeof( SAFE_LOCK_ENTRY ) * ( Stack->Top - Index ));
  131. break;
  132. }
  133. }
  134. Stack->Entries[Index].Enum = Enum;
  135. Stack->Entries[Index].Count = 1;
  136. Stack->Top += 1;
  137. }
  138. }
  139. return;
  140. }
  141. VOID
  142. TrackLockLeave(
  143. DWORD Enum
  144. )
  145. /*++
  146. Routine Description:
  147. Used to remove tracking information about a lock from the stack
  148. Parameters:
  149. Enum ordinal number associated with the lock
  150. Returns:
  151. Nothing, but will assert if not happy
  152. --*/
  153. {
  154. PSAFE_LOCK_STACK Stack;
  155. DWORD Index;
  156. Stack = ( SAFE_LOCK_STACK * )TlsGetValue( SafeLockThreadState );
  157. if ( Stack == NULL ) {
  158. DbgPrint( "No lock tracking information available for this thread\n" );
  159. #ifdef SAFE_LOCK_ASSERT
  160. ASSERT( FALSE );
  161. #endif
  162. return;
  163. }
  164. if ( Stack->Top == 0 ) {
  165. DbgPrint( "Lock tracking stack is empty\n" );
  166. #ifdef SAFE_LOCK_ASSERT
  167. ASSERT( FALSE );
  168. #endif
  169. return;
  170. }
  171. //
  172. // See if this lock has been acquired already
  173. //
  174. for ( Index = 0; Index < Stack->Top; Index++ ) {
  175. if ( Stack->Entries[Index].Enum == Enum ) {
  176. Stack->Entries[Index].Count -= 1;
  177. break;
  178. }
  179. }
  180. if ( Index == Stack->Top ) {
  181. CHAR Buffer[128];
  182. sprintf( Buffer, "Leaving a lock %d that has not been acquired: dt %p SAFE_LOCK_STACK\n", Enum, Stack );
  183. DbgPrint( Buffer );
  184. #ifdef SAFE_LOCK_ASSERT
  185. ASSERT( FALSE );
  186. #endif
  187. } else if ( Stack->Entries[Index].Count == 0 ) {
  188. //
  189. // Compact the stack
  190. //
  191. Stack->Top -= 1;
  192. MoveMemory( &Stack->Entries[Index],
  193. &Stack->Entries[Index+1],
  194. sizeof( SAFE_LOCK_ENTRY ) * ( Stack->Top - Index ));
  195. }
  196. if ( Stack->Top == 0 ) {
  197. LocalFree( Stack );
  198. TlsSetValue( SafeLockThreadState, NULL );
  199. }
  200. return;
  201. }
  202. ///////////////////////////////////////////////////////////////////////////////
  203. //
  204. // RTL_CRITICAL_SECTION wrappers
  205. //
  206. ///////////////////////////////////////////////////////////////////////////////
  207. NTSTATUS
  208. SafeEnterCriticalSection(
  209. PSAFE_CRITICAL_SECTION CriticalSection
  210. )
  211. /*++
  212. Routine Description:
  213. Debug wrapper around RtlEnterCriticalSection.
  214. Asserts if it is not happy.
  215. Arguments:
  216. CriticalSection address of a SAFE_CRITICAL_SECTION to enter
  217. Returns:
  218. See RtlEnterCriticalSection
  219. --*/
  220. {
  221. NTSTATUS Status;
  222. TrackLockEnter( CriticalSection->Enum );
  223. Status = RtlEnterCriticalSection( &CriticalSection->CriticalSection );
  224. return Status;
  225. }
  226. NTSTATUS
  227. SafeLeaveCriticalSection(
  228. PSAFE_CRITICAL_SECTION CriticalSection
  229. )
  230. /*++
  231. Routine Description:
  232. Debug wrapper around RtlLeaveCriticalSection that ensures
  233. proper ordering of locks.
  234. Asserts if it is not happy.
  235. Arguments:
  236. CriticalSection address of a SAFE_CRITICAL_SECTION to leave
  237. Returns:
  238. See RtlLeaveCriticalSection
  239. --*/
  240. {
  241. NTSTATUS Status;
  242. TrackLockLeave( CriticalSection->Enum );
  243. Status = RtlLeaveCriticalSection( &CriticalSection->CriticalSection );
  244. return Status;
  245. }
  246. BOOLEAN
  247. SafeTryEnterCriticalSection(
  248. PSAFE_CRITICAL_SECTION CriticalSection
  249. )
  250. /*++
  251. Routine Description:
  252. Debug wrapper around RtlTryEnterCriticalSection that ensures
  253. proper ordering of locks.
  254. Asserts if it is not happy.
  255. Arguments:
  256. CriticalSection address of a SAFE_CRITICAL_SECTION to enter
  257. Returns:
  258. See RtlTryEnterCriticalSection
  259. --*/
  260. {
  261. BOOLEAN Result;
  262. TrackLockEnter( CriticalSection->Enum );
  263. Result = RtlTryEnterCriticalSection( &CriticalSection->CriticalSection );
  264. if ( !Result ) {
  265. TrackLockLeave( CriticalSection->Enum );
  266. }
  267. return Result;
  268. }
  269. NTSTATUS
  270. SafeInitializeCriticalSection(
  271. PSAFE_CRITICAL_SECTION CriticalSection,
  272. DWORD Enum
  273. )
  274. /*++
  275. Routine Description:
  276. Debug wrapper around RtlInitializeCriticalSection.
  277. Arguments:
  278. CriticalSection address of a SAFE_CRITICAL_SECTION to initialize
  279. Enum ordinal number associated with the critical section
  280. Returns:
  281. See RtlInitializeCriticalSection
  282. --*/
  283. {
  284. NTSTATUS Status;
  285. CriticalSection->Enum = Enum;
  286. Status = RtlInitializeCriticalSection( &CriticalSection->CriticalSection );
  287. return Status;
  288. }
  289. NTSTATUS
  290. SafeInitializeCriticalSectionAndSpinCount(
  291. PSAFE_CRITICAL_SECTION CriticalSection,
  292. ULONG SpinCount,
  293. DWORD Enum
  294. )
  295. /*++
  296. Routine Description:
  297. Debug wrapper around RtlInitializeCriticalSectionAndSpinCount.
  298. Arguments:
  299. CriticalSection address of a SAFE_CRITICAL_SECTION to initialize
  300. SpinCount spin count
  301. Enum ordinal number associated with the critical section
  302. Returns:
  303. See RtlInitializeCriticalSectionAndSpinCount
  304. --*/
  305. {
  306. NTSTATUS Status;
  307. CriticalSection->Enum = Enum;
  308. Status = RtlInitializeCriticalSectionAndSpinCount( &CriticalSection->CriticalSection, SpinCount );
  309. return Status;
  310. }
  311. ULONG
  312. SafeSetCriticalSectionSpinCount(
  313. PSAFE_CRITICAL_SECTION CriticalSection,
  314. ULONG SpinCount
  315. )
  316. /*++
  317. Routine Description:
  318. Debug wrapper around RtlSetCriticalSectionSpinCount.
  319. Arguments:
  320. CriticalSection address of a SAFE_CRITICAL_SECTION to modify
  321. SpinCount see the definition of RtlSetCriticalSectionSpinCount
  322. Returns:
  323. See RtlSetCriticalSectionSpinCount
  324. --*/
  325. {
  326. ULONG Result;
  327. Result = RtlSetCriticalSectionSpinCount( &CriticalSection->CriticalSection, SpinCount );
  328. return Result;
  329. }
  330. NTSTATUS
  331. SafeDeleteCriticalSection(
  332. PSAFE_CRITICAL_SECTION CriticalSection
  333. )
  334. /*++
  335. Routine Description:
  336. Debug wrapper around RtlDeleteCriticalSection.
  337. Arguments:
  338. CriticalSection address of a SAFE_CRITICAL_SECTION to delete
  339. Returns:
  340. See RtlDeleteCriticalSection
  341. --*/
  342. {
  343. NTSTATUS Status;
  344. Status = RtlDeleteCriticalSection( &CriticalSection->CriticalSection );
  345. return Status;
  346. }
  347. ///////////////////////////////////////////////////////////////////////////////
  348. //
  349. // RTL_RESOURCE wrappers
  350. //
  351. ///////////////////////////////////////////////////////////////////////////////
  352. VOID
  353. SafeInitializeResource(
  354. PSAFE_RESOURCE Resource,
  355. DWORD Enum
  356. )
  357. /*++
  358. Routine Description:
  359. Debug wrapper around RtlInitializeResource.
  360. Arguments:
  361. Resource address of a SAFE_RESOURCE to initialize
  362. Enum ordinal number associated with the resource
  363. Returns:
  364. See RtlInitializeResource
  365. --*/
  366. {
  367. Resource->Enum = Enum;
  368. RtlInitializeResource( &Resource->Resource );
  369. return;
  370. }
  371. BOOLEAN
  372. SafeAcquireResourceShared(
  373. PSAFE_RESOURCE Resource,
  374. BOOLEAN Wait
  375. )
  376. /*++
  377. Routine Description:
  378. Debug wrapper around RtlAcquireResourceShared that ensures
  379. proper ordering of locks.
  380. Asserts if it is not happy.
  381. Arguments:
  382. Resource address of a SAFE_RESOURCE to enter
  383. Wait see definition of RtlAcquireResourceShared
  384. Returns:
  385. See RtlAcquireResourceShared
  386. --*/
  387. {
  388. BOOLEAN Result;
  389. TrackLockEnter( Resource->Enum );
  390. Result = RtlAcquireResourceShared( &Resource->Resource, Wait );
  391. if ( !Result ) {
  392. TrackLockLeave( Resource->Enum );
  393. }
  394. return Result;
  395. }
  396. BOOLEAN
  397. SafeAcquireResourceExclusive(
  398. PSAFE_RESOURCE Resource,
  399. BOOLEAN Wait
  400. )
  401. /*++
  402. Routine Description:
  403. Debug wrapper around RtlAcquireResourceExclusive that ensures
  404. proper ordering of locks.
  405. Asserts if it is not happy.
  406. Arguments:
  407. Resource address of a SAFE_RESOURCE to enter
  408. Wait see definition of RtlAcquireResourceExclusive
  409. Returns:
  410. See RtlAcquireResourceExclusive
  411. --*/
  412. {
  413. BOOLEAN Result;
  414. TrackLockEnter( Resource->Enum );
  415. Result = RtlAcquireResourceExclusive( &Resource->Resource, Wait );
  416. if ( !Result ) {
  417. TrackLockLeave( Resource->Enum );
  418. }
  419. return Result;
  420. }
  421. VOID
  422. SafeReleaseResource(
  423. PSAFE_RESOURCE Resource
  424. )
  425. /*++
  426. Routine Description:
  427. Debug wrapper around RtlReleaseResource that ensures
  428. proper ordering of locks.
  429. Asserts if it is not happy.
  430. Arguments:
  431. Resource address of a SAFE_RESOURCE to release
  432. Returns:
  433. See RtlReleaseResource
  434. --*/
  435. {
  436. TrackLockLeave( Resource->Enum );
  437. RtlReleaseResource( &Resource->Resource );
  438. return;
  439. }
  440. VOID
  441. SafeConvertSharedToExclusive(
  442. PSAFE_RESOURCE Resource
  443. )
  444. /*++
  445. Routine Description:
  446. Debug wrapper around RtlConvertSharedToExclusive.
  447. Arguments:
  448. Resource address of a SAFE_RESOURCE to convert
  449. Returns:
  450. See RtlConvertSharedToExclusive
  451. --*/
  452. {
  453. RtlConvertSharedToExclusive( &Resource->Resource );
  454. return;
  455. }
  456. VOID
  457. SafeConvertExclusiveToShared(
  458. PSAFE_RESOURCE Resource
  459. )
  460. /*++
  461. Routine Description:
  462. Debug wrapper around RtlConvertExclusiveToShared.
  463. Arguments:
  464. Resource address of a SAFE_RESOURCE to convert
  465. Returns:
  466. See RtlConvertExclusiveToShared
  467. --*/
  468. {
  469. RtlConvertExclusiveToShared( &Resource->Resource );
  470. return;
  471. }
  472. VOID
  473. SafeDeleteResource (
  474. PSAFE_RESOURCE Resource
  475. )
  476. /*++
  477. Routine Description:
  478. Debug wrapper around RtlDeleteResource.
  479. Arguments:
  480. Resource address of a SAFE_RESOURCE to delete
  481. Returns:
  482. See RtlDeleteResource
  483. --*/
  484. {
  485. RtlDeleteResource( &Resource->Resource );
  486. return;
  487. }
  488. #endif