Task AcquireLocks() { if (!RecordOrReplayCall(MessageType.AcquireLock, out var opid, out var clock, out _)) { var destination = LockSet[0].Locate(process); var message = new AcquireLock { Opid = opid, Clock = clock, Parent = this.Opid, LockSet = LockSet }; process.Send(destination, message); } LockOpid = opid; if (!ReplayReturn(MessageType.GrantLock, opid, out var ignoredResult)) { var continuationInfo = new ContinuationInfo <UnitType>("Lock", OperationType.Event); Continuations[opid] = continuationInfo; return(continuationInfo.Tcs.Task); } else { return(Task.CompletedTask); } }
public Task <TReturn2> PerformActivity <TReturn2>(IActivity <TReturn2> activity) { if (!RecordOrReplayCall(MessageType.PerformActivity, out var opid, out var clock, out _)) { if (!process.Activities.TryGetValue(activity.GetType(), out var activityInfo)) { throw new BuilderException($"undefined activity type {activity.GetType().FullName}."); } if (activityInfo.RequiresLocks(activity, out var locks)) { if (LockSet != null) { foreach (var l in locks) { if (!LockSet.Contains(l)) { throw new SynchronizationDisciplineException("to perform an activity in a locked context, the caller's affinities must contain the callee's affinities"); } } } else { throw new SynchronizationDisciplineException("orchestrations can not call a locked affinity unless they already hold the lock"); } } activityInfo.CanExecuteLocally(activity, opid, out uint destination); var message = activityInfo.CreateRequestMessage(activity); message.Opid = opid; message.Clock = clock; message.Parent = this.Opid; message.LockedByCaller = (LockSet != null); process.Send(destination, message); } if (!ReplayReturn(MessageType.RespondToActivity, opid, out var result)) { var continuationInfo = new ContinuationInfo <TReturn2>(activity.ToString(), OperationType.Activity); Continuations[opid] = continuationInfo; return(continuationInfo.Tcs.Task); } else { if (process.Serializer.DeserializeException(result, out var e)) { return(Task.FromException <TReturn2>(e)); } else { return(Task.FromResult((TReturn2)result)); } } }
public Task <TReturn2> PerformOrchestration <TReturn2>(IOrchestration <TReturn2> orchestration) { if (!RecordOrReplayCall(MessageType.RequestOperation, out var opid, out var clock, out var instanceId)) { if (!process.Orchestrations.TryGetValue(orchestration.GetType(), out var orchestrationInfo)) { throw new BuilderException($"undefined orchestration type {orchestration.GetType().FullName}."); } if (LockSet != null) { if (!orchestrationInfo.RequiresLocks(orchestration, out var locks)) { throw new SynchronizationDisciplineException("an orchestration called from a locked context must be locked also"); } foreach (var l in locks) { if (!LockSet.Contains(l)) { throw new SynchronizationDisciplineException("to perform an orchestration in a locked context, the caller's affinities must contain the callee's affinities"); } } } orchestrationInfo.CanExecuteLocally(orchestration, out uint destination); var message = orchestrationInfo.CreateRequestMessage(orchestration); message.Opid = opid; message.Clock = clock; message.Parent = this.Opid; message.LockedByCaller = (LockSet != null); process.Send(destination, message); } if (!ReplayReturn(MessageType.RespondToOperation, opid, out var result)) { var continuationInfo = new ContinuationInfo <TReturn2>(orchestration.ToString(), OperationType.Orchestration); Continuations[opid] = continuationInfo; return(continuationInfo.Tcs.Task); } else { if (process.Serializer.DeserializeException(result, out var e)) { return(Task.FromException <TReturn2>(e)); } else { return(Task.FromResult((TReturn2)result)); } } }
Task IOrchestrationContext.PerformEvent <TEvent>(TEvent evt) { if (!process.Events.TryGetValue(evt.GetType(), out var eventInfo)) { throw new BuilderException($"undefined event type {evt.GetType().FullName}."); } var effects = eventInfo.GetEffects(evt); if (effects.Count == 0) { return(Task.CompletedTask); // event has no effects } if (LockSet != null) { foreach (var e in effects) { if (!LockSet.Contains(e.UntypedKey)) { throw new SynchronizationDisciplineException("to perform an event in a locked context, the caller's affinities must contain the event's affinities"); } } } if (!RecordOrReplayCall(MessageType.PerformEvent, out var opid, out var clock, out var instanceId)) { var destination = effects[0].Locate(process); var message = eventInfo.CreateMessage(false, evt, 0); message.Opid = opid; message.Clock = clock; message.Parent = this.Opid; message.LockedByCaller = (LockSet != null); process.Send(destination, message); } if (!ReplayReturn(MessageType.AckEvent, opid, out var ignoredResult)) { var continuationInfo = new ContinuationInfo <UnitType>(evt.ToString(), OperationType.Event); Continuations[opid] = continuationInfo; return(continuationInfo.Tcs.Task); } else { return(Task.CompletedTask); } }
private Task <TReturn2> PerformLocal <TState, TReturn2>(object localOperation) { if (!RecordOrReplayCall(MessageType.RequestLocal, out var opid, out var clock, out var instanceId)) { if (!process.States.TryGetValue(typeof(TState), out var stateInfo)) { throw new BuilderException($"undefined state {typeof(TState).FullName}."); } if (LockSet != null) { var partitionKey = stateInfo.GetPartitionKeyForLocalOperation(localOperation); if (!LockSet.Contains(partitionKey)) { throw new SynchronizationDisciplineException("to perform a local operation in a locked context, the caller must include its affinity"); } } var destination = stateInfo.AffinityInfo.LocateAffinity(localOperation); var message = stateInfo.CreateLocalMessage(localOperation, this.Opid, false); message.Opid = opid; message.Clock = clock; message.Parent = this.Opid; message.LockedByCaller = (LockSet != null); process.Send(destination, message); } if (!ReplayReturn(MessageType.RespondToLocal, opid, out var result)) { var continuationInfo = new ContinuationInfo <TReturn2>(localOperation.ToString(), OperationType.Local); Continuations[opid] = continuationInfo; return(continuationInfo.Tcs.Task); } else { if (process.Serializer.DeserializeException(result, out var e)) { return(Task.FromException <TReturn2>(e)); } else { return(Task.FromResult((TReturn2)result)); } } }
public Task Finish() { if (LockSet != null) { throw new SynchronizationDisciplineException($"{nameof(Finish)} cannot be called in a locked context"); } if (ForkedDestinations == null) { return(Task.CompletedTask); } var opIds = new ulong[ForkedDestinations.Count]; int opIdPos = 0; foreach (var destination in ForkedDestinations) { if (!RecordOrReplayCall(MessageType.PerformFinish, out opIds[opIdPos], out var clock, out _)) { var message = new PerformFinish { Opid = opIds[opIdPos], Clock = clock, Parent = this.Opid }; process.Send(destination, message); } opIdPos++; } var finishAcks = new Task[opIds.Length]; for (int i = 0; i < opIds.Length; i++) { if (!ReplayReturn(MessageType.AckEvent, opIds[i], out var ignoredResult)) { var continuationInfo = new ContinuationInfo <UnitType>("finish", OperationType.Finish); Continuations[opIds[i]] = continuationInfo; finishAcks[i] = continuationInfo.Tcs.Task; } } return(Task.WhenAll(finishAcks)); }
Task <bool> IOrchestrationContext.StateExists <TState, TAffinity, TKey>(TKey key) { if (!RecordOrReplayCall(MessageType.PerformPing, out var opid, out var clock, out _)) { var keyInfo = (AffinityInfo <TAffinity, TKey>)process.Affinities[typeof(TAffinity)]; var pkey = new PartitionKey <TKey>() { Index = keyInfo.Index, Key = key, Comparator = keyInfo.Comparator }; if (LockSet == null || !LockSet.Contains(pkey)) { throw new SynchronizationDisciplineException($"{nameof(IOrchestrationContext.StateExists)} can only be called from a context that has already locked the key"); } if (!process.States.TryGetValue(typeof(TState), out var stateInfo)) { throw new BuilderException($"undefined state {typeof(TState).FullName}."); } var destination = pkey.Locate(process); var message = stateInfo.CreateLocalMessage(pkey, this.Opid, MessageType.PerformPing); message.Opid = opid; message.Clock = clock; message.Parent = this.Opid; message.LockedByCaller = (LockSet != null); process.Send(destination, message); } if (!ReplayReturn(MessageType.RespondToLocal, opid, out var result)) { var continuationInfo = new ContinuationInfo <bool>("StateExists", OperationType.Local); Continuations[opid] = continuationInfo; return(continuationInfo.Tcs.Task); } else { return(Task.FromResult((bool)result)); } }
public Task <T> Activity <T>(Func <Guid, Task <T> > body, string name) { // on send, always issue the activity (but pass recorded instance id, if replaying) RecordOrReplayCall(MessageType.RequestExternal, out var opid, out var clock, out var instanceId); process.AddActivity(opid, name, new Task(async() => { var instanceIdOnStart = process.InstanceId; process.ActivityTracer?.Invoke($" Starting activity o{opid:D10} {name} {instanceIdOnStart}"); var stopwatch = process.Telemetry != null ? new Stopwatch() : null; stopwatch?.Start(); object r; try { r = await body(instanceId); } catch (Exception e) { r = process.Serializer.SerializeException(e); } stopwatch?.Stop(); process.ActivityTracer?.Invoke($" Completed activity o{opid:D10} {name} {instanceIdOnStart}"); process.Telemetry?.OnApplicationEvent( processId: process.ProcessId, id: opid.ToString(), name: name, parent: Opid.ToString(), opSide: OperationSide.Caller, opType: OperationType.Activity, duration: stopwatch.Elapsed.TotalMilliseconds ); process.HostServices.Send(process.ProcessId, new RespondToActivity() { Opid = opid, Parent = this.Opid, Clock = clock, InstanceId = instanceIdOnStart, Result = r }); })); // replay the return or create a continuation if (!ReplayReturn(MessageType.RespondToActivity, opid, out var result)) { var continuationInfo = new ContinuationInfo <T>(name, OperationType.Activity); Continuations[opid] = continuationInfo; return(continuationInfo.Tcs.Task); } else { process.RemoveActivity(opid); if (process.Serializer.DeserializeException(result, out var e)) { return(Task.FromException <T>(e)); } else { return(Task.FromResult((T)result)); } } }