xref: /reactos/sdk/lib/rtl/srw.c (revision 9393fc32)
1 /*
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>
6  *
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.
12  */
13 
14 /* INCLUDES *****************************************************************/
15 
16 #include <rtl_vista.h>
17 
18 #define NDEBUG
19 #include <debug.h>
20 
21 /* FUNCTIONS *****************************************************************/
22 
23 #ifdef _WIN64
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)
28 #define _ONE 1LL
29 #else
30 #define InterlockedBitTestAndSetPointer(ptr,val) InterlockedBitTestAndSet((PLONG)ptr,(LONG)val)
31 #define InterlockedAddPointer(ptr,val) InterlockedAdd((PLONG)ptr,(LONG)val)
32 #define InterlockedAndPointer(ptr,val) InterlockedAnd((PLONG)ptr,(LONG)val)
33 #define InterlockedOrPointer(ptr,val) InterlockedOr((PLONG)ptr,(LONG)val)
34 #define _ONE 1L
35 #endif
36 
37 #define RTL_SRWLOCK_OWNED_BIT   0
38 #define RTL_SRWLOCK_CONTENDED_BIT   1
39 #define RTL_SRWLOCK_SHARED_BIT  2
40 #define RTL_SRWLOCK_CONTENTION_LOCK_BIT 3
41 #define RTL_SRWLOCK_OWNED   (_ONE << RTL_SRWLOCK_OWNED_BIT)
42 #define RTL_SRWLOCK_CONTENDED   (_ONE << RTL_SRWLOCK_CONTENDED_BIT)
43 #define RTL_SRWLOCK_SHARED  (_ONE << RTL_SRWLOCK_SHARED_BIT)
44 #define RTL_SRWLOCK_CONTENTION_LOCK (_ONE << RTL_SRWLOCK_CONTENTION_LOCK_BIT)
45 #define RTL_SRWLOCK_MASK    (RTL_SRWLOCK_OWNED | RTL_SRWLOCK_CONTENDED | \
46                              RTL_SRWLOCK_SHARED | RTL_SRWLOCK_CONTENTION_LOCK)
47 #define RTL_SRWLOCK_BITS    4
48 
49 typedef struct _RTLP_SRWLOCK_SHARED_WAKE
50 {
51     LONG Wake;
52     volatile struct _RTLP_SRWLOCK_SHARED_WAKE *Next;
53 } volatile RTLP_SRWLOCK_SHARED_WAKE, *PRTLP_SRWLOCK_SHARED_WAKE;
54 
55 typedef struct _RTLP_SRWLOCK_WAITBLOCK
56 {
57     /* SharedCount is the number of shared acquirers. */
58     LONG SharedCount;
59 
60     /* Last points to the last wait block in the chain. The value
61        is only valid when read from the first wait block. */
62     volatile struct _RTLP_SRWLOCK_WAITBLOCK *Last;
63 
64     /* Next points to the next wait block in the chain. */
65     volatile struct _RTLP_SRWLOCK_WAITBLOCK *Next;
66 
67     union
68     {
69         /* Wake is only valid for exclusive wait blocks */
70         LONG Wake;
71         /* The wake chain is only valid for shared wait blocks */
72         struct
73         {
74             PRTLP_SRWLOCK_SHARED_WAKE SharedWakeChain;
75             PRTLP_SRWLOCK_SHARED_WAKE LastSharedWake;
76         };
77     };
78 
79     BOOLEAN Exclusive;
80 } volatile RTLP_SRWLOCK_WAITBLOCK, *PRTLP_SRWLOCK_WAITBLOCK;
81 
82 
83 static VOID
84 NTAPI
85 RtlpReleaseWaitBlockLockExclusive(IN OUT PRTL_SRWLOCK SRWLock,
86                                   IN PRTLP_SRWLOCK_WAITBLOCK FirstWaitBlock)
87 {
88     PRTLP_SRWLOCK_WAITBLOCK Next;
89     LONG_PTR NewValue;
90 
91     /* NOTE: We're currently in an exclusive lock in contended mode. */
92 
93     Next = FirstWaitBlock->Next;
94     if (Next != NULL)
95     {
96         /* There's more blocks chained, we need to update the pointers
97            in the next wait block and update the wait block pointer. */
98         NewValue = (LONG_PTR)Next | RTL_SRWLOCK_OWNED | RTL_SRWLOCK_CONTENDED;
99         if (!FirstWaitBlock->Exclusive)
100         {
101             /* The next wait block has to be an exclusive lock! */
102             ASSERT(Next->Exclusive);
103 
104             /* Save the shared count */
105             Next->SharedCount = FirstWaitBlock->SharedCount;
106 
107             NewValue |= RTL_SRWLOCK_SHARED;
108         }
109 
110         Next->Last = FirstWaitBlock->Last;
111     }
112     else
113     {
114         /* Convert the lock to a simple lock. */
115         if (FirstWaitBlock->Exclusive)
116             NewValue = RTL_SRWLOCK_OWNED;
117         else
118         {
119             ASSERT(FirstWaitBlock->SharedCount > 0);
120 
121             NewValue = ((LONG_PTR)FirstWaitBlock->SharedCount << RTL_SRWLOCK_BITS) |
122                        RTL_SRWLOCK_SHARED | RTL_SRWLOCK_OWNED;
123         }
124     }
125 
126     (void)InterlockedExchangePointer(&SRWLock->Ptr, (PVOID)NewValue);
127 
128     if (FirstWaitBlock->Exclusive)
129     {
130         (void)InterlockedOr(&FirstWaitBlock->Wake,
131                             TRUE);
132     }
133     else
134     {
135         PRTLP_SRWLOCK_SHARED_WAKE WakeChain, NextWake;
136 
137         /* If we were the first one to acquire the shared
138            lock, we now need to wake all others... */
139         WakeChain = FirstWaitBlock->SharedWakeChain;
140         do
141         {
142             NextWake = WakeChain->Next;
143 
144             (void)InterlockedOr((PLONG)&WakeChain->Wake,
145                                 TRUE);
146 
147             WakeChain = NextWake;
148         } while (WakeChain != NULL);
149     }
150 }
151 
152 
153 static VOID
154 NTAPI
155 RtlpReleaseWaitBlockLockLastShared(IN OUT PRTL_SRWLOCK SRWLock,
156                                    IN PRTLP_SRWLOCK_WAITBLOCK FirstWaitBlock)
157 {
158     PRTLP_SRWLOCK_WAITBLOCK Next;
159     LONG_PTR NewValue;
160 
161     /* NOTE: We're currently in a shared lock in contended mode. */
162 
163     /* The next acquirer to be unwaited *must* be an exclusive lock! */
164     ASSERT(FirstWaitBlock->Exclusive);
165 
166     Next = FirstWaitBlock->Next;
167     if (Next != NULL)
168     {
169         /* There's more blocks chained, we need to update the pointers
170            in the next wait block and update the wait block pointer. */
171         NewValue = (LONG_PTR)Next | RTL_SRWLOCK_OWNED | RTL_SRWLOCK_CONTENDED;
172 
173         Next->Last = FirstWaitBlock->Last;
174     }
175     else
176     {
177         /* Convert the lock to a simple exclusive lock. */
178         NewValue = RTL_SRWLOCK_OWNED;
179     }
180 
181     (void)InterlockedExchangePointer(&SRWLock->Ptr, (PVOID)NewValue);
182 
183     (void)InterlockedOr(&FirstWaitBlock->Wake,
184                         TRUE);
185 }
186 
187 
188 static VOID
189 NTAPI
190 RtlpReleaseWaitBlockLock(IN OUT PRTL_SRWLOCK SRWLock)
191 {
192     InterlockedAndPointer(&SRWLock->Ptr,
193                           ~RTL_SRWLOCK_CONTENTION_LOCK);
194 }
195 
196 
197 static PRTLP_SRWLOCK_WAITBLOCK
198 NTAPI
199 RtlpAcquireWaitBlockLock(IN OUT PRTL_SRWLOCK SRWLock)
200 {
201     LONG_PTR PrevValue;
202     PRTLP_SRWLOCK_WAITBLOCK WaitBlock;
203 
204     while (1)
205     {
206         PrevValue = InterlockedOrPointer(&SRWLock->Ptr,
207                                          RTL_SRWLOCK_CONTENTION_LOCK);
208 
209         if (!(PrevValue & RTL_SRWLOCK_CONTENTION_LOCK))
210             break;
211 
212         YieldProcessor();
213     }
214 
215     if (!(PrevValue & RTL_SRWLOCK_CONTENDED) ||
216         (PrevValue & ~RTL_SRWLOCK_MASK) == 0)
217     {
218         /* Too bad, looks like the wait block was removed in the
219            meanwhile, unlock again */
220         RtlpReleaseWaitBlockLock(SRWLock);
221         return NULL;
222     }
223 
224     WaitBlock = (PRTLP_SRWLOCK_WAITBLOCK)(PrevValue & ~RTL_SRWLOCK_MASK);
225 
226     return WaitBlock;
227 }
228 
229 
230 static VOID
231 NTAPI
232 RtlpAcquireSRWLockExclusiveWait(IN OUT PRTL_SRWLOCK SRWLock,
233                                 IN PRTLP_SRWLOCK_WAITBLOCK WaitBlock)
234 {
235     LONG_PTR CurrentValue;
236 
237     while (1)
238     {
239         CurrentValue = *(volatile LONG_PTR *)&SRWLock->Ptr;
240         if (!(CurrentValue & RTL_SRWLOCK_SHARED))
241         {
242             if (CurrentValue & RTL_SRWLOCK_CONTENDED)
243             {
244                 if (WaitBlock->Wake != 0)
245                 {
246                     /* Our wait block became the first one
247                        in the chain, we own the lock now! */
248                     break;
249                 }
250             }
251             else
252             {
253                 /* The last wait block was removed and/or we're
254                    finally a simple exclusive lock. This means we
255                    don't need to wait anymore, we acquired the lock! */
256                 break;
257             }
258         }
259 
260         YieldProcessor();
261     }
262 }
263 
264 
265 static VOID
266 NTAPI
267 RtlpAcquireSRWLockSharedWait(IN OUT PRTL_SRWLOCK SRWLock,
268                              IN OUT PRTLP_SRWLOCK_WAITBLOCK FirstWait  OPTIONAL,
269                              IN OUT PRTLP_SRWLOCK_SHARED_WAKE WakeChain)
270 {
271     if (FirstWait != NULL)
272     {
273         while (WakeChain->Wake == 0)
274         {
275             YieldProcessor();
276         }
277     }
278     else
279     {
280         LONG_PTR CurrentValue;
281 
282         while (1)
283         {
284             CurrentValue = *(volatile LONG_PTR *)&SRWLock->Ptr;
285             if (CurrentValue & RTL_SRWLOCK_SHARED)
286             {
287                 /* The RTL_SRWLOCK_OWNED bit always needs to be set when
288                    RTL_SRWLOCK_SHARED is set! */
289                 ASSERT(CurrentValue & RTL_SRWLOCK_OWNED);
290 
291                 if (CurrentValue & RTL_SRWLOCK_CONTENDED)
292                 {
293                     if (WakeChain->Wake != 0)
294                     {
295                         /* Our wait block became the first one
296                            in the chain, we own the lock now! */
297                         break;
298                     }
299                 }
300                 else
301                 {
302                     /* The last wait block was removed and/or we're
303                        finally a simple shared lock. This means we
304                        don't need to wait anymore, we acquired the lock! */
305                     break;
306                 }
307             }
308 
309             YieldProcessor();
310         }
311     }
312 }
313 
314 
315 VOID
316 NTAPI
317 RtlInitializeSRWLock(OUT PRTL_SRWLOCK SRWLock)
318 {
319     SRWLock->Ptr = NULL;
320 }
321 
322 
323 VOID
324 NTAPI
325 RtlAcquireSRWLockShared(IN OUT PRTL_SRWLOCK SRWLock)
326 {
327     __ALIGNED(16) RTLP_SRWLOCK_WAITBLOCK StackWaitBlock;
328     RTLP_SRWLOCK_SHARED_WAKE SharedWake;
329     LONG_PTR CurrentValue, NewValue;
330     PRTLP_SRWLOCK_WAITBLOCK First, Shared, FirstWait;
331 
332     while (1)
333     {
334         CurrentValue = *(volatile LONG_PTR *)&SRWLock->Ptr;
335 
336         if (CurrentValue & RTL_SRWLOCK_SHARED)
337         {
338             /* NOTE: It is possible that the RTL_SRWLOCK_OWNED bit is set! */
339 
340             if (CurrentValue & RTL_SRWLOCK_CONTENDED)
341             {
342                 /* There's other waiters already, lock the wait blocks and
343                    increment the shared count */
344                 First = RtlpAcquireWaitBlockLock(SRWLock);
345                 if (First != NULL)
346                 {
347                     FirstWait = NULL;
348 
349                     if (First->Exclusive)
350                     {
351                         /* We need to setup a new wait block! Although
352                            we're currently in a shared lock and we're acquiring
353                            a shared lock, there are exclusive locks queued. We need
354                            to wait until those are released. */
355                         Shared = First->Last;
356 
357                         if (Shared->Exclusive)
358                         {
359                             StackWaitBlock.Exclusive = FALSE;
360                             StackWaitBlock.SharedCount = 1;
361                             StackWaitBlock.Next = NULL;
362                             StackWaitBlock.Last = &StackWaitBlock;
363                             StackWaitBlock.SharedWakeChain = &SharedWake;
364 
365                             Shared->Next = &StackWaitBlock;
366                             First->Last = &StackWaitBlock;
367 
368                             Shared = &StackWaitBlock;
369                             FirstWait = &StackWaitBlock;
370                         }
371                         else
372                         {
373                             Shared->LastSharedWake->Next = &SharedWake;
374                             Shared->SharedCount++;
375                         }
376                     }
377                     else
378                     {
379                         Shared = First;
380                         Shared->LastSharedWake->Next = &SharedWake;
381                         Shared->SharedCount++;
382                     }
383 
384                     SharedWake.Next = NULL;
385                     SharedWake.Wake = 0;
386 
387                     Shared->LastSharedWake = &SharedWake;
388 
389                     RtlpReleaseWaitBlockLock(SRWLock);
390 
391                     RtlpAcquireSRWLockSharedWait(SRWLock,
392                                                  FirstWait,
393                                                  &SharedWake);
394 
395                     /* Successfully incremented the shared count, we acquired the lock */
396                     break;
397                 }
398             }
399             else
400             {
401                 /* This is a fastest path, just increment the number of
402                    current shared locks */
403 
404                 /* Since the RTL_SRWLOCK_SHARED bit is set, the RTL_SRWLOCK_OWNED bit also has
405                    to be set! */
406 
407                 ASSERT(CurrentValue & RTL_SRWLOCK_OWNED);
408 
409                 NewValue = (CurrentValue >> RTL_SRWLOCK_BITS) + 1;
410                 NewValue = (NewValue << RTL_SRWLOCK_BITS) | (CurrentValue & RTL_SRWLOCK_MASK);
411 
412                 if ((LONG_PTR)InterlockedCompareExchangePointer(&SRWLock->Ptr,
413                                                                 (PVOID)NewValue,
414                                                                 (PVOID)CurrentValue) == CurrentValue)
415                 {
416                     /* Successfully incremented the shared count, we acquired the lock */
417                     break;
418                 }
419             }
420         }
421         else
422         {
423             if (CurrentValue & RTL_SRWLOCK_OWNED)
424             {
425                 /* The resource is currently acquired exclusively */
426                 if (CurrentValue & RTL_SRWLOCK_CONTENDED)
427                 {
428                     SharedWake.Next = NULL;
429                     SharedWake.Wake = 0;
430 
431                     /* There's other waiters already, lock the wait blocks and
432                        increment the shared count. If the last block in the chain
433                        is an exclusive lock, add another block. */
434 
435                     StackWaitBlock.Exclusive = FALSE;
436                     StackWaitBlock.SharedCount = 0;
437                     StackWaitBlock.Next = NULL;
438                     StackWaitBlock.Last = &StackWaitBlock;
439                     StackWaitBlock.SharedWakeChain = &SharedWake;
440 
441                     First = RtlpAcquireWaitBlockLock(SRWLock);
442                     if (First != NULL)
443                     {
444                         Shared = First->Last;
445                         if (Shared->Exclusive)
446                         {
447                             Shared->Next = &StackWaitBlock;
448                             First->Last = &StackWaitBlock;
449 
450                             Shared = &StackWaitBlock;
451                             FirstWait = &StackWaitBlock;
452                         }
453                         else
454                         {
455                             FirstWait = NULL;
456                             Shared->LastSharedWake->Next = &SharedWake;
457                         }
458 
459                         Shared->SharedCount++;
460                         Shared->LastSharedWake = &SharedWake;
461 
462                         RtlpReleaseWaitBlockLock(SRWLock);
463 
464                         RtlpAcquireSRWLockSharedWait(SRWLock,
465                                                      FirstWait,
466                                                      &SharedWake);
467 
468                         /* Successfully incremented the shared count, we acquired the lock */
469                         break;
470                     }
471                 }
472                 else
473                 {
474                     SharedWake.Next = NULL;
475                     SharedWake.Wake = 0;
476 
477                     /* We need to setup the first wait block. Currently an exclusive lock is
478                        held, change the lock to contended mode. */
479                     StackWaitBlock.Exclusive = FALSE;
480                     StackWaitBlock.SharedCount = 1;
481                     StackWaitBlock.Next = NULL;
482                     StackWaitBlock.Last = &StackWaitBlock;
483                     StackWaitBlock.SharedWakeChain = &SharedWake;
484                     StackWaitBlock.LastSharedWake = &SharedWake;
485 
486                     NewValue = (ULONG_PTR)&StackWaitBlock | RTL_SRWLOCK_OWNED | RTL_SRWLOCK_CONTENDED;
487                     if ((LONG_PTR)InterlockedCompareExchangePointer(&SRWLock->Ptr,
488                                                                     (PVOID)NewValue,
489                                                                     (PVOID)CurrentValue) == CurrentValue)
490                     {
491                         RtlpAcquireSRWLockSharedWait(SRWLock,
492                                                      &StackWaitBlock,
493                                                      &SharedWake);
494 
495                         /* Successfully set the shared count, we acquired the lock */
496                         break;
497                     }
498                 }
499             }
500             else
501             {
502                 /* This is a fast path, we can simply try to set the shared count to 1 */
503                 NewValue = (1 << RTL_SRWLOCK_BITS) | RTL_SRWLOCK_SHARED | RTL_SRWLOCK_OWNED;
504 
505                 /* The RTL_SRWLOCK_CONTENDED bit should never be set if neither the
506                    RTL_SRWLOCK_SHARED nor the RTL_SRWLOCK_OWNED bit is set */
507                 ASSERT(!(CurrentValue & RTL_SRWLOCK_CONTENDED));
508 
509                 if ((LONG_PTR)InterlockedCompareExchangePointer(&SRWLock->Ptr,
510                                                                 (PVOID)NewValue,
511                                                                 (PVOID)CurrentValue) == CurrentValue)
512                 {
513                     /* Successfully set the shared count, we acquired the lock */
514                     break;
515                 }
516             }
517         }
518 
519         YieldProcessor();
520     }
521 }
522 
523 
524 VOID
525 NTAPI
526 RtlReleaseSRWLockShared(IN OUT PRTL_SRWLOCK SRWLock)
527 {
528     LONG_PTR CurrentValue, NewValue;
529     PRTLP_SRWLOCK_WAITBLOCK WaitBlock;
530     BOOLEAN LastShared;
531 
532     while (1)
533     {
534         CurrentValue = *(volatile LONG_PTR *)&SRWLock->Ptr;
535 
536         if (CurrentValue & RTL_SRWLOCK_SHARED)
537         {
538             if (CurrentValue & RTL_SRWLOCK_CONTENDED)
539             {
540                 /* There's a wait block, we need to wake a pending
541                    exclusive acquirer if this is the last shared release */
542                 WaitBlock = RtlpAcquireWaitBlockLock(SRWLock);
543                 if (WaitBlock != NULL)
544                 {
545                     LastShared = (--WaitBlock->SharedCount == 0);
546 
547                     if (LastShared)
548                         RtlpReleaseWaitBlockLockLastShared(SRWLock,
549                                                            WaitBlock);
550                     else
551                         RtlpReleaseWaitBlockLock(SRWLock);
552 
553                     /* We released the lock */
554                     break;
555                 }
556             }
557             else
558             {
559                 /* This is a fast path, we can simply decrement the shared
560                    count and store the pointer */
561                 NewValue = CurrentValue >> RTL_SRWLOCK_BITS;
562 
563                 if (--NewValue != 0)
564                 {
565                     NewValue = (NewValue << RTL_SRWLOCK_BITS) | RTL_SRWLOCK_SHARED | RTL_SRWLOCK_OWNED;
566                 }
567 
568                 if ((LONG_PTR)InterlockedCompareExchangePointer(&SRWLock->Ptr,
569                                                                 (PVOID)NewValue,
570                                                                 (PVOID)CurrentValue) == CurrentValue)
571                 {
572                     /* Successfully released the lock */
573                     break;
574                 }
575             }
576         }
577         else
578         {
579             /* The RTL_SRWLOCK_SHARED bit has to be present now,
580                even in the contended case! */
581             RtlRaiseStatus(STATUS_RESOURCE_NOT_OWNED);
582         }
583 
584         YieldProcessor();
585     }
586 }
587 
588 
589 VOID
590 NTAPI
591 RtlAcquireSRWLockExclusive(IN OUT PRTL_SRWLOCK SRWLock)
592 {
593     __ALIGNED(16) RTLP_SRWLOCK_WAITBLOCK StackWaitBlock;
594     PRTLP_SRWLOCK_WAITBLOCK First, Last;
595 
596     if (InterlockedBitTestAndSetPointer(&SRWLock->Ptr,
597                                         RTL_SRWLOCK_OWNED_BIT))
598     {
599         LONG_PTR CurrentValue, NewValue;
600 
601         while (1)
602         {
603             CurrentValue = *(volatile LONG_PTR *)&SRWLock->Ptr;
604 
605             if (CurrentValue & RTL_SRWLOCK_SHARED)
606             {
607                 /* A shared lock is being held right now. We need to add a wait block! */
608 
609                 if (CurrentValue & RTL_SRWLOCK_CONTENDED)
610                 {
611                     goto AddWaitBlock;
612                 }
613                 else
614                 {
615                     /* There are no wait blocks so far, we need to add ourselves as the first
616                        wait block. We need to keep the shared count! */
617                     StackWaitBlock.Exclusive = TRUE;
618                     StackWaitBlock.SharedCount = (LONG)(CurrentValue >> RTL_SRWLOCK_BITS);
619                     StackWaitBlock.Next = NULL;
620                     StackWaitBlock.Last = &StackWaitBlock;
621                     StackWaitBlock.Wake = 0;
622 
623                     NewValue = (ULONG_PTR)&StackWaitBlock | RTL_SRWLOCK_SHARED | RTL_SRWLOCK_CONTENDED | RTL_SRWLOCK_OWNED;
624 
625                     if ((LONG_PTR)InterlockedCompareExchangePointer(&SRWLock->Ptr,
626                                                                     (PVOID)NewValue,
627                                                                     (PVOID)CurrentValue) == CurrentValue)
628                     {
629                         RtlpAcquireSRWLockExclusiveWait(SRWLock,
630                                                         &StackWaitBlock);
631 
632                         /* Successfully acquired the exclusive lock */
633                         break;
634                     }
635                 }
636             }
637             else
638             {
639                 if (CurrentValue & RTL_SRWLOCK_OWNED)
640                 {
641                     /* An exclusive lock is being held right now. We need to add a wait block! */
642 
643                     if (CurrentValue & RTL_SRWLOCK_CONTENDED)
644                     {
645 AddWaitBlock:
646                         StackWaitBlock.Exclusive = TRUE;
647                         StackWaitBlock.SharedCount = 0;
648                         StackWaitBlock.Next = NULL;
649                         StackWaitBlock.Last = &StackWaitBlock;
650                         StackWaitBlock.Wake = 0;
651 
652                         First = RtlpAcquireWaitBlockLock(SRWLock);
653                         if (First != NULL)
654                         {
655                             Last = First->Last;
656                             Last->Next = &StackWaitBlock;
657                             First->Last = &StackWaitBlock;
658 
659                             RtlpReleaseWaitBlockLock(SRWLock);
660 
661                             RtlpAcquireSRWLockExclusiveWait(SRWLock,
662                                                             &StackWaitBlock);
663 
664                             /* Successfully acquired the exclusive lock */
665                             break;
666                         }
667                     }
668                     else
669                     {
670                         /* There are no wait blocks so far, we need to add ourselves as the first
671                            wait block. We need to keep the shared count! */
672                         StackWaitBlock.Exclusive = TRUE;
673                         StackWaitBlock.SharedCount = 0;
674                         StackWaitBlock.Next = NULL;
675                         StackWaitBlock.Last = &StackWaitBlock;
676                         StackWaitBlock.Wake = 0;
677 
678                         NewValue = (ULONG_PTR)&StackWaitBlock | RTL_SRWLOCK_OWNED | RTL_SRWLOCK_CONTENDED;
679                         if ((LONG_PTR)InterlockedCompareExchangePointer(&SRWLock->Ptr,
680                                                                         (PVOID)NewValue,
681                                                                         (PVOID)CurrentValue) == CurrentValue)
682                         {
683                             RtlpAcquireSRWLockExclusiveWait(SRWLock,
684                                                             &StackWaitBlock);
685 
686                             /* Successfully acquired the exclusive lock */
687                             break;
688                         }
689                     }
690                 }
691                 else
692                 {
693                     if (!InterlockedBitTestAndSetPointer(&SRWLock->Ptr,
694                                                          RTL_SRWLOCK_OWNED_BIT))
695                     {
696                         /* We managed to get hold of a simple exclusive lock! */
697                         break;
698                     }
699                 }
700             }
701 
702             YieldProcessor();
703         }
704     }
705 }
706 
707 
708 VOID
709 NTAPI
710 RtlReleaseSRWLockExclusive(IN OUT PRTL_SRWLOCK SRWLock)
711 {
712     LONG_PTR CurrentValue, NewValue;
713     PRTLP_SRWLOCK_WAITBLOCK WaitBlock;
714 
715     while (1)
716     {
717         CurrentValue = *(volatile LONG_PTR *)&SRWLock->Ptr;
718 
719         if (!(CurrentValue & RTL_SRWLOCK_OWNED))
720         {
721             RtlRaiseStatus(STATUS_RESOURCE_NOT_OWNED);
722         }
723 
724         if (!(CurrentValue & RTL_SRWLOCK_SHARED))
725         {
726             if (CurrentValue & RTL_SRWLOCK_CONTENDED)
727             {
728                 /* There's a wait block, we need to wake the next pending
729                    acquirer (exclusive or shared) */
730                 WaitBlock = RtlpAcquireWaitBlockLock(SRWLock);
731                 if (WaitBlock != NULL)
732                 {
733                     RtlpReleaseWaitBlockLockExclusive(SRWLock,
734                                                       WaitBlock);
735 
736                     /* We released the lock */
737                     break;
738                 }
739             }
740             else
741             {
742                 /* This is a fast path, we can simply clear the RTL_SRWLOCK_OWNED
743                    bit. All other bits should be 0 now because this is a simple
744                    exclusive lock and no one is waiting. */
745 
746                 ASSERT(!(CurrentValue & ~RTL_SRWLOCK_OWNED));
747 
748                 NewValue = 0;
749                 if ((LONG_PTR)InterlockedCompareExchangePointer(&SRWLock->Ptr,
750                                                                 (PVOID)NewValue,
751                                                                 (PVOID)CurrentValue) == CurrentValue)
752                 {
753                     /* We released the lock */
754                     break;
755                 }
756             }
757         }
758         else
759         {
760             /* The RTL_SRWLOCK_SHARED bit must not be present now,
761                not even in the contended case! */
762             RtlRaiseStatus(STATUS_RESOURCE_NOT_OWNED);
763         }
764 
765         YieldProcessor();
766     }
767 }
768 
769 BOOLEAN
770 NTAPI
771 RtlTryAcquireSRWLockShared(PRTL_SRWLOCK SRWLock)
772 {
773 
774     LONG_PTR CompareValue, NewValue, GotValue;
775 
776     do
777     {
778         CompareValue = *(volatile LONG_PTR *)&SRWLock->Ptr;
779         NewValue = ((CompareValue >> RTL_SRWLOCK_BITS) + 1) | RTL_SRWLOCK_SHARED | RTL_SRWLOCK_OWNED;
780 
781         /* Only increment shared count if there is no waiter */
782         CompareValue &= ~RTL_SRWLOCK_MASK | RTL_SRWLOCK_SHARED | RTL_SRWLOCK_OWNED;
783     } while (
784         ((GotValue = (LONG_PTR)InterlockedCompareExchangePointer(&SRWLock->Ptr, (LONG_PTR*)NewValue, (LONG_PTR*)CompareValue)) != CompareValue)
785         && (((GotValue & RTL_SRWLOCK_MASK) == (RTL_SRWLOCK_SHARED | RTL_SRWLOCK_OWNED)) || (GotValue == 0)));
786 
787     return ((GotValue & RTL_SRWLOCK_MASK) == (RTL_SRWLOCK_SHARED | RTL_SRWLOCK_OWNED)) || (GotValue == 0);
788 }
789 
790 BOOLEAN
791 NTAPI
792 RtlTryAcquireSRWLockExclusive(PRTL_SRWLOCK SRWLock)
793 {
794     return InterlockedCompareExchangePointer(&SRWLock->Ptr, (ULONG_PTR*)RTL_SRWLOCK_OWNED, 0) == 0;
795 }
796