X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2Fcorlib%2FSystem.Threading%2FSpinLock.cs;h=28bb1527340f407d56860e23ee2f6762254b26a6;hb=032f313d5f3b99954cabb3e152b3c8d4424d5a2b;hp=122a3f094b2debef171d29b4646e58336acb5d63;hpb=ed404a7b7434f83c163e28ac1fdedf0cb4d170c7;p=mono.git diff --git a/mcs/class/corlib/System.Threading/SpinLock.cs b/mcs/class/corlib/System.Threading/SpinLock.cs index 122a3f094b2..28bb1527340 100644 --- a/mcs/class/corlib/System.Threading/SpinLock.cs +++ b/mcs/class/corlib/System.Threading/SpinLock.cs @@ -1,6 +1,7 @@ // SpinLock.cs // // Copyright (c) 2008 Jérémie "Garuma" Laval +// Copyright 2011 Xamarin Inc (http://www.xamarin.com). // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -22,13 +23,13 @@ // // + using System; +using System.Collections.Concurrent; using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; -#if NET_4_0 || BOOTSTRAP_NET_4_0 - namespace System.Threading { [StructLayout(LayoutKind.Explicit)] @@ -41,9 +42,11 @@ namespace System.Threading public int Users; } - // Implement the ticket SpinLock algorithm described on http://locklessinc.com/articles/locks/ - // This lock is usable on both endianness - // TODO: some 32 bits platform apparently doesn't support CAS with 64 bits value + /* 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 @@ -53,7 +56,9 @@ namespace System.Threading int threadWhoTookLock; readonly bool isThreadOwnerTrackingEnabled; - static Watch sw = Watch.StartNew (); + static readonly Watch sw = Watch.StartNew (); + + ConcurrentOrderedList stallTickets; public bool IsThreadOwnerTrackingEnabled { get { @@ -83,6 +88,7 @@ namespace System.Threading this.isThreadOwnerTrackingEnabled = enableThreadOwnerTracking; this.threadWhoTookLock = 0; this.ticket = new TicketType (); + this.stallTickets = null; } [MonoTODO ("Not safe against async exceptions")] @@ -93,21 +99,30 @@ namespace System.Threading if (isThreadOwnerTrackingEnabled && IsHeldByCurrentThread) throw new LockRecursionException (); - /* The current ticket algorithm, even though it's a thing of beauty, doesn't make it easy to - * hand back ticket that have been taken in the case of an asynchronous exception and naively - * fixing it bloat a code that should be kept simple. A straightforward possibility is to wrap - * the whole thing in a finally block but due to the while loop a number of bad things can - * happen, thus for the moment the code is left as is in the spirit of "better breaking fast, - * than later in a weird way". - */ - int slot = Interlocked.Increment (ref ticket.Users) - 1; - - SpinWait wait = new SpinWait (); - while (slot != ticket.Value) - wait.SpinOnce (); - - lockTaken = true; - threadWhoTookLock = Thread.CurrentThread.ManagedThreadId; + int slot = -1; + + RuntimeHelpers.PrepareConstrainedRegions (); + try { + slot = Interlocked.Increment (ref ticket.Users) - 1; + + 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; + } else if (slot != -1) { + // We have been interrupted, initialize stallTickets + if (stallTickets == null) + Interlocked.CompareExchange (ref stallTickets, new ConcurrentOrderedList (), null); + stallTickets.TryAdd (slot); + } + } } public void TryEnter (ref bool lockTaken) @@ -133,6 +148,9 @@ namespace System.Threading bool stop = false; do { + while (stallTickets != null && stallTickets.TryRemove (ticket.Value)) + ++ticket.Value; + long u = ticket.Users; long totalValue = (u << 32) | u; long newTotalValue @@ -151,6 +169,7 @@ namespace System.Threading } while (!stop && (millisecondsTimeout == -1 || (sw.ElapsedMilliseconds - start) < millisecondsTimeout)); } + [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)] public void Exit () { Exit (false); @@ -166,20 +185,13 @@ namespace System.Threading throw new SynchronizationLockException ("Current thread is not the owner of this lock"); threadWhoTookLock = int.MinValue; - // Fast path - if (useMemoryBarrier) - Interlocked.Increment (ref ticket.Value); - else - ticket.Value++; + do { + if (useMemoryBarrier) + Interlocked.Increment (ref ticket.Value); + else + ticket.Value++; + } while (stallTickets != null && stallTickets.TryRemove (ticket.Value)); } } } - - // Wraps a SpinLock in a reference when we need to pass - // around the lock - internal class SpinLockWrapper - { - public SpinLock Lock = new SpinLock (false); - } } -#endif