A spin-lock : Custom Lock « Thread « C# / CSharp Tutorial






/*
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








20.17.Custom Lock
20.17.1.A spin-lock