/// <summary>
 /// Initializes a new instance of the <see cref="StateMachineManager"/> class.
 /// </summary>
 internal StateMachineManager(ActorRuntime runtime, StateMachine instance, EventGroup group)
 {
     this.Runtime  = runtime;
     this.Instance = instance;
     this.IsEventHandlerRunning = true;
     this.CurrentEventGroup     = group;
 }
Beispiel #2
0
        /// <summary>
        /// Creates a new <see cref="Actor"/> of the specified <see cref="Type"/>.
        /// </summary>
        private Actor CreateActor(ActorId id, Type type, string name, Actor creator, EventGroup group)
        {
            if (!type.IsSubclassOf(typeof(Actor)))
            {
                this.Assert(false, "Type '{0}' is not an actor.", type.FullName);
            }

            if (id is null)
            {
                id = new ActorId(type, name, this);
            }
            else if (id.Runtime != null && id.Runtime != this)
            {
                this.Assert(false, "Unbound actor id '{0}' was created by another runtime.", id.Value);
            }
            else if (id.Type != type.FullName)
            {
                this.Assert(false, "Cannot bind actor id '{0}' of type '{1}' to an actor of type '{2}'.",
                            id.Value, id.Type, type.FullName);
            }
            else
            {
                id.Bind(this);
            }

            // If no group is provided then inherit the current group from the creator.
            if (group == null && creator != null)
            {
                group = creator.Manager.CurrentEventGroup;
            }

            Actor         actor = ActorFactory.Create(type);
            IActorManager actorManager;

            if (actor is StateMachine stateMachine)
            {
                actorManager = new StateMachineManager(this, stateMachine, group);
            }
            else
            {
                actorManager = new ActorManager(this, actor, group);
            }

            IEventQueue eventQueue = new EventQueue(actorManager);

            actor.Configure(this, id, actorManager, eventQueue);
            actor.SetupEventHandlers();

            if (!this.ActorMap.TryAdd(id, actor))
            {
                string info = "This typically occurs if either the actor id was created by another runtime instance, " +
                              "or if a actor id from a previous runtime generation was deserialized, but the current runtime " +
                              "has not increased its generation value.";
                this.Assert(false, "An actor with id '{0}' was already created in generation '{1}'. {2}", id.Value, id.Generation, info);
            }

            return(actor);
        }
Beispiel #3
0
        /// <summary>
        /// Sends an asynchronous <see cref="Event"/> to an actor.
        /// </summary>
        internal virtual void SendEvent(ActorId targetId, Event e, Actor sender, EventGroup group, SendOptions options)
        {
            EnqueueStatus enqueueStatus = this.EnqueueEvent(targetId, e, sender, group, out Actor target);

            if (enqueueStatus is EnqueueStatus.EventHandlerNotRunning)
            {
                this.RunActorEventHandler(target, null, false);
            }
        }
Beispiel #4
0
        /// <summary>
        /// Enqueues the specified event and its metadata.
        /// </summary>
        internal EnqueueStatus Enqueue(Event e, EventGroup group, EventInfo info)
        {
            if (this.CurrentStatus is Status.Halted)
            {
                return(EnqueueStatus.Dropped);
            }

            return(this.Inbox.Enqueue(e, group, info));
        }
        public void OnReceiveEventWithoutWaiting(Event e, EventGroup group, EventInfo eventInfo)
        {
            if (group != null)
            {
                // Inherit the event group of the receive operation, if it is non-null.
                this.CurrentEventGroup = group;
            }

            this.Runtime.NotifyReceivedEventWithoutWaiting(this.Instance, e, eventInfo);
        }
Beispiel #6
0
        /// <summary>
        /// Sends an asynchronous <see cref="Event"/> to an actor. Returns immediately if the target was
        /// already running. Otherwise blocks until the target handles the event and reaches quiescense.
        /// </summary>
        internal virtual async Task <bool> SendEventAndExecuteAsync(ActorId targetId, Event e, Actor sender,
                                                                    EventGroup group, SendOptions options)
        {
            EnqueueStatus enqueueStatus = this.EnqueueEvent(targetId, e, sender, group, out Actor target);

            if (enqueueStatus is EnqueueStatus.EventHandlerNotRunning)
            {
                await this.RunActorEventHandlerAsync(target, null, false);

                return(true);
            }

            return(enqueueStatus is EnqueueStatus.Dropped);
        }
Beispiel #7
0
        /// <summary>
        /// Enqueues an event to the actor with the specified id.
        /// </summary>
        private EnqueueStatus EnqueueEvent(ActorId targetId, Event e, Actor sender, EventGroup group, out Actor target)
        {
            if (e is null)
            {
                string message = sender != null?
                                 string.Format("{0} is sending a null event.", sender.Id.ToString()) :
                                     "Cannot send a null event.";

                this.Assert(false, message);
            }

            if (targetId is null)
            {
                string message = (sender != null) ?
                                 string.Format("{0} is sending event {1} to a null actor.", sender.Id.ToString(), e.ToString())
                    : string.Format("Cannot send event {0} to a null actor.", e.ToString());

                this.Assert(false, message);
            }

            target = this.GetActorWithId <Actor>(targetId);

            // If no group is provided we default to passing along the group from the sender.
            if (group == null && sender != null)
            {
                group = sender.Manager.CurrentEventGroup;
            }

            Guid opId = group == null ? Guid.Empty : group.Id;

            if (target is null || target.IsHalted)
            {
                this.LogWriter.LogSendEvent(targetId, sender?.Id.Name, sender?.Id.Type,
                                            (sender as StateMachine)?.CurrentStateName ?? string.Empty, e, opId, isTargetHalted: true);
                this.TryHandleDroppedEvent(e, targetId);
                return(EnqueueStatus.Dropped);
            }

            this.LogWriter.LogSendEvent(targetId, sender?.Id.Name, sender?.Id.Type,
                                        (sender as StateMachine)?.CurrentStateName ?? string.Empty, e, opId, isTargetHalted: false);

            EnqueueStatus enqueueStatus = target.Enqueue(e, group, null);

            if (enqueueStatus == EnqueueStatus.Dropped)
            {
                this.TryHandleDroppedEvent(e, targetId);
            }

            return(enqueueStatus);
        }
Beispiel #8
0
        /// <summary>
        /// Creates a new <see cref="Actor"/> of the specified <see cref="Type"/>.
        /// </summary>
        internal virtual ActorId CreateActor(ActorId id, Type type, string name, Event initialEvent,
                                             Actor creator, EventGroup group)
        {
            Actor actor = this.CreateActor(id, type, name, creator, group);

            if (actor is StateMachine)
            {
                this.LogWriter.LogCreateStateMachine(actor.Id, creator?.Id.Name, creator?.Id.Type);
            }
            else
            {
                this.LogWriter.LogCreateActor(actor.Id, creator?.Id.Name, creator?.Id.Type);
            }

            this.RunActorEventHandler(actor, initialEvent, true);
            return(actor.Id);
        }
Beispiel #9
0
 /// <inheritdoc/>
 public virtual Task <ActorId> CreateActorAndExecuteAsync(ActorId id, Type type, Event initialEvent = null,
                                                          EventGroup group = null) =>
 this.CreateActorAndExecuteAsync(id, type, null, initialEvent, null, group);
Beispiel #10
0
 /// <inheritdoc/>
 public virtual ActorId CreateActor(ActorId id, Type type, Event initialEvent = null, EventGroup group = null) =>
 this.CreateActor(id, type, null, initialEvent, null, group);
Beispiel #11
0
 /// <inheritdoc/>
 public virtual Task <ActorId> CreateActorAndExecuteAsync(Type type, string name, Event initialEvent = null,
                                                          EventGroup group = null) =>
 this.CreateActorAndExecuteAsync(null, type, name, initialEvent, null, group);
Beispiel #12
0
 /// <summary>
 /// Sends an asynchronous <see cref="Event"/> to a target.
 /// </summary>
 /// <param name="id">The id of the target.</param>
 /// <param name="e">The event to send.</param>
 /// <param name="group">An optional event group associated with this Actor.</param>
 /// <param name="options">Optional configuration of a send operation.</param>
 protected void SendEvent(ActorId id, Event e, EventGroup group = null, SendOptions options = null) =>
 this.Runtime.SendEvent(id, e, this, group, options);
Beispiel #13
0
 /// <inheritdoc/>
 public virtual ActorId CreateActor(Type type, string name, Event initialEvent = null, EventGroup group = null) =>
 this.CreateActor(null, type, name, initialEvent, null, group);
Beispiel #14
0
 public void OnRaiseEvent(Event e, EventGroup group, EventInfo eventInfo) =>
 this.Runtime.LogWriter.LogRaiseEvent(this.Instance.Id, default, e);
Beispiel #15
0
        /// <summary>
        /// Runs the event handler. The handler terminates if there is no next
        /// event to process or if the actor has halted.
        /// </summary>
        internal async Task RunEventHandlerAsync()
        {
            Event lastDequeuedEvent = null;

            while (this.CurrentStatus != Status.Halted && this.Runtime.IsRunning)
            {
                (DequeueStatus status, Event e, EventGroup group, EventInfo info) = this.Inbox.Dequeue();

                if (group != null)
                {
                    this.Manager.CurrentEventGroup = group;
                }

                if (status is DequeueStatus.Success)
                {
                    // Notify the runtime for a new event to handle. This is only used
                    // during bug-finding and operation bounding, because the runtime
                    // has to schedule an actor when a new operation is dequeued.
                    this.Runtime.NotifyDequeuedEvent(this, e, info);
                    await this.InvokeUserCallbackAsync(UserCallbackType.OnEventDequeued, e);

                    lastDequeuedEvent = e;
                }
                else if (status is DequeueStatus.Raised)
                {
                    // Only supported by types (e.g. StateMachine) that allow
                    // the user to explicitly raise events.
                    this.Runtime.NotifyHandleRaisedEvent(this, e);
                }
                else if (status is DequeueStatus.Default)
                {
                    this.Runtime.LogWriter.LogDefaultEventHandler(this.Id, this.CurrentStateName);

                    // If the default event was dequeued, then notify the runtime.
                    // This is only used during bug-finding, because the runtime must
                    // instrument a scheduling point between default event handlers.
                    this.Runtime.NotifyDefaultEventDequeued(this);
                }
                else if (status is DequeueStatus.NotAvailable)
                {
                    // Terminate the handler as there is no event available.
                    break;
                }

                if (e is TimerElapsedEvent timeoutEvent &&
                    timeoutEvent.Info.Period.TotalMilliseconds < 0)
                {
                    // If the timer is not periodic, then dispose it.
                    this.UnregisterTimer(timeoutEvent.Info);
                }

                if (this.CurrentStatus is Status.Active)
                {
                    // Handles the next event, if the actor is not halted.
                    await this.HandleEventAsync(e);
                }

                if (!this.Inbox.IsEventRaised && lastDequeuedEvent != null && this.CurrentStatus != Status.Halted)
                {
                    // Inform the user that the actor handled the dequeued event.
                    await this.InvokeUserCallbackAsync(UserCallbackType.OnEventHandled, lastDequeuedEvent);

                    lastDequeuedEvent = null;
                }

                if (this.CurrentStatus is Status.Halting)
                {
                    // If the current status is halting, then halt the actor.
                    await this.HaltAsync(e);
                }
            }
        }
 public void OnDropEvent(Event e, EventGroup group, EventInfo eventInfo) =>
 this.Runtime.TryHandleDroppedEvent(e, this.Instance.Id);
Beispiel #17
0
 /// <summary>
 /// Creates a new actor of the specified <see cref="Type"/> and name, using the specified
 /// unbound actor id, and passes the specified optional <see cref="Event"/>. This event
 /// can only be used to access its payload, and cannot be handled.
 /// </summary>
 /// <param name="id">Unbound actor id.</param>
 /// <param name="type">Type of the actor.</param>
 /// <param name="name">Optional name used for logging.</param>
 /// <param name="initialEvent">Optional initialization event.</param>
 /// <param name="group">An optional event group associated with the new Actor.</param>
 protected void CreateActor(ActorId id, Type type, string name, Event initialEvent = null, EventGroup group = null) =>
 this.Runtime.CreateActor(id, type, name, initialEvent, this, group);
Beispiel #18
0
 /// <inheritdoc/>
 public virtual void SendEvent(ActorId targetId, Event initialEvent, EventGroup group = default,
                               SendOptions options = null) =>
 this.SendEvent(targetId, initialEvent, null, group, options);
 public void OnRaiseEvent(Event e, EventGroup group, EventInfo eventInfo) =>
 this.Runtime.NotifyRaisedEvent(this.Instance, e, eventInfo);
 public void OnEnqueueEvent(Event e, EventGroup group, EventInfo eventInfo) =>
 this.Runtime.LogWriter.LogEnqueueEvent(this.Instance.Id, e);
Beispiel #21
0
 /// <inheritdoc/>
 public virtual Task <bool> SendEventAndExecuteAsync(ActorId targetId, Event initialEvent,
                                                     EventGroup group = null, SendOptions options = null) =>
 this.SendEventAndExecuteAsync(targetId, initialEvent, null, group, options);
Beispiel #22
0
 /// <summary>
 /// Creates a new actor of the specified type and with the specified optional
 /// <see cref="Event"/>. This <see cref="Event"/> can only be used to access
 /// its payload, and cannot be handled.
 /// </summary>
 /// <param name="type">Type of the actor.</param>
 /// <param name="initialEvent">Optional initialization event.</param>
 /// <param name="group">An optional event group associated with the new Actor.</param>
 /// <returns>The unique actor id.</returns>
 protected ActorId CreateActor(Type type, Event initialEvent = null, EventGroup group = null) =>
 this.Runtime.CreateActor(null, type, null, initialEvent, this, group);