Skip to content

Commit

Permalink
add atom type
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasteles committed Jan 17, 2024
1 parent 089dc5c commit b55bfa6
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 0 deletions.
23 changes: 23 additions & 0 deletions src/Atom.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace CSharpPlus;

public sealed class Atom<T>(T value) where T : class
{
public T Value => value;

public void Swap(Func<T, T> updater)
{
SpinWait sw = new();
while (true)
{
var curr = value;
var next = updater(curr);
var result = Interlocked.CompareExchange(ref value, next, curr);
if (ReferenceEquals(result, curr))
break;

sw.SpinOnce();
}
}

public void Reset(T resetValue) => value = resetValue;
}
41 changes: 41 additions & 0 deletions tests/CSharpPlus.Tests/AtomTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
namespace CSharpPlus.Tests;

public class AtomTests
{
const int Count = 10_000;

record Integer(int Value)
{
public Integer Increment() => new(Value + 1);
public Integer Decrement() => new(Value - 1);

public static implicit operator int(Integer i) => i.Value;
public static implicit operator Integer(int i) => new(i);
}

[Test]
public void ShouldIncrement()
{
Atom<Integer> atom = new(0);

Parallel.For(0, Count * 2, n =>
atom.Swap(x => n < Count ? x.Increment() : x.Decrement()));

atom.Value.Value.Should().Be(0);
}

[Test]
public async Task ShouldIncrementTask()
{
Atom<Integer> atom = new(0);

await Task.WhenAll(
Enumerable.Range(0, Count * 2)
.Select(n => Task.Run(() =>
atom.Swap(x => n < Count
? x.Increment()
: x.Decrement()))));

atom.Value.Value.Should().Be(0);
}
}

0 comments on commit b55bfa6

Please sign in to comment.