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.

832 lines
22 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. lock.c
  5. Abstract:
  6. This module implements functions for the LAN Manager server FSP's
  7. lock package. This package began as a modification and streamlining
  8. of the executive resource package -- it allowed recursive
  9. acquisition, but didn't provide shared locks. Later, debugging
  10. support in the form of level checking was added.
  11. Coming full circle, the package now serves as a wrapper around the
  12. real resource package. It simply provides debugging support. The
  13. reasons for reverting to using resources include:
  14. 1) The resource package now supports recursive acquisition.
  15. 2) There are a couple of places in the server where shared access
  16. is desirable.
  17. 3) The resource package has a "no-wait" option that disables waiting
  18. for a lock when someone else owns it. This feature is useful to
  19. the server FSD.
  20. Author:
  21. Chuck Lenzmeier (chuckl) 29-Nov-1989
  22. A modification of Gary Kimura's resource.c. This version does
  23. not support shared ownership, only exclusive ownership. Support
  24. for recursive ownership has been added.
  25. David Treadwell (davidtr)
  26. Chuck Lenzmeier (chuckl) 5-Apr-1991
  27. Revert to using resource package.
  28. Environment:
  29. Kernel mode only, LAN Manager server FSP and FSD.
  30. Revision History:
  31. --*/
  32. #include "precomp.h"
  33. #include "lock.tmh"
  34. #pragma hdrstop
  35. #if SRVDBG_LOCK
  36. #define BugCheckFileId SRV_FILE_LOCK
  37. //
  38. // *** This entire module is conditionalized away when SRVDBG_LOCK is off.
  39. //
  40. #ifdef ALLOC_PRAGMA
  41. #pragma alloc_text( PAGE, SrvInitializeLock )
  42. #pragma alloc_text( PAGE, SrvDeleteLock )
  43. #pragma alloc_text( PAGE, SrvReleaseLock )
  44. #endif
  45. #if 0
  46. NOT PAGEABLE -- SrvAcquireLock
  47. NOT PAGEABLE -- SrvCheckListIntegrity
  48. NOT PAGEABLE -- SrvIsEntryInList
  49. NOT PAGEABLE -- SrvIsEntryNotInList
  50. #endif
  51. //
  52. // Lock Level Semantics:
  53. //
  54. // Lock levels are used for server lock debugging as an aid in
  55. // preventing deadlocks. A deadlock may occur if two (or more) threads
  56. // attempt to acquire two (or more) locks in a different order. For
  57. // example, suppose that there are two threads, 0 and 1, and two locks,
  58. // A and B. Then suppose the following happens:
  59. //
  60. // - thread 0 acquires lock A
  61. // - thread 1 acquires lock B
  62. // - thread 0 attempts to acquire lock B, gets blocked
  63. // - thread 1 attempts to acquire lock A, gets blocked.
  64. //
  65. // This results in deadlock, where all threads are blocked and cannot
  66. // become unblocked. To prevent it, all threads must acquire locks in
  67. // the same order. In the above example, if we had the rule that lock A
  68. // must be acquired before lock B, then thread 1 would have blocked
  69. // while attempting to acquire lock A, but thread 0 would have been able
  70. // to acquire lock B and completed its work.
  71. //
  72. // This rule is implemented generally in the server by lock levels. The
  73. // lock levels are set up such that lower-level locks are acquired
  74. // first, then higher level locks. An attempt to acquire locks out of
  75. // order will be caught during debugging. The rules are as follows:
  76. //
  77. // - A lock's level is assigned during initialization.
  78. //
  79. // - A thread may acquire any lock with a level greater than the level
  80. // of the highest held exclusive lock, but an attempt to acquire a
  81. // lock with a level equal to less than the highest lock will fail.
  82. // Note that full level checking is _not_ done for shared locks,
  83. // because of the difficulty of trying to retain information about the
  84. // number of times multiple threads have obtained a given lock for
  85. // shared access.
  86. //
  87. // - Recursive acquisitions of locks are legal, even if there are
  88. // intervening lock acquisitions. For example, this is legal:
  89. // thread acquires lock A
  90. // thread acquires lock B
  91. // thread recursively acquires lock A
  92. //
  93. // - Locks may be released in any order.
  94. //
  95. // Lock debugging is active only when debugging is turned on.
  96. //
  97. #define HAS_TEB(_teb) ((BOOLEAN)(((ULONG)(_teb) <= MM_HIGHEST_USER_ADDRESS) ? FALSE : MmIsNonPagedSystemAddressValid(_teb)))
  98. #define SrvCurrentTeb( ) ((PTEB)(KeGetCurrentThread( )->Teb))
  99. #define SrvTebLockList( ) \
  100. ((PLIST_ENTRY)&(SrvCurrentTeb( )->UserReserved[SRV_TEB_LOCK_LIST]))
  101. #define SrvThreadLockAddress( ) \
  102. ( IsListEmpty( SrvTebLockList( ) ) ? 0 : CONTAINING_RECORD( \
  103. SrvTebLockList( )->Flink, \
  104. SRV_LOCK, \
  105. Header.ThreadListEntry \
  106. ) )
  107. #define SrvThreadLockLevel( ) \
  108. ( IsListEmpty( SrvTebLockList( ) ) ? 0 : CONTAINING_RECORD( \
  109. SrvTebLockList( )->Flink, \
  110. SRV_LOCK, \
  111. Header.ThreadListEntry \
  112. )->Header.LockLevel )
  113. #define SrvThreadLockName( ) \
  114. ( IsListEmpty( SrvTebLockList( ) ) ? "none" : CONTAINING_RECORD( \
  115. SrvTebLockList( )->Flink, \
  116. SRV_LOCK, \
  117. Header.ThreadListEntry \
  118. )->Header.LockName )
  119. KSPIN_LOCK LockSpinLock = {0};
  120. BOOLEAN LockSpinLockInitialized = FALSE;
  121. //
  122. // Forward declarations.
  123. //
  124. #define MAX_LOCKS_HELD 15
  125. VOID
  126. SrvInitializeLock(
  127. IN PSRV_LOCK Lock,
  128. IN ULONG LockLevel,
  129. IN PSZ LockName
  130. )
  131. /*++
  132. Routine Description:
  133. This routine initializes the input lock variable.
  134. Arguments:
  135. Lock - Supplies the lock variable being initialized
  136. LockLevel - Supplies the level of the lock
  137. LockName - Supplies the name of the lock
  138. Return Value:
  139. None.
  140. --*/
  141. {
  142. PAGED_CODE( );
  143. if ( !LockSpinLockInitialized ) {
  144. LockSpinLockInitialized = TRUE;
  145. INITIALIZE_SPIN_LOCK( LockSpinLock );
  146. }
  147. //
  148. // Initialize the executive resource.
  149. //
  150. ExInitializeResource( &Lock->Resource );
  151. //
  152. // Initialize the lock level. This is used to determine whether a
  153. // thread may acquire the lock. Save the lock name.
  154. //
  155. LOCK_LEVEL( Lock ) = LockLevel;
  156. LOCK_NAME( Lock ) = LockName;
  157. IF_DEBUG(LOCKS) {
  158. SrvPrint3( "Initialized %s(%lx, L%lx)\n",
  159. LOCK_NAME( Lock ), Lock, LOCK_LEVEL( Lock ) );
  160. }
  161. return;
  162. } // SrvInitializeLock
  163. VOID
  164. SrvDeleteLock (
  165. IN PSRV_LOCK Lock
  166. )
  167. /*++
  168. Routine Description:
  169. This routine deletes (i.e., uninitializes) a lock variable.
  170. Arguments:
  171. Lock - Supplies the lock variable being deleted
  172. Return Value:
  173. None.
  174. --*/
  175. {
  176. PAGED_CODE( );
  177. //
  178. // Make sure the lock is unowned.
  179. //
  180. if ( LOCK_NUMBER_OF_ACTIVE( Lock ) != 0 ) {
  181. IF_DEBUG(ERRORS) {
  182. SrvPrint1( "SrvDeleteLock: Thread %d\n", KeGetCurrentThread( ) );
  183. }
  184. //
  185. // This internal error bugchecks the system.
  186. //
  187. INTERNAL_ERROR(
  188. ERROR_LEVEL_IMPOSSIBLE,
  189. "SrvDeleteLock: Attempt to delete owned lock %s(%lx)",
  190. LOCK_NAME( Lock ),
  191. Lock
  192. );
  193. }
  194. //
  195. // Delete the resource.
  196. //
  197. ExDeleteResource( &Lock->Resource );
  198. return;
  199. } // SrvDeleteLock
  200. BOOLEAN
  201. SrvAcquireLock(
  202. IN PSRV_LOCK Lock,
  203. IN BOOLEAN Wait,
  204. IN BOOLEAN Exclusive
  205. )
  206. /*++
  207. Routine Description:
  208. The routine acquires a lock.
  209. Arguments:
  210. Lock - Supplies the lock to acquire
  211. Wait - Indicates whether the caller wants to wait for the resource
  212. if it is already owned
  213. Exclusive - Indicates whether exlusive or shared access is desired
  214. Return Value:
  215. BOOLEAN - Indicates whether the lock was acquired. This will always
  216. be TRUE if Wait is TRUE.
  217. --*/
  218. {
  219. PKTHREAD currentThread;
  220. PTEB currentTeb;
  221. BOOLEAN hasTeb;
  222. ULONG threadLockLevel;
  223. BOOLEAN lockAcquired;
  224. KIRQL oldIrql;
  225. currentThread = (PKTHREAD)ExGetCurrentResourceThread( );
  226. currentTeb = SrvCurrentTeb( );
  227. hasTeb = HAS_TEB(currentTeb);
  228. //
  229. // Make sure that we are at an IRQL lower than DISPATCH_LEVEL, if
  230. // Wait is TRUE. We cannot wait to acquire a lock at that IRQL or
  231. // above.
  232. //
  233. ASSERT( !Wait || (KeGetCurrentIrql( ) < DISPATCH_LEVEL) );
  234. //
  235. // If this thread does not have a nonpaged TEB, do not do lock-level
  236. // debugging. (We might be at DPC level, so we can't take page
  237. // faults.)
  238. //
  239. if ( hasTeb ) {
  240. //
  241. // Make sure that this thread has been initialized for lock
  242. // debugging. If not, initialize it.
  243. //
  244. ACQUIRE_SPIN_LOCK( LockSpinLock, &oldIrql );
  245. if ( (ULONG)currentTeb->UserReserved[SRV_TEB_LOCK_INIT] !=
  246. 0xbabababa ) {
  247. PLIST_ENTRY tebLockList = SrvTebLockList( );
  248. InitializeListHead( tebLockList );
  249. currentTeb->UserReserved[SRV_TEB_LOCK_INIT] = (PVOID)0xbabababa;
  250. }
  251. RELEASE_SPIN_LOCK( LockSpinLock, oldIrql );
  252. //
  253. // Make sure that the list of locks in the TEB is consistent.
  254. //
  255. SrvCheckListIntegrity( SrvTebLockList( ), MAX_LOCKS_HELD );
  256. //
  257. // The "lock level" of this thread is the highest level of the
  258. // locks currently held exclusively. If this thread holds no
  259. // locks, the lock level of the thread is 0 and it can acquire
  260. // any lock.
  261. //
  262. threadLockLevel = SrvThreadLockLevel( );
  263. //
  264. // Make sure that the lock the thread is attempting to acquire
  265. // has a higher level than the last-acquired exclusive lock.
  266. // Note that a recursive exclusive acquisition of a lock should
  267. // succeed, even if a different, higher-level lock has been
  268. // acquired since the lock was originally acquired. Shared
  269. // acquisition of a lock that is already held exclusively must
  270. // fail.
  271. //
  272. // *** We do NOT make this check if the caller isn't going to
  273. // wait for the lock, because no-wait acquisitions cannot
  274. // actually induce deadlock. The server FSD does this at
  275. // DPC level, potentially having interrupted a server FSP
  276. // thread that holds a higher-level lock.
  277. //
  278. if ( Wait &&
  279. (LOCK_LEVEL( Lock ) <= threadLockLevel) &&
  280. (!Exclusive ||
  281. !ExIsResourceAcquiredExclusive( &Lock->Resource )) ) {
  282. SrvPrint4( "Thread %lx, last lock %s(%lx, L%lx) attempted to ",
  283. currentThread,
  284. SrvThreadLockName( ), SrvThreadLockAddress( ),
  285. threadLockLevel );
  286. SrvPrint4( "acquire %s(%lx, L%lx) for %s access.\n",
  287. LOCK_NAME( Lock ), Lock, LOCK_LEVEL( Lock ),
  288. Exclusive ? "exclusive" : "shared" );
  289. #if DBG
  290. DbgBreakPoint( );
  291. #endif
  292. }
  293. }
  294. //
  295. // Acquire the lock.
  296. //
  297. if ( Exclusive ) {
  298. lockAcquired = ExAcquireResourceExclusive( &Lock->Resource, Wait );
  299. } else {
  300. lockAcquired = ExAcquireResourceShared( &Lock->Resource, Wait );
  301. }
  302. //
  303. // If the lock could not be acquired (Wait == FALSE), print a debug
  304. // message.
  305. //
  306. if ( !lockAcquired ) {
  307. IF_DEBUG(LOCKS) {
  308. SrvPrint4( "%s(%lx, L%lx) no-wait %s acquistion ",
  309. LOCK_NAME( Lock ), Lock, LOCK_LEVEL( Lock ),
  310. Exclusive ? "exclusive" : "shared" );
  311. SrvPrint1( "by thread %lx failed\n", currentThread );
  312. }
  313. } else if ( !Exclusive ) {
  314. //
  315. // For shared locks, we don't retain any information about
  316. // the fact that they're owned by this thread.
  317. //
  318. IF_DEBUG(LOCKS) {
  319. PSZ name = hasTeb ? SrvThreadLockName( ) : "n/a";
  320. PVOID address = hasTeb ? SrvThreadLockAddress( ) : 0;
  321. ULONG level = hasTeb ? threadLockLevel : (ULONG)-1;
  322. SrvPrint4( "%s(%lx, L%lx) acquired shared by thread %lx, ",
  323. LOCK_NAME( Lock ), Lock, LOCK_LEVEL( Lock ),
  324. currentThread );
  325. SrvPrint3( "last lock %s(%lx L%lx)\n", name, address, level );
  326. }
  327. } else {
  328. //
  329. // The thread acquired the lock for exclusive access.
  330. //
  331. if ( LOCK_NUMBER_OF_ACTIVE( Lock ) == 1 ) {
  332. //
  333. // This is the first time the thread acquired the lock for
  334. // exclusive access. Update the thread's lock state.
  335. //
  336. IF_DEBUG(LOCKS) {
  337. PSZ name = hasTeb ? SrvThreadLockName( ) : "n/a";
  338. PVOID address = hasTeb ? SrvThreadLockAddress( ) : 0;
  339. ULONG level = hasTeb ? threadLockLevel : (ULONG)-1;
  340. SrvPrint4( "%s(%lx, L%lx) acquired exclusive by thread %lx, ",
  341. LOCK_NAME( Lock ), Lock, LOCK_LEVEL( Lock ),
  342. currentThread );
  343. SrvPrint3( "last lock %s(%lx L%lx)\n", name, address, level );
  344. }
  345. if ( hasTeb ) {
  346. //
  347. // Insert the lock on the thread's list of locks.
  348. //
  349. ExInterlockedInsertHeadList(
  350. SrvTebLockList( ),
  351. LOCK_THREAD_LIST( Lock ),
  352. &LockSpinLock
  353. );
  354. }
  355. } else {
  356. //
  357. // This is a recursive acquisition of the lock.
  358. //
  359. IF_DEBUG(LOCKS) {
  360. SrvPrint4( "%s(%lx, L%lx) reacquired by thread %lx; ",
  361. LOCK_NAME( Lock ), Lock, LOCK_LEVEL( Lock ),
  362. currentThread );
  363. SrvPrint1( "count %ld\n", LOCK_NUMBER_OF_ACTIVE( Lock ) );
  364. }
  365. }
  366. }
  367. return lockAcquired;
  368. } // SrvAcquireLock
  369. VOID
  370. SrvReleaseLock(
  371. IN PSRV_LOCK Lock
  372. )
  373. /*++
  374. Routine Description:
  375. This routine releases a lock.
  376. Arguments:
  377. Lock - Supplies the lock to release
  378. Return Value:
  379. None.
  380. --*/
  381. {
  382. PKTHREAD currentThread;
  383. PTEB currentTeb;
  384. BOOLEAN hasTeb;
  385. PAGED_CODE( );
  386. currentThread = (PKTHREAD)ExGetCurrentResourceThread( );
  387. currentTeb = SrvCurrentTeb( );
  388. hasTeb = HAS_TEB(currentTeb);
  389. //
  390. // Make sure the lock is really owned by the current thread.
  391. //
  392. if ( LOCK_NUMBER_OF_ACTIVE( Lock ) == 0 ) {
  393. // !!! Should crash server on internal error here.
  394. SrvPrint3( "Thread %lx releasing unowned lock %s(%lx)\n",
  395. currentThread, LOCK_NAME( Lock ), Lock );
  396. DbgBreakPoint( );
  397. } else if ( (Lock->Resource.Flag & ResourceOwnedExclusive) &&
  398. !ExIsResourceAcquiredExclusive(&Lock->Resource) ) {
  399. // !!! Should crash server on internal error here.
  400. SrvPrint4( "Thread %lx releasing lock %s(%lx) owned by "
  401. "thread %lx\n",
  402. currentThread, LOCK_NAME( Lock ), Lock,
  403. Lock->Resource.InitialOwnerThreads[0] );
  404. DbgBreakPoint( );
  405. } else if ( !(Lock->Resource.Flag & ResourceOwnedExclusive) ) {
  406. //
  407. // The thread is releasing shared access to the lock.
  408. //
  409. IF_DEBUG(LOCKS) {
  410. SrvPrint4( "%s(%lx, L%lx) released shared by thread %lx\n",
  411. LOCK_NAME( Lock ), Lock, LOCK_LEVEL( Lock ),
  412. currentThread );
  413. }
  414. } else if ( LOCK_NUMBER_OF_ACTIVE( Lock ) == 1 ) {
  415. //
  416. // The thread is fully releasing exclusive access to the lock.
  417. //
  418. if ( hasTeb ) {
  419. //
  420. // Remove the lock from the list of locks held by this
  421. // thread.
  422. //
  423. ExInterlockedRemoveHeadList(
  424. LOCK_THREAD_LIST( Lock )->Blink,
  425. &LockSpinLock
  426. );
  427. LOCK_THREAD_LIST( Lock )->Flink = NULL;
  428. LOCK_THREAD_LIST( Lock )->Blink = NULL;
  429. //
  430. // Make sure that the list of locks in the TEB is consistent.
  431. //
  432. SrvCheckListIntegrity( SrvTebLockList( ), MAX_LOCKS_HELD );
  433. }
  434. IF_DEBUG(LOCKS) {
  435. PSZ name = hasTeb ? SrvThreadLockName( ) : "n/a";
  436. PVOID address = hasTeb ? SrvThreadLockAddress( ) : 0;
  437. ULONG level = hasTeb ? SrvThreadLockLevel( ) : (ULONG)-1;
  438. SrvPrint4( "%s(%lx, L%lx) released by thread %lx, ",
  439. LOCK_NAME( Lock ), Lock, LOCK_LEVEL( Lock ),
  440. currentThread );
  441. SrvPrint3( "new last lock %s(%lx L%lx)\n", name, address, level );
  442. }
  443. } else {
  444. //
  445. // The thread is partially releasing exclusive access to the
  446. // lock.
  447. //
  448. IF_DEBUG(LOCKS) {
  449. SrvPrint4( "%s(%lx, L%lx) semireleased by thread %lx; ",
  450. LOCK_NAME( Lock ), Lock, LOCK_LEVEL( Lock ),
  451. currentThread );
  452. SrvPrint1( "new count %ld\n", LOCK_NUMBER_OF_ACTIVE( Lock ) - 1 );
  453. }
  454. }
  455. //
  456. // Now actually do the release.
  457. //
  458. ExReleaseResource( &Lock->Resource );
  459. return;
  460. } // SrvReleaseLock
  461. #endif // SRVDBG_LIST
  462. #if SRVDBG_LIST || SRVDBG_LOCK
  463. ULONG
  464. SrvCheckListIntegrity (
  465. IN PLIST_ENTRY ListHead,
  466. IN ULONG MaxEntries
  467. )
  468. /*++
  469. Routine Description:
  470. This debug routine checks the integrity of a doubly-linked list by
  471. walking the list forward and backward. If the number of elements is
  472. different in either direction, or there are too many entries in the
  473. list, execution is stopped.
  474. *** It is the responsibility of the calling routine to do any
  475. necessary synchronization.
  476. Arguments:
  477. ListHead - a pointer to the head of the list.
  478. MaxEntries - if the number of entries in the list exceeds this
  479. number, breakpoint.
  480. Return Value:
  481. ULONG - the number of entries in the list.
  482. --*/
  483. {
  484. PLIST_ENTRY current;
  485. ULONG entriesSoFar;
  486. ULONG flinkEntries;
  487. for ( current = ListHead->Flink, entriesSoFar = 0;
  488. current != ListHead;
  489. current = current->Flink ) {
  490. if ( ++entriesSoFar >= MaxEntries ) {
  491. SrvPrint2( "Seen %ld entries in list at %lx\n",
  492. entriesSoFar, ListHead );
  493. DbgBreakPoint( );
  494. }
  495. }
  496. flinkEntries = entriesSoFar;
  497. for ( current = ListHead->Blink, entriesSoFar = 0;
  498. current != ListHead;
  499. current = current->Blink ) {
  500. if ( ++entriesSoFar >= MaxEntries ) {
  501. SrvPrint2( "Seen %ld entries in list at %lx\n",
  502. entriesSoFar, ListHead );
  503. DbgBreakPoint( );
  504. }
  505. }
  506. if ( flinkEntries != entriesSoFar ) {
  507. SrvPrint3( "In list %lx, Flink entries: %ld, Blink entries: %lx\n",
  508. ListHead, flinkEntries, entriesSoFar );
  509. DbgBreakPoint( );
  510. }
  511. return entriesSoFar;
  512. } // SrvCheckListIntegrity
  513. #endif // SRVDBG_LIST || SRVDBG_LOCK
  514. #if SRVDBG_LIST
  515. VOID
  516. SrvIsEntryInList (
  517. IN PLIST_ENTRY ListHead,
  518. IN PLIST_ENTRY ListEntry
  519. )
  520. /*++
  521. Routine Description:
  522. This debug routine determines whether the specified list entry is
  523. contained within the list. If not, execution is stopped. This is
  524. meant to be called before removing an entry from a list.
  525. *** It is the responsibility of the calling routine to do any
  526. necessary synchronization.
  527. Arguments:
  528. ListHead - a pointer to the head of the list.
  529. ListEntry - a pointer to the entry to check.
  530. Return Value:
  531. None.
  532. --*/
  533. {
  534. PLIST_ENTRY checkEntry;
  535. //
  536. // Walk the list. If we find the entry we're looking for, quit.
  537. //
  538. for ( checkEntry = ListHead->Flink;
  539. checkEntry != ListHead;
  540. checkEntry = checkEntry->Flink ) {
  541. if ( checkEntry == ListEntry ) {
  542. return;
  543. }
  544. if ( checkEntry == ListEntry ) {
  545. SrvPrint2( "Entry at %lx supposedly in list at %lx but list is "
  546. "circular.", ListEntry, ListHead );
  547. }
  548. }
  549. //
  550. // If we got here without returning, then the entry is not in the
  551. // list and something has gone wrong.
  552. //
  553. SrvPrint2( "SrvIsEntryInList: entry at %lx not found in list at %lx\n",
  554. ListEntry, ListHead );
  555. DbgBreakPoint( );
  556. return;
  557. } // SrvIsEntryInList
  558. VOID
  559. SrvIsEntryNotInList (
  560. IN PLIST_ENTRY ListHead,
  561. IN PLIST_ENTRY ListEntry
  562. )
  563. /*++
  564. Routine Description:
  565. This debug routine determines whether the specified list entry is
  566. contained within the list. If it is, execution is stopped. This is
  567. meant to be called before inserting an entry in a list.
  568. *** It is the responsibility of the calling routine to do any
  569. necessary synchronization.
  570. Arguments:
  571. ListHead - a pointer to the head of the list.
  572. ListEntry - a pointer to the entry to check.
  573. Return Value:
  574. None.
  575. --*/
  576. {
  577. PLIST_ENTRY checkEntry;
  578. //
  579. // Walk the list. If we find the entry we're looking for, break.
  580. //
  581. for ( checkEntry = ListHead->Flink;
  582. checkEntry != ListHead;
  583. checkEntry = checkEntry->Flink ) {
  584. if ( checkEntry == ListEntry ) {
  585. SrvPrint2( "SrvIsEntryNotInList: entry at %lx found in list "
  586. "at %lx\n", ListEntry, ListHead );
  587. DbgBreakPoint( );
  588. }
  589. }
  590. //
  591. // If we got here without returning, then the entry is not in the
  592. // list, so we can return.
  593. //
  594. return;
  595. } // SrvIsEntryNotInList
  596. #endif // SRVDBG_LIST