Skip to content

Commit

Permalink
Merge pull request #18 from nventive/dev/dr/TryAddDistinct
Browse files Browse the repository at this point in the history
Add the Transactional.TryAddDistinct()
  • Loading branch information
dr1rrb authored Jun 15, 2018
2 parents 5942354 + 95bec1a commit 8206eb9
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 10 deletions.
11 changes: 4 additions & 7 deletions src/Uno.Core.Tests/Threading/AsyncLockFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -417,18 +417,15 @@ public async Task TestReleaseThenReAcquireWithConcurrentAccess()
await otherThread.AdvanceTo(2);
Assert.IsFalse(otherThread.HasLock());

Task reLocking;
using (await sut.LockAsync(CancellationToken.None))
{
await Task.Yield();

reLocking = otherThread.AdvanceTo(3);
await Task.Delay(10);

Assert.AreEqual(TaskStatus.WaitingForActivation, reLocking.Status);
await otherThread.AdvanceAndFreezeBefore(3);
Assert.IsFalse(otherThread.HasLock());
}

await reLocking;
await otherThread.AdvanceTo(4);
Assert.IsTrue(otherThread.HasLock());
}

Expand All @@ -450,11 +447,11 @@ async Task OtherThread(CancellationToken ct, AsyncTestRunner r)
await Task.Yield();
r.HasLock(true);
r.Sync(position: 3);
r.Sync(position: 4);
}

await Task.Yield();
r.HasLock(false);
r.Sync(position: 4);
}
}

Expand Down
90 changes: 89 additions & 1 deletion src/Uno.Core.Tests/TransactionalFixture.ImmutableList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public void When_List_AddDistinct_Then_ItemAdded()
var list = ImmutableList<object>.Empty;
var item = new object();

Transactional.AddDistinct(ref list, item, EqualityComparer<object>.Default);
Transactional.AddDistinct(ref list, item);

Assert.AreEqual(1, list.Count);
Assert.IsTrue(list.Contains(item));
Expand All @@ -44,12 +44,100 @@ public void When_List_AddDistinct_Twice_Then_ItemAddedOnlyOnce()
var list = ImmutableList<object>.Empty;
var item = new object();

var result1 = Transactional.AddDistinct(ref list, item);
var result2 = Transactional.AddDistinct(ref list, item);

Assert.AreEqual(1, list.Count);
Assert.IsTrue(list.Contains(item));
Assert.AreSame(result1, result2);
}

[TestMethod]
public void When_List_AddDistinctWithComparer_Then_ItemAdded()
{
var list = ImmutableList<object>.Empty;
var item = new object();

Transactional.AddDistinct(ref list, item, EqualityComparer<object>.Default);

Assert.AreEqual(1, list.Count);
Assert.IsTrue(list.Contains(item));
}

[TestMethod]
public void When_List_AddDistinctWithComparer_Twice_Then_ItemAddedOnlyOnce()
{
var list = ImmutableList<object>.Empty;
var item = new object();

var result1 = Transactional.AddDistinct(ref list, item, EqualityComparer<object>.Default);
var result2 = Transactional.AddDistinct(ref list, item, EqualityComparer<object>.Default);

Assert.AreEqual(1, list.Count);
Assert.IsTrue(list.Contains(item));
Assert.AreSame(result1, result2);
}

[TestMethod]
public void When_List_TryAddDistinct_Then_ItemAdded()
{
var list = ImmutableList<object>.Empty;
var item = new object();

var added = Transactional.TryAddDistinct(ref list, item);

Assert.IsTrue(added);
Assert.AreEqual(1, list.Count);
Assert.IsTrue(list.Contains(item));
}

[TestMethod]
public void When_List_TryAddDistinct_Twice_Then_ItemAddedOnlyOnce()
{
var list = ImmutableList<object>.Empty;
var item = new object();

var added1 = Transactional.TryAddDistinct(ref list, item);
var result1 = list;
var added2 = Transactional.TryAddDistinct(ref list, item);
var result2 = list;

Assert.IsTrue(added1);
Assert.IsFalse(added2);
Assert.AreEqual(1, list.Count);
Assert.IsTrue(list.Contains(item));
Assert.AreSame(result1, result2);
}

[TestMethod]
public void When_List_TryAddDistinctWithComparer_Then_ItemAdded()
{
var list = ImmutableList<object>.Empty;
var item = new object();

var added = Transactional.TryAddDistinct(ref list, item, EqualityComparer<object>.Default);

Assert.IsTrue(added);
Assert.AreEqual(1, list.Count);
Assert.IsTrue(list.Contains(item));
}

[TestMethod]
public void When_List_TryAddDistinctWithCOmparer_Twice_Then_ItemAddedOnlyOnce()
{
var list = ImmutableList<object>.Empty;
var item = new object();

var added1 = Transactional.TryAddDistinct(ref list, item, EqualityComparer<object>.Default);
var result1 = list;
var added2 = Transactional.TryAddDistinct(ref list, item, EqualityComparer<object>.Default);
var result2 = list;

Assert.IsTrue(added1);
Assert.IsFalse(added2);
Assert.AreEqual(1, list.Count);
Assert.IsTrue(list.Contains(item));
Assert.AreSame(result1, result2);
}
}
}
9 changes: 7 additions & 2 deletions src/Uno.Core.Tests/_TestUtils/AsyncTestRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Reactive;
using System.Runtime.CompilerServices;
Expand All @@ -31,7 +32,7 @@ namespace Uno.Core.Tests.TestUtils
public class AsyncTestRunner : IDisposable
{
#if DEBUG
private const int _beatTimeout = 10*1000;
private static int _beatTimeout => Debugger.IsAttached ? 10 * 1000 : 100;
#else
private const int _beatTimeout = 100;
#endif
Expand Down Expand Up @@ -199,7 +200,11 @@ public Task AdvanceTo(int position)
{
var flag = new SyncFlag(position);
Interlocked.Exchange(ref _syncFlag, flag)?.Canceled();
if (position <= _syncPosition)
if (position == _syncPosition)
{
return Task.CompletedTask;
}
else if (position < _syncPosition)
{
throw new InvalidOperationException("Thread is already at positon " + _syncPosition);
}
Expand Down
71 changes: 71 additions & 0 deletions src/Uno.Core/Transactional.Immutables.cs
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,29 @@ public static TList Add<TList, T>(ref TList list, T item)
}
}

/// <summary>
/// Transactionally add an item to a list if not already present.
/// </summary>
public static TList AddDistinct<TList, T>(ref TList list, T item)
where TList : class, IImmutableList<T>
{
while (true)
{
var capture = list;
if (capture.IndexOf(item) >= 0)
{
return capture;
}

var updated = (TList)capture.Add(item);

if (Interlocked.CompareExchange(ref list, updated, capture) == capture)
{
return updated;
}
}
}

/// <summary>
/// Transactionally add an item to a list if not already present.
/// </summary>
Expand All @@ -563,6 +586,54 @@ public static TList AddDistinct<TList, T>(ref TList list, T item, IEqualityCompa
}
}

/// <summary>
/// Transactionally try to add an item to a list if not already present.
/// </summary>
/// <returns>True if item was added, false if item was already present</returns>
public static bool TryAddDistinct<TList, T>(ref TList list, T item)
where TList : class, IImmutableList<T>
{
while (true)
{
var capture = list;
if (capture.IndexOf(item) >= 0)
{
return false;
}

var updated = (TList)capture.Add(item);

if (Interlocked.CompareExchange(ref list, updated, capture) == capture)
{
return true;
}
}
}

/// <summary>
/// Transactionally try to add an item to a list if not already present.
/// </summary>
/// <returns>True if item was added, false if item was already present</returns>
public static bool TryAddDistinct<TList, T>(ref TList list, T item, IEqualityComparer<T> comparer)
where TList : class, IImmutableList<T>
{
while (true)
{
var capture = list;
if (capture.IndexOf(item, comparer) >= 0)
{
return false;
}

var updated = (TList)capture.Add(item);

if (Interlocked.CompareExchange(ref list, updated, capture) == capture)
{
return true;
}
}
}

/// <summary>
/// Transactionally remove an item from a list.
/// </summary>
Expand Down

0 comments on commit 8206eb9

Please sign in to comment.