Skip to content

Instantly share code, notes, and snippets.

@RazmikDev
Last active September 4, 2015 12:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RazmikDev/e2ac07c45d5e046bacd6 to your computer and use it in GitHub Desktop.
Save RazmikDev/e2ac07c45d5e046bacd6 to your computer and use it in GitHub Desktop.
Class that provides methods to take locks on Int32 values using CAS operation.
using System;
using System.Runtime.CompilerServices;
using System.Threading;
namespace Rs.Threading
{
/// <summary>
/// Provides methods to take locks on <see cref="Int32"/> values using CAS operation.
/// </summary>
public static class Int32Lock
{
/// <summary>
/// Attempts to set a user-mode lock on provided integer value <paramref name="lockValue"/> using CAS operation.
/// </summary>
/// <param name="lockValue">An <see cref="Int32"/> value that indicates the lock state (<c>1</c> - lock is set, <c>0</c> - lock is not set).</param>
/// <returns><c>true</c> if <paramref name="lockValue"/> is locked successfully and <paramref name="lockValue"/> is set to <c>1</c>; otherwise, <c>false</c>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Boolean TryLock(ref Int32 lockValue)
{
if (lockValue == 1)
return false; //
return Interlocked.CompareExchange(ref lockValue, 1, 0) == 0;
}
/// <summary>
/// Tries to set a user-mode lock on provided integer value <paramref name="lockValue"/> in specified <paramref name="millisecondsTimeout"/> using CAS operation.
/// </summary>
/// <param name="lockValue">An <see cref="Int32"/> value that indicates the lock state (<c>1</c> - lock is set, <c>0</c> - lock is not set).</param>
/// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see cref="Timeout.Infinite"/> (-1) to wait indefinitely.</param>
/// <returns><c>true</c> if <paramref name="lockValue"/> is locked successfully and <paramref name="lockValue"/> is set to <c>1</c>; otherwise, <c>false</c>.</returns>
public static Boolean WaitLock(ref Int32 lockValue, Int32 millisecondsTimeout = Timeout.Infinite)
{
var timeoutIsInfinite = millisecondsTimeout == Timeout.Infinite;
if (millisecondsTimeout < 0 && !timeoutIsInfinite)
throw new ArgumentOutOfRangeException("millisecondsTimeout");
// If lock is taken at first attempt returns true without spinning.
if (TryLock(ref lockValue))
return true;
// If lock is not taken at first attempt and timeout is 0 - returns false.
if (millisecondsTimeout == 0)
return false;
// Capture current ticks count if timeout is not 0 and not infinite.
Int64 startTicks = 0;
if (millisecondsTimeout != 0 && !timeoutIsInfinite)
startTicks = DateTime.UtcNow.Ticks;
// Starts spinning
var spinner = new SpinWait();
while (!TryLock(ref lockValue))
{
spinner.SpinOnce();
// Skips elapsed time check if timeout is infinite or next spin will trigger a forced context switch.
if (timeoutIsInfinite || !spinner.NextSpinWillYield)
continue;
// If timeout exceeded returns false.
if (millisecondsTimeout <= (DateTime.UtcNow.Ticks - startTicks) / TimeSpan.TicksPerMillisecond)
return false;
}
return true;
}
/// <summary>
/// Waits for integer value lock releasing using <see cref="SpinWait"/>.
/// </summary>
/// <param name="lockValue">The integer value that can be locked.</param>
public static void Wait(ref Int32 lockValue)
{
if (lockValue == 0)
return;
// Starts spinning
var spinner = new SpinWait();
while (lockValue != 0)
{
spinner.SpinOnce();
}
}
/// <summary>
/// Releases the user-mode lock on provided integer value <paramref name="lockValue"/> using CAS operation.
/// </summary>
/// <param name="lockValue">An object to unlock.</param>
/// <exception cref="ArgumentException">If <paramref name="lockValue"/> is not equals to <c>1</c>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Release(ref Int32 lockValue)
{
if (Interlocked.CompareExchange(ref lockValue, 0, 1) != 1)
throw new ArgumentException("Cannot release not locked or unacceptable integer lock value");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment