188 lines
7.0 KiB
C#
188 lines
7.0 KiB
C#
|
using System.Threading;
|
||
|
using Unity.Mathematics;
|
||
|
|
||
|
namespace Unity.Collections.LowLevel.Unsafe
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// A 32-bit atomic counter.
|
||
|
/// </summary>
|
||
|
/// <remarks>Rather than have its own int, a counter *points* to an int. This arrangement lets counters in different jobs share reference to the same underlying int.</remarks>
|
||
|
[GenerateTestsForBurstCompatibility]
|
||
|
public unsafe struct UnsafeAtomicCounter32
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// The int that is modified by this counter.
|
||
|
/// </summary>
|
||
|
/// <value>The int that is modified by this counter.</value>
|
||
|
public int* Counter;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Initializes and returns an instance of UnsafeAtomicCounter32.
|
||
|
/// </summary>
|
||
|
/// <param name="ptr">A pointer to the int to be modified by this counter.</param>
|
||
|
public UnsafeAtomicCounter32(void* ptr)
|
||
|
{
|
||
|
Counter = (int*)ptr;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Non-atomically sets this counter to a value.
|
||
|
/// </summary>
|
||
|
/// <param name="value">The value to set. Defaults to 0</param>
|
||
|
public void Reset(int value = 0)
|
||
|
{
|
||
|
*Counter = value;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Atomically adds a value to this counter.
|
||
|
/// </summary>
|
||
|
/// <param name="value">The value to add.</param>
|
||
|
/// <returns>The original value before the add.</returns>
|
||
|
public int Add(int value)
|
||
|
{
|
||
|
return Interlocked.Add(ref UnsafeUtility.AsRef<int>(Counter), value) - value;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Atomically subtracts a value from this counter.
|
||
|
/// </summary>
|
||
|
/// <param name="value">The value to subtract.</param>
|
||
|
/// <returns>The original value before the subtract.</returns>
|
||
|
public int Sub(int value) => Add(-value);
|
||
|
|
||
|
/// <summary>
|
||
|
/// Atomically adds a value to this counter. The result will not be greater than a maximum value.
|
||
|
/// </summary>
|
||
|
/// <param name="value">The value to add to this counter.</param>
|
||
|
/// <param name="max">The maximum which the result will not be greater than.</param>
|
||
|
/// <returns>The original value before the add.</returns>
|
||
|
public int AddSat(int value, int max = int.MaxValue)
|
||
|
{
|
||
|
int oldVal;
|
||
|
int newVal = *Counter;
|
||
|
do
|
||
|
{
|
||
|
oldVal = newVal;
|
||
|
newVal = newVal >= max ? max : math.min(max, newVal + value);
|
||
|
newVal = Interlocked.CompareExchange(ref UnsafeUtility.AsRef<int>(Counter), newVal, oldVal);
|
||
|
}
|
||
|
while (oldVal != newVal && oldVal != max);
|
||
|
|
||
|
return oldVal;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Atomically subtracts a value from this counter. The result will not be less than a minimum value.
|
||
|
/// </summary>
|
||
|
/// <param name="value">The value to subtract from this counter.</param>
|
||
|
/// <param name="min">The minimum which the result will not be less than.</param>
|
||
|
/// <returns>The original value before the subtract.</returns>
|
||
|
public int SubSat(int value, int min = int.MinValue)
|
||
|
{
|
||
|
int oldVal;
|
||
|
int newVal = *Counter;
|
||
|
do
|
||
|
{
|
||
|
oldVal = newVal;
|
||
|
newVal = newVal <= min ? min : math.max(min, newVal - value);
|
||
|
newVal = Interlocked.CompareExchange(ref UnsafeUtility.AsRef<int>(Counter), newVal, oldVal);
|
||
|
}
|
||
|
while (oldVal != newVal && oldVal != min);
|
||
|
|
||
|
return oldVal;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// A 64-bit atomic counter.
|
||
|
/// </summary>
|
||
|
/// <remarks>Rather than have its own long, a counter *points* to a long. This arrangement lets counters in different jobs share reference to the same underlying long.</remarks>
|
||
|
[GenerateTestsForBurstCompatibility]
|
||
|
public unsafe struct UnsafeAtomicCounter64
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// The long that is modified by this counter.
|
||
|
/// </summary>
|
||
|
/// <value>The long that is modified by this counter.</value>
|
||
|
public long* Counter;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Initializes and returns an instance of UnsafeAtomicCounter64.
|
||
|
/// </summary>
|
||
|
/// <param name="ptr">A pointer to the long to be modified by this counter.</param>
|
||
|
public UnsafeAtomicCounter64(void* ptr)
|
||
|
{
|
||
|
Counter = (long*)ptr;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Non-atomically sets this counter to a value.
|
||
|
/// </summary>
|
||
|
/// <param name="value">The value to set. Defaults to 0</param>
|
||
|
public void Reset(long value = 0)
|
||
|
{
|
||
|
*Counter = value;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Atomically adds a value to this counter.
|
||
|
/// </summary>
|
||
|
/// <param name="value">The value to add.</param>
|
||
|
/// <returns>The original value before the add.</returns>
|
||
|
public long Add(long value)
|
||
|
{
|
||
|
return Interlocked.Add(ref UnsafeUtility.AsRef<long>(Counter), value) - value;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Atomically subtracts a value from this counter.
|
||
|
/// </summary>
|
||
|
/// <param name="value">The value to subtract.</param>
|
||
|
/// <returns>The original value before the subtract.</returns>
|
||
|
public long Sub(long value) => Add(-value);
|
||
|
|
||
|
/// <summary>
|
||
|
/// Atomically adds a value to this counter. The result will not be greater than a maximum value.
|
||
|
/// </summary>
|
||
|
/// <param name="value">The value to add to this counter.</param>
|
||
|
/// <param name="max">The maximum which the result will not be greater than.</param>
|
||
|
/// <returns>The original value before the add.</returns>
|
||
|
public long AddSat(long value, long max = long.MaxValue)
|
||
|
{
|
||
|
long oldVal;
|
||
|
long newVal = *Counter;
|
||
|
do
|
||
|
{
|
||
|
oldVal = newVal;
|
||
|
newVal = newVal >= max ? max : math.min(max, newVal + value);
|
||
|
newVal = Interlocked.CompareExchange(ref UnsafeUtility.AsRef<long>(Counter), newVal, oldVal);
|
||
|
}
|
||
|
while (oldVal != newVal && oldVal != max);
|
||
|
|
||
|
return oldVal;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Atomically subtracts a value from this counter. The result will not be less than a minimum value.
|
||
|
/// </summary>
|
||
|
/// <param name="value">The value to subtract from this counter.</param>
|
||
|
/// <param name="min">The minimum which the result will not be less than.</param>
|
||
|
/// <returns>The original value before the subtract.</returns>
|
||
|
public long SubSat(long value, long min = long.MinValue)
|
||
|
{
|
||
|
long oldVal;
|
||
|
long newVal = *Counter;
|
||
|
do
|
||
|
{
|
||
|
oldVal = newVal;
|
||
|
newVal = newVal <= min ? min : math.max(min, newVal - value);
|
||
|
newVal = Interlocked.CompareExchange(ref UnsafeUtility.AsRef<long>(Counter), newVal, oldVal);
|
||
|
}
|
||
|
while (oldVal != newVal && oldVal != min);
|
||
|
|
||
|
return oldVal;
|
||
|
}
|
||
|
}
|
||
|
}
|