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 *****************************************************************/
16 #include <rtl_vista.h>
21 /* FUNCTIONS *****************************************************************/
24 #define InterlockedBitTestAndSetPointer(ptr,val) InterlockedBitTestAndSet64((PLONGLONG)ptr,(LONGLONG)val)
25 #define InterlockedAddPointer(ptr,val) InterlockedAdd64((PLONGLONG)ptr,(LONGLONG)val)
26 #define InterlockedAndPointer(ptr,val) InterlockedAnd64((PLONGLONG)ptr,(LONGLONG)val)
27 #define InterlockedOrPointer(ptr,val) InterlockedOr64((PLONGLONG)ptr,(LONGLONG)val)
29 #define InterlockedBitTestAndSetPointer(ptr,val) InterlockedBitTestAndSet((PLONG)ptr,(LONG)val)
30 #define InterlockedAddPointer(ptr,val) InterlockedAdd((PLONG)ptr,(LONG)val)
31 #define InterlockedAndPointer(ptr,val) InterlockedAnd((PLONG)ptr,(LONG)val)
32 #define InterlockedOrPointer(ptr,val) InterlockedOr((PLONG)ptr,(LONG)val)
35 #define RTL_SRWLOCK_OWNED_BIT 0
36 #define RTL_SRWLOCK_CONTENDED_BIT 1
37 #define RTL_SRWLOCK_SHARED_BIT 2
38 #define RTL_SRWLOCK_CONTENTION_LOCK_BIT 3
39 #define RTL_SRWLOCK_OWNED (1 << RTL_SRWLOCK_OWNED_BIT)
40 #define RTL_SRWLOCK_CONTENDED (1 << RTL_SRWLOCK_CONTENDED_BIT)
41 #define RTL_SRWLOCK_SHARED (1 << RTL_SRWLOCK_SHARED_BIT)
42 #define RTL_SRWLOCK_CONTENTION_LOCK (1 << RTL_SRWLOCK_CONTENTION_LOCK_BIT)
43 #define RTL_SRWLOCK_MASK (RTL_SRWLOCK_OWNED | RTL_SRWLOCK_CONTENDED | \
44 RTL_SRWLOCK_SHARED | RTL_SRWLOCK_CONTENTION_LOCK)
45 #define RTL_SRWLOCK_BITS 4
47 typedef struct _RTLP_SRWLOCK_SHARED_WAKE
50 volatile struct _RTLP_SRWLOCK_SHARED_WAKE
*Next
;
51 } volatile RTLP_SRWLOCK_SHARED_WAKE
, *PRTLP_SRWLOCK_SHARED_WAKE
;
53 typedef struct _RTLP_SRWLOCK_WAITBLOCK
55 /* SharedCount is the number of shared acquirers. */
58 /* Last points to the last wait block in the chain. The value
59 is only valid when read from the first wait block. */
60 volatile struct _RTLP_SRWLOCK_WAITBLOCK
*Last
;
62 /* Next points to the next wait block in the chain. */
63 volatile struct _RTLP_SRWLOCK_WAITBLOCK
*Next
;
67 /* Wake is only valid for exclusive wait blocks */
69 /* The wake chain is only valid for shared wait blocks */
72 PRTLP_SRWLOCK_SHARED_WAKE SharedWakeChain
;
73 PRTLP_SRWLOCK_SHARED_WAKE LastSharedWake
;
78 } volatile RTLP_SRWLOCK_WAITBLOCK
, *PRTLP_SRWLOCK_WAITBLOCK
;
83 RtlpReleaseWaitBlockLockExclusive(IN OUT PRTL_SRWLOCK SRWLock
,
84 IN PRTLP_SRWLOCK_WAITBLOCK FirstWaitBlock
)
86 PRTLP_SRWLOCK_WAITBLOCK Next
;
89 /* NOTE: We're currently in an exclusive lock in contended mode. */
91 Next
= FirstWaitBlock
->Next
;
94 /* There's more blocks chained, we need to update the pointers
95 in the next wait block and update the wait block pointer. */
96 NewValue
= (LONG_PTR
)Next
| RTL_SRWLOCK_OWNED
| RTL_SRWLOCK_CONTENDED
;
97 if (!FirstWaitBlock
->Exclusive
)
99 /* The next wait block has to be an exclusive lock! */
100 ASSERT(Next
->Exclusive
);
102 /* Save the shared count */
103 Next
->SharedCount
= FirstWaitBlock
->SharedCount
;
105 NewValue
|= RTL_SRWLOCK_SHARED
;
108 Next
->Last
= FirstWaitBlock
->Last
;
112 /* Convert the lock to a simple lock. */
113 if (FirstWaitBlock
->Exclusive
)
114 NewValue
= RTL_SRWLOCK_OWNED
;
117 ASSERT(FirstWaitBlock
->SharedCount
> 0);
119 NewValue
= ((LONG_PTR
)FirstWaitBlock
->SharedCount
<< RTL_SRWLOCK_BITS
) |
120 RTL_SRWLOCK_SHARED
| RTL_SRWLOCK_OWNED
;
124 (void)InterlockedExchangePointer(&SRWLock
->Ptr
, (PVOID
)NewValue
);
126 if (FirstWaitBlock
->Exclusive
)
128 (void)InterlockedOr(&FirstWaitBlock
->Wake
,
133 PRTLP_SRWLOCK_SHARED_WAKE WakeChain
, NextWake
;
135 /* If we were the first one to acquire the shared
136 lock, we now need to wake all others... */
137 WakeChain
= FirstWaitBlock
->SharedWakeChain
;
140 NextWake
= WakeChain
->Next
;
142 (void)InterlockedOr((PLONG
)&WakeChain
->Wake
,
145 WakeChain
= NextWake
;
146 } while (WakeChain
!= NULL
);
153 RtlpReleaseWaitBlockLockLastShared(IN OUT PRTL_SRWLOCK SRWLock
,
154 IN PRTLP_SRWLOCK_WAITBLOCK FirstWaitBlock
)
156 PRTLP_SRWLOCK_WAITBLOCK Next
;
159 /* NOTE: We're currently in a shared lock in contended mode. */
161 /* The next acquirer to be unwaited *must* be an exclusive lock! */
162 ASSERT(FirstWaitBlock
->Exclusive
);
164 Next
= FirstWaitBlock
->Next
;
167 /* There's more blocks chained, we need to update the pointers
168 in the next wait block and update the wait block pointer. */
169 NewValue
= (LONG_PTR
)Next
| RTL_SRWLOCK_OWNED
| RTL_SRWLOCK_CONTENDED
;
171 Next
->Last
= FirstWaitBlock
->Last
;
175 /* Convert the lock to a simple exclusive lock. */
176 NewValue
= RTL_SRWLOCK_OWNED
;
179 (void)InterlockedExchangePointer(&SRWLock
->Ptr
, (PVOID
)NewValue
);
181 (void)InterlockedOr(&FirstWaitBlock
->Wake
,
188 RtlpReleaseWaitBlockLock(IN OUT PRTL_SRWLOCK SRWLock
)
190 InterlockedAndPointer(&SRWLock
->Ptr
,
191 ~RTL_SRWLOCK_CONTENTION_LOCK
);
195 static PRTLP_SRWLOCK_WAITBLOCK
197 RtlpAcquireWaitBlockLock(IN OUT PRTL_SRWLOCK SRWLock
)
200 PRTLP_SRWLOCK_WAITBLOCK WaitBlock
;
204 PrevValue
= InterlockedOrPointer(&SRWLock
->Ptr
,
205 RTL_SRWLOCK_CONTENTION_LOCK
);
207 if (!(PrevValue
& RTL_SRWLOCK_CONTENTION_LOCK
))
213 if (!(PrevValue
& RTL_SRWLOCK_CONTENDED
) ||
214 (PrevValue
& ~RTL_SRWLOCK_MASK
) == 0)
216 /* Too bad, looks like the wait block was removed in the
217 meanwhile, unlock again */
218 RtlpReleaseWaitBlockLock(SRWLock
);
222 WaitBlock
= (PRTLP_SRWLOCK_WAITBLOCK
)(PrevValue
& ~RTL_SRWLOCK_MASK
);
230 RtlpAcquireSRWLockExclusiveWait(IN OUT PRTL_SRWLOCK SRWLock
,
231 IN PRTLP_SRWLOCK_WAITBLOCK WaitBlock
)
233 LONG_PTR CurrentValue
;
237 CurrentValue
= *(volatile LONG_PTR
*)&SRWLock
->Ptr
;
238 if (!(CurrentValue
& RTL_SRWLOCK_SHARED
))
240 if (CurrentValue
& RTL_SRWLOCK_CONTENDED
)
242 if (WaitBlock
->Wake
!= 0)
244 /* Our wait block became the first one
245 in the chain, we own the lock now! */
251 /* The last wait block was removed and/or we're
252 finally a simple exclusive lock. This means we
253 don't need to wait anymore, we acquired the lock! */
265 RtlpAcquireSRWLockSharedWait(IN OUT PRTL_SRWLOCK SRWLock
,
266 IN OUT PRTLP_SRWLOCK_WAITBLOCK FirstWait OPTIONAL
,
267 IN OUT PRTLP_SRWLOCK_SHARED_WAKE WakeChain
)
269 if (FirstWait
!= NULL
)
271 while (WakeChain
->Wake
== 0)
278 LONG_PTR CurrentValue
;
282 CurrentValue
= *(volatile LONG_PTR
*)&SRWLock
->Ptr
;
283 if (CurrentValue
& RTL_SRWLOCK_SHARED
)
285 /* The RTL_SRWLOCK_OWNED bit always needs to be set when
286 RTL_SRWLOCK_SHARED is set! */
287 ASSERT(CurrentValue
& RTL_SRWLOCK_OWNED
);
289 if (CurrentValue
& RTL_SRWLOCK_CONTENDED
)
291 if (WakeChain
->Wake
!= 0)
293 /* Our wait block became the first one
294 in the chain, we own the lock now! */
300 /* The last wait block was removed and/or we're
301 finally a simple shared lock. This means we
302 don't need to wait anymore, we acquired the lock! */
315 RtlInitializeSRWLock(OUT PRTL_SRWLOCK SRWLock
)
323 RtlAcquireSRWLockShared(IN OUT PRTL_SRWLOCK SRWLock
)
325 __ALIGNED(16) RTLP_SRWLOCK_WAITBLOCK StackWaitBlock
;
326 RTLP_SRWLOCK_SHARED_WAKE SharedWake
;
327 LONG_PTR CurrentValue
, NewValue
;
328 PRTLP_SRWLOCK_WAITBLOCK First
, Shared
, FirstWait
;
332 CurrentValue
= *(volatile LONG_PTR
*)&SRWLock
->Ptr
;
334 if (CurrentValue
& RTL_SRWLOCK_SHARED
)
336 /* NOTE: It is possible that the RTL_SRWLOCK_OWNED bit is set! */
338 if (CurrentValue
& RTL_SRWLOCK_CONTENDED
)
340 /* There's other waiters already, lock the wait blocks and
341 increment the shared count */
342 First
= RtlpAcquireWaitBlockLock(SRWLock
);
347 if (First
->Exclusive
)
349 /* We need to setup a new wait block! Although
350 we're currently in a shared lock and we're acquiring
351 a shared lock, there are exclusive locks queued. We need
352 to wait until those are released. */
353 Shared
= First
->Last
;
355 if (Shared
->Exclusive
)
357 StackWaitBlock
.Exclusive
= FALSE
;
358 StackWaitBlock
.SharedCount
= 1;
359 StackWaitBlock
.Next
= NULL
;
360 StackWaitBlock
.Last
= &StackWaitBlock
;
361 StackWaitBlock
.SharedWakeChain
= &SharedWake
;
363 Shared
->Next
= &StackWaitBlock
;
364 First
->Last
= &StackWaitBlock
;
366 Shared
= &StackWaitBlock
;
367 FirstWait
= &StackWaitBlock
;
371 Shared
->LastSharedWake
->Next
= &SharedWake
;
372 Shared
->SharedCount
++;
378 Shared
->LastSharedWake
->Next
= &SharedWake
;
379 Shared
->SharedCount
++;
382 SharedWake
.Next
= NULL
;
385 Shared
->LastSharedWake
= &SharedWake
;
387 RtlpReleaseWaitBlockLock(SRWLock
);
389 RtlpAcquireSRWLockSharedWait(SRWLock
,
393 /* Successfully incremented the shared count, we acquired the lock */
399 /* This is a fastest path, just increment the number of
400 current shared locks */
402 /* Since the RTL_SRWLOCK_SHARED bit is set, the RTL_SRWLOCK_OWNED bit also has
405 ASSERT(CurrentValue
& RTL_SRWLOCK_OWNED
);
407 NewValue
= (CurrentValue
>> RTL_SRWLOCK_BITS
) + 1;
408 NewValue
= (NewValue
<< RTL_SRWLOCK_BITS
) | (CurrentValue
& RTL_SRWLOCK_MASK
);
410 if ((LONG_PTR
)InterlockedCompareExchangePointer(&SRWLock
->Ptr
,
412 (PVOID
)CurrentValue
) == CurrentValue
)
414 /* Successfully incremented the shared count, we acquired the lock */
421 if (CurrentValue
& RTL_SRWLOCK_OWNED
)
423 /* The resource is currently acquired exclusively */
424 if (CurrentValue
& RTL_SRWLOCK_CONTENDED
)
426 SharedWake
.Next
= NULL
;
429 /* There's other waiters already, lock the wait blocks and
430 increment the shared count. If the last block in the chain
431 is an exclusive lock, add another block. */
433 StackWaitBlock
.Exclusive
= FALSE
;
434 StackWaitBlock
.SharedCount
= 0;
435 StackWaitBlock
.Next
= NULL
;
436 StackWaitBlock
.Last
= &StackWaitBlock
;
437 StackWaitBlock
.SharedWakeChain
= &SharedWake
;
439 First
= RtlpAcquireWaitBlockLock(SRWLock
);
442 Shared
= First
->Last
;
443 if (Shared
->Exclusive
)
445 Shared
->Next
= &StackWaitBlock
;
446 First
->Last
= &StackWaitBlock
;
448 Shared
= &StackWaitBlock
;
449 FirstWait
= &StackWaitBlock
;
454 Shared
->LastSharedWake
->Next
= &SharedWake
;
457 Shared
->SharedCount
++;
458 Shared
->LastSharedWake
= &SharedWake
;
460 RtlpReleaseWaitBlockLock(SRWLock
);
462 RtlpAcquireSRWLockSharedWait(SRWLock
,
466 /* Successfully incremented the shared count, we acquired the lock */
472 SharedWake
.Next
= NULL
;
475 /* We need to setup the first wait block. Currently an exclusive lock is
476 held, change the lock to contended mode. */
477 StackWaitBlock
.Exclusive
= FALSE
;
478 StackWaitBlock
.SharedCount
= 1;
479 StackWaitBlock
.Next
= NULL
;
480 StackWaitBlock
.Last
= &StackWaitBlock
;
481 StackWaitBlock
.SharedWakeChain
= &SharedWake
;
482 StackWaitBlock
.LastSharedWake
= &SharedWake
;
484 NewValue
= (ULONG_PTR
)&StackWaitBlock
| RTL_SRWLOCK_OWNED
| RTL_SRWLOCK_CONTENDED
;
485 if ((LONG_PTR
)InterlockedCompareExchangePointer(&SRWLock
->Ptr
,
487 (PVOID
)CurrentValue
) == CurrentValue
)
489 RtlpAcquireSRWLockSharedWait(SRWLock
,
493 /* Successfully set the shared count, we acquired the lock */
500 /* This is a fast path, we can simply try to set the shared count to 1 */
501 NewValue
= (1 << RTL_SRWLOCK_BITS
) | RTL_SRWLOCK_SHARED
| RTL_SRWLOCK_OWNED
;
503 /* The RTL_SRWLOCK_CONTENDED bit should never be set if neither the
504 RTL_SRWLOCK_SHARED nor the RTL_SRWLOCK_OWNED bit is set */
505 ASSERT(!(CurrentValue
& RTL_SRWLOCK_CONTENDED
));
507 if ((LONG_PTR
)InterlockedCompareExchangePointer(&SRWLock
->Ptr
,
509 (PVOID
)CurrentValue
) == CurrentValue
)
511 /* Successfully set the shared count, we acquired the lock */
524 RtlReleaseSRWLockShared(IN OUT PRTL_SRWLOCK SRWLock
)
526 LONG_PTR CurrentValue
, NewValue
;
527 PRTLP_SRWLOCK_WAITBLOCK WaitBlock
;
532 CurrentValue
= *(volatile LONG_PTR
*)&SRWLock
->Ptr
;
534 if (CurrentValue
& RTL_SRWLOCK_SHARED
)
536 if (CurrentValue
& RTL_SRWLOCK_CONTENDED
)
538 /* There's a wait block, we need to wake a pending
539 exclusive acquirer if this is the last shared release */
540 WaitBlock
= RtlpAcquireWaitBlockLock(SRWLock
);
541 if (WaitBlock
!= NULL
)
543 LastShared
= (--WaitBlock
->SharedCount
== 0);
546 RtlpReleaseWaitBlockLockLastShared(SRWLock
,
549 RtlpReleaseWaitBlockLock(SRWLock
);
551 /* We released the lock */
557 /* This is a fast path, we can simply decrement the shared
558 count and store the pointer */
559 NewValue
= CurrentValue
>> RTL_SRWLOCK_BITS
;
563 NewValue
= (NewValue
<< RTL_SRWLOCK_BITS
) | RTL_SRWLOCK_SHARED
| RTL_SRWLOCK_OWNED
;
566 if ((LONG_PTR
)InterlockedCompareExchangePointer(&SRWLock
->Ptr
,
568 (PVOID
)CurrentValue
) == CurrentValue
)
570 /* Successfully released the lock */
577 /* The RTL_SRWLOCK_SHARED bit has to be present now,
578 even in the contended case! */
579 RtlRaiseStatus(STATUS_RESOURCE_NOT_OWNED
);
589 RtlAcquireSRWLockExclusive(IN OUT PRTL_SRWLOCK SRWLock
)
591 __ALIGNED(16) RTLP_SRWLOCK_WAITBLOCK StackWaitBlock
;
592 PRTLP_SRWLOCK_WAITBLOCK First
, Last
;
594 if (InterlockedBitTestAndSetPointer(&SRWLock
->Ptr
,
595 RTL_SRWLOCK_OWNED_BIT
))
597 LONG_PTR CurrentValue
, NewValue
;
601 CurrentValue
= *(volatile LONG_PTR
*)&SRWLock
->Ptr
;
603 if (CurrentValue
& RTL_SRWLOCK_SHARED
)
605 /* A shared lock is being held right now. We need to add a wait block! */
607 if (CurrentValue
& RTL_SRWLOCK_CONTENDED
)
613 /* There are no wait blocks so far, we need to add ourselves as the first
614 wait block. We need to keep the shared count! */
615 StackWaitBlock
.Exclusive
= TRUE
;
616 StackWaitBlock
.SharedCount
= (LONG
)(CurrentValue
>> RTL_SRWLOCK_BITS
);
617 StackWaitBlock
.Next
= NULL
;
618 StackWaitBlock
.Last
= &StackWaitBlock
;
619 StackWaitBlock
.Wake
= 0;
621 NewValue
= (ULONG_PTR
)&StackWaitBlock
| RTL_SRWLOCK_SHARED
| RTL_SRWLOCK_CONTENDED
| RTL_SRWLOCK_OWNED
;
623 if ((LONG_PTR
)InterlockedCompareExchangePointer(&SRWLock
->Ptr
,
625 (PVOID
)CurrentValue
) == CurrentValue
)
627 RtlpAcquireSRWLockExclusiveWait(SRWLock
,
630 /* Successfully acquired the exclusive lock */
637 if (CurrentValue
& RTL_SRWLOCK_OWNED
)
639 /* An exclusive lock is being held right now. We need to add a wait block! */
641 if (CurrentValue
& RTL_SRWLOCK_CONTENDED
)
644 StackWaitBlock
.Exclusive
= TRUE
;
645 StackWaitBlock
.SharedCount
= 0;
646 StackWaitBlock
.Next
= NULL
;
647 StackWaitBlock
.Last
= &StackWaitBlock
;
648 StackWaitBlock
.Wake
= 0;
650 First
= RtlpAcquireWaitBlockLock(SRWLock
);
654 Last
->Next
= &StackWaitBlock
;
655 First
->Last
= &StackWaitBlock
;
657 RtlpReleaseWaitBlockLock(SRWLock
);
659 RtlpAcquireSRWLockExclusiveWait(SRWLock
,
662 /* Successfully acquired the exclusive lock */
668 /* There are no wait blocks so far, we need to add ourselves as the first
669 wait block. We need to keep the shared count! */
670 StackWaitBlock
.Exclusive
= TRUE
;
671 StackWaitBlock
.SharedCount
= 0;
672 StackWaitBlock
.Next
= NULL
;
673 StackWaitBlock
.Last
= &StackWaitBlock
;
674 StackWaitBlock
.Wake
= 0;
676 NewValue
= (ULONG_PTR
)&StackWaitBlock
| RTL_SRWLOCK_OWNED
| RTL_SRWLOCK_CONTENDED
;
677 if ((LONG_PTR
)InterlockedCompareExchangePointer(&SRWLock
->Ptr
,
679 (PVOID
)CurrentValue
) == CurrentValue
)
681 RtlpAcquireSRWLockExclusiveWait(SRWLock
,
684 /* Successfully acquired the exclusive lock */
691 if (!InterlockedBitTestAndSetPointer(&SRWLock
->Ptr
,
692 RTL_SRWLOCK_OWNED_BIT
))
694 /* We managed to get hold of a simple exclusive lock! */
708 RtlReleaseSRWLockExclusive(IN OUT PRTL_SRWLOCK SRWLock
)
710 LONG_PTR CurrentValue
, NewValue
;
711 PRTLP_SRWLOCK_WAITBLOCK WaitBlock
;
715 CurrentValue
= *(volatile LONG_PTR
*)&SRWLock
->Ptr
;
717 if (!(CurrentValue
& RTL_SRWLOCK_OWNED
))
719 RtlRaiseStatus(STATUS_RESOURCE_NOT_OWNED
);
722 if (!(CurrentValue
& RTL_SRWLOCK_SHARED
))
724 if (CurrentValue
& RTL_SRWLOCK_CONTENDED
)
726 /* There's a wait block, we need to wake the next pending
727 acquirer (exclusive or shared) */
728 WaitBlock
= RtlpAcquireWaitBlockLock(SRWLock
);
729 if (WaitBlock
!= NULL
)
731 RtlpReleaseWaitBlockLockExclusive(SRWLock
,
734 /* We released the lock */
740 /* This is a fast path, we can simply clear the RTL_SRWLOCK_OWNED
741 bit. All other bits should be 0 now because this is a simple
742 exclusive lock and no one is waiting. */
744 ASSERT(!(CurrentValue
& ~RTL_SRWLOCK_OWNED
));
747 if ((LONG_PTR
)InterlockedCompareExchangePointer(&SRWLock
->Ptr
,
749 (PVOID
)CurrentValue
) == CurrentValue
)
751 /* We released the lock */
758 /* The RTL_SRWLOCK_SHARED bit must not be present now,
759 not even in the contended case! */
760 RtlRaiseStatus(STATUS_RESOURCE_NOT_OWNED
);