Skip to content

Commit 113594e

Browse files
David EllingsworthDavid Ellingsworth
authored andcommitted
Remove the CustomSynchronizationContext and instead use Task.Run to run async methods safely from the Thread Pool.
1 parent ac183d5 commit 113594e

1 file changed

Lines changed: 3 additions & 106 deletions

File tree

src/RestSharp/AsyncHelpers.cs

Lines changed: 3 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -14,121 +14,18 @@
1414
//
1515
// Adapted from Rebus
1616

17-
using System.Collections.Concurrent;
18-
using System.Runtime.ExceptionServices;
19-
2017
namespace RestSharp;
2118

2219
static class AsyncHelpers {
23-
/// <summary>
24-
/// Executes a task synchronously on the calling thread by installing a temporary synchronization context that queues continuations
25-
/// </summary>
26-
/// <param name="task">Callback for asynchronous task to run</param>
27-
static void RunSync(Func<Task> task) {
28-
CustomSynchronizationContext.Run(task);
29-
}
30-
3120
/// <summary>
3221
/// Executes a task synchronously on the calling thread by installing a temporary synchronization context that queues continuations
3322
/// </summary>
3423
/// <param name="task">Callback for asynchronous task to run</param>
3524
/// <typeparam name="T">Return type for the task</typeparam>
3625
/// <returns>Return value from the task</returns>
3726
public static T RunSync<T>(Func<Task<T>> task) {
38-
T result = default!;
39-
RunSync(async () => { result = await task(); });
40-
return result;
41-
}
42-
43-
/// <summary>
44-
/// Synchronization context that can be "pumped" in order to have it execute continuations posted back to it
45-
/// </summary>
46-
class CustomSynchronizationContext : SynchronizationContext {
47-
readonly ConcurrentQueue<Tuple<SendOrPostCallback, object?>> _items = new();
48-
readonly AutoResetEvent _workItemsWaiting = new(false);
49-
readonly Func<Task> _task;
50-
ExceptionDispatchInfo? _caughtException;
51-
bool _done;
52-
53-
/// <summary>
54-
/// Constructor for the custom context
55-
/// </summary>
56-
/// <param name="task">Task to execute</param>
57-
private CustomSynchronizationContext(Func<Task> task) =>
58-
_task = task ?? throw new ArgumentNullException(nameof(task), "Please remember to pass a Task to be executed");
59-
60-
/// <summary>
61-
/// When overridden in a derived class, dispatches an asynchronous message to a synchronization context.
62-
/// </summary>
63-
/// <param name="function">Callback function</param>
64-
/// <param name="state">Callback state</param>
65-
public override void Post(SendOrPostCallback function, object? state) {
66-
_items.Enqueue(Tuple.Create(function, state));
67-
_workItemsWaiting.Set();
68-
}
69-
70-
/// <summary>
71-
/// Enqueues the function to be executed and executes all resulting continuations until it is completely done
72-
/// </summary>
73-
private void Run() {
74-
var currentContext = SynchronizationContext.Current;
75-
76-
try {
77-
SynchronizationContext.SetSynchronizationContext(this);
78-
79-
Post(PostCallback, null);
80-
81-
while (!_done) {
82-
if (_items.TryDequeue(out var task)) {
83-
task.Item1(task.Item2);
84-
if (_caughtException == null) {
85-
continue;
86-
}
87-
_caughtException.Throw();
88-
}
89-
else {
90-
_workItemsWaiting.WaitOne();
91-
}
92-
}
93-
}
94-
finally {
95-
SynchronizationContext.SetSynchronizationContext(currentContext);
96-
}
97-
98-
99-
return;
100-
101-
// This method is only called from within this custom context for the initial task.
102-
async void PostCallback(object? _) {
103-
try {
104-
// Do not call ConfigureAwait(false) here to ensure all continuations are
105-
// queued on this context, not the thread pool.
106-
await _task().ContinueWith(_ => _done = true);
107-
}
108-
catch (Exception exception) {
109-
_caughtException = ExceptionDispatchInfo.Capture(exception);
110-
throw;
111-
}
112-
}
113-
}
114-
115-
public static void Run(Func<Task> task) {
116-
var customContext = new CustomSynchronizationContext(task);
117-
118-
customContext.Run();
119-
}
120-
121-
/// <summary>
122-
/// When overridden in a derived class, dispatches a synchronous message to a synchronization context.
123-
/// </summary>
124-
/// <param name="function">Callback function</param>
125-
/// <param name="state">Callback state</param>
126-
public override void Send(SendOrPostCallback function, object? state) => throw new NotSupportedException("Cannot send to same thread");
127-
128-
/// <summary>
129-
/// When overridden in a derived class, creates a copy of the synchronization context. Not needed, so just return ourselves.
130-
/// </summary>
131-
/// <returns>Copy of the context</returns>
132-
public override SynchronizationContext CreateCopy() => this;
27+
var t = Task.Run(async () => await task().ConfigureAwait(false));
28+
29+
return t.GetAwaiter().GetResult();
13330
}
13431
}

0 commit comments

Comments
 (0)