/*
 * Process Hacker - 
 *   fast resource lock
 * 
 * Copyright (C) 2009 wj32
 * 
 * This file is part of Process Hacker.
 * 
 * Process Hacker is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Process Hacker is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Process Hacker.  If not, see <http://www.gnu.org/licenses/>.
 */

#define DEFER_EVENT_CREATION
//#define ENABLE_STATISTICS
//#define RIGOROUS_CHECKS

using System;
using System.Runtime.InteropServices;
using System.Security;
using System.Threading;

namespace GMap.NET.Internals
{
#if !MONO && !PocketPC
   /// <summary>
   /// Provides a fast resource (reader-writer) lock.
   /// </summary>
   /// <remarks>
   /// There are three types of acquire methods in this lock:
   /// 
   /// Normal methods (AcquireExclusive, AcquireShared) are preferred 
   /// for general purpose use.
   /// Busy wait methods (SpinAcquireExclusive, SpinAcquireShared) are 
   /// preferred if very little time is spent while the lock is acquired. 
   /// However, these do not give exclusive acquires precedence over 
   /// shared acquires.
   /// Try methods (TryAcquireExclusive, TryAcquireShared) can be used to 
   /// quickly test if the lock is available.
   /// 
   /// Note that all three types of functions can be used concurrently 
   /// in the same class instance.
   /// </remarks>
   internal sealed class FastResourceLock : IDisposable
   {
      // Details
      //
      // Resource lock value width: 32 bits.
      // Lock owned (either exclusive or shared): L (1 bit).
      // Exclusive waking: W (1 bit).
      // Shared owners count: SC (10 bits).
      // Shared waiters count: SW (10 bits).
      // Exclusive waiters count: EW (10 bits).
      //
      // Acquire exclusive:
      // {L=0,W=0,SC=0,SW,EW=0} -> {L=1,W=0,SC=0,SW,EW=0}
      // {L=0,W=1,SC=0,SW,EW} or {L=1,W,SC,SW,EW} ->
      //     {L,W,SC,SW,EW+1},
      //     wait on event,
      //     {L=0,W=1,SC=0,SW,EW} -> {L=1,W=0,SC=0,SW,EW}
      //
      // Acquire shared:
      // {L=0,W=0,SC=0,SW,EW=0} -> {L=1,W=0,SC=1,SW,EW=0}
      // {L=1,W=0,SC>0,SW,EW=0} -> {L=1,W=0,SC+1,SW,EW=0}
      // {L=1,W=0,SC=0,SW,EW=0} or {L,W=1,SC,SW,EW} or
      //     {L,W,SC,SW,EW>0} -> {L,W,SC,SW+1,EW},
      //     wait on event,
      //     retry.
      //
      // Release exclusive:
      // {L=1,W=0,SC=0,SW,EW>0} ->
      //     {L=0,W=1,SC=0,SW,EW-1},
      //     release one exclusive waiter.
      // {L=1,W=0,SC=0,SW,EW=0} ->
      //     {L=0,W=0,SC=0,SW=0,EW=0},
      //     release all shared waiters.
      //
      // Note that we never do a direct acquire when W=1 
      // (i.e. L=0 if W=1), so here we don't have to check 
      // the value of W.
      //
      // Release shared:
      // {L=1,W=0,SC>1,SW,EW} -> {L=1,W=0,SC-1,SW,EW}
      // {L=1,W=0,SC=1,SW,EW=0} -> {L=0,W=0,SC=0,SW,EW=0}
      // {L=1,W=0,SC=1,SW,EW>0} ->
      //     {L=0,W=1,SC=0,SW,EW-1},
      //     release one exclusive waiter.
      //
      // Again, we don't need to check the value of W.
      //
      // Convert exclusive to shared:
      // {L=1,W=0,SC=0,SW,EW} ->
      //     {L=1,W=0,SC=1,SW=0,EW},
      //     release all shared waiters.
      //
      // Convert shared to exclusive:
      // {L=1,W=0,SC=1,SW,EW} ->
      //     {L=1,W=0,SC=0,SW,EW}
      //

      /* */

      // Note: I have included many small optimizations in the code 
      // because of the CLR's dumbass JIT compiler.

      #region Constants

      // Lock owned: 1 bit.
      private const int LockOwned = 0x1;

      // Exclusive waking: 1 bit.
      private const int LockExclusiveWaking = 0x2;

      // Shared owners count: 10 bits.
      private const int LockSharedOwnersShift = 2;
      private const int LockSharedOwnersMask = 0x3ff;
      private const int LockSharedOwnersIncrement = 0x4;

      // Shared waiters count: 10 bits.
      private const int LockSharedWaitersShift = 12;
      private const int LockSharedWaitersMask = 0x3ff;
      private const int LockSharedWaitersIncrement = 0x1000;

      // Exclusive waiters count: 10 bits.
      private const int LockExclusiveWaitersShift = 22;
      private const int LockExclusiveWaitersMask = 0x3ff;
      private const int LockExclusiveWaitersIncrement = 0x400000;

      private const int ExclusiveMask = LockExclusiveWaking | (LockExclusiveWaitersMask << LockExclusiveWaitersShift);

      #endregion

      public struct Statistics
      {
         /// <summary>
         /// The number of times the lock has been acquired in exclusive mode.
         /// </summary>
         public int AcqExcl;
         /// <summary>
         /// The number of times the lock has been acquired in shared mode.
         /// </summary>
         public int AcqShrd;
         /// <summary>
         /// The number of times either the fast path was retried due to the 
         /// spin count or the exclusive waiter went to sleep.
         /// </summary>
         /// <remarks>
         /// This number is usually much higher than AcqExcl, and indicates 
         /// a good spin count if AcqExclSlp is very small.
         /// </remarks>
         public int AcqExclCont;
         /// <summary>
         /// The number of times either the fast path was retried due to the 
         /// spin count or the shared waiter went to sleep.
         /// </summary>
         /// <remarks>
         /// This number is usually much higher than AcqShrd, and indicates 
         /// a good spin count if AcqShrdSlp is very small.
         /// </remarks>
         public int AcqShrdCont;
         /// <summary>
         /// The number of times exclusive waiters have gone to sleep.
         /// </summary>
         /// <remarks>
         /// If this number is high and not much time is spent in the 
         /// lock, consider increasing the spin count.
         /// </remarks>
         public int AcqExclSlp;
         /// <summary>
         /// The number of times shared waiters have gone to sleep.
         /// </summary>
         /// <remarks>
         /// If this number is high and not much time is spent in the 
         /// lock, consider increasing the spin count.
         /// </remarks>
         public int AcqShrdSlp;
         /// <summary>
         /// The highest number of exclusive waiters at any one time.
         /// </summary>
         public int PeakExclWtrsCount;
         /// <summary>
         /// The highest number of shared waiters at any one time.
         /// </summary>
         public int PeakShrdWtrsCount;
      }

      // The number of times to spin before going to sleep.
      private static readonly int SpinCount = NativeMethods.SpinCount;

      private int _value;
      private IntPtr _sharedWakeEvent;
      private IntPtr _exclusiveWakeEvent;

#if ENABLE_STATISTICS
        private int _acqExclCount = 0;
        private int _acqShrdCount = 0;
        private int _acqExclContCount = 0;
        private int _acqShrdContCount = 0;
        private int _acqExclSlpCount = 0;
        private int _acqShrdSlpCount = 0;
        private int _peakExclWtrsCount = 0;
        private int _peakShrdWtrsCount = 0;
#endif

      /// <summary>
      /// Creates a FastResourceLock.
      /// </summary>
      public FastResourceLock()
      {
         _value = 0;

#if !DEFER_EVENT_CREATION
            _sharedWakeEvent = NativeMethods.CreateSemaphore(IntPtr.Zero, 0, int.MaxValue, null);
            _exclusiveWakeEvent = NativeMethods.CreateSemaphore(IntPtr.Zero, 0, int.MaxValue, null);
#endif
      }

      ~FastResourceLock()
      {
         this.Dispose(false);
      }

      private void Dispose(bool disposing)
      {
         if(_sharedWakeEvent != IntPtr.Zero)
         {
            NativeMethods.CloseHandle(_sharedWakeEvent);
            _sharedWakeEvent = IntPtr.Zero;
         }

         if(_exclusiveWakeEvent != IntPtr.Zero)
         {
            NativeMethods.CloseHandle(_exclusiveWakeEvent);
            _exclusiveWakeEvent = IntPtr.Zero;
         }
      }

      /// <summary>
      /// Disposes resources associated with the FastResourceLock.
      /// </summary>
      public void Dispose()
      {
         this.Dispose(true);
         GC.SuppressFinalize(this);
      }

      /// <summary>
      /// Gets the number of exclusive waiters.
      /// </summary>
      public int ExclusiveWaiters
      {
         get
         {
            return (_value >> LockExclusiveWaitersShift) & LockExclusiveWaitersMask;
         }
      }

      /// <summary>
      /// Gets whether the lock is owned in either 
      /// exclusive or shared mode.
      /// </summary>
      public bool Owned
      {
         get
         {
            return (_value & LockOwned) != 0;
         }
      }

      /// <summary>
      /// Gets the number of shared owners.
      /// </summary>
      public int SharedOwners
      {
         get
         {
            return (_value >> LockSharedOwnersShift) & LockSharedOwnersMask;
         }
      }

      /// <summary>
      /// Gets the number of shared waiters.
      /// </summary>
      public int SharedWaiters
      {
         get
         {
            return (_value >> LockSharedWaitersShift) & LockSharedWaitersMask;
         }
      }

      /// <summary>
      /// Acquires the lock in exclusive mode, blocking 
      /// if necessary.
      /// </summary>
      /// <remarks>
      /// Exclusive acquires are given precedence over shared 
      /// acquires.
      /// </remarks>
      public void AcquireExclusive()
      {
         int value;
         int i = 0;

#if ENABLE_STATISTICS
            Interlocked.Increment(ref _acqExclCount);

#endif
         while(true)
         {
            value = _value;

            // Case 1: lock not owned AND an exclusive waiter is not waking up.
            // Here we don't have to check if there are exclusive waiters, because 
            // if there are the lock would be owned, and we are checking that anyway.
            if((value & (LockOwned | LockExclusiveWaking)) == 0)
            {
#if RIGOROUS_CHECKS
                    System.Diagnostics.Trace.Assert(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) == 0);
                    System.Diagnostics.Trace.Assert(((value >> LockExclusiveWaitersShift) & LockExclusiveWaitersMask) == 0);

#endif
               if(Interlocked.CompareExchange(
                   ref _value,
                   value + LockOwned,
                   value
                   ) == value)
                  break;
            }
            // Case 2: lock owned OR lock not owned and an exclusive waiter is waking up. 
            // The second case means an exclusive waiter has just been woken up and is 
            // going to acquire the lock. We have to go to sleep to make sure we don't 
            // steal the lock.
            else if(i >= SpinCount)
            {
#if DEFER_EVENT_CREATION
               // This call must go *before* the next operation. Otherwise, 
               // we will have a race condition between potential releasers 
               // and us.
               this.EnsureEventCreated(ref _exclusiveWakeEvent);

#endif
               if(Interlocked.CompareExchange(
                   ref _value,
                   value + LockExclusiveWaitersIncrement,
                   value
                   ) == value)
               {
#if ENABLE_STATISTICS
                        Interlocked.Increment(ref _acqExclSlpCount);

                        int exclWtrsCount = (value >> LockExclusiveWaitersShift) & LockExclusiveWaitersMask;

                        Interlocked2.Set(
                            ref _peakExclWtrsCount,
                            (p) => p < exclWtrsCount,
                            (p) => exclWtrsCount
                            );

#endif
                  // Go to sleep.
                  if(NativeMethods.WaitForSingleObject(
                      _exclusiveWakeEvent,
                      Timeout.Infinite
                      ) != NativeMethods.WaitObject0)
                     UtilsBreak("Utils.MsgFailedToWaitIndefinitely");

                  // Acquire the lock. 
                  // At this point *no one* should be able to steal the lock from us.
                  do
                  {
                     value = _value;
#if RIGOROUS_CHECKS

                            System.Diagnostics.Trace.Assert((value & LockOwned) == 0);
                            System.Diagnostics.Trace.Assert((value & LockExclusiveWaking) != 0);
#endif
                  } while(Interlocked.CompareExchange(
                      ref _value,
                      value + LockOwned - LockExclusiveWaking,
                      value
                      ) != value);

                  break;
               }
            }

#if ENABLE_STATISTICS
                Interlocked.Increment(ref _acqExclContCount);
#endif
            i++;
         }
      }

      /// <summary>
      /// Acquires the lock in shared mode, blocking 
      /// if necessary.
      /// </summary>
      /// <remarks>
      /// Exclusive acquires are given precedence over shared 
      /// acquires.
      /// </remarks>
      public void AcquireShared()
      {
         int value;
         int i = 0;

#if ENABLE_STATISTICS
            Interlocked.Increment(ref _acqShrdCount);

#endif
         while(true)
         {
            value = _value;

            // Case 1: lock not owned AND no exclusive waiter is waking up AND 
            // there are no shared owners AND there are no exclusive waiters
            if((value & (
                LockOwned |
                (LockSharedOwnersMask << LockSharedOwnersShift) |
                ExclusiveMask
                )) == 0)
            {
               if(Interlocked.CompareExchange(
                   ref _value,
                   value + LockOwned + LockSharedOwnersIncrement,
                   value
                   ) == value)
                  break;
            }
            // Case 2: lock is owned AND no exclusive waiter is waking up AND 
            // there are shared owners AND there are no exclusive waiters
            else if(
                (value & LockOwned) != 0 &&
                ((value >> LockSharedOwnersShift) & LockSharedOwnersMask) != 0 &&
                (value & ExclusiveMask) == 0
                )
            {
               if(Interlocked.CompareExchange(
                   ref _value,
                   value + LockSharedOwnersIncrement,
                   value
                   ) == value)
                  break;
            }
            // Other cases.
            else if(i >= SpinCount)
            {
#if DEFER_EVENT_CREATION
               this.EnsureEventCreated(ref _sharedWakeEvent);

#endif
               if(Interlocked.CompareExchange(
                   ref _value,
                   value + LockSharedWaitersIncrement,
                   value
                   ) == value)
               {
#if ENABLE_STATISTICS
                        Interlocked.Increment(ref _acqShrdSlpCount);

                        int shrdWtrsCount = (value >> LockSharedWaitersShift) & LockSharedWaitersMask;

                        Interlocked2.Set(
                            ref _peakShrdWtrsCount,
                            (p) => p < shrdWtrsCount,
                            (p) => shrdWtrsCount
                            );

#endif
                  // Go to sleep.
                  if(NativeMethods.WaitForSingleObject(
                      _sharedWakeEvent,
                      Timeout.Infinite
                      ) != NativeMethods.WaitObject0)
                     UtilsBreak("Utils.MsgFailedToWaitIndefinitely");

                  // Go back and try again.
                  continue;
               }
            }

#if ENABLE_STATISTICS
                Interlocked.Increment(ref _acqShrdContCount);
#endif
            i++;
         }
      }

      public static void UtilsBreak(string logMessage)
      {
         System.Diagnostics.Debugger.Log(0, "Error", logMessage);
         System.Diagnostics.Debugger.Break();
      }

      /// <summary>
      /// Converts the ownership mode from exclusive to shared.
      /// </summary>
      /// <remarks>
      /// Exclusive acquires are not given a chance to acquire 
      /// the lock before this function does - as a result, 
      /// this function will never block.
      /// </remarks>
      public void ConvertExclusiveToShared()
      {
         int value;
         int sharedWaiters;

         while(true)
         {
            value = _value;
#if RIGOROUS_CHECKS

                    System.Diagnostics.Trace.Assert((value & LockOwned) != 0);
                    System.Diagnostics.Trace.Assert((value & LockExclusiveWaking) == 0);
                    System.Diagnostics.Trace.Assert(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) == 0);
#endif

            sharedWaiters = (value >> LockSharedWaitersShift) & LockSharedWaitersMask;

            if(Interlocked.CompareExchange(
                ref _value,
                (value + LockSharedOwnersIncrement) & ~(LockSharedWaitersMask << LockSharedWaitersShift),
                value
                ) == value)
            {
               if(sharedWaiters != 0)
                  NativeMethods.ReleaseSemaphore(_sharedWakeEvent, sharedWaiters, IntPtr.Zero);

               break;
            }
         }
      }

#if DEFER_EVENT_CREATION
      /// <summary>
      /// Checks if the specified event has been created, and 
      /// if not, creates it.
      /// </summary>
      /// <param name="handle">A reference to the event handle.</param>
      private void EnsureEventCreated(ref IntPtr handle)
      {
         IntPtr eventHandle;

         if(Thread.VolatileRead(ref handle) != IntPtr.Zero)
            return;

         eventHandle = NativeMethods.CreateSemaphore(IntPtr.Zero, 0, int.MaxValue, null);

         if(Interlocked.CompareExchange(ref handle, eventHandle, IntPtr.Zero) != IntPtr.Zero)
            NativeMethods.CloseHandle(eventHandle);
      }
#endif

      /// <summary>
      /// Gets statistics information for the lock.
      /// </summary>
      /// <returns>A structure containing statistics.</returns>
      public Statistics GetStatistics()
      {
#if ENABLE_STATISTICS
            return new Statistics()
            {
                AcqExcl = _acqExclCount,
                AcqShrd = _acqShrdCount,
                AcqExclCont = _acqExclContCount,
                AcqShrdCont = _acqShrdContCount,
                AcqExclSlp = _acqExclSlpCount,
                AcqShrdSlp = _acqShrdSlpCount,
                PeakExclWtrsCount = _peakExclWtrsCount,
                PeakShrdWtrsCount = _peakShrdWtrsCount
            };
#else
         return new Statistics();
#endif
      }

      /// <summary>
      /// Releases the lock in exclusive mode.
      /// </summary>
      public void ReleaseExclusive()
      {
         int value;

         while(true)
         {
            value = _value;
#if RIGOROUS_CHECKS

                System.Diagnostics.Trace.Assert((value & LockOwned) != 0);
                System.Diagnostics.Trace.Assert((value & LockExclusiveWaking) == 0);
                System.Diagnostics.Trace.Assert(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) == 0);
#endif

            // Case 1: if we have exclusive waiters, release one.
            if(((value >> LockExclusiveWaitersShift) & LockExclusiveWaitersMask) != 0)
            {
               if(Interlocked.CompareExchange(
                   ref _value,
                   value - LockOwned + LockExclusiveWaking - LockExclusiveWaitersIncrement,
                   value
                   ) == value)
               {
                  NativeMethods.ReleaseSemaphore(_exclusiveWakeEvent, 1, IntPtr.Zero);

                  break;
               }
            }
            // Case 2: if we have shared waiters, release all of them.
            else
            {
               int sharedWaiters;

               sharedWaiters = (value >> LockSharedWaitersShift) & LockSharedWaitersMask;

               if(Interlocked.CompareExchange(
                   ref _value,
                   value & ~(LockOwned | (LockSharedWaitersMask << LockSharedWaitersShift)),
                   value
                   ) == value)
               {
                  if(sharedWaiters != 0)
                     NativeMethods.ReleaseSemaphore(_sharedWakeEvent, sharedWaiters, IntPtr.Zero);

                  break;
               }
            }
         }
      }

      /// <summary>
      /// Releases the lock in shared mode.
      /// </summary>
      public void ReleaseShared()
      {
         int value;
         int sharedOwners;

         while(true)
         {
            value = _value;
#if RIGOROUS_CHECKS

                System.Diagnostics.Trace.Assert((value & LockOwned) != 0);
                System.Diagnostics.Trace.Assert((value & LockExclusiveWaking) == 0);
                System.Diagnostics.Trace.Assert(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) != 0);
#endif

            sharedOwners = (value >> LockSharedOwnersShift) & LockSharedOwnersMask;

            // Case 1: there are multiple shared owners.
            if(sharedOwners > 1)
            {
               if(Interlocked.CompareExchange(
                   ref _value,
                   value - LockSharedOwnersIncrement,
                   value
                   ) == value)
                  break;
            }
            // Case 2: we are the last shared owner AND there are exclusive waiters.
            else if(((value >> LockExclusiveWaitersShift) & LockExclusiveWaitersMask) != 0)
            {
               if(Interlocked.CompareExchange(
                   ref _value,
                   value - LockOwned + LockExclusiveWaking - LockSharedOwnersIncrement - LockExclusiveWaitersIncrement,
                   value
                   ) == value)
               {
                  NativeMethods.ReleaseSemaphore(_exclusiveWakeEvent, 1, IntPtr.Zero);

                  break;
               }
            }
            // Case 3: we are the last shared owner AND there are no exclusive waiters.
            else
            {
               if(Interlocked.CompareExchange(
                   ref _value,
                   value - LockOwned - LockSharedOwnersIncrement,
                   value
                   ) == value)
                  break;
            }
         }
      }

      /// <summary>
      /// Acquires the lock in exclusive mode, busy waiting 
      /// if necessary.
      /// </summary>
      /// <remarks>
      /// Exclusive acquires are *not* given precedence over shared 
      /// acquires for busy wait methods.
      /// </remarks>
      public void SpinAcquireExclusive()
      {
         int value;

         while(true)
         {
            value = _value;

            if((value & (LockOwned | LockExclusiveWaking)) == 0)
            {
               if(Interlocked.CompareExchange(
                   ref _value,
                   value + LockOwned,
                   value
                   ) == value)
                  break;
            }

            if(NativeMethods.SpinEnabled)
               Thread.SpinWait(8);
            else
               Thread.Sleep(0);
         }
      }

      /// <summary>
      /// Acquires the lock in shared mode, busy waiting 
      /// if necessary.
      /// </summary>
      /// <remarks>
      /// Exclusive acquires are *not* given precedence over shared 
      /// acquires for busy wait methods.
      /// </remarks>
      public void SpinAcquireShared()
      {
         int value;

         while(true)
         {
            value = _value;

            if((value & ExclusiveMask) == 0)
            {
               if((value & LockOwned) == 0)
               {
                  if(Interlocked.CompareExchange(
                      ref _value,
                      value + LockOwned + LockSharedOwnersIncrement,
                      value
                      ) == value)
                     break;
               }
               else if(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) != 0)
               {
                  if(Interlocked.CompareExchange(
                      ref _value,
                      value + LockSharedOwnersIncrement,
                      value
                      ) == value)
                     break;
               }
            }

            if(NativeMethods.SpinEnabled)
               Thread.SpinWait(8);
            else
               Thread.Sleep(0);
         }
      }

      /// <summary>
      /// Converts the ownership mode from shared to exclusive, 
      /// busy waiting if necessary.
      /// </summary>
      public void SpinConvertSharedToExclusive()
      {
         int value;

         while(true)
         {
            value = _value;

            // Can't convert if there are other shared owners.
            if(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) == 1)
            {
               if(Interlocked.CompareExchange(
                   ref _value,
                   value - LockSharedOwnersIncrement,
                   value
                   ) == value)
                  break;
            }

            if(NativeMethods.SpinEnabled)
               Thread.SpinWait(8);
            else
               Thread.Sleep(0);
         }
      }

      /// <summary>
      /// Attempts to acquire the lock in exclusive mode.
      /// </summary>
      /// <returns>Whether the lock was acquired.</returns>
      public bool TryAcquireExclusive()
      {
         int value;

         value = _value;

         if((value & (LockOwned | LockExclusiveWaking)) != 0)
            return false;

         return Interlocked.CompareExchange(
             ref _value,
             value + LockOwned,
             value
             ) == value;
      }

      /// <summary>
      /// Attempts to acquire the lock in shared mode.
      /// </summary>
      /// <returns>Whether the lock was acquired.</returns>
      public bool TryAcquireShared()
      {
         int value;

         value = _value;

         if((value & ExclusiveMask) != 0)
            return false;

         if((value & LockOwned) == 0)
         {
            return Interlocked.CompareExchange(
                ref _value,
                value + LockOwned + LockSharedOwnersIncrement,
                value
                ) == value;
         }
         else if(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) != 0)
         {
            return Interlocked.CompareExchange(
                ref _value,
                value + LockSharedOwnersIncrement,
                value
                ) == value;
         }
         else
         {
            return false;
         }
      }

      /// <summary>
      /// Attempts to convert the ownership mode from shared 
      /// to exclusive.
      /// </summary>
      /// <returns>Whether the lock was converted.</returns>
      public bool TryConvertSharedToExclusive()
      {
         int value;

         while(true)
         {
            value = _value;
#if RIGOROUS_CHECKS

                    System.Diagnostics.Trace.Assert((value & LockOwned) != 0);
                    System.Diagnostics.Trace.Assert((value & LockExclusiveWaking) == 0);
                    System.Diagnostics.Trace.Assert(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) != 0);
#endif

            // Can't convert if there are other shared owners.
            if(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) != 1)
               return false;

            if(Interlocked.CompareExchange(
                ref _value,
                value - LockSharedOwnersIncrement,
                value
                ) == value)
               return true;
         }
      }
   }

   [SuppressUnmanagedCodeSecurity]
   internal class NativeMethods
   {
      public const int WaitObject0 = 0x0;
      public const int WaitAbandoned = 0x80;
      public const int WaitTimeout = 0x102;
      public const int WaitFailed = -1;

      public static readonly int SpinCount = Environment.ProcessorCount != 1 ? 4000 : 0;
      public static readonly bool SpinEnabled = Environment.ProcessorCount != 1;

      // We need to import some stuff. We can't use 
      // ProcessHacker.Native because it depends on this library.

      [DllImport("kernel32.dll")]
      public static extern bool CloseHandle(
          [In] IntPtr Handle
          );

      [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
      public static extern IntPtr CreateEvent(
          [In] [Optional] IntPtr EventAttributes,
          [In] bool ManualReset,
          [In] bool InitialState,
          [In] [Optional] string Name
          );

      [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
      public static extern IntPtr CreateSemaphore(
          [In] [Optional] IntPtr SemaphoreAttributes,
          [In] int InitialCount,
          [In] int MaximumCount,
          [In] [Optional] string Name
          );

      [DllImport("kernel32.dll")]
      public static extern bool ReleaseSemaphore(
          [In] IntPtr SemaphoreHandle,
          [In] int ReleaseCount,
          [In] IntPtr PreviousCount // out int
          );

      [DllImport("kernel32.dll")]
      public static extern bool ResetEvent(
          [In] IntPtr EventHandle
          );

      [DllImport("kernel32.dll")]
      public static extern bool SetEvent(
          [In] IntPtr EventHandle
          );

      [DllImport("kernel32.dll")]
      public static extern int WaitForSingleObject(
          [In] IntPtr Handle,
          [In] int Milliseconds
          );

      [DllImport("ntdll.dll")]
      public static extern int NtCreateKeyedEvent(
          [Out] out IntPtr KeyedEventHandle,
          [In] int DesiredAccess,
          [In] [Optional] IntPtr ObjectAttributes,
          [In] int Flags
          );

      [DllImport("ntdll.dll")]
      public static extern int NtReleaseKeyedEvent(
          [In] IntPtr KeyedEventHandle,
          [In] IntPtr KeyValue,
          [In] bool Alertable,
          [In] [Optional] IntPtr Timeout
          );

      [DllImport("ntdll.dll")]
      public static extern int NtWaitForKeyedEvent(
          [In] IntPtr KeyedEventHandle,
          [In] IntPtr KeyValue,
          [In] bool Alertable,
          [In] [Optional] IntPtr Timeout
          );
   }
#endif
}
