You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
130 lines
3.9 KiB
130 lines
3.9 KiB
5 years ago
|
// Needed for Workaround
|
||
|
#if !NET_4_6
|
||
|
using System;
|
||
|
using LinqInternal.Collections.ThreadSafe;
|
||
|
using LinqInternal.Threading.Needles;
|
||
|
|
||
|
namespace LinqInternal.Threading
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// Represents a context to execute operation without reentry.
|
||
|
/// </summary>
|
||
|
[System.Diagnostics.DebuggerNonUserCode]
|
||
|
internal sealed class ReentryGuard
|
||
|
{
|
||
|
private readonly RuntimeUniqueIdProdiver.UniqueId _id;
|
||
|
private readonly SafeQueue<Action> _workQueue;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Initializes a new instance of the <see cref="ReentryGuard" /> class.
|
||
|
/// </summary>
|
||
|
public ReentryGuard()
|
||
|
{
|
||
|
_workQueue = new SafeQueue<Action>();
|
||
|
_id = RuntimeUniqueIdProdiver.GetNextId();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets a value indicating whether or not the current thread did enter.
|
||
|
/// </summary>
|
||
|
public bool IsTaken
|
||
|
{
|
||
|
get { return ReentryGuardHelper.IsTaken(_id); }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Executes an operation-
|
||
|
/// </summary>
|
||
|
/// <param name="operation">The operation to execute.</param>
|
||
|
/// <returns>Returns a promise to finish the execution.</returns>
|
||
|
public IPromise Execute(Action operation)
|
||
|
{
|
||
|
var result = AddExecution(operation, _workQueue);
|
||
|
ExecutePending(_workQueue, _id);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Executes an operation-
|
||
|
/// </summary>
|
||
|
/// <typeparam name="T">The return value of the operation.</typeparam>
|
||
|
/// <param name="operation">The operation to execute.</param>
|
||
|
/// <returns>Returns a promise to finish the execution.</returns>
|
||
|
public IPromise<T> Execute<T>(Func<T> operation)
|
||
|
{
|
||
|
var result = AddExecution(operation, _workQueue);
|
||
|
ExecutePending(_workQueue, _id);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
private static IPromise AddExecution(Action action, SafeQueue<Action> queue)
|
||
|
{
|
||
|
var promised = new Promise(false);
|
||
|
var result = new ReadOnlyPromise(promised, false);
|
||
|
queue.Add
|
||
|
(
|
||
|
() =>
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
action.Invoke();
|
||
|
promised.SetCompleted();
|
||
|
}
|
||
|
catch (Exception exception)
|
||
|
{
|
||
|
promised.SetError(exception);
|
||
|
}
|
||
|
}
|
||
|
);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
private static IPromise<T> AddExecution<T>(Func<T> action, SafeQueue<Action> queue)
|
||
|
{
|
||
|
var promised = new PromiseNeedle<T>(false);
|
||
|
var result = new ReadOnlyPromiseNeedle<T>(promised, false);
|
||
|
queue.Add
|
||
|
(
|
||
|
() =>
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
promised.Value = action.Invoke();
|
||
|
}
|
||
|
catch (Exception exception)
|
||
|
{
|
||
|
promised.SetError(exception);
|
||
|
}
|
||
|
}
|
||
|
);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
private static void ExecutePending(SafeQueue<Action> queue, RuntimeUniqueIdProdiver.UniqueId id)
|
||
|
{
|
||
|
var didEnter = false;
|
||
|
try
|
||
|
{
|
||
|
didEnter = ReentryGuardHelper.Enter(id);
|
||
|
if (!didEnter)
|
||
|
{
|
||
|
// called from inside this method - skip
|
||
|
return;
|
||
|
}
|
||
|
Action action;
|
||
|
while (queue.TryTake(out action))
|
||
|
{
|
||
|
action.Invoke();
|
||
|
}
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
if (didEnter)
|
||
|
{
|
||
|
ReentryGuardHelper.Leave(id);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|