Skip to content

Instantly share code, notes, and snippets.

@eiriktsarpalis
Last active April 6, 2020 10:26
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save eiriktsarpalis/acc45229a329a395143d39cea4f9924f to your computer and use it in GitHub Desktop.
Save eiriktsarpalis/acc45229a329a395143d39cea4f9924f to your computer and use it in GitHub Desktop.
Delayable MethodBuilders
using System;
using System.Collections.Concurrent;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Security;
using System.Threading.Tasks;
namespace ThunkBuilder
{
[AsyncMethodBuilder(typeof(ThunkBuilder<>))]
public delegate T Thunk<T>();
public struct ThunkAwaiter<T> : ICriticalNotifyCompletion
{
public Thunk<T> Thunk { get; }
public ThunkAwaiter(Thunk<T> thunk) { Thunk = thunk; }
public T GetResult() => Thunk();
public bool IsCompleted => true;
public void OnCompleted(Action continuation) => throw new NotSupportedException();
public void UnsafeOnCompleted(Action continuation) => throw new NotSupportedException();
}
public static class ThunkExtensions
{
public static ThunkAwaiter<T> GetAwaiter<T>(this Thunk<T> thunk) => new ThunkAwaiter<T>(thunk);
public static ThunkAwaiter<T> GetAwaiter<T>(this Func<T> func) => new ThunkAwaiter<T>(new Thunk<T>(func.Invoke));
}
public class ThunkBuilder<T>
{
public Thunk<T> Task { get; private set; }
private IAsyncStateMachine StateMachine { get; set; }
public static ThunkBuilder<T> Create() => new ThunkBuilder<T>();
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
{
StateMachine = stateMachine;
Task = new Thunk<T>(Run);
}
private T Run()
{
var clonedBuilder = Clone();
clonedBuilder.StateMachine.MoveNext();
return clonedBuilder.Task();
}
public void SetStateMachine(IAsyncStateMachine stateMachine)
{
}
public void SetResult(T result)
{
Task = new Thunk<T>(() => result);
}
public void SetException(Exception exception)
{
Task = new Thunk<T>(() => { ExceptionDispatchInfo.Capture(exception).Throw(); return default; });
}
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
{
}
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TStateMachine : IAsyncStateMachine
{
}
private ThunkBuilder<T> Clone()
{
var clonedBuilder = new ThunkBuilder<T>();
var stateMachineRefl = s_cloneRefl.GetOrAdd(StateMachine.GetType(), GetMembers);
var clonedStateMachine = (IAsyncStateMachine)stateMachineRefl.memberwiseClone.Invoke(StateMachine, null);
stateMachineRefl.builder.SetValue(clonedStateMachine, clonedBuilder);
clonedBuilder.StateMachine = clonedStateMachine;
return clonedBuilder;
static (MethodInfo, FieldInfo) GetMembers(Type type)
{
var memberwiseClone = type.GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic);
var field = type.GetField("<>t__builder", BindingFlags.Instance | BindingFlags.Public);
return (memberwiseClone, field);
}
}
private static ConcurrentDictionary<Type, (MethodInfo memberwiseClone, FieldInfo builder)> s_cloneRefl = new ConcurrentDictionary<Type, (MethodInfo, FieldInfo)>();
}
class Program
{
static async Thunk<int> Fibonacci(int n)
{
if (n <= 1) return n;
return await Fibonacci(n - 2) + await Fibonacci(n - 1);
}
static async Thunk<bool> Test()
{
for (int i = 0; i < 10; i++)
Console.WriteLine($"f_{i} = {await Fibonacci(i)}");
return false;
}
static void Main()
{
Thunk<bool> t = Test();
// check if built delegate can be executed concurrently
Parallel.For(0, 10, _ => t());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment