The code below is part of an example from MSDN. It is the example on how to use SpinLock but to my eye there is a race condition in it.
Why I think this is because of the Dim lockTaken As Boolean = False line in the UpdateWithSpinLock method. It appears to me that the following could occur:
Thread 1 enters UpdateWithSpinLock method and executes as far as _queue.Enqueue(d) and a context switch occurs.
Thread 1 now has the SpinLock and lockTaken is True.
Thread 2 enters and only executes as far as the line Dim lockTaken As Boolean = False.
Thread 2 has now set lockTaken back to False and a context switch occurs.
Thread 1 continues and tests lockTaken in the Finally block and finds it to be False (it should be True for Thread 1) so doesn't release the SpinLock.
Thread 1 exits the method leaving the lock inplace and thread 2 waiting forever.
Imports System.Threading
Imports System.Threading.Tasks
Class SpinLockDemo2
Const N As Integer = 100000
Shared _queue = New Queue(Of Data)()
Shared _lock = New Object()
Shared _spinlock = New SpinLock()
Class Data
Public Name As String
Public Number As Double
End Class
Shared Sub Main()
UseSpinLock()
Console.WriteLine("Press a key")
Console.ReadKey()
End Sub
Private Shared Sub UpdateWithSpinLock(ByVal d As Data, ByVal i As Integer)
Dim lockTaken As Boolean = False
Try
_spinlock.Enter(lockTaken)
_queue.Enqueue(d)
Finally
If lockTaken Then
_spinlock.Exit(False)
End If
End Try
End Sub
Private Shared Sub UseSpinLock()
Dim sw = Stopwatch.StartNew()
Parallel.Invoke(
Sub()
For i As Integer = 0 To N - 1
UpdateWithSpinLock(New Data() With {.Name = i.ToString(), .Number = i}, i)
Next
End Sub,
Sub()
For i As Integer = 0 To N - 1
UpdateWithSpinLock(New Data() With {.Name = i.ToString(), .Number = i}, i)
Next
End Sub
)
sw.Stop()
Console.WriteLine("elapsed ms with spinlock: {0}", sw.ElapsedMilliseconds)
End Sub
End Class
Is the way I'm interpreting this correct. If it's not could you please show me what I'm missing.
In that code,
lockTakenis a local variable so no instance of that method running in one thread could change the value of that variable in another instance of the method running on a different thread. What you're describing would require thatlockTakenwas a member variable.