Add pfx to the mobile profile
[mono.git] / mcs / class / corlib / System.Threading / SpinLock.cs
index 64fec975d27d24e221c0519472b7462e89663b69..59ac22b3e3732eee246c223af0fc4dd86816aba1 100644 (file)
@@ -1,4 +1,3 @@
-#if NET_4_0
 // SpinLock.cs
 //
 // Copyright (c) 2008 Jérémie "Garuma" Laval
 //
 //
 
+#if NET_4_0 || MOBILE
+
 using System;
+using System.Collections.Concurrent;
 using System.Runtime.ConstrainedExecution;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
 
 namespace System.Threading
 {
+       [StructLayout(LayoutKind.Explicit)]
+       internal struct TicketType {
+               [FieldOffset(0)]
+               public long TotalValue;
+               [FieldOffset(0)]
+               public int Value;
+               [FieldOffset(4)]
+               public int Users;
+       }
+
+       /* Implement the ticket SpinLock algorithm described on http://locklessinc.com/articles/locks/
+        * This lock is usable on both endianness.
+        * All the try/finally patterns in this class and various extra gimmicks compared to the original
+        * algorithm are here to avoid problems caused by asynchronous exceptions.
+        */
+       [System.Diagnostics.DebuggerDisplay ("IsHeld = {IsHeld}")]
+       [System.Diagnostics.DebuggerTypeProxy ("System.Threading.SpinLock+SystemThreading_SpinLockDebugView")]
        public struct SpinLock
        {
-               const int isFree = 0;
-               const int isOwned = 1;
-               int lockState;
-               readonly SpinWait sw;
+               TicketType ticket;
+
                int threadWhoTookLock;
                readonly bool isThreadOwnerTrackingEnabled;
-               
+
+               static Watch sw = Watch.StartNew ();
+
+               ConcurrentOrderedList<int> stallTickets;
+
                public bool IsThreadOwnerTrackingEnabled {
                        get {
                                return isThreadOwnerTrackingEnabled;
                        }
                }
-               
+
+               public bool IsHeld {
+                       get {
+                               // No need for barrier here
+                               long totalValue = ticket.TotalValue;
+                               return (totalValue >> 32) != (totalValue & 0xFFFFFFFF);
+                       }
+               }
+
                public bool IsHeldByCurrentThread {
                        get {
                                if (isThreadOwnerTrackingEnabled)
-                                       return lockState == isOwned && Thread.CurrentThread.ManagedThreadId == threadWhoTookLock;
+                                       return IsHeld && Thread.CurrentThread.ManagedThreadId == threadWhoTookLock;
                                else
-                                       return lockState == isOwned;
+                                       return IsHeld;
                        }
                }
 
-               public SpinLock (bool trackId)
+               public SpinLock (bool enableThreadOwnerTracking)
                {
-                       this.isThreadOwnerTrackingEnabled = trackId;
+                       this.isThreadOwnerTrackingEnabled = enableThreadOwnerTracking;
                        this.threadWhoTookLock = 0;
-                       this.lockState = isFree;
-                       this.sw = new SpinWait();
+                       this.ticket = new TicketType ();
+                       this.stallTickets = null;
                }
-               
-               void CheckAndSetThreadId ()
-               {
-                       // FIXME: LockRecursionException is not implement atm, swap line when it is
-                       if (threadWhoTookLock == Thread.CurrentThread.ManagedThreadId)
-                               //throw new LockRecursionException("The current thread has already acquired this lock.");
-                               throw new Exception ("The current thread has already acquired this lock.");
-                       threadWhoTookLock = Thread.CurrentThread.ManagedThreadId;
-               }
-               
-               public void ReliableEnter (ref bool lockTaken)
+
+               [MonoTODO ("Not safe against async exceptions")]
+               public void Enter (ref bool lockTaken)
                {
+                       if (lockTaken)
+                               throw new ArgumentException ("lockTaken", "lockTaken must be initialized to false");
+                       if (isThreadOwnerTrackingEnabled && IsHeldByCurrentThread)
+                               throw new LockRecursionException ();
+
+                       int slot = -1;
+
+                       RuntimeHelpers.PrepareConstrainedRegions ();
                        try {
-                               Enter ();
-                               lockTaken = lockState == isOwned && Thread.CurrentThread.ManagedThreadId == threadWhoTookLock;
-                       } catch {
-                               lockTaken = false;
-                       }
-               }
-               
-               // FIXME
-               //[ReliabilityContractAttribute]
-               public void Enter () 
-               {
-                       int result = Interlocked.Exchange (ref lockState, isOwned);
-                       
-                       //Thread.BeginCriticalRegion();
-                       while (result == isOwned) {
-                               // If resource available, set it to in-use and return
-                               if (result == isFree) {
-                                       result = Interlocked.Exchange (ref lockState, isOwned);
-                                       if (result == isFree)
-                                               break;
-                               }
-                               
-                               // Efficiently spin, until the resource looks like it might 
-                               // be free. NOTE: Just reading here (as compared to repeatedly 
-                               // calling Exchange) improves performance because writing 
-                               // forces all CPUs to update this value
-                               //while (Thread.VolatileRead (ref lockState) == isOwned) {
-                                       sw.SpinOnce ();
-                               //}
-                       }
-                       
-                       CheckAndSetThreadId ();
-               }
-               
-               public bool TryEnter ()
-               {
-                       //Thread.BeginCriticalRegion();
+                               slot = Interlocked.Increment (ref ticket.Users) - 1;
 
-                       // If resource available, set it to in-use and return
-                       if (Interlocked.Exchange (ref lockState, isOwned) == isFree) {
-                               CheckAndSetThreadId ();
-                               return true;
-                       }
-                       return false;
-               }
-               
-               public bool TryEnter (TimeSpan timeout)
-               {
-                       return TryEnter ((int)timeout.TotalMilliseconds);
-               }
-               
-               public bool TryEnter (int milliSeconds)
-               {
-                       //Thread.BeginCriticalRegion();
-                       
-                       Watch sw = Watch.StartNew ();
-                       bool result = false;
-                       
-                       while (sw.ElapsedMilliseconds < milliSeconds) {
-                               if (lockState == isFree && Interlocked.Exchange (ref lockState, isOwned) == isFree) {
+                               SpinWait wait = new SpinWait ();
+                               while (slot != ticket.Value) {
+                                       wait.SpinOnce ();
+
+                                       while (stallTickets != null && stallTickets.TryRemove (ticket.Value))
+                                               ++ticket.Value;
+                               }
+                       } finally {
+                               if (slot == ticket.Value) {
+                                       lockTaken = true;
                                        threadWhoTookLock = Thread.CurrentThread.ManagedThreadId;
-                                       result = true;
+                               } else if (slot != -1) {
+                                       // We have been interrupted, initialize stallTickets
+                                       if (stallTickets == null)
+                                               Interlocked.CompareExchange (ref stallTickets, new ConcurrentOrderedList<int> (), null);
+                                       stallTickets.TryAdd (slot);
                                }
                        }
-                       sw.Stop ();
-                       return result;
                }
-               
-               public void TryReliableEnter (ref bool lockTaken)
+
+               public void TryEnter (ref bool lockTaken)
                {
-                       try {
-                               lockTaken = TryEnter ();
-                       } catch {
-                               lockTaken = false;
-                       }
+                       TryEnter (0, ref lockTaken);
                }
-               
-               public void TryReliableEnter (TimeSpan timeout, ref bool lockTaken)
+
+               public void TryEnter (TimeSpan timeout, ref bool lockTaken)
                {
-                       TryReliableEnter ((int)timeout.TotalMilliseconds, ref lockTaken);
+                       TryEnter ((int)timeout.TotalMilliseconds, ref lockTaken);
                }
-               
-               public void TryReliableEnter (int milliSeconds, ref bool lockTaken)
+
+               public void TryEnter (int millisecondsTimeout, ref bool lockTaken)
                {
-                       //Thread.BeginCriticalRegion();
-                       
-                       Watch sw = Watch.StartNew ();
-                       
-                       while (sw.ElapsedMilliseconds < milliSeconds) {
-                               TryReliableEnter (ref lockTaken);
-                       }
-                       sw.Stop ();
+                       if (millisecondsTimeout < -1)
+                               throw new ArgumentOutOfRangeException ("milliSeconds", "millisecondsTimeout is a negative number other than -1");
+                       if (lockTaken)
+                               throw new ArgumentException ("lockTaken", "lockTaken must be initialized to false");
+                       if (isThreadOwnerTrackingEnabled && IsHeldByCurrentThread)
+                               throw new LockRecursionException ();
+
+                       long start = millisecondsTimeout == -1 ? 0 : sw.ElapsedMilliseconds;
+                       bool stop = false;
+
+                       do {
+                               while (stallTickets != null && stallTickets.TryRemove (ticket.Value))
+                                       ++ticket.Value;
+
+                               long u = ticket.Users;
+                               long totalValue = (u << 32) | u;
+                               long newTotalValue
+                                       = BitConverter.IsLittleEndian ? (u << 32) | (u + 1) : ((u + 1) << 32) | u;
+                               
+                               RuntimeHelpers.PrepareConstrainedRegions ();
+                               try {}
+                               finally {
+                                       lockTaken = Interlocked.CompareExchange (ref ticket.TotalValue, newTotalValue, totalValue) == totalValue;
+                               
+                                       if (lockTaken) {
+                                               threadWhoTookLock = Thread.CurrentThread.ManagedThreadId;
+                                               stop = true;
+                                       }
+                               }
+               } while (!stop && (millisecondsTimeout == -1 || (sw.ElapsedMilliseconds - start) < millisecondsTimeout));
                }
 
-               //FIXME
-               //[ReliabilityContractAttribute]
-               public void Exit () 
-               { 
+               [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
+               public void Exit ()
+               {
                        Exit (false);
                }
 
-               public void Exit (bool flushReleaseWrites) 
-               { 
-                       threadWhoTookLock = int.MinValue;
-                       
-                       // Mark the resource as available
-                       if (!flushReleaseWrites) {
-                               lockState = isFree;
-                       } else {
-                               Interlocked.Exchange (ref lockState, isFree);
+               [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
+               public void Exit (bool useMemoryBarrier)
+               {
+                       RuntimeHelpers.PrepareConstrainedRegions ();
+                       try {}
+                       finally {
+                               if (isThreadOwnerTrackingEnabled && !IsHeldByCurrentThread)
+                                       throw new SynchronizationLockException ("Current thread is not the owner of this lock");
+
+                               threadWhoTookLock = int.MinValue;
+                               do {
+                                       if (useMemoryBarrier)
+                                               Interlocked.Increment (ref ticket.Value);
+                                       else
+                                               ticket.Value++;
+                               } while (stallTickets != null && stallTickets.TryRemove (ticket.Value));
                        }
-                       //Thread.EndCriticalRegion();
                }
        }
-       
+
        // Wraps a SpinLock in a reference when we need to pass
        // around the lock
        internal class SpinLockWrapper
        {
-               public readonly SpinLock Lock = new SpinLock (false);
+               public SpinLock Lock = new SpinLock (false);
        }
 }
 #endif