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

829 lines
22 KiB

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