Subversion Repositories Projects

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2498 - 1
/*
2
 * Process Hacker -
3
 *   fast resource lock
4
 *
5
 * Copyright (C) 2009 wj32
6
 *
7
 * This file is part of Process Hacker.
8
 *
9
 * Process Hacker is free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation, either version 3 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * Process Hacker is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with Process Hacker.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
 
23
//#define DEFER_EVENT_CREATION
24
//#define ENABLE_STATISTICS
25
//#define RIGOROUS_CHECKS
26
 
27
using System;
28
using System.Runtime.InteropServices;
29
using System.Security;
30
using System.Threading;
31
 
32
namespace GMap.NET.Internals
33
{
34
#if !MONO && !PocketPC
35
   /// <summary>
36
   /// Provides a fast resource (reader-writer) lock.
37
   /// </summary>
38
   /// <remarks>
39
   /// There are three types of acquire methods in this lock:
40
   /// 
41
   /// Normal methods (AcquireExclusive, AcquireShared) are preferred 
42
   /// for general purpose use.
43
   /// Busy wait methods (SpinAcquireExclusive, SpinAcquireShared) are 
44
   /// preferred if very little time is spent while the lock is acquired. 
45
   /// However, these do not give exclusive acquires precedence over 
46
   /// shared acquires.
47
   /// Try methods (TryAcquireExclusive, TryAcquireShared) can be used to 
48
   /// quickly test if the lock is available.
49
   /// 
50
   /// Note that all three types of functions can be used concurrently 
51
   /// in the same class instance.
52
   /// </remarks>
53
   internal sealed class FastResourceLock : IDisposable
54
   {
55
      // Details
56
      //
57
      // Resource lock value width: 32 bits.
58
      // Lock owned (either exclusive or shared): L (1 bit).
59
      // Exclusive waking: W (1 bit).
60
      // Shared owners count: SC (10 bits).
61
      // Shared waiters count: SW (10 bits).
62
      // Exclusive waiters count: EW (10 bits).
63
      //
64
      // Acquire exclusive:
65
      // {L=0,W=0,SC=0,SW,EW=0} -> {L=1,W=0,SC=0,SW,EW=0}
66
      // {L=0,W=1,SC=0,SW,EW} or {L=1,W,SC,SW,EW} ->
67
      //     {L,W,SC,SW,EW+1},
68
      //     wait on event,
69
      //     {L=0,W=1,SC=0,SW,EW} -> {L=1,W=0,SC=0,SW,EW}
70
      //
71
      // Acquire shared:
72
      // {L=0,W=0,SC=0,SW,EW=0} -> {L=1,W=0,SC=1,SW,EW=0}
73
      // {L=1,W=0,SC>0,SW,EW=0} -> {L=1,W=0,SC+1,SW,EW=0}
74
      // {L=1,W=0,SC=0,SW,EW=0} or {L,W=1,SC,SW,EW} or
75
      //     {L,W,SC,SW,EW>0} -> {L,W,SC,SW+1,EW},
76
      //     wait on event,
77
      //     retry.
78
      //
79
      // Release exclusive:
80
      // {L=1,W=0,SC=0,SW,EW>0} ->
81
      //     {L=0,W=1,SC=0,SW,EW-1},
82
      //     release one exclusive waiter.
83
      // {L=1,W=0,SC=0,SW,EW=0} ->
84
      //     {L=0,W=0,SC=0,SW=0,EW=0},
85
      //     release all shared waiters.
86
      //
87
      // Note that we never do a direct acquire when W=1 
88
      // (i.e. L=0 if W=1), so here we don't have to check 
89
      // the value of W.
90
      //
91
      // Release shared:
92
      // {L=1,W=0,SC>1,SW,EW} -> {L=1,W=0,SC-1,SW,EW}
93
      // {L=1,W=0,SC=1,SW,EW=0} -> {L=0,W=0,SC=0,SW,EW=0}
94
      // {L=1,W=0,SC=1,SW,EW>0} ->
95
      //     {L=0,W=1,SC=0,SW,EW-1},
96
      //     release one exclusive waiter.
97
      //
98
      // Again, we don't need to check the value of W.
99
      //
100
      // Convert exclusive to shared:
101
      // {L=1,W=0,SC=0,SW,EW} ->
102
      //     {L=1,W=0,SC=1,SW=0,EW},
103
      //     release all shared waiters.
104
      //
105
      // Convert shared to exclusive:
106
      // {L=1,W=0,SC=1,SW,EW} ->
107
      //     {L=1,W=0,SC=0,SW,EW}
108
      //
109
 
110
      /* */
111
 
112
      // Note: I have included many small optimizations in the code 
113
      // because of the CLR's dumbass JIT compiler.
114
 
115
      #region Constants
116
 
117
      // Lock owned: 1 bit.
118
      private const int LockOwned = 0x1;
119
 
120
      // Exclusive waking: 1 bit.
121
      private const int LockExclusiveWaking = 0x2;
122
 
123
      // Shared owners count: 10 bits.
124
      private const int LockSharedOwnersShift = 2;
125
      private const int LockSharedOwnersMask = 0x3ff;
126
      private const int LockSharedOwnersIncrement = 0x4;
127
 
128
      // Shared waiters count: 10 bits.
129
      private const int LockSharedWaitersShift = 12;
130
      private const int LockSharedWaitersMask = 0x3ff;
131
      private const int LockSharedWaitersIncrement = 0x1000;
132
 
133
      // Exclusive waiters count: 10 bits.
134
      private const int LockExclusiveWaitersShift = 22;
135
      private const int LockExclusiveWaitersMask = 0x3ff;
136
      private const int LockExclusiveWaitersIncrement = 0x400000;
137
 
138
      private const int ExclusiveMask = LockExclusiveWaking | (LockExclusiveWaitersMask << LockExclusiveWaitersShift);
139
 
140
      #endregion
141
 
142
      public struct Statistics
143
      {
144
         /// <summary>
145
         /// The number of times the lock has been acquired in exclusive mode.
146
         /// </summary>
147
         public int AcqExcl;
148
         /// <summary>
149
         /// The number of times the lock has been acquired in shared mode.
150
         /// </summary>
151
         public int AcqShrd;
152
         /// <summary>
153
         /// The number of times either the fast path was retried due to the 
154
         /// spin count or the exclusive waiter went to sleep.
155
         /// </summary>
156
         /// <remarks>
157
         /// This number is usually much higher than AcqExcl, and indicates 
158
         /// a good spin count if AcqExclSlp is very small.
159
         /// </remarks>
160
         public int AcqExclCont;
161
         /// <summary>
162
         /// The number of times either the fast path was retried due to the 
163
         /// spin count or the shared waiter went to sleep.
164
         /// </summary>
165
         /// <remarks>
166
         /// This number is usually much higher than AcqShrd, and indicates 
167
         /// a good spin count if AcqShrdSlp is very small.
168
         /// </remarks>
169
         public int AcqShrdCont;
170
         /// <summary>
171
         /// The number of times exclusive waiters have gone to sleep.
172
         /// </summary>
173
         /// <remarks>
174
         /// If this number is high and not much time is spent in the 
175
         /// lock, consider increasing the spin count.
176
         /// </remarks>
177
         public int AcqExclSlp;
178
         /// <summary>
179
         /// The number of times shared waiters have gone to sleep.
180
         /// </summary>
181
         /// <remarks>
182
         /// If this number is high and not much time is spent in the 
183
         /// lock, consider increasing the spin count.
184
         /// </remarks>
185
         public int AcqShrdSlp;
186
         /// <summary>
187
         /// The highest number of exclusive waiters at any one time.
188
         /// </summary>
189
         public int PeakExclWtrsCount;
190
         /// <summary>
191
         /// The highest number of shared waiters at any one time.
192
         /// </summary>
193
         public int PeakShrdWtrsCount;
194
      }
195
 
196
      // The number of times to spin before going to sleep.
197
      private static readonly int SpinCount = NativeMethods.SpinCount;
198
 
199
      private int _value;
200
      private IntPtr _sharedWakeEvent;
201
      private IntPtr _exclusiveWakeEvent;
202
 
203
#if ENABLE_STATISTICS
204
        private int _acqExclCount = 0;
205
        private int _acqShrdCount = 0;
206
        private int _acqExclContCount = 0;
207
        private int _acqShrdContCount = 0;
208
        private int _acqExclSlpCount = 0;
209
        private int _acqShrdSlpCount = 0;
210
        private int _peakExclWtrsCount = 0;
211
        private int _peakShrdWtrsCount = 0;
212
#endif
213
 
214
      /// <summary>
215
      /// Creates a FastResourceLock.
216
      /// </summary>
217
      public FastResourceLock()
218
      {
219
         _value = 0;
220
 
221
#if !DEFER_EVENT_CREATION
222
            _sharedWakeEvent = NativeMethods.CreateSemaphore(IntPtr.Zero, 0, int.MaxValue, null);
223
            _exclusiveWakeEvent = NativeMethods.CreateSemaphore(IntPtr.Zero, 0, int.MaxValue, null);
224
#endif
225
      }
226
 
227
      ~FastResourceLock()
228
      {
229
         this.Dispose(false);
230
      }
231
 
232
      private void Dispose(bool disposing)
233
      {
234
         if(_sharedWakeEvent != IntPtr.Zero)
235
         {
236
            NativeMethods.CloseHandle(_sharedWakeEvent);
237
            _sharedWakeEvent = IntPtr.Zero;
238
         }
239
 
240
         if(_exclusiveWakeEvent != IntPtr.Zero)
241
         {
242
            NativeMethods.CloseHandle(_exclusiveWakeEvent);
243
            _exclusiveWakeEvent = IntPtr.Zero;
244
         }
245
      }
246
 
247
      /// <summary>
248
      /// Disposes resources associated with the FastResourceLock.
249
      /// </summary>
250
      public void Dispose()
251
      {
252
         this.Dispose(true);
253
         GC.SuppressFinalize(this);
254
      }
255
 
256
      /// <summary>
257
      /// Gets the number of exclusive waiters.
258
      /// </summary>
259
      public int ExclusiveWaiters
260
      {
261
         get
262
         {
263
            return (_value >> LockExclusiveWaitersShift) & LockExclusiveWaitersMask;
264
         }
265
      }
266
 
267
      /// <summary>
268
      /// Gets whether the lock is owned in either 
269
      /// exclusive or shared mode.
270
      /// </summary>
271
      public bool Owned
272
      {
273
         get
274
         {
275
            return (_value & LockOwned) != 0;
276
         }
277
      }
278
 
279
      /// <summary>
280
      /// Gets the number of shared owners.
281
      /// </summary>
282
      public int SharedOwners
283
      {
284
         get
285
         {
286
            return (_value >> LockSharedOwnersShift) & LockSharedOwnersMask;
287
         }
288
      }
289
 
290
      /// <summary>
291
      /// Gets the number of shared waiters.
292
      /// </summary>
293
      public int SharedWaiters
294
      {
295
         get
296
         {
297
            return (_value >> LockSharedWaitersShift) & LockSharedWaitersMask;
298
         }
299
      }
300
 
301
      /// <summary>
302
      /// Acquires the lock in exclusive mode, blocking 
303
      /// if necessary.
304
      /// </summary>
305
      /// <remarks>
306
      /// Exclusive acquires are given precedence over shared 
307
      /// acquires.
308
      /// </remarks>
309
      public void AcquireExclusive()
310
      {
311
         int value;
312
         int i = 0;
313
 
314
#if ENABLE_STATISTICS
315
            Interlocked.Increment(ref _acqExclCount);
316
 
317
#endif
318
         while(true)
319
         {
320
            value = _value;
321
 
322
            // Case 1: lock not owned AND an exclusive waiter is not waking up.
323
            // Here we don't have to check if there are exclusive waiters, because 
324
            // if there are the lock would be owned, and we are checking that anyway.
325
            if((value & (LockOwned | LockExclusiveWaking)) == 0)
326
            {
327
#if RIGOROUS_CHECKS
328
                    System.Diagnostics.Trace.Assert(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) == 0);
329
                    System.Diagnostics.Trace.Assert(((value >> LockExclusiveWaitersShift) & LockExclusiveWaitersMask) == 0);
330
 
331
#endif
332
               if(Interlocked.CompareExchange(
333
                   ref _value,
334
                   value + LockOwned,
335
                   value
336
                   ) == value)
337
                  break;
338
            }
339
            // Case 2: lock owned OR lock not owned and an exclusive waiter is waking up. 
340
            // The second case means an exclusive waiter has just been woken up and is 
341
            // going to acquire the lock. We have to go to sleep to make sure we don't 
342
            // steal the lock.
343
            else if(i >= SpinCount)
344
            {
345
#if DEFER_EVENT_CREATION
346
               // This call must go *before* the next operation. Otherwise, 
347
               // we will have a race condition between potential releasers 
348
               // and us.
349
               this.EnsureEventCreated(ref _exclusiveWakeEvent);
350
 
351
#endif
352
               if(Interlocked.CompareExchange(
353
                   ref _value,
354
                   value + LockExclusiveWaitersIncrement,
355
                   value
356
                   ) == value)
357
               {
358
#if ENABLE_STATISTICS
359
                        Interlocked.Increment(ref _acqExclSlpCount);
360
 
361
                        int exclWtrsCount = (value >> LockExclusiveWaitersShift) & LockExclusiveWaitersMask;
362
 
363
                        Interlocked2.Set(
364
                            ref _peakExclWtrsCount,
365
                            (p) => p < exclWtrsCount,
366
                            (p) => exclWtrsCount
367
                            );
368
 
369
#endif
370
                  // Go to sleep.
371
                  if(NativeMethods.WaitForSingleObject(
372
                      _exclusiveWakeEvent,
373
                      Timeout.Infinite
374
                      ) != NativeMethods.WaitObject0)
375
                     UtilsBreak("Utils.MsgFailedToWaitIndefinitely");
376
 
377
                  // Acquire the lock. 
378
                  // At this point *no one* should be able to steal the lock from us.
379
                  do
380
                  {
381
                     value = _value;
382
#if RIGOROUS_CHECKS
383
 
384
                            System.Diagnostics.Trace.Assert((value & LockOwned) == 0);
385
                            System.Diagnostics.Trace.Assert((value & LockExclusiveWaking) != 0);
386
#endif
387
                  } while(Interlocked.CompareExchange(
388
                      ref _value,
389
                      value + LockOwned - LockExclusiveWaking,
390
                      value
391
                      ) != value);
392
 
393
                  break;
394
               }
395
            }
396
 
397
#if ENABLE_STATISTICS
398
                Interlocked.Increment(ref _acqExclContCount);
399
#endif
400
            i++;
401
         }
402
      }
403
 
404
      /// <summary>
405
      /// Acquires the lock in shared mode, blocking 
406
      /// if necessary.
407
      /// </summary>
408
      /// <remarks>
409
      /// Exclusive acquires are given precedence over shared 
410
      /// acquires.
411
      /// </remarks>
412
      public void AcquireShared()
413
      {
414
         int value;
415
         int i = 0;
416
 
417
#if ENABLE_STATISTICS
418
            Interlocked.Increment(ref _acqShrdCount);
419
 
420
#endif
421
         while(true)
422
         {
423
            value = _value;
424
 
425
            // Case 1: lock not owned AND no exclusive waiter is waking up AND 
426
            // there are no shared owners AND there are no exclusive waiters
427
            if((value & (
428
                LockOwned |
429
                (LockSharedOwnersMask << LockSharedOwnersShift) |
430
                ExclusiveMask
431
                )) == 0)
432
            {
433
               if(Interlocked.CompareExchange(
434
                   ref _value,
435
                   value + LockOwned + LockSharedOwnersIncrement,
436
                   value
437
                   ) == value)
438
                  break;
439
            }
440
            // Case 2: lock is owned AND no exclusive waiter is waking up AND 
441
            // there are shared owners AND there are no exclusive waiters
442
            else if(
443
                (value & LockOwned) != 0 &&
444
                ((value >> LockSharedOwnersShift) & LockSharedOwnersMask) != 0 &&
445
                (value & ExclusiveMask) == 0
446
                )
447
            {
448
               if(Interlocked.CompareExchange(
449
                   ref _value,
450
                   value + LockSharedOwnersIncrement,
451
                   value
452
                   ) == value)
453
                  break;
454
            }
455
            // Other cases.
456
            else if(i >= SpinCount)
457
            {
458
#if DEFER_EVENT_CREATION
459
               this.EnsureEventCreated(ref _sharedWakeEvent);
460
 
461
#endif
462
               if(Interlocked.CompareExchange(
463
                   ref _value,
464
                   value + LockSharedWaitersIncrement,
465
                   value
466
                   ) == value)
467
               {
468
#if ENABLE_STATISTICS
469
                        Interlocked.Increment(ref _acqShrdSlpCount);
470
 
471
                        int shrdWtrsCount = (value >> LockSharedWaitersShift) & LockSharedWaitersMask;
472
 
473
                        Interlocked2.Set(
474
                            ref _peakShrdWtrsCount,
475
                            (p) => p < shrdWtrsCount,
476
                            (p) => shrdWtrsCount
477
                            );
478
 
479
#endif
480
                  // Go to sleep.
481
                  if(NativeMethods.WaitForSingleObject(
482
                      _sharedWakeEvent,
483
                      Timeout.Infinite
484
                      ) != NativeMethods.WaitObject0)
485
                     UtilsBreak("Utils.MsgFailedToWaitIndefinitely");
486
 
487
                  // Go back and try again.
488
                  continue;
489
               }
490
            }
491
 
492
#if ENABLE_STATISTICS
493
                Interlocked.Increment(ref _acqShrdContCount);
494
#endif
495
            i++;
496
         }
497
      }
498
 
499
      public static void UtilsBreak(string logMessage)
500
      {
501
         System.Diagnostics.Debugger.Log(0, "Error", logMessage);
502
         System.Diagnostics.Debugger.Break();
503
      }
504
 
505
      /// <summary>
506
      /// Converts the ownership mode from exclusive to shared.
507
      /// </summary>
508
      /// <remarks>
509
      /// Exclusive acquires are not given a chance to acquire 
510
      /// the lock before this function does - as a result, 
511
      /// this function will never block.
512
      /// </remarks>
513
      public void ConvertExclusiveToShared()
514
      {
515
         int value;
516
         int sharedWaiters;
517
 
518
         while(true)
519
         {
520
            value = _value;
521
#if RIGOROUS_CHECKS
522
 
523
                    System.Diagnostics.Trace.Assert((value & LockOwned) != 0);
524
                    System.Diagnostics.Trace.Assert((value & LockExclusiveWaking) == 0);
525
                    System.Diagnostics.Trace.Assert(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) == 0);
526
#endif
527
 
528
            sharedWaiters = (value >> LockSharedWaitersShift) & LockSharedWaitersMask;
529
 
530
            if(Interlocked.CompareExchange(
531
                ref _value,
532
                (value + LockSharedOwnersIncrement) & ~(LockSharedWaitersMask << LockSharedWaitersShift),
533
                value
534
                ) == value)
535
            {
536
               if(sharedWaiters != 0)
537
                  NativeMethods.ReleaseSemaphore(_sharedWakeEvent, sharedWaiters, IntPtr.Zero);
538
 
539
               break;
540
            }
541
         }
542
      }
543
 
544
#if DEFER_EVENT_CREATION
545
      /// <summary>
546
      /// Checks if the specified event has been created, and 
547
      /// if not, creates it.
548
      /// </summary>
549
      /// <param name="handle">A reference to the event handle.</param>
550
      private void EnsureEventCreated(ref IntPtr handle)
551
      {
552
         IntPtr eventHandle;
553
 
554
         if(Thread.VolatileRead(ref handle) != IntPtr.Zero)
555
            return;
556
 
557
         eventHandle = NativeMethods.CreateSemaphore(IntPtr.Zero, 0, int.MaxValue, null);
558
 
559
         if(Interlocked.CompareExchange(ref handle, eventHandle, IntPtr.Zero) != IntPtr.Zero)
560
            NativeMethods.CloseHandle(eventHandle);
561
      }
562
#endif
563
 
564
      /// <summary>
565
      /// Gets statistics information for the lock.
566
      /// </summary>
567
      /// <returns>A structure containing statistics.</returns>
568
      public Statistics GetStatistics()
569
      {
570
#if ENABLE_STATISTICS
571
            return new Statistics()
572
            {
573
                AcqExcl = _acqExclCount,
574
                AcqShrd = _acqShrdCount,
575
                AcqExclCont = _acqExclContCount,
576
                AcqShrdCont = _acqShrdContCount,
577
                AcqExclSlp = _acqExclSlpCount,
578
                AcqShrdSlp = _acqShrdSlpCount,
579
                PeakExclWtrsCount = _peakExclWtrsCount,
580
                PeakShrdWtrsCount = _peakShrdWtrsCount
581
            };
582
#else
583
         return new Statistics();
584
#endif
585
      }
586
 
587
      /// <summary>
588
      /// Releases the lock in exclusive mode.
589
      /// </summary>
590
      public void ReleaseExclusive()
591
      {
592
         int value;
593
 
594
         while(true)
595
         {
596
            value = _value;
597
#if RIGOROUS_CHECKS
598
 
599
                System.Diagnostics.Trace.Assert((value & LockOwned) != 0);
600
                System.Diagnostics.Trace.Assert((value & LockExclusiveWaking) == 0);
601
                System.Diagnostics.Trace.Assert(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) == 0);
602
#endif
603
 
604
            // Case 1: if we have exclusive waiters, release one.
605
            if(((value >> LockExclusiveWaitersShift) & LockExclusiveWaitersMask) != 0)
606
            {
607
               if(Interlocked.CompareExchange(
608
                   ref _value,
609
                   value - LockOwned + LockExclusiveWaking - LockExclusiveWaitersIncrement,
610
                   value
611
                   ) == value)
612
               {
613
                  NativeMethods.ReleaseSemaphore(_exclusiveWakeEvent, 1, IntPtr.Zero);
614
 
615
                  break;
616
               }
617
            }
618
            // Case 2: if we have shared waiters, release all of them.
619
            else
620
            {
621
               int sharedWaiters;
622
 
623
               sharedWaiters = (value >> LockSharedWaitersShift) & LockSharedWaitersMask;
624
 
625
               if(Interlocked.CompareExchange(
626
                   ref _value,
627
                   value & ~(LockOwned | (LockSharedWaitersMask << LockSharedWaitersShift)),
628
                   value
629
                   ) == value)
630
               {
631
                  if(sharedWaiters != 0)
632
                     NativeMethods.ReleaseSemaphore(_sharedWakeEvent, sharedWaiters, IntPtr.Zero);
633
 
634
                  break;
635
               }
636
            }
637
         }
638
      }
639
 
640
      /// <summary>
641
      /// Releases the lock in shared mode.
642
      /// </summary>
643
      public void ReleaseShared()
644
      {
645
         int value;
646
         int sharedOwners;
647
 
648
         while(true)
649
         {
650
            value = _value;
651
#if RIGOROUS_CHECKS
652
 
653
                System.Diagnostics.Trace.Assert((value & LockOwned) != 0);
654
                System.Diagnostics.Trace.Assert((value & LockExclusiveWaking) == 0);
655
                System.Diagnostics.Trace.Assert(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) != 0);
656
#endif
657
 
658
            sharedOwners = (value >> LockSharedOwnersShift) & LockSharedOwnersMask;
659
 
660
            // Case 1: there are multiple shared owners.
661
            if(sharedOwners > 1)
662
            {
663
               if(Interlocked.CompareExchange(
664
                   ref _value,
665
                   value - LockSharedOwnersIncrement,
666
                   value
667
                   ) == value)
668
                  break;
669
            }
670
            // Case 2: we are the last shared owner AND there are exclusive waiters.
671
            else if(((value >> LockExclusiveWaitersShift) & LockExclusiveWaitersMask) != 0)
672
            {
673
               if(Interlocked.CompareExchange(
674
                   ref _value,
675
                   value - LockOwned + LockExclusiveWaking - LockSharedOwnersIncrement - LockExclusiveWaitersIncrement,
676
                   value
677
                   ) == value)
678
               {
679
                  NativeMethods.ReleaseSemaphore(_exclusiveWakeEvent, 1, IntPtr.Zero);
680
 
681
                  break;
682
               }
683
            }
684
            // Case 3: we are the last shared owner AND there are no exclusive waiters.
685
            else
686
            {
687
               if(Interlocked.CompareExchange(
688
                   ref _value,
689
                   value - LockOwned - LockSharedOwnersIncrement,
690
                   value
691
                   ) == value)
692
                  break;
693
            }
694
         }
695
      }
696
 
697
      /// <summary>
698
      /// Acquires the lock in exclusive mode, busy waiting 
699
      /// if necessary.
700
      /// </summary>
701
      /// <remarks>
702
      /// Exclusive acquires are *not* given precedence over shared 
703
      /// acquires for busy wait methods.
704
      /// </remarks>
705
      public void SpinAcquireExclusive()
706
      {
707
         int value;
708
 
709
         while(true)
710
         {
711
            value = _value;
712
 
713
            if((value & (LockOwned | LockExclusiveWaking)) == 0)
714
            {
715
               if(Interlocked.CompareExchange(
716
                   ref _value,
717
                   value + LockOwned,
718
                   value
719
                   ) == value)
720
                  break;
721
            }
722
 
723
            if(NativeMethods.SpinEnabled)
724
               Thread.SpinWait(8);
725
            else
726
               Thread.Sleep(0);
727
         }
728
      }
729
 
730
      /// <summary>
731
      /// Acquires the lock in shared mode, busy waiting 
732
      /// if necessary.
733
      /// </summary>
734
      /// <remarks>
735
      /// Exclusive acquires are *not* given precedence over shared 
736
      /// acquires for busy wait methods.
737
      /// </remarks>
738
      public void SpinAcquireShared()
739
      {
740
         int value;
741
 
742
         while(true)
743
         {
744
            value = _value;
745
 
746
            if((value & ExclusiveMask) == 0)
747
            {
748
               if((value & LockOwned) == 0)
749
               {
750
                  if(Interlocked.CompareExchange(
751
                      ref _value,
752
                      value + LockOwned + LockSharedOwnersIncrement,
753
                      value
754
                      ) == value)
755
                     break;
756
               }
757
               else if(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) != 0)
758
               {
759
                  if(Interlocked.CompareExchange(
760
                      ref _value,
761
                      value + LockSharedOwnersIncrement,
762
                      value
763
                      ) == value)
764
                     break;
765
               }
766
            }
767
 
768
            if(NativeMethods.SpinEnabled)
769
               Thread.SpinWait(8);
770
            else
771
               Thread.Sleep(0);
772
         }
773
      }
774
 
775
      /// <summary>
776
      /// Converts the ownership mode from shared to exclusive, 
777
      /// busy waiting if necessary.
778
      /// </summary>
779
      public void SpinConvertSharedToExclusive()
780
      {
781
         int value;
782
 
783
         while(true)
784
         {
785
            value = _value;
786
 
787
            // Can't convert if there are other shared owners.
788
            if(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) == 1)
789
            {
790
               if(Interlocked.CompareExchange(
791
                   ref _value,
792
                   value - LockSharedOwnersIncrement,
793
                   value
794
                   ) == value)
795
                  break;
796
            }
797
 
798
            if(NativeMethods.SpinEnabled)
799
               Thread.SpinWait(8);
800
            else
801
               Thread.Sleep(0);
802
         }
803
      }
804
 
805
      /// <summary>
806
      /// Attempts to acquire the lock in exclusive mode.
807
      /// </summary>
808
      /// <returns>Whether the lock was acquired.</returns>
809
      public bool TryAcquireExclusive()
810
      {
811
         int value;
812
 
813
         value = _value;
814
 
815
         if((value & (LockOwned | LockExclusiveWaking)) != 0)
816
            return false;
817
 
818
         return Interlocked.CompareExchange(
819
             ref _value,
820
             value + LockOwned,
821
             value
822
             ) == value;
823
      }
824
 
825
      /// <summary>
826
      /// Attempts to acquire the lock in shared mode.
827
      /// </summary>
828
      /// <returns>Whether the lock was acquired.</returns>
829
      public bool TryAcquireShared()
830
      {
831
         int value;
832
 
833
         value = _value;
834
 
835
         if((value & ExclusiveMask) != 0)
836
            return false;
837
 
838
         if((value & LockOwned) == 0)
839
         {
840
            return Interlocked.CompareExchange(
841
                ref _value,
842
                value + LockOwned + LockSharedOwnersIncrement,
843
                value
844
                ) == value;
845
         }
846
         else if(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) != 0)
847
         {
848
            return Interlocked.CompareExchange(
849
                ref _value,
850
                value + LockSharedOwnersIncrement,
851
                value
852
                ) == value;
853
         }
854
         else
855
         {
856
            return false;
857
         }
858
      }
859
 
860
      /// <summary>
861
      /// Attempts to convert the ownership mode from shared 
862
      /// to exclusive.
863
      /// </summary>
864
      /// <returns>Whether the lock was converted.</returns>
865
      public bool TryConvertSharedToExclusive()
866
      {
867
         int value;
868
 
869
         while(true)
870
         {
871
            value = _value;
872
#if RIGOROUS_CHECKS
873
 
874
                    System.Diagnostics.Trace.Assert((value & LockOwned) != 0);
875
                    System.Diagnostics.Trace.Assert((value & LockExclusiveWaking) == 0);
876
                    System.Diagnostics.Trace.Assert(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) != 0);
877
#endif
878
 
879
            // Can't convert if there are other shared owners.
880
            if(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) != 1)
881
               return false;
882
 
883
            if(Interlocked.CompareExchange(
884
                ref _value,
885
                value - LockSharedOwnersIncrement,
886
                value
887
                ) == value)
888
               return true;
889
         }
890
      }
891
   }
892
 
893
   [SuppressUnmanagedCodeSecurity]
894
   internal class NativeMethods
895
   {
896
      public const int WaitObject0 = 0x0;
897
      public const int WaitAbandoned = 0x80;
898
      public const int WaitTimeout = 0x102;
899
      public const int WaitFailed = -1;
900
 
901
      public static readonly int SpinCount = Environment.ProcessorCount != 1 ? 4000 : 0;
902
      public static readonly bool SpinEnabled = Environment.ProcessorCount != 1;
903
 
904
      // We need to import some stuff. We can't use 
905
      // ProcessHacker.Native because it depends on this library.
906
 
907
      [DllImport("kernel32.dll")]
908
      public static extern bool CloseHandle(
909
          [In] IntPtr Handle
910
          );
911
 
912
      [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
913
      public static extern IntPtr CreateEvent(
914
          [In] [Optional] IntPtr EventAttributes,
915
          [In] bool ManualReset,
916
          [In] bool InitialState,
917
          [In] [Optional] string Name
918
          );
919
 
920
      [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
921
      public static extern IntPtr CreateSemaphore(
922
          [In] [Optional] IntPtr SemaphoreAttributes,
923
          [In] int InitialCount,
924
          [In] int MaximumCount,
925
          [In] [Optional] string Name
926
          );
927
 
928
      [DllImport("kernel32.dll")]
929
      public static extern bool ReleaseSemaphore(
930
          [In] IntPtr SemaphoreHandle,
931
          [In] int ReleaseCount,
932
          [In] IntPtr PreviousCount // out int
933
          );
934
 
935
      [DllImport("kernel32.dll")]
936
      public static extern bool ResetEvent(
937
          [In] IntPtr EventHandle
938
          );
939
 
940
      [DllImport("kernel32.dll")]
941
      public static extern bool SetEvent(
942
          [In] IntPtr EventHandle
943
          );
944
 
945
      [DllImport("kernel32.dll")]
946
      public static extern int WaitForSingleObject(
947
          [In] IntPtr Handle,
948
          [In] int Milliseconds
949
          );
950
 
951
      [DllImport("ntdll.dll")]
952
      public static extern int NtCreateKeyedEvent(
953
          [Out] out IntPtr KeyedEventHandle,
954
          [In] int DesiredAccess,
955
          [In] [Optional] IntPtr ObjectAttributes,
956
          [In] int Flags
957
          );
958
 
959
      [DllImport("ntdll.dll")]
960
      public static extern int NtReleaseKeyedEvent(
961
          [In] IntPtr KeyedEventHandle,
962
          [In] IntPtr KeyValue,
963
          [In] bool Alertable,
964
          [In] [Optional] IntPtr Timeout
965
          );
966
 
967
      [DllImport("ntdll.dll")]
968
      public static extern int NtWaitForKeyedEvent(
969
          [In] IntPtr KeyedEventHandle,
970
          [In] IntPtr KeyValue,
971
          [In] bool Alertable,
972
          [In] [Optional] IntPtr Timeout
973
          );
974
   }
975
#endif
976
}