Skip to content

Commit 62d575f

Browse files
committed
Fix race condition.
1 parent 7e7e26b commit 62d575f

File tree

1 file changed

+11
-9
lines changed

1 file changed

+11
-9
lines changed

src/BenchmarkDotNet/Engines/BenchmarkSynchronizationContext.cs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public T ExecuteUntilComplete<T>(ValueTask<T> valueTask)
3838
internal sealed class BenchmarkDotNetSynchronizationContext : SynchronizationContext
3939
{
4040
private readonly SynchronizationContext? previousContext;
41+
private readonly object syncRoot = new();
4142
// Use 2 arrays to reduce lock contention while executing. The common case is only 1 callback will be queued at a time.
4243
private (SendOrPostCallback d, object? state)[] queue = new (SendOrPostCallback d, object? state)[1];
4344
private (SendOrPostCallback d, object? state)[] executing = new (SendOrPostCallback d, object? state)[1];
@@ -56,7 +57,7 @@ public override void Post(SendOrPostCallback d, object? state)
5657
{
5758
if (d is null) throw new ArgumentNullException(nameof(d));
5859

59-
lock (queue)
60+
lock (syncRoot)
6061
{
6162
ThrowIfDisposed();
6263

@@ -67,7 +68,7 @@ public override void Post(SendOrPostCallback d, object? state)
6768
}
6869
queue[index] = (d, state);
6970

70-
Monitor.Pulse(queue);
71+
Monitor.Pulse(syncRoot);
7172
}
7273
}
7374

@@ -77,7 +78,7 @@ internal void Dispose()
7778
{
7879
int count;
7980
(SendOrPostCallback d, object? state)[] executing;
80-
lock (queue)
81+
lock (syncRoot)
8182
{
8283
ThrowIfDisposed();
8384

@@ -132,9 +133,9 @@ internal T ExecuteUntilComplete<T>(ValueTask<T> valueTask)
132133
private void OnCompleted()
133134
{
134135
isCompleted = true;
135-
lock (queue)
136+
lock (syncRoot)
136137
{
137-
Monitor.Pulse(queue);
138+
Monitor.Pulse(syncRoot);
138139
}
139140
}
140141

@@ -145,7 +146,7 @@ private void ExecuteUntilComplete()
145146
{
146147
int count;
147148
(SendOrPostCallback d, object? state)[] executing;
148-
lock (queue)
149+
lock (syncRoot)
149150
{
150151
count = queueCount;
151152
queueCount = 0;
@@ -155,8 +156,9 @@ private void ExecuteUntilComplete()
155156
this.executing = executing;
156157
for (int i = 0; i < count; ++i)
157158
{
158-
executing[i].d(executing[i].state);
159+
var (d, state) = executing[i];
159160
executing[i] = default;
161+
d(state);
160162
}
161163
if (count > 0)
162164
{
@@ -177,9 +179,9 @@ private void ExecuteUntilComplete()
177179
}
178180

179181
// Yield the thread and wait for completion or for a posted callback.
180-
lock (queue)
182+
lock (syncRoot)
181183
{
182-
Monitor.Wait(queue);
184+
Monitor.Wait(syncRoot);
183185
}
184186
// Reset the spinner.
185187
spinner = new();

0 commit comments

Comments
 (0)