Could you please see if they are no bugs in this implementation? I am especially interested in knowing if there could be here any multithreading problems (such as race conditions).
It is written in C# for .NET 3.5.
public interface IAsyncOperation
{
OperationToken Token { get; }
bool IsCompleted { get; }
Exception Exception { get; }
bool Wait(int timeout);
}
public interface IAsyncOperation<out TResult> : IAsyncOperation
{
TResult Result { get; }
}
/// <summary>
/// Asynchronous operation that may be executed on a separate thread.
/// IsCompleted and Exception would be synchronized after invoking Wait.
/// </summary>
public abstract class AsyncOperation : IAsyncOperation
{
protected readonly object CompletionLock = new object();
private volatile bool isCompleted; // volatile in order not to change the order of writing Exception and Result
protected AsyncOperation(bool isCompleted = false)
{
IsCompleted = isCompleted;
Token = OperationToken.New();
}
public OperationToken Token { get; protected set; }
public bool IsCompleted
{
get { return isCompleted; }
protected set { isCompleted = value; }
}
public Exception Exception { get; protected set; }
public bool Wait(int timeout)
{
if (!IsCompleted)
{
lock (CompletionLock)
{
if (!IsCompleted)
{
// when migrated to .NET 4.5 then implement with ManualResetEventSlim
Monitor.Wait(CompletionLock, timeout);
}
}
}
return IsCompleted;
}
protected void Complete()
{
if (IsCompleted)
{
throw new InvalidOperationException("The operation was already completed");
}
lock (CompletionLock)
{
if (IsCompleted)
{
throw new InvalidOperationException("The operation was already completed");
}
IsCompleted = true;
Monitor.PulseAll(CompletionLock);
}
}
protected void Complete(Exception exception)
{
if (!IsCompleted)
{
lock (CompletionLock)
{
if (!IsCompleted)
{
Exception = exception;
IsCompleted = true;
Monitor.PulseAll(CompletionLock);
}
}
}
}
}
public abstract class AsyncOperation<TResult> : AsyncOperation, IAsyncOperation<TResult>
{
protected AsyncOperation(bool isCompleted = false)
: base(isCompleted)
{
}
public TResult Result { get; private set; }
protected void Complete(TResult result)
{
if (!IsCompleted)
{
lock (CompletionLock)
{
if (!IsCompleted)
{
Result = result;
IsCompleted = true;
Monitor.PulseAll(CompletionLock);
}
}
}
}
}
/// <summary>
/// Invokes the action on the ThreadPool.
/// </summary>
public class PooledOperation : AsyncOperation
{
private readonly Action<OperationToken> action;
private bool isStarted;
public PooledOperation(Action<OperationToken> action)
{
this.action = action;
ThreadPool.QueueUserWorkItem(Execute);
}
public static IAsyncOperation<TResult> Run<TResult>(Func<OperationToken, TResult> function)
{
var result = new PooledOperation<TResult>(function);
result.Start();
return result;
}
public static IAsyncOperation Run(Action<OperationToken> action)
{
var result = new PooledOperation(action);
result.Start();
return result;
}
public void Start()
{
if (!isStarted)
{
isStarted = ThreadPool.QueueUserWorkItem(Execute);
}
}
private void Execute(object state)
{
try
{
action(Token);
Complete();
}
catch (Exception ex)
{
Complete(ex);
}
}
}
public class PooledOperation<TResult> : AsyncOperation<TResult>
{
private readonly Func<OperationToken, TResult> function;
public PooledOperation(Func<OperationToken, TResult> function)
{
this.function = function;
}
public void Start()
{
if (!IsCompleted)
{
ThreadPool.QueueUserWorkItem(Execute);
}
}
private void Execute(object state)
{
try
{
TResult result = function(Token);
Complete(result);
}
catch (Exception ex)
{
Complete(ex);
}
}
}
/// <summary>
/// Used in own ThreadPool-like implementation.
/// </summary>
internal class AsyncCommand : AsyncOperation
{
private readonly Action<OperationToken> command;
public AsyncCommand(Action<OperationToken> command)
{
command.ThrowIfNull("command");
this.command = command;
}
public void Execute()
{
if (IsCompleted)
{
throw new InvalidOperationException("The operation was already completed");
}
try
{
command(Token);
Complete();
}
catch (Exception ex)
{
Complete(ex);
}
}
}