C# thread-safe atomic counter class.
//-----------------------------------------------------------------------
// <copyright file="AtomicCounter.cs" company="Akka.NET Project">
// Copyright (C) 2009-2019 Lightbend Inc. <http://www.lightbend.com>
// Copyright (C) 2013-2019 .NET Foundation <https://github.com/akkadotnet/akka.net>
// </copyright>
//-----------------------------------------------------------------------
using System.Threading;
namespace Akka.Util.Internal
{
/// <summary>
/// An atomic 32 bit integer counter.
/// </summary>
public class AtomicCounter
{
/// <summary>
/// Creates an instance of an AtomicCounter.
/// </summary>
/// <param name="initialValue">The initial value of this counter.</param>
public AtomicCounter(int initialValue)
{
_value = initialValue;
}
/// <summary>
/// Creates an instance of an AtomicCounter with a starting value of -1.
/// </summary>
public AtomicCounter()
{
_value = -1;
}
/// <summary>
/// The current value of the atomic counter.
/// </summary>
private int _value;
/// <summary>
/// Retrieves the current value of the counter
/// </summary>
public int Current { get { return _value; } }
/// <summary>
/// Increments the counter and returns the next value
/// </summary>
/// <returns>TBD</returns>
public int Next()
{
return Interlocked.Increment(ref _value);
}
/// <summary>
/// Decrements the counter and returns the next value
/// </summary>
/// <returns>TBD</returns>
public int Decrement()
{
return Interlocked.Decrement(ref _value);
}
/// <summary>
/// Atomically increments the counter by one.
/// </summary>
/// <returns>The original value.</returns>
public int GetAndIncrement()
{
var nextValue = Next();
return nextValue - 1;
}
/// <summary>
/// Atomically increments the counter by one.
/// </summary>
/// <returns>The new value.</returns>
public int IncrementAndGet()
{
var nextValue = Next();
return nextValue;
}
/// <summary>
/// Atomically decrements the counter by one.
/// </summary>
/// <returns>The original value.</returns>
public int GetAndDecrement()
{
var previousValue = Decrement();
return previousValue + 1;
}
/// <summary>
/// Atomically decrements the counter by one.
/// </summary>
/// <returns>The new value.</returns>
public int DecrementAndGet()
{
return Decrement();
}
/// <summary>
/// Returns the current value and adds the specified value to the counter.
/// </summary>
/// <param name="amount">The amount to add to the counter.</param>
/// <returns>The original value before additions.</returns>
public int GetAndAdd(int amount)
{
var newValue = Interlocked.Add(ref _value, amount);
return newValue - amount;
}
/// <summary>
/// Adds the specified value to the counter and returns the new value.
/// </summary>
/// <param name="amount">The amount to add to the counter.</param>
/// <returns>The new value after additions.</returns>
public int AddAndGet(int amount)
{
var newValue = Interlocked.Add(ref _value, amount);
return newValue;
}
/// <summary>
/// Resets the counter to zero.
/// </summary>
public void Reset()
{
Interlocked.Exchange(ref _value, 0);
}
/// <summary>
/// Returns current counter value and sets a new value on it's place in one operation.
/// </summary>
/// <param name="value">TBD</param>
/// <returns>TBD</returns>
public int GetAndSet(int value)
{
return Interlocked.Exchange(ref _value, value);
}
/// <summary>
/// Compares current counter value with provided <paramref name="expected"/> value,
/// and sets it to <paramref name="newValue"/> if compared values where equal.
/// Returns true if replacement has succeed.
/// </summary>
/// <param name="expected">TBD</param>
/// <param name="newValue">TBD</param>
/// <returns>TBD</returns>
public bool CompareAndSet(int expected, int newValue)
{
return Interlocked.CompareExchange(ref _value, newValue, expected) != _value;
}
}
}
//
// AtomicCounter.cs (by Microsoft)
// https://github.com/microsoft/reverse-proxy/blob/master/src/ReverseProxy.Core/Util/AtomicCounter.cs
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Threading;
namespace Microsoft.ReverseProxy.Core.Util
{
public class AtomicCounter
{
private int _value;
/// <summary>
/// Gets the current value of the counter.
/// </summary>
public int Value => Volatile.Read(ref _value);
/// <summary>
/// Atomically increments the counter value by 1.
/// </summary>
public void Increment()
{
Interlocked.Increment(ref _value);
}
/// <summary>
/// Atomically decrements the counter value by 1.
/// </summary>
public void Decrement()
{
Interlocked.Decrement(ref _value);
}
/// <summary>
/// Atomically increments the counter value by 1, but only up to the
/// specified <paramref name="max"/>.
/// </summary>
/// <param name="max">Maximum value to increment to.</param>
/// <returns>True if the value was incremented; false otherwise.</returns>
public bool IncrementCapped(int max)
{
while (true)
{
var val = Volatile.Read(ref _value);
if (val >= max)
{
return false;
}
if (Interlocked.CompareExchange(ref _value, val + 1, val) == val)
{
return true;
}
}
}
/// <summary>
/// Atomically decrements the counter value by 1, but only down to the
/// specified <paramref name="min"/>.
/// </summary>
/// <param name="min">Minimum value to decrement to.</param>
/// <returns>True if the value was decremented; false otherwise.</returns>
public bool DecrementCapped(int min)
{
while (true)
{
var val = Volatile.Read(ref _value);
if (val <= min)
{
return false;
}
if (Interlocked.CompareExchange(ref _value, val - 1, val) == val)
{
return true;
}
}
}
}
}