Skip to content

Commit 9d3e0f0

Browse files
committed
separating shutdown concerns from dispose concerns as best as reasonable at this time
1 parent 06e57f1 commit 9d3e0f0

File tree

4 files changed

+66
-12
lines changed

4 files changed

+66
-12
lines changed

pkgs/sdk/server/src/Internal/DataSources/PollingDataSource.cs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ internal sealed class PollingDataSource : IDataSource
2323
private readonly Logger _log;
2424
private CancellationTokenSource _canceller;
2525

26+
private bool _disposed = false;
27+
private readonly AtomicBoolean _shuttingDown = new AtomicBoolean(false);
28+
2629
internal PollingDataSource(
2730
LdClientContext context,
2831
IFeatureRequestor featureRequestor,
@@ -128,22 +131,32 @@ private async Task UpdateTaskAsync()
128131

129132
void IDisposable.Dispose()
130133
{
134+
// dispose is currently overloaded with shutdown responsibility, we handle this first
135+
Shutdown(null);
136+
131137
Dispose(true);
132138
GC.SuppressFinalize(this);
133139
}
134140

135141
private void Dispose(bool disposing)
136142
{
137-
if (disposing)
138-
{
139-
Shutdown(null);
143+
if (_disposed) return;
144+
145+
if (disposing) {
146+
// dispose managed resources if any
147+
_featureRequestor.Dispose();
140148
}
149+
150+
_disposed = true;
141151
}
142152

143153
private void Shutdown(DataSourceStatus.ErrorInfo? errorInfo)
144154
{
155+
// Prevent concurrent shutdown calls - only allow the first call to proceed
156+
// GetAndSet returns the OLD value, so if it was already true, we return early
157+
if (_shuttingDown.GetAndSet(true)) return;
158+
145159
_canceller?.Cancel();
146-
_featureRequestor.Dispose();
147160
_dataSourceUpdates.UpdateStatus(DataSourceState.Off, errorInfo);
148161
}
149162

pkgs/sdk/server/src/Internal/DataSources/StreamingDataSource.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ internal class StreamingDataSource : IDataSource
5252

5353
private IEnumerable<KeyValuePair<string, IEnumerable<string>>> _headers;
5454

55+
private bool _disposed = false;
56+
private readonly AtomicBoolean _shuttingDown = new AtomicBoolean(false);
57+
5558
internal delegate IEventSource EventSourceCreator(Uri streamUri,
5659
HttpConfiguration httpConfig);
5760

@@ -104,20 +107,30 @@ public Task<bool> Start()
104107

105108
public void Dispose()
106109
{
110+
// dispose is currently overloaded with shutdown responsibility, we handle this first
111+
Shutdown(null);
112+
107113
Dispose(true);
108114
GC.SuppressFinalize(this);
109115
}
110116

111117
private void Dispose(bool disposing)
112118
{
113-
if (disposing)
114-
{
115-
Shutdown(null);
119+
if (_disposed) return;
120+
121+
if (disposing) {
122+
// dispose managed resources if any
116123
}
124+
125+
_disposed = true;
117126
}
118127

119128
private void Shutdown(DataSourceStatus.ErrorInfo? errorInfo)
120129
{
130+
// Prevent concurrent shutdown calls - only allow the first call to proceed
131+
// GetAndSet returns the OLD value, so if it was already true, we return early
132+
if (_shuttingDown.GetAndSet(true)) return;
133+
121134
_es.Close();
122135
if (_storeStatusMonitoringEnabled)
123136
{

pkgs/sdk/server/src/Internal/FDv2DataSources/FDv2PollingDataSource.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ internal sealed class FDv2PollingDataSource : IDataSource
3030
private CancellationTokenSource _canceler;
3131
private string _environmentId;
3232

33+
private bool _disposed = false;
34+
private readonly AtomicBoolean _shuttingDown = new AtomicBoolean(false);
35+
3336
internal FDv2PollingDataSource(
3437
LdClientContext context,
3538
IDataSourceUpdates dataSourceUpdates,
@@ -230,21 +233,32 @@ private void ProcessChangeSet(FDv2ChangeSet fdv2ChangeSet)
230233

231234
void IDisposable.Dispose()
232235
{
236+
// dispose is currently overloaded with shutdown responsibility, we handle this first
237+
Shutdown(null);
238+
233239
Dispose(true);
234240
GC.SuppressFinalize(this);
235241
}
236242

237243
private void Dispose(bool disposing)
238244
{
239-
if (!disposing) return;
245+
if (_disposed) return;
240246

241-
Shutdown(null);
247+
if (disposing) {
248+
// dispose managed resources if any
249+
_requestor.Dispose();
250+
}
251+
252+
_disposed = true;
242253
}
243254

244255
private void Shutdown(DataSourceStatus.ErrorInfo? errorInfo)
245256
{
257+
// Prevent concurrent shutdown calls - only allow the first call to proceed
258+
// GetAndSet returns the OLD value, so if it was already true, we return early
259+
if (_shuttingDown.GetAndSet(true)) return;
260+
246261
_canceler?.Cancel();
247-
_requestor.Dispose();
248262
_dataSourceUpdates.UpdateStatus(DataSourceState.Off, errorInfo);
249263
}
250264
}

pkgs/sdk/server/src/Internal/FDv2DataSources/FDv2StreamingDataSource.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ internal delegate IEventSource EventSourceCreator(Uri streamUri,
5656
/// </summary>
5757
private readonly AtomicBoolean _lastStoreUpdateFailed = new AtomicBoolean(false);
5858

59+
private bool _disposed = false;
60+
private readonly AtomicBoolean _shuttingDown = new AtomicBoolean(false);
61+
5962
internal FDv2StreamingDataSource(
6063
LdClientContext context,
6164
IDataSourceUpdates dataSourceUpdates,
@@ -102,19 +105,30 @@ internal FDv2StreamingDataSource(
102105

103106
public void Dispose()
104107
{
108+
// dispose is currently overloaded with shutdown responsibility, we handle this first
109+
Shutdown(null);
110+
105111
Dispose(true);
106112
GC.SuppressFinalize(this);
107113
}
108114

109115
private void Dispose(bool disposing)
110116
{
111-
if (!disposing) return;
117+
if (_disposed) return;
112118

113-
Shutdown(null);
119+
if (disposing) {
120+
// dispose managed resources if any
121+
}
122+
123+
_disposed = true;
114124
}
115125

116126
private void Shutdown(DataSourceStatus.ErrorInfo? errorInfo)
117127
{
128+
// Prevent concurrent shutdown calls - only allow the first call to proceed
129+
// GetAndSet returns the OLD value, so if it was already true, we return early
130+
if (_shuttingDown.GetAndSet(true)) return;
131+
118132
_es.Close();
119133
if (_storeStatusMonitoringEnabled)
120134
{

0 commit comments

Comments
 (0)