Mega Code Archive

 
Categories / C# Tutorial / Thread
 

A spin-lock

/* Quote from Professional .NET Framework 2.0 (Programmer to Programmer) (Paperback) by Joe Duffy (Author) # Paperback: 601 pages # Publisher: Wrox (April 10, 2006) # Language: English # ISBN-10: 0764571354 # ISBN-13: 978-0764571350 */ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime; using System.Runtime.CompilerServices; using System.Security; using System.Text; using System.Threading; class SpinLock {     private int state;     private EventWaitHandle available = new AutoResetEvent(false);     // This looks at the total number of hardware threads available; if it's     // only 1, we will use an optimized code path     private static bool isSingleProc = (Environment.ProcessorCount == 1);     private const int outerTryCount = 5;     private const int cexTryCount = 100;     public void Enter(out bool taken)     {         // Taken is an out parameter so that we set it *inside* the critical         // region, rather than returning it and permitting aborts to creep in.         // Without this, the caller could take the lock, but not release it         // because it didn't know it had to.         taken = false;         while (!taken)         {             if (isSingleProc)             {                 // Don't busy wait on 1-logical processor machines; try                 // a single swap, and if it fails, drop back to EventWaitHandle.                 Thread.BeginCriticalRegion();                 taken = Interlocked.CompareExchange(ref state, 1, 0) == 0;                 if (!taken)                     Thread.EndCriticalRegion();             }             else             {                 for (int i = 0; !taken && i < outerTryCount; i++)                 {                     // Tell the CLR we're in a critical region;                     // interrupting could lead to deadlocks.                     Thread.BeginCriticalRegion();                     // Try 'cexTryCount' times to CEX the state variable:                     int tries = 0;                     while (!(taken =                         Interlocked.CompareExchange(ref state, 1, 0) == 0) &&                         tries++ < cexTryCount)                     {                         Thread.SpinWait(1);                     }                     if (!taken)                     {                         // We failed to acquire in the busy spin, mark the end                         // of our critical region and yield to let another                         // thread make forward progress.                         Thread.EndCriticalRegion();                         Thread.Sleep(0);                     }                 }             }             // If we didn't acquire the lock, block.             if (!taken) available.WaitOne();         }         return;     }     public void Enter()     {         // Convenience method. Using this could be prone to deadlocks.         bool b;         Enter(out b);     }     public void Exit()     {         if (Interlocked.CompareExchange(ref state, 0, 1) == 1)         {              // We notify the waking threads inside our critical region so             // that an abort doesn't cause us to lose a pulse, (which could             // lead to deadlocks).             available.Set();             Thread.EndCriticalRegion();         }     } } public class MainClass {     public static void Main()     {         SpinLock sl1 = new SpinLock();         sl1.Enter();         try         {             Console.WriteLine("Acquired the spin lock");         }         finally         {             sl1.Exit();             Console.WriteLine("Released the spin lock");         }     } } Acquired the spin lock Released the spin lock