1414//
1515// Adapted from Rebus
1616
17- using System . Collections . Concurrent ;
18- using System . Runtime . ExceptionServices ;
19-
2017namespace RestSharp ;
2118
2219static 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