private static AsyncMethodState DeserializeInner(object currentInstance, JToken json) { if (json == null || !json.Any()) { return(null); } var state = new AsyncMethodState(); state.AsyncMethodName = json["asyncMethodName"].Value <string>(); state.StateMachineAssemblyName = json["stateMachineAssemblyName"].Value <string>(); state.StateMachineTypeName = json["stateMachineTypeName"].Value <string>(); state.StateNumber = CreateMember(json["stateNumber"]["memberName"].Value <string>(), json["stateNumber"]["value"].Value <int>()); state.CurrentAwaiter = CreateMember(json["currentAwaiter"]["memberName"].Value <string>(), DeserializeInner(currentInstance, json["currentAwaiter"]["value"])); state.Locals = new List <Member <object> >(); foreach (var local in json["locals"]) { var value = local["value"] == null //|| Type.GetType(local["type"].ToString()).IsSubclassOf(typeof(Task)) ? null : (local["type"].Value <string>() == currentInstance.GetType().FullName ? currentInstance : local["value"].ToObject(Assembly.Load(local["assembly"].Value <string>()).GetType(local["type"].Value <string>()))); state.Locals.Add(CreateMember(local["memberName"].Value <string>(), value)); } return(state); }
private static string Serialize(AsyncMethodState state, int resumeCount) { var json = new JObject(); json["resumeCount"] = resumeCount; json["state"] = SerializeInner(state); return(json.ToString()); }
public void OnCompleted(Action continuation) { if (_result > 0) { continuation(); return; } var asyncMethod = TryGetStateMachineForDebugger(continuation).Target as IAsyncStateMachine; AsyncMethodState state = null; while (true) { var state1 = new AsyncMethodState(); state1.AsyncMethodName = asyncMethod.Name(); state1.AsyncMethodTaskId = asyncMethod.Task().Id; state1.StateMachineAssemblyName = asyncMethod.GetType().Assembly.FullName; state1.StateMachineTypeName = asyncMethod.GetType().FullName; state1.StateNumber = asyncMethod.GetCurrentStateNumber(); state1.CurrentAwaiter = CreateMember(asyncMethod.GetCurrentAwaiter().MemberName, state); state1.Locals = asyncMethod.GetLocalsAndParameters().ToList(); state = state1; var parent = asyncMethod.GetAsyncMethodThatsAwaitingThisOne(); if (parent == null) { Serialize(state, 0); continuation(); return; //throw new NoAwaitingParentException($"Can't figure out which async method is awaiting {asyncMethod.Name()}"); } var parentAwaiter = parent.GetCurrentAwaiter(); if (parentAwaiter == null) { throw new NotSupportedException($"Async method {parent.Name()} has awaiter types that we don't know how to checkpoint"); } var name = parentAwaiter.Value.GetType().ToString(); if (parentAwaiter.Value is RunWithCheckpointingAwaiter) { var saver = parentAwaiter.Value as RunWithCheckpointingAwaiter; saver.fn.Clear(); saver.fn.Append(Serialize(state, 0)); break; } else if (parentAwaiter.Value.GetType().ToString().StartsWith("System.Runtime.CompilerServices.TaskAwaiter")) { asyncMethod = parent; } else { throw new NotSupportedException($"Async method {parent.Name()} is awaiting a {parentAwaiter.Value.GetType().ToString()}, but we only know how to checkpoint awaits on normal Tasks"); } } continuation(); }
private static JObject SerializeInner(AsyncMethodState state) { if (state == null) { return(null); } var json = new JObject(); json["asyncMethodName"] = state.AsyncMethodName; json["stateMachineAssemblyName"] = state.StateMachineAssemblyName; json["stateMachineTypeName"] = state.StateMachineTypeName; json["stateNumber"] = new JObject { { "memberName", state.StateNumber.MemberName }, { "value", state.StateNumber.Value } }; json["currentAwaiter"] = new JObject { { "memberName", state.CurrentAwaiter.MemberName }, { "value", SerializeInner(state.CurrentAwaiter.Value) } }; var locals = new JArray(); json["locals"] = locals; foreach (var local in state.Locals) { if (local.Value == null) { locals.Add(new JObject { { "memberName", local.MemberName } }); } else { locals.Add(new JObject { { "memberName", local.MemberName }, { "assembly", local.Value.GetType().Assembly.FullName }, { "type", local.Value.GetType().FullName }, { "value", JToken.FromObject(local.Value) } }); } } return(json); }
private static void SaveContextInner(AsyncMethodState state) { if (state == null) { return; } SaveContextInner(state.CurrentAwaiter.Value); foreach (var local in state.Locals.Where(l => l.Value != null)) { if (local.Value.GetType() == typeof(AsyncContext)) { Immortal.InstanceProxy.Immortal.TaskIdToSequenceNumber.Data.AddOrUpdate(state.AsyncMethodTaskId, ((AsyncContext)local.Value).SequenceNumber, (k, v) => v); } } }
private static ReadStateMachineResult ReconstructStateMachine(AsyncMethodState state) { var bf = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; // sm = new StateMachineType(); var assembly = Assembly.Load(state.StateMachineAssemblyName); var sm = Activator.CreateInstance(assembly.GetType(state.StateMachineTypeName)) as IAsyncStateMachine; // sm.builder = BuilderType.Create() var builderField = sm.BuilderField(); builderField.SetValue(sm, builderField.FieldType.GetMethod("Create").Invoke(null, new object[] { })); try { Expression.Lambda <Action>(Expression.Call(Expression.Field(Expression.Constant(sm), builderField), builderField.FieldType.GetMethod("SetStateMachine"), Expression.Constant(sm))).Compile().Invoke(); } catch (Exception e) { Console.WriteLine(e); throw; } // sm.builder.SetStateMachine(sm) // sm.state = i sm.GetType().GetField(state.StateNumber.MemberName).SetValue(sm, state.StateNumber.Value); // sm.<local> = <local_value> foreach (var local in state.Locals) { if (local.Value != null && local.Value.GetType() == typeof(SerializableTaskCompletionSource) && sm.GetType().GetField(local.MemberName, bf).FieldType.IsSubclassOf(typeof(Task))) { SerializableTaskCompletionSource stcs; if (((SerializableTaskCompletionSource)local.Value).IsCompleted) { stcs = (SerializableTaskCompletionSource)local.Value; } else { Immortal.InstanceProxy.Immortal.CallCache.Data.TryGetValue(((SerializableTaskCompletionSource)local.Value).SequenceNumber, out stcs); } var getAwaitableTaskMethod = typeof(SerializableTaskCompletionSource).GetMethods().FirstOrDefault(m => m.GetCustomAttributes(typeof(SerializableTaskCompletionSource.GetAwaitableTaskAttribute)).Any()); if (getAwaitableTaskMethod != null) { var genericGetAwaitabeTaskMethod = getAwaitableTaskMethod.MakeGenericMethod(stcs.ResultType.Type); var value = genericGetAwaitabeTaskMethod.Invoke(stcs, new object[] { }); sm.GetType().GetField(local.MemberName, bf).SetValue(sm, value); } } else { sm.GetType().GetField(local.MemberName, bf).SetValue(sm, local.Value); } } ReadStateMachineResult awaited; if (state.CurrentAwaiter.Value == null) { // sm.awaiter = new CheckpointSaveAwaiter() var awaiter = new CheckpointSaveAwaiter() { _result = 0 }; awaited = new ReadStateMachineResult() { AwaiterForAwaitingThisStateMachine = awaiter, LeafCheckpointSaveAwaiter = awaiter }; sm.GetType().GetField(state.CurrentAwaiter.MemberName, bf).SetValue(sm, awaiter); } else { // sm.awaiter = <nested state machine's awaiter> awaited = ReconstructStateMachine(state.CurrentAwaiter.Value); var awaiter = awaited.AwaiterForAwaitingThisStateMachine; sm.GetType().GetField(state.CurrentAwaiter.MemberName, bf).SetValue(sm, awaiter); } // sm.builder.AwaitOnCompleted(ref child_awaiter, ref sm); var varSm = Expression.Variable(sm.GetType(), "sm"); var varAwaiter = Expression.Variable(awaited.AwaiterForAwaitingThisStateMachine.GetType(), "awaiter"); var expression = Expression.Lambda <Action>(Expression.Block( new[] { varSm, varAwaiter }, Expression.Assign(varSm, Expression.Constant(sm)), Expression.Assign(varAwaiter, Expression.Constant(awaited.AwaiterForAwaitingThisStateMachine)), Expression.Call( Expression.Field(varSm, builderField), builderField.FieldType.GetMethod("AwaitOnCompleted").MakeGenericMethod(awaited.AwaiterForAwaitingThisStateMachine.GetType(), sm.GetType()), new Expression[] { varAwaiter, varSm }))); var lambda = expression.Compile(); if (state.CurrentAwaiter.Value == null) { awaited.LeafActionToStartWork = lambda; } else { lambda(); } // task = sm.Builder.Task var task = Expression.Lambda <Func <object> >(Expression.Property(Expression.Field(Expression.Constant(sm), builderField), builderField.FieldType.GetProperty("Task"))).Compile().Invoke(); // task.GetAwaiter(); var taskAwaiter = task.GetType().GetMethod("GetAwaiter").Invoke(task, new object[] { }); return(new ReadStateMachineResult { StateMachine = sm, Task = task as Task, AwaiterForAwaitingThisStateMachine = taskAwaiter, LeafActionToStartWork = awaited.LeafActionToStartWork, LeafCheckpointSaveAwaiter = awaited.LeafCheckpointSaveAwaiter }); }
private static JObject SerializeInner(AsyncMethodState state) { if (state == null) { return(null); } var json = new JObject(); json["asyncMethodName"] = state.AsyncMethodName; json["stateMachineAssemblyName"] = state.StateMachineAssemblyName; json["stateMachineTypeName"] = state.StateMachineTypeName; json["stateNumber"] = new JObject { { "memberName", state.StateNumber.MemberName }, { "value", state.StateNumber.Value } }; json["currentAwaiter"] = new JObject { { "memberName", state.CurrentAwaiter.MemberName }, { "value", SerializeInner(state.CurrentAwaiter.Value) } }; var locals = new JArray(); json["locals"] = locals; foreach (var local in state.Locals) { if (local.Value == null) { locals.Add(new JObject { { "memberName", local.MemberName } }); } else if (local.Value.GetType().IsSubclassOf(typeof(Task))) { SerializableTaskCompletionSource stcs; if (((Task)local.Value).IsCompleted) { var task = (Task)local.Value; var property = task.GetType().GetProperty("Result", BindingFlags.Public | BindingFlags.Instance); var result = property.GetValue(task); stcs = new SerializableTaskCompletionSource(result.GetType()); stcs.SetResult(result); } else if (!Immortal.InstanceProxy.Immortal.TaskIdToSequenceNumber.Data.TryRemove(((Task)local.Value).Id, out var sequenceNumber) || !Immortal.InstanceProxy.Immortal.CallCache.Data.TryGetValue(sequenceNumber, out stcs)) { stcs = new SerializableTaskCompletionSource(); } locals.Add(new JObject { { "memberName", local.MemberName }, { "assembly", stcs.GetType().Assembly.FullName }, { "type", stcs.GetType().FullName }, { "value", JToken.FromObject(stcs) } }); } else { if (local.Value.GetType() == typeof(AsyncContext)) { Immortal.InstanceProxy.Immortal.TaskIdToSequenceNumber.Data.AddOrUpdate(state.AsyncMethodTaskId, ((AsyncContext)local.Value).SequenceNumber, (k, v) => v); } locals.Add(new JObject { { "memberName", local.MemberName }, { "assembly", local.Value.GetType().Assembly.FullName }, { "type", local.Value.GetType().FullName }, { "value", JToken.FromObject(local.Value) } }); } } return(json); }
private static ReadStateMachineResult ReconstructStateMachine(AsyncMethodState state) { var bf = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; // sm = new StateMachineType(); var sm = Activator.CreateInstance(state.StateMachineAssemblyName, state.StateMachineTypeName).Unwrap() as IAsyncStateMachine; // sm.builder = BuilderType.Create() var builderField = sm.BuilderField(); builderField.SetValue(sm, builderField.FieldType.GetMethod("Create").Invoke(null, new object[] { })); // sm.builder.SetStateMachine(sm) Expression.Lambda <Action>(Expression.Call(Expression.Field(Expression.Constant(sm), builderField), builderField.FieldType.GetMethod("SetStateMachine"), Expression.Constant(sm))).Compile().Invoke(); // sm.state = i sm.GetType().GetField(state.StateNumber.MemberName).SetValue(sm, state.StateNumber.Value); // sm.<local> = <local_value> foreach (var local in state.Locals) { sm.GetType().GetField(local.MemberName, bf).SetValue(sm, local.Value); } ReadStateMachineResult awaited; if (state.CurrentAwaiter.Value == null) { // sm.awaiter = new CheckpointSaveAwaiter() var awaiter = new CheckpointSaveAwaiter() { _result = 0 }; awaited = new ReadStateMachineResult() { AwaiterForAwaitingThisStateMachine = awaiter, LeafCheckpointSaveAwaiter = awaiter }; sm.GetType().GetField(state.CurrentAwaiter.MemberName, bf).SetValue(sm, awaiter); } else { // sm.awaiter = <nested state machine's awaiter> awaited = ReconstructStateMachine(state.CurrentAwaiter.Value); var awaiter = awaited.AwaiterForAwaitingThisStateMachine; sm.GetType().GetField(state.CurrentAwaiter.MemberName, bf).SetValue(sm, awaiter); } // sm.builder.AwaitOnCompleted(ref child_awaiter, ref sm); var varSm = Expression.Variable(sm.GetType(), "sm"); var varAwaiter = Expression.Variable(awaited.AwaiterForAwaitingThisStateMachine.GetType(), "awaiter"); var expression = Expression.Lambda <Action>(Expression.Block( new[] { varSm, varAwaiter }, Expression.Assign(varSm, Expression.Constant(sm)), Expression.Assign(varAwaiter, Expression.Constant(awaited.AwaiterForAwaitingThisStateMachine)), Expression.Call( Expression.Field(varSm, builderField), builderField.FieldType.GetMethod("AwaitOnCompleted").MakeGenericMethod(awaited.AwaiterForAwaitingThisStateMachine.GetType(), sm.GetType()), new Expression[] { varAwaiter, varSm }))); var lambda = expression.Compile(); if (state.CurrentAwaiter.Value == null) { awaited.LeafActionToStartWork = lambda; } else { lambda(); } // task = sm.Builder.Task var task = Expression.Lambda <Func <object> >(Expression.Property(Expression.Field(Expression.Constant(sm), builderField), builderField.FieldType.GetProperty("Task"))).Compile().Invoke(); // task.GetAwaiter(); var taskAwaiter = task.GetType().GetMethod("GetAwaiter").Invoke(task, new object[] { }); return(new ReadStateMachineResult { StateMachine = sm, Task = task as Task, AwaiterForAwaitingThisStateMachine = taskAwaiter, LeafActionToStartWork = awaited.LeafActionToStartWork, LeafCheckpointSaveAwaiter = awaited.LeafCheckpointSaveAwaiter }); }