Last active
February 16, 2017 16:52
-
-
Save sgoguen/02ba2b0688eae7e836ee7dbba0aa6934 to your computer and use it in GitHub Desktop.
A lightweight data structure for creating tasks that are completed elsewhere
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// A poor reimplementation of IAsyncResult. It was a fun exercise though | |
public interface ITaskTrigger<T> { | |
void SetComplete(T value); | |
void ThrowException(Exception ex); | |
} | |
public class AsyncRequest<T, U> : ITaskTrigger<U> { | |
public readonly T Request; | |
public readonly ITaskTrigger<U> Trigger; | |
internal AsyncRequest(T request, ITaskTrigger<U> trigger) { | |
Request = request; | |
Trigger = trigger; | |
} | |
public void SetComplete(U value) => Trigger.SetComplete(value); | |
public void ThrowException(Exception ex) => Trigger.ThrowException(ex); | |
} | |
public static class AsyncRequest { | |
public class TaskTriggerException : Exception { | |
public TaskTriggerException(string message, Exception inner) : base(message, inner) { } | |
} | |
public static Task<T> SendTo<T>(this T value, Action<ITaskTrigger<T>> sendTriggerTo) { | |
var triggeredTask = CreateTriggeredTask<T>(); | |
sendTriggerTo(triggeredTask.Trigger); | |
return triggeredTask.Completion; | |
} | |
public static Task<U> SendReqTo<T, U>(this T value, Action<AsyncRequest<T, U>> sendTriggerTo) { | |
var triggeredTask = CreateTriggeredTask<U>(); | |
var asyncRequest = new AsyncRequest<T,U>(value, triggeredTask.Trigger); | |
sendTriggerTo(asyncRequest); | |
return triggeredTask.Completion; | |
} | |
private static TriggeredTask<T> CreateTriggeredTask<T>() { | |
var trigger = new Task(() => { }); | |
T response = default(T); | |
Exception thrownException = null; | |
// Create the set response trigger | |
var setResponse = new Action<T>(value => { | |
try { | |
lock (trigger) { | |
response = value; | |
trigger.Start(); | |
} | |
} catch (Exception ex) { | |
throw new TaskTriggerException("Cannot set response twice", ex); | |
} | |
}); | |
// Create the throw exception trigger | |
var throwException = new Action<Exception>(ex => { | |
try { | |
lock (trigger) { | |
thrownException = ex; | |
trigger.Start(); | |
} | |
} catch (Exception inner) { | |
throw new TaskTriggerException("Cannot set exception twice", inner); | |
} | |
}); | |
var taskTrigger = new BasicTaskTrigger<T>(setResponse, throwException); | |
var completion = Task.Run(async () => { | |
await trigger; | |
lock (trigger) { | |
if (thrownException != null) { | |
throw thrownException; | |
} | |
return response; | |
} | |
}); | |
return new TriggeredTask<T>(taskTrigger, completion); | |
} | |
internal struct TriggeredTask<T> { | |
public readonly ITaskTrigger<T> Trigger; | |
public readonly Task<T> Completion; | |
internal TriggeredTask(ITaskTrigger<T> taskTrigger, Task<T> completion) { | |
Trigger = taskTrigger; | |
Completion = completion; | |
} | |
} | |
private class BasicTaskTrigger<T> : ITaskTrigger<T> { | |
public readonly Action<T> _SetComplete; | |
public readonly Action<Exception> _ThrowException; | |
internal BasicTaskTrigger(Action<T> setComplete, Action<Exception> throwException) { | |
_SetComplete = setComplete; | |
_ThrowException = throwException; | |
} | |
public void SetComplete(T value) => _SetComplete(value); | |
public void ThrowException(Exception ex) => _ThrowException(ex); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment