19

I am trying to use SpinLock, but even this most basic code in a single threaded Console app throws the following exception when I callSpinLock.Exit()

System.Threading.SynchronizationLockException was unhandled by user code
  Message=The calling thread does not hold the lock.  Source=mscorlib

Here is the entire source code...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace ConsoleApplication48
{
    class Program
    {
        static readonly SpinLock SpinLock = new SpinLock();
        static void Main(string[] args)
        {
            bool lockTaken = false;
            try
            {
                SpinLock.Enter(ref lockTaken);
                if (lockTaken)
                    Console.WriteLine("Lock taken");
            }
            finally
            {
                if (lockTaken)
                    SpinLock.Exit();
            }
            Console.WriteLine("Done");
        }
    }
}
Peter Morris
  • 20,174
  • 9
  • 81
  • 146

1 Answers1

32

SpinLock is a struct, and you are reading it from a readonly field. The C# spec says that in this case, in order to call a potentially mutating function, the struct must be copied to a mutable local variable. This happens under the covers.

Your calls to Enter and Exit happen on a fresh copy of your lock. For that reason, Enter is operating on an unlocked lock.

Don't make the SpinLock variable readonly because it is being mutated.

usr
  • 168,620
  • 35
  • 240
  • 369
  • Ah, so using "readonly" with a value type not only specifies that the field is not assignable but also implies that the value is immutable and to ensure this it copies the value when you read it. Thanks – Peter Morris Jun 27 '12 at 14:53
  • 3
    WOW! thanks! This one would have taken days to find without your clever explanation. Thanks so much! – Eric Ouellet Apr 03 '19 at 14:20
  • 2
    Oh boi, need a "send tip" feature here because this probably just saved me days of frustration. – Nick May 20 '20 at 20:19
  • Oh man that was killing me; thank you. They really need to throw readonly SpinLock fields into Roslyn. – Jason C May 22 '21 at 20:01
  • Hey check it, even better: https://github.com/dotnet/runtime/issues/33773 (via https://github.com/dotnet/roslyn/issues/53620). – Jason C May 22 '21 at 23:03