From 0ea7206dfcdfbd1a76ba17ff8ffab9bfc159c746 Mon Sep 17 00:00:00 2001 From: Dean Ellis Date: Fri, 16 Jun 2023 14:33:28 +0100 Subject: [PATCH 1/5] [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. --- .../Utilities/Aapt2Daemon.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs b/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs index ec6aa25a035..230bae422c7 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs @@ -271,9 +271,15 @@ private void Aapt2DaemonStart () { // Ignore this error. It occurs when the Task is cancelled. } - aapt2.StandardInput.WriteLine ("quit"); - aapt2.StandardInput.WriteLine (); - aapt2.WaitForExit ((int)TimeSpan.FromSeconds (5).TotalMilliseconds); + try { + aapt2.StandardInput.WriteLine ("quit"); + aapt2.StandardInput.WriteLine (); + aapt2.WaitForExit ((int)TimeSpan.FromSeconds (5).TotalMilliseconds); + } + catch (IOException) + { + // Ignore this error. It occurs when the Build it cancelled. + } } bool IsAapt2Warning (string singleLine) From 5a33bb3a8d6c2988faf990abb70d2b67d0e3bfc4 Mon Sep 17 00:00:00 2001 From: Dean Ellis Date: Fri, 16 Jun 2023 15:17:04 +0100 Subject: [PATCH 2/5] Fix up formatting --- src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs b/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs index 230bae422c7..333f2ef2897 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs @@ -275,9 +275,7 @@ private void Aapt2DaemonStart () aapt2.StandardInput.WriteLine ("quit"); aapt2.StandardInput.WriteLine (); aapt2.WaitForExit ((int)TimeSpan.FromSeconds (5).TotalMilliseconds); - } - catch (IOException) - { + } catch (IOException) { // Ignore this error. It occurs when the Build it cancelled. } } From f40e91ee8cc87ce5d67e33299b96630a583c4263 Mon Sep 17 00:00:00 2001 From: Dean Ellis Date: Mon, 19 Jun 2023 11:23:15 +0100 Subject: [PATCH 3/5] Add Debug Logging --- src/Xamarin.Android.Build.Tasks/Tasks/Aapt2.cs | 2 +- src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/Aapt2.cs b/src/Xamarin.Android.Build.Tasks/Tasks/Aapt2.cs index 193a72e630f..270cba59190 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/Aapt2.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/Aapt2.cs @@ -84,7 +84,7 @@ public override bool Execute () DaemonMaxInstanceCount = maxInstances; else DaemonMaxInstanceCount = Math.Min (DaemonMaxInstanceCount, maxInstances); - daemon = Aapt2Daemon.GetInstance (BuildEngine4, GenerateFullPathToTool (), + daemon = Aapt2Daemon.GetInstance (BuildEngine4, LogDebugMessage, GenerateFullPathToTool (), DaemonMaxInstanceCount, GetRequiredDaemonInstances (), registerInDomain: DaemonKeepInDomain); return base.Execute (); } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs b/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs index 333f2ef2897..c2eadb133fb 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs @@ -19,13 +19,13 @@ internal class Aapt2Daemon : IDisposable internal static object RegisterTaskObjectKey => TypeFullName; - public static Aapt2Daemon GetInstance (IBuildEngine4 engine, string aapt2, int numberOfInstances, int initalNumberOfDaemons, bool registerInDomain = false) + public static Aapt2Daemon GetInstance (IBuildEngine4 engine, Action log, string aapt2, int numberOfInstances, int initalNumberOfDaemons, bool registerInDomain = false) { var area = registerInDomain ? RegisteredTaskObjectLifetime.AppDomain : RegisteredTaskObjectLifetime.Build; var daemon = engine.GetRegisteredTaskObjectAssemblyLocal (RegisterTaskObjectKey, area); if (daemon == null) { - daemon = new Aapt2Daemon (aapt2, numberOfInstances, initalNumberOfDaemons); + daemon = new Aapt2Daemon (aapt2, numberOfInstances, initalNumberOfDaemons, log); engine.RegisterTaskObjectAssemblyLocal (RegisterTaskObjectKey, daemon, area, allowEarlyCollection: false); } return daemon; @@ -66,6 +66,7 @@ public void Complete (bool result) long jobsRunning = 0; long jobId = 0; int maxInstances = 0; + Action logger = null; public CancellationToken Token => tcs.Token; @@ -86,10 +87,11 @@ public bool JobsRunning public int CurrentInstances => daemons.Count; - public Aapt2Daemon (string aapt2, int maxNumberOfInstances, int initalNumberOfDaemons) + public Aapt2Daemon (string aapt2, int maxNumberOfInstances, int initalNumberOfDaemons, Action log) { Aapt2 = aapt2; maxInstances = maxNumberOfInstances; + logger = log; for (int i = 0; i < initalNumberOfDaemons; i++) { SpawnAapt2Daemon (); } @@ -277,6 +279,7 @@ private void Aapt2DaemonStart () aapt2.WaitForExit ((int)TimeSpan.FromSeconds (5).TotalMilliseconds); } catch (IOException) { // Ignore this error. It occurs when the Build it cancelled. + logger?.Invoke ("Aapt2Daemon: Ignoring IOException. Build was cancelled."); } } From 25e5a1ca75a92b3b3bf6553b8567e1207fb27c27 Mon Sep 17 00:00:00 2001 From: Dean Ellis Date: Tue, 20 Jun 2023 10:10:48 +0100 Subject: [PATCH 4/5] use nameof --- src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs b/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs index c2eadb133fb..e236f394152 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs @@ -279,7 +279,7 @@ private void Aapt2DaemonStart () aapt2.WaitForExit ((int)TimeSpan.FromSeconds (5).TotalMilliseconds); } catch (IOException) { // Ignore this error. It occurs when the Build it cancelled. - logger?.Invoke ("Aapt2Daemon: Ignoring IOException. Build was cancelled."); + logger?.Invoke ($"{nameof (Aapt2Daemon)}: Ignoring IOException. Build was cancelled."); } } From 034e01218a9737cdbe59856c637ba80e7eef6b98 Mon Sep 17 00:00:00 2001 From: Dean Ellis Date: Wed, 21 Jun 2023 15:36:46 +0100 Subject: [PATCH 5/5] Add additional try/catch --- src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs b/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs index e236f394152..7454f024c41 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs @@ -279,7 +279,12 @@ private void Aapt2DaemonStart () aapt2.WaitForExit ((int)TimeSpan.FromSeconds (5).TotalMilliseconds); } catch (IOException) { // Ignore this error. It occurs when the Build it cancelled. - logger?.Invoke ($"{nameof (Aapt2Daemon)}: Ignoring IOException. Build was cancelled."); + try { + logger?.Invoke ($"{nameof (Aapt2Daemon)}: Ignoring IOException. Build was cancelled."); + } catch { + // The Logger might not exist if the daemon exits after the logger has been + // collected. + } } }