C# Client Library
A C# Client Library for the AnalyzeRe REST API
Loading...
Searching...
No Matches
AsyncRequestMonitor.cs
Go to the documentation of this file.
1using System;
2using System.Threading;
3
5using RestSharp;
6
8{
9 // TODO: Find a way to toggle between synchronous and asynchronous polling.
10 // TODO: Support a non-generically-typed class instantiation / invocation (no return value)
11 // TODO: Generic request monitor to be revisited - consider a method of restoring adding OnPoll support.
13 public sealed class AsyncRequestMonitor<T> : IDisposable
14 {
15 #region Public Properties
18 public PollingOptions PollingOptions { get; set; }
19
21 public bool IsPollingActive { get; private set; }
22
24 public T Result { get; private set; }
25
27 public Exception ResultException { get; private set; }
28
32 public TimeSpan Elapsed { get; private set; }
33 #endregion Public Properties
34
35 #region Private Properties
36 // Used to cancel polling when the user calls Stop or the original
37 // PollingOptions' cancellation token is cancelled.
38 private CancellationTokenSource _cancellation;
39
40 // The function to poll (if applicable)
41 private readonly Func<T> _functionToPoll;
42
43 // The resource to monitor (if applicable)
44 private readonly IStoredAPIResource_WithStatus _resourceToMonitor;
45
46 // The thread on which active polling is taking place.
47 private Thread _pollingThread;
48 #endregion Private Properties
49
50 #region Events
51 #region OnCompleted
53 public class OnCompletedEventArgs : EventArgs
54 {
56 public T ReturnValue { get; set; }
63 }
70 #endregion OnCompleted
71
72 #region OnPoll
74 [Obsolete("Polling status updates are currently unavailable.")]
75 public class OnPollEventArgs : EventArgs
76 {
78 public double RetryAfter { get; set; }
80 public IRestResponse Response { get; set; }
89 }
93 [Obsolete("Polling status updates are currently unavailable.")]
95
98 [Obsolete("Polling status updates are currently unavailable.")]
100
101 [Obsolete("Polling status updates are currently unavailable.")]
102 private void InvokeOnPoll(OnPollEventArgs e) => OnPoll?.Invoke(this, e);
103 #endregion OnPoll
104
105 #region OnError
107 public class OnErrorEventArgs : EventArgs
108 {
110 public Exception Exception { get; set; }
112 public ServerError ServerError { get; set; }
114 public IRestResponse Response { get; set; }
125 }
132 #endregion OnError
133 #endregion Events
134
135 #region Constructors
143 {
144 _functionToPoll = functionThatMight503;
145 _resourceToMonitor = null;
147 }
148
156 {
158 throw new ArgumentException("The resource to monitor must implement " +
159 "the IStoredAPIResource_WithStatus interface.");
160 _functionToPoll = null;
161 _resourceToMonitor = asWithStatus;
163 }
164 #endregion Constructors
165
166 #region Destructor
167
169 public void Dispose()
170 {
171 Dispose(true);
172 GC.SuppressFinalize(this);
173 }
174
177 {
178 Dispose(false);
179 }
180
184 private void Dispose(bool disposing)
185 {
186 if (!disposing) return;
187 Stop();
188 }
189 #endregion Destructor
190
191 #region Public Methods
193 public void Start()
194 {
195 if (IsPollingActive || _pollingThread != null)
196 throw new Exception("A request is already being monitored.");
197
198 // Set up the polling options to use on the asynchronous method
199 _cancellation?.Dispose();
200 _cancellation = new CancellationTokenSource();
202 if (PollingOptions == null)
203 newPollingOptions = new PollingOptions(cancellationToken: _cancellation.Token);
204 else
205 {
206 // Kind of a pain, but we need to make a copy of the user's polling options to add
207 // our own cancellation token so that we can cancel the polling if the user calls Stop.
208 PollingOptions.CancellationToken?.Register(_cancellation.Cancel);
211 PollingOptions.BackoffRate, _cancellation.Token);
212 }
213
214 // Set up the polling delegate based on the type of resource we are polling:
216 // 1. A request that may raise a 503-RetryAfter
217 if (_functionToPoll != null)
218 _delegate = () => API.PollUntilReady(_functionToPoll, newPollingOptions);
219 // or 2. An IStoredAPIResource_WithStatus
220 else if (_resourceToMonitor != null)
221 _delegate = () => (T)_resourceToMonitor.PollUntilReady(newPollingOptions);
222 else
223 throw new ArgumentException("Start invoked without proper construction.");
224
225 // Begin polling
226 IsPollingActive = true;
227 _pollingThread = new Thread(() => PollAsync(_delegate));
228 Result = default(T);
229 ResultException = null;
230 _pollingThread.Start();
231 }
232
234 public void Stop()
235 {
236 _cancellation?.Cancel();
237 _pollingThread?.Join();
238 _pollingThread = null;
239 _cancellation?.Dispose();
240 _cancellation = null;
241 }
242
247 public T Wait()
248 {
249 _pollingThread?.Join();
250 if (ResultException != null)
251 throw new AggregateException("The asynchronous request failed. " +
252 "See inner exception.", ResultException);
253 return Result;
254 }
255 #endregion Public Methods
256
257 #region Async Polling
258 // Function that the polling thread will run.
259 private void PollAsync(Func<T> PollingDelegate)
260 {
261 DateTime startTime = DateTime.UtcNow;
262 try
263 {
265 OnCompleted?.Invoke(this, new OnCompletedEventArgs(Result));
266 }
267 catch (APIRequestException e)
268 {
270 OnError?.Invoke(this, new OnErrorEventArgs(e, e.ServerError, e.RestResponse));
271 }
272 catch (Exception e)
273 {
275 OnError?.Invoke(this, new OnErrorEventArgs(e, null, null));
276 }
277 finally
278 {
279 Elapsed = DateTime.UtcNow - startTime;
280 IsPollingActive = false;
281 _pollingThread = null;
282 _cancellation?.Dispose();
283 _cancellation = null;
284 }
285 }
286 #endregion Async Polling
287 }
288}
A custom exception class that includes the RestSharp.IRestResponse that generated the exception,...
Describes a collection of resources which can be listed.
Used to deserialize server error messages.
Definition ServerError.cs:9
API methods / requests made available to the user.
Determines the behaviour of the API when automatically retrying a request whose result is not yet rea...
CancellationToken? CancellationToken
A cancellation token (if available) supplied to the polling method to allow the polling task to be ca...
int MaxPollTotalTime
The maximum time (in milliseconds) to poll the request before raising a NotWaitingException....
int MaxPollInterval
The maximum time (in milliseconds) to wait between retrying the request. (Default is a maximum of 10 ...
int MinPollInterval
The minimum time (in milliseconds) to wait between retrying the request. (Default is no minimum time....
double BackoffRate
The rate at which the polling interval is increased after repeated attempts. The interval will not be...
OnCompletedEventArgs(T returnValue)
Create a new OnCompletedEventArgs.
IRestResponse Response
The IRestResponse received (if any).
OnErrorEventArgs(Exception ex, ServerError serverError, IRestResponse response)
Create a new OnErrorEventArgs.
Exception Exception
The exception encountered (if any).
IRestResponse Response
The IRestResponse received.
OnPollEventArgs(double retryAfterSeconds, IRestResponse restResponse)
Create a new OnPollEventArgs.
Can be used to asynchronously poll a request until the result is ready.
delegate void OnErrorEventHandler(object sender, OnErrorEventArgs e)
A request has failed in an unexpected way.
delegate void OnCompletedEventHandler(object sender, OnCompletedEventArgs e)
The request has returned with a successful code.
OnPollEventHandler OnPoll
Event fired when a request has returned with a 503 code and RetryAfter header.
OnErrorEventHandler OnError
Event fired when a request has failed in an unexpected way.
void Start()
Begin polling the delegate asynchronously.
OnCompletedEventHandler OnCompleted
Event fired when the request has returned with a successful code.
delegate void OnPollEventHandler(object sender, OnPollEventArgs e)
A request has returned with a 503 code and RetryAfter header.
bool IsPollingActive
Returns true if a thread is actively polling this delegate.
PollingOptions PollingOptions
Controls the polling behaviour (how frequently the function is polled and for how long)....
AsyncRequestMonitor(T resourceToMonitor, PollingOptions pollingOptions=null)
Prepare this request monitor to monitor the specified resource until it completes.
T Result
The result of the delegate after successfully returning.
void Stop()
Cancel any pending requests and stop polling.
void Dispose()
Cancel the polling thread if running and dispose.
Exception ResultException
The error encountered in retrieving the result (if any).
AsyncRequestMonitor(Func< T > functionThatMight503, PollingOptions pollingOptions=null)
Prepare this request monitor to executes the specified delegate request until it completes.
TimeSpan Elapsed
The time taken between the first request and when polling ceased (either due to success or error)....
T Wait()
Join the polling thread and block further execution until the results is available to be returned on ...
Describes an APIResource class that has a "status" property and corresponding "status_message" which ...