Skip to content

Commit e76cce2

Browse files
author
Michael Hahn
committed
improve output reading in race condition scenarios
1 parent 90bf469 commit e76cce2

File tree

1 file changed

+44
-35
lines changed

1 file changed

+44
-35
lines changed

sdk/identity/Azure.Identity/src/ProcessRunner.cs

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,24 @@ internal sealed class ProcessRunner
1515
{
1616
private readonly IProcess _process;
1717
private readonly TimeSpan _timeout;
18-
private readonly TaskCompletionSource<string> _tcs;
18+
private readonly TaskCompletionSource<bool> _processExitedComplectionSource;
1919
private readonly CancellationToken _cancellationToken;
2020
private readonly CancellationTokenSource _timeoutCts;
2121
private CancellationTokenRegistration _ctRegistration;
2222
private readonly StringBuilder _stdOutBuilder;
2323
private readonly StringBuilder _stdErrBuilder;
24-
private readonly TaskCompletionSource<bool> _stdOutCompletionSource;
25-
private readonly TaskCompletionSource<bool> _stdErrCompletionSource;
24+
private readonly TaskCompletionSource<string> _stdOutCompletionSource;
25+
private readonly TaskCompletionSource<string> _stdErrCompletionSource;
2626

2727
public ProcessRunner(IProcess process, TimeSpan timeout, CancellationToken cancellationToken)
2828
{
2929
_process = process;
3030
_timeout = timeout;
31-
_tcs = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
31+
_processExitedComplectionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
3232
_stdOutBuilder = new StringBuilder();
3333
_stdErrBuilder = new StringBuilder();
34-
_stdOutCompletionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
35-
_stdErrCompletionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
34+
_stdOutCompletionSource = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
35+
_stdErrCompletionSource = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
3636

3737
if (timeout.TotalMilliseconds >= 0)
3838
{
@@ -49,25 +49,40 @@ public async Task<string> RunAsync()
4949
{
5050
StartProcess();
5151

52-
await _stdOutCompletionSource.Task.ConfigureAwait(false);
53-
await _stdErrCompletionSource.Task.ConfigureAwait(false);
54-
55-
return await _tcs.Task.ConfigureAwait(false);
52+
try
53+
{
54+
await _processExitedComplectionSource.Task.ConfigureAwait(false);
55+
var output = await _stdOutCompletionSource.Task.ConfigureAwait(false);
56+
await _stdErrCompletionSource.Task.ConfigureAwait(false);
57+
return output;
58+
}
59+
finally
60+
{
61+
DisposeProcess();
62+
}
5663
}
5764

5865
public string Run()
5966
{
6067
StartProcess();
6168
#pragma warning disable AZC0102 // Do not use GetAwaiter().GetResult().
62-
_stdOutCompletionSource.Task.GetAwaiter().GetResult();
63-
_stdErrCompletionSource.Task.GetAwaiter().GetResult();
64-
return _tcs.Task.GetAwaiter().GetResult();
69+
try
70+
{
71+
_processExitedComplectionSource.Task.GetAwaiter().GetResult();
72+
var output = _stdOutCompletionSource.Task.GetAwaiter().GetResult();
73+
_stdErrCompletionSource.Task.GetAwaiter().GetResult();
74+
return output;
75+
}
76+
finally
77+
{
78+
DisposeProcess();
79+
}
6580
#pragma warning restore AZC0102 // Do not use GetAwaiter().GetResult().
6681
}
6782

6883
private void StartProcess()
6984
{
70-
if (TrySetCanceled() || _tcs.Task.IsCompleted)
85+
if (TrySetCanceled() || _processExitedComplectionSource.Task.IsCompleted)
7186
{
7287
return;
7388
}
@@ -91,18 +106,26 @@ private void HandleExit()
91106
{
92107
if (_process.ExitCode == 0)
93108
{
94-
TrySetResult(_stdOutBuilder.ToString());
109+
_processExitedComplectionSource.TrySetResult(true);
95110
}
96111
else
97112
{
98-
TrySetException(new InvalidOperationException(_stdErrBuilder.ToString()));
113+
_processExitedComplectionSource.TrySetResult(false);
99114
}
100115
}
101116
private void HandleStdErrDataReceived(object sender, DataReceivedEventArgsWrapper e)
102117
{
103118
if (e.Data is null)
104119
{
105-
_stdErrCompletionSource.TrySetResult(true);
120+
var result = _stdErrBuilder.ToString();
121+
if (result.Length == 0)
122+
{
123+
_stdErrCompletionSource.TrySetResult(result);
124+
}
125+
else
126+
{
127+
_stdErrCompletionSource.TrySetException(new InvalidOperationException(result));
128+
}
106129
}
107130
else
108131
{
@@ -114,7 +137,7 @@ private void HandleStdOutDataReceived(object sender, DataReceivedEventArgsWrappe
114137
{
115138
if (e.Data is null)
116139
{
117-
_stdOutCompletionSource.TrySetResult(true);
140+
_stdOutCompletionSource.TrySetResult(_stdOutBuilder.ToString());
118141
}
119142
else
120143
{
@@ -124,7 +147,7 @@ private void HandleStdOutDataReceived(object sender, DataReceivedEventArgsWrappe
124147

125148
private void HandleCancel()
126149
{
127-
if (_tcs.Task.IsCompleted)
150+
if (_processExitedComplectionSource.Task.IsCompleted)
128151
{
129152
return;
130153
}
@@ -137,41 +160,27 @@ private void HandleCancel()
137160
}
138161
catch (Exception ex)
139162
{
140-
TrySetException(ex);
163+
_processExitedComplectionSource.TrySetException(ex);
141164
return;
142165
}
143166
}
144167

145168
TrySetCanceled();
146169
}
147170

148-
private void TrySetResult(string result)
149-
{
150-
DisposeProcess();
151-
_tcs.TrySetResult(result);
152-
}
153-
154171
private bool TrySetCanceled()
155172
{
156173
if (_cancellationToken.IsCancellationRequested)
157174
{
158175
DisposeProcess();
159176
_stdOutCompletionSource.TrySetCanceled(_cancellationToken);
160177
_stdErrCompletionSource.TrySetCanceled(_cancellationToken);
161-
_tcs.TrySetCanceled(_cancellationToken);
178+
_processExitedComplectionSource.TrySetCanceled(_cancellationToken);
162179
}
163180

164181
return _cancellationToken.IsCancellationRequested;
165182
}
166183

167-
private void TrySetException(Exception exception)
168-
{
169-
DisposeProcess();
170-
_stdOutCompletionSource.TrySetResult(false);
171-
_stdErrCompletionSource.TrySetResult(false);
172-
_tcs.TrySetException(exception);
173-
}
174-
175184
private void DisposeProcess()
176185
{
177186
_process.OutputDataReceived -= HandleStdOutDataReceived;

0 commit comments

Comments
 (0)