2020-04-11 15:48:02 +02:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Runtime.CompilerServices;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
|
|
namespace Capnp.Util
|
|
|
|
|
{
|
2020-04-20 21:36:06 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// A task-like object which enforces that all await operations from the same thread leave in the exact order they were issued.
|
|
|
|
|
/// Note that an ordinary .NET Task does not fulfill this requirement if completed by a thread which is different from the
|
|
|
|
|
/// awaiting thread.
|
|
|
|
|
/// </summary>
|
2020-04-20 08:14:02 +02:00
|
|
|
|
public class StrictlyOrderedAwaitTask: INotifyCompletion
|
2020-04-11 15:48:02 +02:00
|
|
|
|
{
|
2020-04-15 21:52:56 +02:00
|
|
|
|
class Cover { }
|
|
|
|
|
class Seal { }
|
|
|
|
|
|
|
|
|
|
static readonly Cover s_cover = new Cover();
|
|
|
|
|
static readonly Seal s_seal = new Seal();
|
2020-04-15 08:13:42 +02:00
|
|
|
|
|
2020-04-19 18:53:09 +02:00
|
|
|
|
readonly Task _awaitedTask;
|
2020-04-15 21:52:56 +02:00
|
|
|
|
object? _state;
|
2020-04-11 15:48:02 +02:00
|
|
|
|
|
2020-04-20 21:36:06 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Constructs an instance
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="awaitedTask">Task on which the order shall be enforced</param>
|
2020-04-19 18:53:09 +02:00
|
|
|
|
public StrictlyOrderedAwaitTask(Task awaitedTask)
|
2020-04-11 15:48:02 +02:00
|
|
|
|
{
|
|
|
|
|
_awaitedTask = awaitedTask;
|
2020-04-15 21:52:56 +02:00
|
|
|
|
_state = s_cover;
|
2020-04-11 15:48:02 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-20 21:36:06 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// await pattern implementation
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>An object suitable for the await pattern</returns>
|
2020-04-19 18:53:09 +02:00
|
|
|
|
public StrictlyOrderedAwaitTask GetAwaiter()
|
2020-04-11 15:48:02 +02:00
|
|
|
|
{
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-15 08:13:42 +02:00
|
|
|
|
async void AwaitInternal()
|
2020-04-11 15:48:02 +02:00
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
await _awaitedTask;
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
SpinWait.SpinUntil(() =>
|
|
|
|
|
{
|
2020-04-15 08:13:42 +02:00
|
|
|
|
Action? continuations;
|
|
|
|
|
do
|
2020-04-11 15:48:02 +02:00
|
|
|
|
{
|
2020-04-15 21:52:56 +02:00
|
|
|
|
continuations = (Action?)Interlocked.Exchange(ref _state, null);
|
2020-04-15 08:13:42 +02:00
|
|
|
|
continuations?.Invoke();
|
2020-04-11 15:48:02 +02:00
|
|
|
|
|
2020-04-15 08:13:42 +02:00
|
|
|
|
} while (continuations != null);
|
2020-04-11 15:48:02 +02:00
|
|
|
|
|
2020-04-15 21:52:56 +02:00
|
|
|
|
return Interlocked.CompareExchange(ref _state, s_seal, null) == null;
|
2020-04-11 15:48:02 +02:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-20 21:36:06 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Part of await pattern implementation. Do not use directly.
|
|
|
|
|
/// </summary>
|
2020-04-15 08:13:42 +02:00
|
|
|
|
public void OnCompleted(Action continuation)
|
|
|
|
|
{
|
2020-04-15 21:52:56 +02:00
|
|
|
|
bool first = false;
|
|
|
|
|
|
2020-04-15 08:13:42 +02:00
|
|
|
|
SpinWait.SpinUntil(() => {
|
2020-04-15 21:52:56 +02:00
|
|
|
|
object? cur, next;
|
2020-04-15 08:13:42 +02:00
|
|
|
|
cur = Volatile.Read(ref _state);
|
2020-04-15 21:52:56 +02:00
|
|
|
|
first = false;
|
2020-04-15 08:13:42 +02:00
|
|
|
|
switch (cur)
|
|
|
|
|
{
|
2020-04-15 21:52:56 +02:00
|
|
|
|
case Cover cover:
|
2020-04-15 08:13:42 +02:00
|
|
|
|
next = continuation;
|
2020-04-15 21:52:56 +02:00
|
|
|
|
first = true;
|
2020-04-15 08:13:42 +02:00
|
|
|
|
break;
|
|
|
|
|
|
2020-04-15 21:52:56 +02:00
|
|
|
|
case null:
|
|
|
|
|
next = continuation;
|
|
|
|
|
break;
|
2020-04-15 08:13:42 +02:00
|
|
|
|
|
|
|
|
|
case Action action:
|
|
|
|
|
next = action + continuation;
|
|
|
|
|
break;
|
2020-04-15 21:52:56 +02:00
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
continuation();
|
|
|
|
|
return true;
|
2020-04-15 08:13:42 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Interlocked.CompareExchange(ref _state, next, cur) == cur;
|
|
|
|
|
});
|
2020-04-15 21:52:56 +02:00
|
|
|
|
|
|
|
|
|
if (first)
|
|
|
|
|
{
|
|
|
|
|
AwaitInternal();
|
|
|
|
|
}
|
2020-04-15 08:13:42 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-20 21:36:06 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Whether the underlying task did complete and it is safe to skip continuation registration.
|
|
|
|
|
/// </summary>
|
2020-04-21 07:20:34 +02:00
|
|
|
|
public bool IsCompleted => _awaitedTask.IsCompleted && (_state == s_cover || _state == s_seal);
|
2020-04-11 15:48:02 +02:00
|
|
|
|
|
2020-04-20 21:36:06 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Part of await pattern implementation. Do not use directly.
|
|
|
|
|
/// </summary>
|
2020-04-19 18:53:09 +02:00
|
|
|
|
public void GetResult() => _awaitedTask.GetAwaiter().GetResult();
|
2020-04-11 15:48:02 +02:00
|
|
|
|
|
2020-04-20 21:36:06 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Task on which the order shall be enforced.
|
|
|
|
|
/// </summary>
|
2020-04-19 18:53:09 +02:00
|
|
|
|
public Task WrappedTask => _awaitedTask;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-20 21:36:06 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// A task-like object which enforces that all await operations from the same thread leave in the exact order they were issued.
|
|
|
|
|
/// Note that an ordinary .NET Task does not fulfill this requirement if completed by a thread which is different from the
|
|
|
|
|
/// awaiting thread.
|
|
|
|
|
/// </summary>
|
2020-04-20 08:14:02 +02:00
|
|
|
|
public class StrictlyOrderedAwaitTask<T> : StrictlyOrderedAwaitTask
|
2020-04-19 18:53:09 +02:00
|
|
|
|
{
|
2020-04-20 21:36:06 +02:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Constructs an instance
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="awaitedTask">Task on which the order shall be enforced</param>
|
2020-04-19 18:53:09 +02:00
|
|
|
|
public StrictlyOrderedAwaitTask(Task<T> awaitedTask): base(awaitedTask)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-20 21:36:06 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Task on which the order shall be enforced.
|
|
|
|
|
/// </summary>
|
2020-04-19 18:53:09 +02:00
|
|
|
|
public new Task<T> WrappedTask => (Task<T>)base.WrappedTask;
|
2020-04-20 21:36:06 +02:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// await pattern implementation
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>An object suitable for the await pattern</returns>
|
2020-04-19 18:53:09 +02:00
|
|
|
|
public new StrictlyOrderedAwaitTask<T> GetAwaiter() => this;
|
|
|
|
|
|
2020-04-20 21:36:06 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Part of await pattern implementation. Do not use directly.
|
|
|
|
|
/// </summary>
|
2020-04-19 18:53:09 +02:00
|
|
|
|
public new T GetResult() => WrappedTask.GetAwaiter().GetResult();
|
|
|
|
|
|
2020-04-20 21:36:06 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Redirects to the wrapped Task's result.
|
|
|
|
|
/// </summary>
|
2020-04-19 18:53:09 +02:00
|
|
|
|
public T Result => WrappedTask.Result;
|
2020-04-11 15:48:02 +02:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-20 21:36:06 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Extension methods to simplify the use of <see cref="StrictlyOrderedAwaitTask"/>
|
|
|
|
|
/// </summary>
|
2020-04-20 08:14:02 +02:00
|
|
|
|
public static class StrictlyOrderedTaskExtensions
|
2020-04-11 15:48:02 +02:00
|
|
|
|
{
|
2020-04-20 21:36:06 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Converts the task to a task-like object which enforces that all await operations from the same thread leave in the exact order they were issued.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <typeparam name="T">The type of the result produced by the Task</typeparam>
|
|
|
|
|
/// <param name="task">Task to wrap</param>
|
|
|
|
|
/// <returns>awaitable object</returns>
|
2020-04-11 15:48:02 +02:00
|
|
|
|
public static StrictlyOrderedAwaitTask<T> EnforceAwaitOrder<T>(this Task<T> task)
|
|
|
|
|
{
|
|
|
|
|
return new StrictlyOrderedAwaitTask<T>(task);
|
|
|
|
|
}
|
2020-04-19 18:53:09 +02:00
|
|
|
|
|
2020-04-20 21:36:06 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Converts the task to a task-like object which enforces that all await operations from the same thread leave in the exact order they were issued.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="task">Task to wrap</param>
|
|
|
|
|
/// <returns>awaitable object</returns>
|
2020-04-19 18:53:09 +02:00
|
|
|
|
public static StrictlyOrderedAwaitTask EnforceAwaitOrder(this Task task)
|
|
|
|
|
{
|
|
|
|
|
return new StrictlyOrderedAwaitTask(task);
|
|
|
|
|
}
|
2020-04-11 15:48:02 +02:00
|
|
|
|
}
|
|
|
|
|
}
|