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));
                }
            }
        }
예제 #3
0
 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));
         }
     }
 }
예제 #4
0
        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);
            }
        }
예제 #5
0
 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));
     }
 }
예제 #8
0
        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));
                }
            }
        }