Skip to content

Instantly share code, notes, and snippets.

@sgoguen
Last active February 16, 2017 16:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sgoguen/02ba2b0688eae7e836ee7dbba0aa6934 to your computer and use it in GitHub Desktop.
Save sgoguen/02ba2b0688eae7e836ee7dbba0aa6934 to your computer and use it in GitHub Desktop.
A lightweight data structure for creating tasks that are completed elsewhere
// 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