Skip to content

Commit a6c08bc

Browse files
authored
Fix concurrency issue with dotnet test for MTP (#50960)
1 parent e6f25ab commit a6c08bc

File tree

1 file changed

+60
-53
lines changed

1 file changed

+60
-53
lines changed

src/Cli/dotnet/Commands/Test/MTP/TestApplication.cs

Lines changed: 60 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ internal sealed class TestApplication(
2020
TerminalTestReporter output,
2121
Action<CommandLineOptionMessages> onHelpRequested) : IDisposable
2222
{
23+
private readonly Lock _requestLock = new();
2324
private readonly BuildOptions _buildOptions = buildOptions;
2425
private readonly Action<CommandLineOptionMessages> _onHelpRequested = onHelpRequested;
2526
private readonly TestApplicationHandler _handler = new(output, module, testOptions);
@@ -199,63 +200,69 @@ private async Task WaitConnectionAsync(CancellationToken token)
199200

200201
private Task<IResponse> OnRequest(NamedPipeServer server, IRequest request)
201202
{
202-
try
203+
// We need to lock as we might be called concurrently when test app child processes all communicate with us.
204+
// For example, in a case of a sharding extension, we could get test result messages concurrently.
205+
// To be the most safe, we lock the whole OnRequest.
206+
lock (_requestLock)
203207
{
204-
switch (request)
208+
try
205209
{
206-
case HandshakeMessage handshakeMessage:
207-
_handshakes.Add(server, handshakeMessage);
208-
string negotiatedVersion = GetSupportedProtocolVersion(handshakeMessage);
209-
OnHandshakeMessage(handshakeMessage, negotiatedVersion.Length > 0);
210-
return Task.FromResult((IResponse)CreateHandshakeMessage(negotiatedVersion));
211-
212-
case CommandLineOptionMessages commandLineOptionMessages:
213-
OnCommandLineOptionMessages(commandLineOptionMessages);
214-
break;
215-
216-
case DiscoveredTestMessages discoveredTestMessages:
217-
OnDiscoveredTestMessages(discoveredTestMessages);
218-
break;
219-
220-
case TestResultMessages testResultMessages:
221-
OnTestResultMessages(testResultMessages);
222-
break;
223-
224-
case FileArtifactMessages fileArtifactMessages:
225-
OnFileArtifactMessages(fileArtifactMessages);
226-
break;
227-
228-
case TestSessionEvent sessionEvent:
229-
OnSessionEvent(sessionEvent);
230-
break;
231-
232-
// If we don't recognize the message, log and skip it
233-
case UnknownMessage unknownMessage:
234-
Logger.LogTrace($"Request '{request.GetType()}' with Serializer ID = {unknownMessage.SerializerId} is unsupported.");
235-
return Task.FromResult((IResponse)VoidResponse.CachedInstance);
236-
237-
default:
238-
// If it doesn't match any of the above, throw an exception
239-
throw new NotSupportedException(string.Format(CliCommandStrings.CmdUnsupportedMessageRequestTypeException, request.GetType()));
210+
switch (request)
211+
{
212+
case HandshakeMessage handshakeMessage:
213+
_handshakes.Add(server, handshakeMessage);
214+
string negotiatedVersion = GetSupportedProtocolVersion(handshakeMessage);
215+
OnHandshakeMessage(handshakeMessage, negotiatedVersion.Length > 0);
216+
return Task.FromResult((IResponse)CreateHandshakeMessage(negotiatedVersion));
217+
218+
case CommandLineOptionMessages commandLineOptionMessages:
219+
OnCommandLineOptionMessages(commandLineOptionMessages);
220+
break;
221+
222+
case DiscoveredTestMessages discoveredTestMessages:
223+
OnDiscoveredTestMessages(discoveredTestMessages);
224+
break;
225+
226+
case TestResultMessages testResultMessages:
227+
OnTestResultMessages(testResultMessages);
228+
break;
229+
230+
case FileArtifactMessages fileArtifactMessages:
231+
OnFileArtifactMessages(fileArtifactMessages);
232+
break;
233+
234+
case TestSessionEvent sessionEvent:
235+
OnSessionEvent(sessionEvent);
236+
break;
237+
238+
// If we don't recognize the message, log and skip it
239+
case UnknownMessage unknownMessage:
240+
Logger.LogTrace($"Request '{request.GetType()}' with Serializer ID = {unknownMessage.SerializerId} is unsupported.");
241+
return Task.FromResult((IResponse)VoidResponse.CachedInstance);
242+
243+
default:
244+
// If it doesn't match any of the above, throw an exception
245+
throw new NotSupportedException(string.Format(CliCommandStrings.CmdUnsupportedMessageRequestTypeException, request.GetType()));
246+
}
247+
}
248+
catch (Exception ex)
249+
{
250+
// BE CAREFUL:
251+
// When handling some of the messages, we may throw an exception in unexpected state.
252+
// (e.g, OnSessionEvent may throw if we receive TestSessionEnd without TestSessionStart).
253+
// (or if we receive help-related messages when not in help mode)
254+
// In that case, we FailFast.
255+
// The lack of FailFast *might* have unintended consequences, such as breaking the internal loop of pipe server.
256+
// In that case, maybe MTP app will continue waiting for response, but we don't send the response and are waiting for
257+
// MTP app process exit (which doesn't happen).
258+
// So, we explicitly FailFast here.
259+
string exAsString = ex.ToString();
260+
Logger.LogTrace(exAsString);
261+
Environment.FailFast(exAsString);
240262
}
241-
}
242-
catch (Exception ex)
243-
{
244-
// BE CAREFUL:
245-
// When handling some of the messages, we may throw an exception in unexpected state.
246-
// (e.g, OnSessionEvent may throw if we receive TestSessionEnd without TestSessionStart).
247-
// (or if we receive help-related messages when not in help mode)
248-
// In that case, we FailFast.
249-
// The lack of FailFast *might* have unintended consequences, such as breaking the internal loop of pipe server.
250-
// In that case, maybe MTP app will continue waiting for response, but we don't send the response and are waiting for
251-
// MTP app process exit (which doesn't happen).
252-
// So, we explicitly FailFast here.
253-
string exAsString = ex.ToString();
254-
Logger.LogTrace(exAsString);
255-
Environment.FailFast(exAsString);
256-
}
257263

258-
return Task.FromResult((IResponse)VoidResponse.CachedInstance);
264+
return Task.FromResult((IResponse)VoidResponse.CachedInstance);
265+
}
259266
}
260267

261268
private static string GetSupportedProtocolVersion(HandshakeMessage handshakeMessage)

0 commit comments

Comments
 (0)