Skip to content

Commit ad95b98

Browse files
authored
[Xamarin.Android.Build.Tasks] Handle IOException in Aapt2Daemon (#8130)
* [Xamarin.Android.Build.Tasks] Handle IOException in Aapt2Daemon When cancelling a build on the command line we occasionally see the following exception. ``` Unhandled exception. System.IO.IOException: Broken pipe ---> System.Net.Sockets.SocketException (32): Broken pipe at System.IO.Pipes.PipeStream.WriteCore(ReadOnlySpan`1 buffer) --- End of inner exception stack trace --- at System.IO.Pipes.PipeStream.WriteCore(ReadOnlySpan`1 buffer) at System.IO.StreamWriter.Flush(Boolean flushStream, Boolean flushEncoder) at System.IO.StreamWriter.WriteLine(String value) at Xamarin.Android.Tasks.Aapt2Daemon.Aapt2DaemonStart() at System.Threading.Thread.StartHelper.Callback(Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) --- End of stack trace from previous location --- at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) Unhandled exception. System.IO.IOException: Broken pipe ---> System.Net.Sockets.SocketException (32): Broken pipe at System.IO.Pipes.PipeStream.WriteCore(ReadOnlySpan`1 buffer) --- End of inner exception stack trace --- at System.IO.Pipes.PipeStream.WriteCore(ReadOnlySpan`1 buffer) at System.IO.StreamWriter.Flush(Boolean flushStream, Boolean flushEncoder) at System.IO.StreamWriter.WriteLine(String value) at Xamarin.Android.Tasks.Aapt2Daemon.Aapt2DaemonStart() at System.Threading.Thread.StartHelper.Callback(Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) --- End of stack trace from previous location --- at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) Unhandled exception. System.IO.IOException: Broken pipe ---> System.Net.Sockets.SocketException (32): Broken pipe at System.IO.Pipes.PipeStream.WriteCore(ReadOnlySpan`1 buffer) --- End of inner exception stack trace --- at System.IO.Pipes.PipeStream.WriteCore(ReadOnlySpan`1 buffer) at System.IO.StreamWriter.Flush(Boolean flushStream, Boolean flushEncoder) at System.IO.StreamWriter.WriteLine(String value) at Xamarin.Android.Tasks.Aapt2Daemon.Aapt2DaemonStart() at System.Threading.Thread.StartHelper.Callback(Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) --- End of stack trace from previous location --- at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) ``` This happens because we are calling `Stream.WriteLine` after the process has already exited. So lets catch this exception and ignore it. * use nameof * Add additional try/catch
1 parent 2b1ec45 commit ad95b98

File tree

2 files changed

+19
-7
lines changed

2 files changed

+19
-7
lines changed

src/Xamarin.Android.Build.Tasks/Tasks/Aapt2.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public override bool Execute ()
8484
DaemonMaxInstanceCount = maxInstances;
8585
else
8686
DaemonMaxInstanceCount = Math.Min (DaemonMaxInstanceCount, maxInstances);
87-
daemon = Aapt2Daemon.GetInstance (BuildEngine4, GenerateFullPathToTool (),
87+
daemon = Aapt2Daemon.GetInstance (BuildEngine4, LogDebugMessage, GenerateFullPathToTool (),
8888
DaemonMaxInstanceCount, GetRequiredDaemonInstances (), registerInDomain: DaemonKeepInDomain);
8989
return base.Execute ();
9090
}

src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ internal class Aapt2Daemon : IDisposable
1919

2020
internal static object RegisterTaskObjectKey => TypeFullName;
2121

22-
public static Aapt2Daemon GetInstance (IBuildEngine4 engine, string aapt2, int numberOfInstances, int initalNumberOfDaemons, bool registerInDomain = false)
22+
public static Aapt2Daemon GetInstance (IBuildEngine4 engine, Action<string> log, string aapt2, int numberOfInstances, int initalNumberOfDaemons, bool registerInDomain = false)
2323
{
2424
var area = registerInDomain ? RegisteredTaskObjectLifetime.AppDomain : RegisteredTaskObjectLifetime.Build;
2525
var daemon = engine.GetRegisteredTaskObjectAssemblyLocal<Aapt2Daemon> (RegisterTaskObjectKey, area);
2626
if (daemon == null)
2727
{
28-
daemon = new Aapt2Daemon (aapt2, numberOfInstances, initalNumberOfDaemons);
28+
daemon = new Aapt2Daemon (aapt2, numberOfInstances, initalNumberOfDaemons, log);
2929
engine.RegisterTaskObjectAssemblyLocal (RegisterTaskObjectKey, daemon, area, allowEarlyCollection: false);
3030
}
3131
return daemon;
@@ -66,6 +66,7 @@ public void Complete (bool result)
6666
long jobsRunning = 0;
6767
long jobId = 0;
6868
int maxInstances = 0;
69+
Action<string> logger = null;
6970

7071
public CancellationToken Token => tcs.Token;
7172

@@ -86,10 +87,11 @@ public bool JobsRunning
8687

8788
public int CurrentInstances => daemons.Count;
8889

89-
public Aapt2Daemon (string aapt2, int maxNumberOfInstances, int initalNumberOfDaemons)
90+
public Aapt2Daemon (string aapt2, int maxNumberOfInstances, int initalNumberOfDaemons, Action<string> log)
9091
{
9192
Aapt2 = aapt2;
9293
maxInstances = maxNumberOfInstances;
94+
logger = log;
9395
for (int i = 0; i < initalNumberOfDaemons; i++) {
9496
SpawnAapt2Daemon ();
9597
}
@@ -271,9 +273,19 @@ private void Aapt2DaemonStart ()
271273
{
272274
// Ignore this error. It occurs when the Task is cancelled.
273275
}
274-
aapt2.StandardInput.WriteLine ("quit");
275-
aapt2.StandardInput.WriteLine ();
276-
aapt2.WaitForExit ((int)TimeSpan.FromSeconds (5).TotalMilliseconds);
276+
try {
277+
aapt2.StandardInput.WriteLine ("quit");
278+
aapt2.StandardInput.WriteLine ();
279+
aapt2.WaitForExit ((int)TimeSpan.FromSeconds (5).TotalMilliseconds);
280+
} catch (IOException) {
281+
// Ignore this error. It occurs when the Build it cancelled.
282+
try {
283+
logger?.Invoke ($"{nameof (Aapt2Daemon)}: Ignoring IOException. Build was cancelled.");
284+
} catch {
285+
// The Logger might not exist if the daemon exits after the logger has been
286+
// collected.
287+
}
288+
}
277289
}
278290

279291
bool IsAapt2Warning (string singleLine)

0 commit comments

Comments
 (0)