Last active
April 6, 2020 10:26
-
-
Save eiriktsarpalis/acc45229a329a395143d39cea4f9924f to your computer and use it in GitHub Desktop.
Delayable MethodBuilders
This file contains 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
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