forked from microsoft/vs-threading
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAsyncBarrier.cs
More file actions
79 lines (72 loc) · 3.07 KB
/
Copy pathAsyncBarrier.cs
File metadata and controls
79 lines (72 loc) · 3.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading
{
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
/// <summary>
/// An asynchronous barrier that blocks the signaler until all other participants have signaled.
/// </summary>
public class AsyncBarrier
{
/// <summary>
/// The number of participants being synchronized.
/// </summary>
private readonly int participantCount;
/// <summary>
/// The set of participants who have reached the barrier, with their awaiters that can resume those participants.
/// </summary>
private readonly Stack<TaskCompletionSource<EmptyStruct>> waiters;
/// <summary>
/// Initializes a new instance of the <see cref="AsyncBarrier"/> class.
/// </summary>
/// <param name="participants">The number of participants.</param>
public AsyncBarrier(int participants)
{
Requires.Range(participants > 0, nameof(participants));
this.participantCount = participants;
// Allocate the stack so no resizing is necessary.
// We don't need space for the last participant, since we never have to store it.
this.waiters = new Stack<TaskCompletionSource<EmptyStruct>>(participants - 1);
}
/// <summary>
/// Signals that a participant is ready, and returns a Task
/// that completes when all other participants have also signaled ready.
/// </summary>
/// <returns>A Task, which will complete (or may already be completed) when the last participant calls this method.</returns>
public Task SignalAndWait()
{
lock (this.waiters)
{
if (this.waiters.Count + 1 == this.participantCount)
{
// This is the last one we were waiting for.
// Unleash everyone that preceded this one.
while (this.waiters.Count > 0)
{
Task.Factory.StartNew(
state => ((TaskCompletionSource<EmptyStruct>)state!).SetResult(default(EmptyStruct)),
this.waiters.Pop(),
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.Default);
}
// And allow this one to continue immediately.
return Task.CompletedTask;
}
else
{
// We need more folks. So suspend this caller.
var tcs = new TaskCompletionSource<EmptyStruct>();
this.waiters.Push(tcs);
return tcs.Task;
}
}
}
}
}