C# Client Library
A C# Client Library for the AnalyzeRe REST API
Loading...
Searching...
No Matches
SimulationStatusMonitor.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Concurrent;
3using System.Collections.Generic;
4using System.Globalization;
5using System.Linq;
6using System.Net;
7using System.Threading;
8using RestSharp;
9
10namespace AnalyzeRe.Utilities
11{
13 [Obsolete("This class has not been maintained in some time and " +
14 "does not use the recommended exponential back-off polling mechanism. " +
15 "Consider using the API.PollUntil() method.")]
17 {
18 private const NumberStyles DoubleParseStyle = NumberStyles.AllowDecimalPoint;
19 private static readonly CultureInfo ParseCulture = CultureInfo.InvariantCulture;
20
21 #region Private Fields
22 private readonly ConcurrentDictionary<string, SimulationStatus> _simulations;
23 #endregion Private Fields
24
25 #region Public Fields
27 public double MaxPollInterval = 5.0;
29 public int MaxTimeouts = 3;
31 public int NumActivePolls => _numActivePolls;
32 private int _numActivePolls = 0;
34 public List<SimulationStatus> MonitoredSimulations => _simulations.Values.ToList();
35 #endregion Public Fields
36
37 #region Public Events
44
51 #endregion Public Events
52
53 #region Constructor
56 {
57 _simulations = new ConcurrentDictionary<string, SimulationStatus>();
58 }
59 #endregion Constructor
60
61 #region Public Control Methods
63 public void AbortAll()
64 {
65 foreach (SimulationStatus active in _simulations.Where(sim => sim.Value.BeingPolled)
66 .Select(kvp => kvp.Value))
67 {
68 active.BeingPolled = false;
69 active.Exception = new Exception("Monitoring Cancelled.");
70 }
71 }
72
74 public void Reset()
75 {
76 AbortAll();
77 _simulations.Values.ToList().ForEach(sim =>
78 sim.SimulationStatusChanged -= OnSimulationStatusChanged);
79 _simulations.Clear();
80 }
81 #endregion Public Control Methods
82
83 #region Event Monitoring
87 public void MonitorSimulationRequest(IAPIResourceView view, Action simulationRequest)
88 {
89 // If this view has been monitored and its state is Complete, nothing to monitor, so return.
90 // If its state is Running, or Queued, it's being monitored already, so return.
91 // If its state is Error, however, we might wish to re-initiate monitoring (the error might have been resolved).
92 if (_simulations.ContainsKey(view.id) && _simulations[view.id].State != SimulationStatus.SimulationState.Error)
93 return;
94 Thread t = new Thread(() => PollSimulationResultsUntilReady(this, view, simulationRequest, MaxPollInterval));
95 t.Start();
96 }
97
98 private void OnSimulationStatusChanged(object sender, EventArgs e)
99 {
100 SimulationStatusChanged?.Invoke(this, sender as SimulationStatus);
101 }
102 #endregion Event Monitoring
103
104 #region Threaded polling
106 private static void PollSimulationResultsUntilReady(SimulationStatusMonitor owner, IAPIResourceView view, Action simulationRequest, double maxPollInterval)
107 {
108 SimulationStatus status = new SimulationStatus(view.id) { BeingPolled = true };
109 bool added = owner._simulations.TryAdd(view.id, status);
110 if (added)
111 {
112 status.Description = view is PortfolioView ? "Portfolio (" + ((PortfolioView)view).layer_views.Count + " layer" + (((PortfolioView)view).layer_views.Count > 1 ? "s)" : ")")
113 : view is ILayerView ? ((ILayerView)view).layer.type + " Layer" + (String.IsNullOrWhiteSpace(((ILayerView)view).layer.description) ? "" : " \"" + ((ILayerView)view).layer.description + "\"") : null;
114 owner.SimulationStatusAdded?.Invoke(owner, status);
115 status.SimulationStatusChanged += owner.OnSimulationStatusChanged;
116 }
117 else
118 { // Another thread is/was already polling this view's simulation.
119 status = owner._simulations[view.id];
120 // If it's still actively being polled, or already completed successfully, we don't need another polling thread.
121 if (status.BeingPolled || status.State == SimulationStatus.SimulationState.Complete)
122 return;
123 else
124 {
125 status.BeingPolled = true; // Last polling must have failed, so poll this view again.
126 status.Exception = null;
127 }
128 }
129
130 if (status.BeingPolled) status.State = SimulationStatus.SimulationState.Queued; // Narrow the race condition window, but it's not such an important one as to merit a lock.
131 int timeouts = 0;
132 Interlocked.Increment(ref owner._numActivePolls);
133 while (status.BeingPolled) // Poll until the server responds successfully to the request, or we encounter a non-'503-ServiceUnavailable' error.
134 {
135 try
136 {
137 simulationRequest();
138 // Success. Update some properties and quit the thread.
139 Interlocked.Decrement(ref owner._numActivePolls);
140 status.TimeRemaining = 0;
141 status.State = SimulationStatus.SimulationState.Complete;
142 status.BeingPolled = false;
143 }
144 catch (APIRequestException e)
145 {
146 try
147 {
148 if (e.RestResponse == null ||
149 e.RestResponse.StatusCode != HttpStatusCode.ServiceUnavailable)
150 throw;
151 Parameter retryAfter = e.RestResponse.Headers.FirstOrDefault(header =>
152 header.Name.Equals("Retry-After", StringComparison.OrdinalIgnoreCase));
153 // Re-throw the original '503-Service Unavailable'. We won't poll since there's no Retry-After header.
154 if (retryAfter == null)
155 throw;
156
157 if (!Double.TryParse(retryAfter.Value.ToString(), DoubleParseStyle, ParseCulture, out double retryAfterSeconds))
158 throw new Exception("The server provided a Retry-After header, " +
159 "but it could not be parsed: " + retryAfter.Value, e);
160
161 status.TimeRemaining = retryAfterSeconds;
162 double nextSleep_seconds = Math.Min(maxPollInterval, retryAfterSeconds);
163 Thread.Sleep((int)(1000 * nextSleep_seconds));
164 }
165 catch (Exception ex)
166 {
167 Interlocked.Decrement(ref owner._numActivePolls);
168 status.Exception = ex;
169 status.BeingPolled = false;
170 }
171 }
172 catch (TimeoutException ex)
173 {
174 timeouts++;
175 if (timeouts >= owner.MaxTimeouts)
176 { // Too many timeouts, notify anyone monitoring of the error.
177 Interlocked.Decrement(ref owner._numActivePolls);
178 status.Exception = ex;
179 status.BeingPolled = false;
180 }
181 }
182 catch (Exception ex)
183 { // An unknown exception occurred. We cannot keep polling.
184 Interlocked.Decrement(ref owner._numActivePolls);
185 status.Exception = ex;
186 status.BeingPolled = false;
187 }
188 }
189 }
190 #endregion Threaded polling
191 }
192}
Monitors the status of analyses as they run.
void Reset()
Cancels all active polling and clears the list of known simulations.
SimulationStatusAddedDelegate SimulationStatusAdded
An event that fires whenever a new SimulationStatus is added to this monitor.
void AbortAll()
Cancels all active polling threads.
SimulationStatusMonitor()
Initialize a new SimulationStatusMonitor with an empty list of monitored simulations.
delegate void SimulationStatusChangedDelegate(SimulationStatusMonitor sender, SimulationStatus changed)
A handler that can respond to a SimulationStatusChanged event.
List< SimulationStatus > MonitoredSimulations
Copy of current simulations, so something just hooking into the monitor can get a history.
void MonitorSimulationRequest(IAPIResourceView view, Action simulationRequest)
Initiates a request for the specified view's simulation results and monitors it until completion.
double MaxPollInterval
Maximum time before polling the view to get a new time remaining.
delegate void SimulationStatusAddedDelegate(SimulationStatusMonitor sender, SimulationStatus added)
A handler that can respond to a SimulationStatusAdded event.
int MaxTimeouts
Maximum number of consecutive timeouts before we stop polling the server and return the timeout error...
SimulationStatusChangedDelegate SimulationStatusChanged
An event that fires whenever a SimulationStatus being monitored undergoes a change.
int NumActivePolls
Tracks the number of currently running threads.
Describes the status of a simulation being monitored.
SimulationState
The running state of a simulation represented by a SimulationStatus object.
PortfolioView and LayerView interface.
string id
The resource's unique identifier. It will be used in the request URL when requesting the resource fro...