2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * PURPOSE: Slim Reader/Writer (SRW) Routines
5 * PROGRAMMER: Thomas Weidenmueller <w3seek@reactos.com>
7 * NOTES: The algorithms used in this implementation
8 * may be different from Vista's implementation.
9 * Since applications should treat the RTL_SRWLOCK
10 * structure as opaque data, it should not matter.
11 * The algorithms are probably not as optimized.
14 /* INCLUDES *****************************************************************/
21 /* FIXME *********************************************************************/
23 /* FIXME: Interlocked functions that need to be made into a public header */
26 InterlockedAnd(IN OUT
volatile LONG
*Target
,
35 j
= _InterlockedCompareExchange((PLONG
)Target
,
46 InterlockedOr(IN OUT
volatile LONG
*Target
,
55 j
= _InterlockedCompareExchange((PLONG
)Target
,
64 /* FUNCTIONS *****************************************************************/
67 #define InterlockedBitTestAndSetPointer(ptr,val) InterlockedBitTestAndSet64((PLONGLONG)ptr,(LONGLONG)val)
68 #define InterlockedAddPointer(ptr,val) InterlockedAdd64((PLONGLONG)ptr,(LONGLONG)val)
69 #define InterlockedAndPointer(ptr,val) InterlockedAnd64((PLONGLONG)ptr,(LONGLONG)val)
70 #define InterlockedOrPointer(ptr,val) InterlockedOr64((PLONGLONG)ptr,(LONGLONG)val)
72 #define InterlockedBitTestAndSetPointer(ptr,val) InterlockedBitTestAndSet((PLONG)ptr,(LONG)val)
73 #define InterlockedAddPointer(ptr,val) InterlockedAdd((PLONG)ptr,(LONG)val)
74 #define InterlockedAndPointer(ptr,val) InterlockedAnd((PLONG)ptr,(LONG)val)
75 #define InterlockedOrPointer(ptr,val) InterlockedOr((PLONG)ptr,(LONG)val)
78 #define RTL_SRWLOCK_OWNED_BIT 0
79 #define RTL_SRWLOCK_CONTENDED_BIT 1
80 #define RTL_SRWLOCK_SHARED_BIT 2
81 #define RTL_SRWLOCK_CONTENTION_LOCK_BIT 3
82 #define RTL_SRWLOCK_OWNED (1 << RTL_SRWLOCK_OWNED_BIT)
83 #define RTL_SRWLOCK_CONTENDED (1 << RTL_SRWLOCK_CONTENDED_BIT)
84 #define RTL_SRWLOCK_SHARED (1 << RTL_SRWLOCK_SHARED_BIT)
85 #define RTL_SRWLOCK_CONTENTION_LOCK (1 << RTL_SRWLOCK_CONTENTION_LOCK_BIT)
86 #define RTL_SRWLOCK_MASK (RTL_SRWLOCK_OWNED | RTL_SRWLOCK_CONTENDED | \
87 RTL_SRWLOCK_SHARED | RTL_SRWLOCK_CONTENTION_LOCK)
88 #define RTL_SRWLOCK_BITS 4
91 /* This macro will cause the code to assert if compiled with a buggy
92 version of GCC that doesn't align the wait blocks properly on the stack! */
93 #define ASSERT_SRW_WAITBLOCK(ptr) \
94 ASSERT(((ULONG_PTR)ptr & ((1 << RTL_SRWLOCK_BITS) - 1)) == 0)
96 #define ASSERT_SRW_WAITBLOCK(ptr)
99 typedef struct _RTLP_SRWLOCK_SHARED_WAKE
102 volatile struct _RTLP_SRWLOCK_SHARED_WAKE
*Next
;
103 } volatile RTLP_SRWLOCK_SHARED_WAKE
, *PRTLP_SRWLOCK_SHARED_WAKE
;
105 typedef struct _RTLP_SRWLOCK_WAITBLOCK
107 /* SharedCount is the number of shared acquirers. */
110 /* Last points to the last wait block in the chain. The value
111 is only valid when read from the first wait block. */
112 volatile struct _RTLP_SRWLOCK_WAITBLOCK
*Last
;
114 /* Next points to the next wait block in the chain. */
115 volatile struct _RTLP_SRWLOCK_WAITBLOCK
*Next
;
119 /* Wake is only valid for exclusive wait blocks */
121 /* The wake chain is only valid for shared wait blocks */
124 PRTLP_SRWLOCK_SHARED_WAKE SharedWakeChain
;
125 PRTLP_SRWLOCK_SHARED_WAKE LastSharedWake
;
130 } volatile RTLP_SRWLOCK_WAITBLOCK
, *PRTLP_SRWLOCK_WAITBLOCK
;
135 RtlpReleaseWaitBlockLockExclusive(IN OUT PRTL_SRWLOCK SRWLock
,
136 IN PRTLP_SRWLOCK_WAITBLOCK FirstWaitBlock
)
138 PRTLP_SRWLOCK_WAITBLOCK Next
;
141 /* NOTE: We're currently in an exclusive lock in contended mode. */
143 Next
= FirstWaitBlock
->Next
;
146 /* There's more blocks chained, we need to update the pointers
147 in the next wait block and update the wait block pointer. */
148 NewValue
= (LONG_PTR
)Next
| RTL_SRWLOCK_OWNED
| RTL_SRWLOCK_CONTENDED
;
149 if (!FirstWaitBlock
->Exclusive
)
151 /* The next wait block has to be an exclusive lock! */
152 ASSERT(Next
->Exclusive
);
154 /* Save the shared count */
155 Next
->SharedCount
= FirstWaitBlock
->SharedCount
;
157 NewValue
|= RTL_SRWLOCK_SHARED
;
160 Next
->Last
= FirstWaitBlock
->Last
;
164 /* Convert the lock to a simple lock. */
165 if (FirstWaitBlock
->Exclusive
)
166 NewValue
= RTL_SRWLOCK_OWNED
;
169 ASSERT(FirstWaitBlock
->SharedCount
> 0);
171 NewValue
= ((LONG_PTR
)FirstWaitBlock
->SharedCount
<< RTL_SRWLOCK_BITS
) |
172 RTL_SRWLOCK_SHARED
| RTL_SRWLOCK_OWNED
;
176 (void)_InterlockedExchange((PLONG
)&SRWLock
->Ptr
, NewValue
);
178 if (FirstWaitBlock
->Exclusive
)
180 (void)InterlockedOr(&FirstWaitBlock
->Wake
,
185 PRTLP_SRWLOCK_SHARED_WAKE WakeChain
, Next
;
187 /* If we were the first one to acquire the shared
188 lock, we now need to wake all others... */
189 WakeChain
= FirstWaitBlock
->SharedWakeChain
;
192 Next
= WakeChain
->Next
;
194 (void)InterlockedOr((PLONG
)&WakeChain
->Wake
,
198 } while (WakeChain
!= NULL
);
205 RtlpReleaseWaitBlockLockLastShared(IN OUT PRTL_SRWLOCK SRWLock
,
206 IN PRTLP_SRWLOCK_WAITBLOCK FirstWaitBlock
)
208 PRTLP_SRWLOCK_WAITBLOCK Next
;
211 /* NOTE: We're currently in a shared lock in contended mode. */
213 /* The next acquirer to be unwaited *must* be an exclusive lock! */
214 ASSERT(FirstWaitBlock
->Exclusive
);
216 Next
= FirstWaitBlock
->Next
;
219 /* There's more blocks chained, we need to update the pointers
220 in the next wait block and update the wait block pointer. */
221 NewValue
= (LONG_PTR
)Next
| RTL_SRWLOCK_OWNED
| RTL_SRWLOCK_CONTENDED
;
223 Next
->Last
= FirstWaitBlock
->Last
;
227 /* Convert the lock to a simple exclusive lock. */
228 NewValue
= RTL_SRWLOCK_OWNED
;
231 (void)_InterlockedExchange((PLONG
)&SRWLock
->Ptr
, NewValue
);
233 (void)InterlockedOr(&FirstWaitBlock
->Wake
,
240 RtlpReleaseWaitBlockLock(IN OUT PRTL_SRWLOCK SRWLock
)
242 InterlockedAndPointer(&SRWLock
->Ptr
,
243 ~RTL_SRWLOCK_CONTENTION_LOCK
);
247 static PRTLP_SRWLOCK_WAITBLOCK
249 RtlpAcquireWaitBlockLock(IN OUT PRTL_SRWLOCK SRWLock
)
252 PRTLP_SRWLOCK_WAITBLOCK WaitBlock
;
256 PrevValue
= InterlockedOrPointer(&SRWLock
->Ptr
,
257 RTL_SRWLOCK_CONTENTION_LOCK
);
259 if (!(PrevValue
& RTL_SRWLOCK_CONTENTION_LOCK
))
265 if (!(PrevValue
& RTL_SRWLOCK_CONTENDED
) ||
266 (PrevValue
& ~RTL_SRWLOCK_MASK
) == 0)
268 /* Too bad, looks like the wait block was removed in the
269 meanwhile, unlock again */
270 RtlpReleaseWaitBlockLock(SRWLock
);
274 WaitBlock
= (PRTLP_SRWLOCK_WAITBLOCK
)(PrevValue
& ~RTL_SRWLOCK_MASK
);
276 ASSERT_SRW_WAITBLOCK(WaitBlock
);
283 RtlpAcquireSRWLockExclusiveWait(IN OUT PRTL_SRWLOCK SRWLock
,
284 IN PRTLP_SRWLOCK_WAITBLOCK WaitBlock
)
286 LONG_PTR CurrentValue
;
290 CurrentValue
= *(volatile LONG_PTR
*)&SRWLock
->Ptr
;
291 if (!(CurrentValue
& RTL_SRWLOCK_SHARED
))
293 if (CurrentValue
& RTL_SRWLOCK_CONTENDED
)
295 if (WaitBlock
->Wake
!= 0)
297 /* Our wait block became the first one
298 in the chain, we own the lock now! */
304 /* The last wait block was removed and/or we're
305 finally a simple exclusive lock. This means we
306 don't need to wait anymore, we acquired the lock! */
318 RtlpAcquireSRWLockSharedWait(IN OUT PRTL_SRWLOCK SRWLock
,
319 IN OUT PRTLP_SRWLOCK_WAITBLOCK FirstWait OPTIONAL
,
320 IN OUT PRTLP_SRWLOCK_SHARED_WAKE WakeChain
)
322 if (FirstWait
!= NULL
)
324 while (WakeChain
->Wake
== 0)
331 LONG_PTR CurrentValue
;
335 CurrentValue
= *(volatile LONG_PTR
*)&SRWLock
->Ptr
;
336 if (CurrentValue
& RTL_SRWLOCK_SHARED
)
338 /* The RTL_SRWLOCK_OWNED bit always needs to be set when
339 RTL_SRWLOCK_SHARED is set! */
340 ASSERT(CurrentValue
& RTL_SRWLOCK_OWNED
);
342 if (CurrentValue
& RTL_SRWLOCK_CONTENDED
)
344 if (WakeChain
->Wake
!= 0)
346 /* Our wait block became the first one
347 in the chain, we own the lock now! */
353 /* The last wait block was removed and/or we're
354 finally a simple shared lock. This means we
355 don't need to wait anymore, we acquired the lock! */
368 RtlInitializeSRWLock(OUT PRTL_SRWLOCK SRWLock
)
376 RtlAcquireSRWLockShared(IN OUT PRTL_SRWLOCK SRWLock
)
378 __ALIGNED(16) RTLP_SRWLOCK_WAITBLOCK StackWaitBlock
;
379 RTLP_SRWLOCK_SHARED_WAKE SharedWake
;
380 LONG_PTR CurrentValue
, NewValue
;
381 PRTLP_SRWLOCK_WAITBLOCK First
, Shared
, FirstWait
;
385 CurrentValue
= *(volatile LONG_PTR
*)&SRWLock
->Ptr
;
387 if (CurrentValue
& RTL_SRWLOCK_SHARED
)
389 /* NOTE: It is possible that the RTL_SRWLOCK_OWNED bit is set! */
391 if (CurrentValue
& RTL_SRWLOCK_CONTENDED
)
393 /* There's other waiters already, lock the wait blocks and
394 increment the shared count */
395 First
= RtlpAcquireWaitBlockLock(SRWLock
);
400 if (First
->Exclusive
)
402 /* We need to setup a new wait block! Although
403 we're currently in a shared lock and we're acquiring
404 a shared lock, there are exclusive locks queued. We need
405 to wait until those are released. */
406 Shared
= First
->Last
;
408 if (Shared
->Exclusive
)
410 StackWaitBlock
.Exclusive
= FALSE
;
411 StackWaitBlock
.SharedCount
= 1;
412 StackWaitBlock
.Next
= NULL
;
413 StackWaitBlock
.Last
= &StackWaitBlock
;
414 StackWaitBlock
.SharedWakeChain
= &SharedWake
;
416 Shared
->Next
= &StackWaitBlock
;
417 First
->Last
= &StackWaitBlock
;
419 Shared
= &StackWaitBlock
;
420 FirstWait
= &StackWaitBlock
;
424 Shared
->LastSharedWake
->Next
= &SharedWake
;
425 Shared
->SharedCount
++;
431 Shared
->LastSharedWake
->Next
= &SharedWake
;
432 Shared
->SharedCount
++;
435 SharedWake
.Next
= NULL
;
438 Shared
->LastSharedWake
= &SharedWake
;
440 ASSERT_SRW_WAITBLOCK(Shared
);
442 RtlpReleaseWaitBlockLock(SRWLock
);
444 RtlpAcquireSRWLockSharedWait(SRWLock
,
448 /* Successfully incremented the shared count, we acquired the lock */
454 /* This is a fastest path, just increment the number of
455 current shared locks */
457 /* Since the RTL_SRWLOCK_SHARED bit is set, the RTL_SRWLOCK_OWNED bit also has
460 ASSERT(CurrentValue
& RTL_SRWLOCK_OWNED
);
462 NewValue
= (CurrentValue
>> RTL_SRWLOCK_BITS
) + 1;
463 NewValue
= (NewValue
<< RTL_SRWLOCK_BITS
) | (CurrentValue
& RTL_SRWLOCK_MASK
);
465 if (_InterlockedCompareExchange((PLONG
)&SRWLock
->Ptr
,
467 CurrentValue
) == CurrentValue
)
469 /* Successfully incremented the shared count, we acquired the lock */
476 if (CurrentValue
& RTL_SRWLOCK_OWNED
)
478 /* The resource is currently acquired exclusively */
479 if (CurrentValue
& RTL_SRWLOCK_CONTENDED
)
481 SharedWake
.Next
= NULL
;
484 /* There's other waiters already, lock the wait blocks and
485 increment the shared count. If the last block in the chain
486 is an exclusive lock, add another block. */
488 StackWaitBlock
.Exclusive
= FALSE
;
489 StackWaitBlock
.SharedCount
= 0;
490 StackWaitBlock
.Next
= NULL
;
491 StackWaitBlock
.Last
= &StackWaitBlock
;
492 StackWaitBlock
.SharedWakeChain
= &SharedWake
;
494 First
= RtlpAcquireWaitBlockLock(SRWLock
);
497 Shared
= First
->Last
;
498 if (Shared
->Exclusive
)
500 Shared
->Next
= &StackWaitBlock
;
501 First
->Last
= &StackWaitBlock
;
503 Shared
= &StackWaitBlock
;
504 FirstWait
= &StackWaitBlock
;
509 Shared
->LastSharedWake
->Next
= &SharedWake
;
512 ASSERT_SRW_WAITBLOCK(Shared
);
514 Shared
->SharedCount
++;
515 Shared
->LastSharedWake
= &SharedWake
;
517 RtlpReleaseWaitBlockLock(SRWLock
);
519 RtlpAcquireSRWLockSharedWait(SRWLock
,
523 /* Successfully incremented the shared count, we acquired the lock */
529 SharedWake
.Next
= NULL
;
532 /* We need to setup the first wait block. Currently an exclusive lock is
533 held, change the lock to contended mode. */
534 StackWaitBlock
.Exclusive
= FALSE
;
535 StackWaitBlock
.SharedCount
= 1;
536 StackWaitBlock
.Next
= NULL
;
537 StackWaitBlock
.Last
= &StackWaitBlock
;
538 StackWaitBlock
.SharedWakeChain
= &SharedWake
;
539 StackWaitBlock
.LastSharedWake
= &SharedWake
;
541 ASSERT_SRW_WAITBLOCK(&StackWaitBlock
);
543 NewValue
= (ULONG_PTR
)&StackWaitBlock
| RTL_SRWLOCK_OWNED
| RTL_SRWLOCK_CONTENDED
;
544 if (_InterlockedCompareExchange((PLONG
)&SRWLock
->Ptr
,
546 CurrentValue
) == CurrentValue
)
548 RtlpAcquireSRWLockSharedWait(SRWLock
,
552 /* Successfully set the shared count, we acquired the lock */
559 /* This is a fast path, we can simply try to set the shared count to 1 */
560 NewValue
= (1 << RTL_SRWLOCK_BITS
) | RTL_SRWLOCK_SHARED
| RTL_SRWLOCK_OWNED
;
562 /* The RTL_SRWLOCK_CONTENDED bit should never be set if neither the
563 RTL_SRWLOCK_SHARED nor the RTL_SRWLOCK_OWNED bit is set */
564 ASSERT(!(CurrentValue
& RTL_SRWLOCK_CONTENDED
));
566 if (_InterlockedCompareExchange((PLONG
)&SRWLock
->Ptr
,
568 CurrentValue
) == CurrentValue
)
570 /* Successfully set the shared count, we acquired the lock */
583 RtlReleaseSRWLockShared(IN OUT PRTL_SRWLOCK SRWLock
)
585 LONG_PTR CurrentValue
, NewValue
;
586 PRTLP_SRWLOCK_WAITBLOCK WaitBlock
;
591 CurrentValue
= *(volatile LONG_PTR
*)&SRWLock
->Ptr
;
593 if (CurrentValue
& RTL_SRWLOCK_SHARED
)
595 if (CurrentValue
& RTL_SRWLOCK_CONTENDED
)
597 /* There's a wait block, we need to wake a pending
598 exclusive acquirer if this is the last shared release */
599 WaitBlock
= RtlpAcquireWaitBlockLock(SRWLock
);
600 if (WaitBlock
!= NULL
)
602 LastShared
= (--WaitBlock
->SharedCount
== 0);
605 RtlpReleaseWaitBlockLockLastShared(SRWLock
,
608 RtlpReleaseWaitBlockLock(SRWLock
);
610 /* We released the lock */
616 /* This is a fast path, we can simply decrement the shared
617 count and store the pointer */
618 NewValue
= CurrentValue
>> RTL_SRWLOCK_BITS
;
622 NewValue
= (NewValue
<< RTL_SRWLOCK_BITS
) | RTL_SRWLOCK_SHARED
| RTL_SRWLOCK_OWNED
;
625 if (_InterlockedCompareExchange((PLONG
)&SRWLock
->Ptr
,
627 CurrentValue
) == CurrentValue
)
629 /* Successfully released the lock */
636 /* The RTL_SRWLOCK_SHARED bit has to be present now,
637 even in the contended case! */
638 RtlRaiseStatus(STATUS_RESOURCE_NOT_OWNED
);
648 RtlAcquireSRWLockExclusive(IN OUT PRTL_SRWLOCK SRWLock
)
650 __ALIGNED(16) RTLP_SRWLOCK_WAITBLOCK StackWaitBlock
;
651 PRTLP_SRWLOCK_WAITBLOCK First
, Last
;
653 if (InterlockedBitTestAndSetPointer(&SRWLock
->Ptr
,
654 RTL_SRWLOCK_OWNED_BIT
))
656 LONG_PTR CurrentValue
, NewValue
;
660 CurrentValue
= *(volatile LONG_PTR
*)&SRWLock
->Ptr
;
662 if (CurrentValue
& RTL_SRWLOCK_SHARED
)
664 /* A shared lock is being held right now. We need to add a wait block! */
666 if (CurrentValue
& RTL_SRWLOCK_CONTENDED
)
672 /* There are no wait blocks so far, we need to add ourselves as the first
673 wait block. We need to keep the shared count! */
674 StackWaitBlock
.Exclusive
= TRUE
;
675 StackWaitBlock
.SharedCount
= CurrentValue
>> RTL_SRWLOCK_BITS
;
676 StackWaitBlock
.Next
= NULL
;
677 StackWaitBlock
.Last
= &StackWaitBlock
;
678 StackWaitBlock
.Wake
= 0;
680 ASSERT_SRW_WAITBLOCK(&StackWaitBlock
);
682 NewValue
= (ULONG_PTR
)&StackWaitBlock
| RTL_SRWLOCK_SHARED
| RTL_SRWLOCK_CONTENDED
| RTL_SRWLOCK_OWNED
;
684 if (_InterlockedCompareExchange((PLONG
)&SRWLock
->Ptr
,
686 CurrentValue
) == CurrentValue
)
688 RtlpAcquireSRWLockExclusiveWait(SRWLock
,
691 /* Successfully acquired the exclusive lock */
698 if (CurrentValue
& RTL_SRWLOCK_OWNED
)
700 /* An exclusive lock is being held right now. We need to add a wait block! */
702 if (CurrentValue
& RTL_SRWLOCK_CONTENDED
)
705 StackWaitBlock
.Exclusive
= TRUE
;
706 StackWaitBlock
.SharedCount
= 0;
707 StackWaitBlock
.Next
= NULL
;
708 StackWaitBlock
.Last
= &StackWaitBlock
;
709 StackWaitBlock
.Wake
= 0;
711 ASSERT_SRW_WAITBLOCK(&StackWaitBlock
);
713 First
= RtlpAcquireWaitBlockLock(SRWLock
);
717 Last
->Next
= &StackWaitBlock
;
718 First
->Last
= &StackWaitBlock
;
720 RtlpReleaseWaitBlockLock(SRWLock
);
722 RtlpAcquireSRWLockExclusiveWait(SRWLock
,
725 /* Successfully acquired the exclusive lock */
731 /* There are no wait blocks so far, we need to add ourselves as the first
732 wait block. We need to keep the shared count! */
733 StackWaitBlock
.Exclusive
= TRUE
;
734 StackWaitBlock
.SharedCount
= 0;
735 StackWaitBlock
.Next
= NULL
;
736 StackWaitBlock
.Last
= &StackWaitBlock
;
737 StackWaitBlock
.Wake
= 0;
739 ASSERT_SRW_WAITBLOCK(&StackWaitBlock
);
741 NewValue
= (ULONG_PTR
)&StackWaitBlock
| RTL_SRWLOCK_OWNED
| RTL_SRWLOCK_CONTENDED
;
742 if (_InterlockedCompareExchange((PLONG
)&SRWLock
->Ptr
,
744 CurrentValue
) == CurrentValue
)
746 RtlpAcquireSRWLockExclusiveWait(SRWLock
,
749 /* Successfully acquired the exclusive lock */
756 if (!InterlockedBitTestAndSetPointer(&SRWLock
->Ptr
,
757 RTL_SRWLOCK_OWNED_BIT
))
759 /* We managed to get hold of a simple exclusive lock! */
773 RtlReleaseSRWLockExclusive(IN OUT PRTL_SRWLOCK SRWLock
)
775 LONG_PTR CurrentValue
, NewValue
;
776 PRTLP_SRWLOCK_WAITBLOCK WaitBlock
;
780 CurrentValue
= *(volatile LONG_PTR
*)&SRWLock
->Ptr
;
782 if (!(CurrentValue
& RTL_SRWLOCK_OWNED
))
784 RtlRaiseStatus(STATUS_RESOURCE_NOT_OWNED
);
787 if (!(CurrentValue
& RTL_SRWLOCK_SHARED
))
789 if (CurrentValue
& RTL_SRWLOCK_CONTENDED
)
791 /* There's a wait block, we need to wake the next pending
792 acquirer (exclusive or shared) */
793 WaitBlock
= RtlpAcquireWaitBlockLock(SRWLock
);
794 if (WaitBlock
!= NULL
)
796 RtlpReleaseWaitBlockLockExclusive(SRWLock
,
799 /* We released the lock */
805 /* This is a fast path, we can simply clear the RTL_SRWLOCK_OWNED
806 bit. All other bits should be 0 now because this is a simple
807 exclusive lock and no one is waiting. */
809 ASSERT(!(CurrentValue
& ~RTL_SRWLOCK_OWNED
));
812 if (_InterlockedCompareExchange((PLONG
)&SRWLock
->Ptr
,
814 CurrentValue
) == CurrentValue
)
816 /* We released the lock */
823 /* The RTL_SRWLOCK_SHARED bit must not be present now,
824 not even in the contended case! */
825 RtlRaiseStatus(STATUS_RESOURCE_NOT_OWNED
);