public void SemanticCorrectnessTest () { sl = new SpinLock (false); bool taken = false; bool taken2 = false; sl.Enter (ref taken); Assert.IsTrue (taken, "#1"); sl.TryEnter (ref taken2); Assert.IsFalse (taken2, "#2"); sl.Exit (); sl.TryEnter (ref taken2); Assert.IsTrue (taken2, "#3"); }
public IThreadPoolWorkItem TrySteal(ref bool missedSteal) { while (true) { if (CanSteal) { bool taken = false; try { m_foreignLock.TryEnter(ref taken); if (taken) { // Increment head, and ensure read of tail doesn't move before it (fence). int head = m_headIndex; Interlocked.Exchange(ref m_headIndex, head + 1); if (head < m_tailIndex) { int idx = head & m_mask; IThreadPoolWorkItem obj = Volatile.Read(ref m_array[idx]); // Check for nulls in the array. if (obj == null) { continue; } m_array[idx] = null; return(obj); } else { // Failed, restore head. m_headIndex = head; } } } finally { if (taken) { m_foreignLock.Exit(useMemoryBarrier: false); } } missedSteal = true; } return(null); } }
/// <summary> /// Test TryEnter invalid cases /// </summary> /// <returns>True if succeeded, false otherwise</returns> private static void RunSpinLockTest3_TryEnter(bool enableThreadIDs) { Exception exception = null; SpinLock slock = new SpinLock(enableThreadIDs); bool lockTaken = false; #region Recursive lock if (enableThreadIDs) // only valid if thread IDs are on { // Test recursive locks slock.Enter(ref lockTaken); try { if (lockTaken) { bool dummy = false; // reacquire the lock slock.Enter(ref dummy); } } catch (Exception ex) { // LockRecursionException must be thrown exception = ex; } if (lockTaken) { slock.Exit(); if (exception == null || exception.GetType() != typeof(LockRecursionException)) { Assert.True(false, string.Format("SpinLock.TryEnter() failed, recursive locks without exception")); } if (slock.IsHeldByCurrentThread) { Assert.True(false, string.Format("SpinLock.TryEnter() failed, IsHeld is true after calling Exit")); } } else { Assert.True(false, string.Format("LockRecursionException was not thrown?")); } } #endregion #region timeout > int.max // Test invalid argument handling, too long timeout exception = null; try { lockTaken = false; slock.TryEnter(TimeSpan.MaxValue, ref lockTaken); } catch (Exception ex) { exception = ex; } if (exception == null || exception.GetType() != typeof(ArgumentOutOfRangeException)) { Assert.True(false, string.Format(@"SpinLock.TryEnter() failed, timeout.Totalmilliseconds > int.maxValue without throwing ArgumentOutOfRangeException " + exception)); } #endregion #region Timeout > int.max // Test invalid argument handling, timeout < -1 exception = null; try { lockTaken = false; slock.TryEnter(-2, ref lockTaken); } catch (Exception ex) { exception = ex; } if (exception == null || exception.GetType() != typeof(ArgumentOutOfRangeException)) { Assert.True(false, string.Format(@"SpinLock.TryEnter() failed, timeout < -1 without throwing ArgumentOutOfRangeException")); } #endregion }
/// <summary> /// Test SpinLock.TryEnter(Timespan) by generating random timespan milliseconds /// </summary> /// <param name="threadsCount">Number of threads that call enter/exit</param> /// <returns>True if succeeded, false otherwise</returns> private static void RunSpinLockTest2_TryEnter(int threadsCount, bool enableThreadIDs) { for (int j = 0; j < 2; j++) { bool useMemoryBarrier = j == 0; Task[] threads = new Task[threadsCount]; SpinLock slock = new SpinLock(enableThreadIDs); int succeeded = 0; int failed = 0; // Run threads for (int i = 0; i < threadsCount; i++) { threads[i] = new Task(delegate (object x) { // Generate random timespan bool lockTaken = false; TimeSpan time = TimeSpan.FromMilliseconds(20); slock.TryEnter(time, ref lockTaken); if (lockTaken) { // add some delay in the critical section Task.WaitAll(Task.Delay(15)); Interlocked.Increment(ref succeeded); slock.Exit(useMemoryBarrier); } else { // Failed to get the lock within the timeout Interlocked.Increment(ref failed); } }, i); threads[i].Start(TaskScheduler.Default); } // Wait all threads for (int i = 0; i < threadsCount; i++) { threads[i].Wait(); } // succeeded + failed must be equal to the threads count. if (succeeded + failed != threadsCount) { Assert.True(false, string.Format("SpinLock.TryEnter() failed, actual count: " + (succeeded + failed) + " expected :" + threadsCount)); } } }
/// <summary> /// Test SpinLock.TryEnter() by launching n threads, each one calls TryEnter, the succeeded threads increment /// a counter variable and failed threads increment failed variable, count + failed must be equal to n /// </summary> /// <param name="threadsCount">Number of threads that call enter/exit</param> /// <returns>True if succeeded, false otherwise</returns> private static void RunSpinLockTest1_TryEnter(int threadsCount, bool enableThreadIDs) { for (int j = 0; j < 2; j++) { bool useMemoryBarrier = j == 0; Task[] threads = new Task[threadsCount]; SpinLock slock = new SpinLock(enableThreadIDs); int succeeded = 0; int failed = 0; // Run threads for (int i = 0; i < threadsCount; i++) { threads[i] = Task.Run(delegate () { bool lockTaken = false; slock.TryEnter(ref lockTaken); if (lockTaken) { // Increment succeeded counter Interlocked.Increment(ref succeeded); slock.Exit(useMemoryBarrier); } else { // Increment failed counter Interlocked.Increment(ref failed); } }); } // Wait all threads for (int i = 0; i < threadsCount; i++) { threads[i].Wait(); } // succeeded + failed must be equal to the threads count. if (succeeded + failed != threadsCount) { Assert.True(false, string.Format("SpinLock.TryEnter() failed, actual count: " + (succeeded + failed) + " expected :" + threadsCount)); } } }
/// <summary> /// Test TryEnter invalid cases /// </summary> /// <returns>True if succeeded, false otherwise</returns> private static bool RunSpinLockTest3_TryEnter(bool enableThreadIDs) { TestHarness.TestLog("SpinLock.TryEnter(invalid cases)"); Exception exception = null; SpinLock slock = new SpinLock(enableThreadIDs); bool lockTaken = false; #region Recursive lock if (enableThreadIDs) // only valid if thread IDs are on { // Test recursive locks slock.Enter(ref lockTaken); try { if (lockTaken) { bool dummy = false; // reacquire the lock slock.Enter(ref dummy); } } catch (Exception ex) { // LockRecursionException must be thrown exception = ex; } if (lockTaken) { slock.Exit(); //TODO: uncomment after finishing type forwarding in clr integration if (exception == null /*|| exception.GetType() != typeof(LockRecursionException)*/) { TestHarness.TestLog("SpinLock.TryEnter() failed, recursive locks without exception"); return false; } if (slock.IsHeldByCurrentThread) { TestHarness.TestLog("SpinLock.TryEnter() failed, IsHeld is true after calling Exit"); return false; } } else { return false; } } #endregion #region timeout > int.max // Test invalid argument handling, too long timeout exception = null; try { lockTaken = false; slock.TryEnter(TimeSpan.MaxValue, ref lockTaken); } catch (Exception ex) { exception = ex; } if (exception == null || exception.GetType() != typeof(ArgumentOutOfRangeException)) { TestHarness.TestLog(@"SpinLock.TryEnter() failed, timeout.Totalmilliseconds > int.maxValue without throwing ArgumentOutOfRangeException " + exception); return false; } #endregion #region Timeout > int.max // Test invalid argument handling, timeout < -1 exception = null; try { lockTaken = false; slock.TryEnter(-2, ref lockTaken); } catch (Exception ex) { exception = ex; } if (exception == null || exception.GetType() != typeof(ArgumentOutOfRangeException)) { TestHarness.TestLog(@"SpinLock.TryEnter() failed, timeout < -1 without throwing ArgumentOutOfRangeException"); return false; } #endregion TestHarness.TestLog("SpinLock.TryEnter() passed."); return true; }
/// <summary> /// Test SpinLock.TryEnter(Timespan) by generating random timespan milliseconds /// </summary> /// <param name="threadsCount">Number of threads that call enter/exit</param> /// <returns>True if succeeded, false otherwise</returns> private static bool RunSpinLockTest2_TryEnter(int threadsCount, bool enableThreadIDs) { TestHarness.TestLog("SpinLock.TryEnter(" + threadsCount + " threads)"); Thread[] threads = new Thread[threadsCount]; SpinLock slock = new SpinLock(enableThreadIDs); int succeeded = 0; int failed = 0; // Run threads for (int i = 0; i < threadsCount; i++) { threads[i] = new Thread(delegate(object x) { // Generate random timespan Random rand = new Random(33); bool lockTaken = false; TimeSpan time = TimeSpan.FromMilliseconds(rand.Next(-1, 20)); slock.TryEnter(time, ref lockTaken); if (lockTaken) { // add some delay in the critical section Thread.Sleep(15); Interlocked.Increment(ref succeeded); slock.Exit(); } else { // Failed to get the lock within the timeout Interlocked.Increment(ref failed); } }); threads[i].Start(i); } // Wait all threads for (int i = 0; i < threadsCount; i++) { threads[i].Join(); } // succeeded + failed must be equal to the threads count. if (succeeded + failed != threadsCount) { TestHarness.TestLog("SpinLock.TryEnter() failed, actual count: " + (succeeded + failed) + " expected :" + threadsCount); return false; } TestHarness.TestLog("SpinLock.TryEnter() passed."); return true; }
/// <summary> /// Test SpinLock.TryEnter() by launching n threads, each one calls TryEnter, the succeeded threads increment /// a counter variable and failed threads increment failed variable, count + failed must be equal to n /// </summary> /// <param name="threadsCount">Number of threads that call enter/exit</param> /// <returns>True if succeeded, false otherwise</returns> private static bool RunSpinLockTest1_TryEnter(int threadsCount, bool enableThreadIDs) { TestHarness.TestLog("SpinLock.TryEnter(" + threadsCount + " threads)"); Thread[] threads = new Thread[threadsCount]; SpinLock slock = new SpinLock(enableThreadIDs); int succeeded = 0; int failed = 0; // Run threads for (int i = 0; i < threadsCount; i++) { threads[i] = new Thread(delegate() { bool lockTaken = false; slock.TryEnter(ref lockTaken); if (lockTaken) { // Increment succeeded counter Interlocked.Increment(ref succeeded); slock.Exit(); } else { // Increment failed counter Interlocked.Increment(ref failed); } }); threads[i].Start(); } // Wait all threads for (int i = 0; i < threadsCount; i++) { threads[i].Join(); } // succeeded + failed must be equal to the threads count. if (succeeded + failed != threadsCount) { TestHarness.TestLog("SpinLock.TryEnter() failed, actual count: " + (succeeded + failed) + " expected :" + threadsCount); return false; } TestHarness.TestLog("SpinLock.TryEnter() passed."); return true; }
public static void EnterExit() { var sl = new SpinLock(); Assert.True(sl.IsThreadOwnerTrackingEnabled); for (int i = 0; i < 4; i++) { Assert.False(sl.IsHeld); Assert.False(sl.IsHeldByCurrentThread); bool lockTaken = false; if (i % 2 == 0) sl.Enter(ref lockTaken); else sl.TryEnter(ref lockTaken); Assert.True(lockTaken); Assert.True(sl.IsHeld); Assert.True(sl.IsHeldByCurrentThread); Task.Factory.StartNew(() => { Assert.True(sl.IsHeld); Assert.False(sl.IsHeldByCurrentThread); }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default).GetAwaiter().GetResult(); sl.Exit(); } }