diff --git a/src/Stateless/StateMachine.Async.cs b/src/Stateless/StateMachine.Async.cs
index b319813a..169138de 100644
--- a/src/Stateless/StateMachine.Async.cs
+++ b/src/Stateless/StateMachine.Async.cs
@@ -135,28 +135,37 @@ async Task InternalFireAsync(TTrigger trigger, params object[] args)
/// A variable-length parameters list containing arguments.
async Task InternalFireQueuedAsync(TTrigger trigger, params object[] args)
{
- if (_firing)
+ if (!_firing)
{
- _eventQueue.Enqueue(new QueuedTrigger { Trigger = trigger, Args = args });
- return;
- }
-
- try
- {
- _firing = true;
-
- await InternalFireOneAsync(trigger, args).ConfigureAwait(RetainSynchronizationContext);
+ await _lock.WaitAsync();
- while (_eventQueue.Count != 0)
+ if(!_firing)
{
- var queuedEvent = _eventQueue.Dequeue();
- await InternalFireOneAsync(queuedEvent.Trigger, queuedEvent.Args).ConfigureAwait(RetainSynchronizationContext);
+ try
+ {
+ _firing = true;
+ _lock.Release();
+ await InternalFireOneAsync(trigger, args).ConfigureAwait(RetainSynchronizationContext);
+
+ while (!_eventQueue.IsEmpty)
+ {
+ bool suuccess = _eventQueue.TryDequeue(out QueuedTrigger queuedEvent);
+ if (suuccess)
+ {
+ await InternalFireOneAsync(queuedEvent.Trigger, queuedEvent.Args).ConfigureAwait(RetainSynchronizationContext);
+ }
+ }
+ return;
+ }
+ finally
+ {
+ _firing = false;
+ }
}
+ _lock.Release();
}
- finally
- {
- _firing = false;
- }
+
+ _eventQueue.Enqueue(new QueuedTrigger { Trigger = trigger, Args = args });
}
async Task InternalFireOneAsync(TTrigger trigger, params object[] args)
diff --git a/src/Stateless/StateMachine.cs b/src/Stateless/StateMachine.cs
index 270cc620..abe5f74d 100644
--- a/src/Stateless/StateMachine.cs
+++ b/src/Stateless/StateMachine.cs
@@ -1,7 +1,9 @@
using Stateless.Reflection;
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
+using System.Threading;
namespace Stateless
{
@@ -32,6 +34,7 @@ public partial class StateMachine
private readonly OnTransitionedEvent _onTransitionCompletedEvent;
private readonly TState _initialState;
private readonly FiringMode _firingMode;
+ private readonly SemaphoreSlim _lock;
private class QueuedTrigger
{
@@ -39,7 +42,7 @@ private class QueuedTrigger
public object[] Args { get; set; }
}
- private readonly Queue _eventQueue = new Queue();
+ private readonly ConcurrentQueue _eventQueue = new ConcurrentQueue();
private bool _firing;
///
@@ -102,6 +105,7 @@ public StateMachine(TState initialState, FiringMode firingMode) : this()
_unhandledTriggerAction = new UnhandledTriggerAction.Sync(DefaultUnhandledTriggerAction);
_onTransitionedEvent = new OnTransitionedEvent();
_onTransitionCompletedEvent = new OnTransitionedEvent();
+ _lock = new SemaphoreSlim(1, 1);
}
///
@@ -374,8 +378,12 @@ private void InternalFireQueued(TTrigger trigger, params object[] args)
// Empty queue for triggers
while (_eventQueue.Any())
{
- var queuedEvent = _eventQueue.Dequeue();
- InternalFireOne(queuedEvent.Trigger, queuedEvent.Args);
+ bool suuccess = _eventQueue.TryDequeue(out QueuedTrigger queuedEvent);
+ if(suuccess)
+ {
+ InternalFireOne(queuedEvent.Trigger, queuedEvent.Args);
+ }
+
}
}
finally
diff --git a/test/Stateless.Tests/InitialTransitionFixture.cs b/test/Stateless.Tests/InitialTransitionFixture.cs
index 22cfa4e8..abf671a2 100644
--- a/test/Stateless.Tests/InitialTransitionFixture.cs
+++ b/test/Stateless.Tests/InitialTransitionFixture.cs
@@ -335,5 +335,55 @@ public async void AsyncTransitionEvents_OrderingWithInitialTransition()
Assert.Equal(expectedOrdering[i], actualOrdering[i]);
}
}
+
+ [Fact]
+ public async Task CheckMultipleFireAsync()
+ {
+ for (int i = 0; i < 200; i++)
+ {
+ await CheckAsyncMethod();
+ }
+ }
+
+ private async Task CheckAsyncMethod()
+ {
+ // Create a state machine
+ var sm = new StateMachine(State.A);
+
+ sm.Configure(State.A).Permit(Trigger.X, State.B);
+ sm.Configure(State.A).Permit(Trigger.Y, State.C);
+ sm.Configure(State.B).Permit(Trigger.Y, State.D);
+ sm.Configure(State.B).Permit(Trigger.X, State.A);
+ sm.Configure(State.C).Permit(Trigger.Y, State.D);
+ sm.Configure(State.C).Permit(Trigger.X, State.A);
+ sm.Configure(State.D).Permit(Trigger.Y, State.B);
+ sm.Configure(State.D).Permit(Trigger.X, State.C);
+
+
+ // Create some tasks
+ List tasks = new List();
+
+ for (int i = 0; i < 20; i++)
+ {
+ Task taskTriggerX = Task.Run(() => FireTrigger(sm, Trigger.X, false));
+ Task taskTriggerY = Task.Run(() => FireTrigger(sm, Trigger.Y, false));
+ Task taskTriggerDelay = Task.Run(() => FireTrigger(sm, Trigger.X, true));
+ tasks.Add(taskTriggerX);
+ tasks.Add(taskTriggerY);
+ tasks.Add(taskTriggerDelay);
+ }
+
+ // Wait for tasks to complete
+ await Task.WhenAll(tasks);
+ }
+
+ private async Task FireTrigger(StateMachine sm, Trigger trigger, bool addDelay)
+ {
+ if(addDelay)
+ {
+ await Task.Delay(20);
+ }
+ await sm.FireAsync(trigger);
+ }
}
}