/// <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; }
/// <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); }
/// <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); } }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <inheritdoc/> public virtual Task <ActorId> CreateActorAndExecuteAsync(ActorId id, Type type, Event initialEvent = null, EventGroup group = null) => this.CreateActorAndExecuteAsync(id, type, null, initialEvent, null, group);
/// <inheritdoc/> public virtual ActorId CreateActor(ActorId id, Type type, Event initialEvent = null, EventGroup group = null) => this.CreateActor(id, type, null, initialEvent, null, group);
/// <inheritdoc/> public virtual Task <ActorId> CreateActorAndExecuteAsync(Type type, string name, Event initialEvent = null, EventGroup group = null) => this.CreateActorAndExecuteAsync(null, type, name, initialEvent, null, group);
/// <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);
/// <inheritdoc/> public virtual ActorId CreateActor(Type type, string name, Event initialEvent = null, EventGroup group = null) => this.CreateActor(null, type, name, initialEvent, null, group);
public void OnRaiseEvent(Event e, EventGroup group, EventInfo eventInfo) => this.Runtime.LogWriter.LogRaiseEvent(this.Instance.Id, default, e);
/// <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);
/// <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);
/// <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);
/// <inheritdoc/> public virtual Task <bool> SendEventAndExecuteAsync(ActorId targetId, Event initialEvent, EventGroup group = null, SendOptions options = null) => this.SendEventAndExecuteAsync(targetId, initialEvent, null, group, options);
/// <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);