镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

[Xamarin.Android.Build.Tasks] Retire finite process facade - #13518

Closed
simonrozsival wants to merge 8 commits into
simonrozsival-aapt2-daemon-lifetimefrom
simonrozsival-process-resource-cleanup
Closed

simonrozsival wants to merge 8 commits into
simonrozsival-aapt2-daemon-lifetimefrom
simonrozsival-process-resource-cleanup

Conversation

@simonrozsival

@simonrozsival simonrozsival commented Nov 12, 2026 •

Copy link
Copy Markdown
Member

Related to #12981. Stacked on #13515 (simonrozsival-aapt2-daemon-lifetime, 7b7fced0b208fd0570e2d622dcab46f7efedbfc3).

Retire the unused MonoAndroidHelper.RunProcess overloads and their event, cancellation-registration, and callback machinery. Migrate real finite uname, Java, lint, aapt2-version, emulator-discovery, keytool, and strip callers directly to native process APIs with structured arguments and bounded execution/output draining, preserving diagnostics and error policies. Own both redirected readers explicitly after native capture joins rather than relying on finalization. No replacement process facade or persistent daemon/emulator changes.

Keep completed emulator names on inherited-pipe timeout. Strip both streams concurrently as raw character chunks so XA0142 retains unterminated stdout/stderr even when an exited root leaves inherited pipes open; remove partial outputs. Fixture-owned descendants are killed and observed with bounded managed Process.WaitForExit(5000) before fixture deletion.

Bound NativeAOT linker/readobj/objdump output draining independently of root execution. Root exit starts a 30-second EOF deadline; active linking remains controlled by MSBuild cancellation, not an arbitrary linker cap. Join owned readers, bound live-root termination observation, preserve complete-line symbol parsing, partial linker diagnostics, error mappings, failed-link cleanup and atomic metadata output. Before the fix, three established-suite inherited-EOF regressions took 60 seconds and failed; the 31-second active-link control passed.

Local validation at current head b331dd5c7ef3cdbd91933097c1e0cc3b6c477e20 after replaying all six finite commits onto the updated AAPT2 parent:

  • DOTNET_CLI_USE_MSBUILD_SERVER=0 MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build src/Microsoft.Android.Build.Tasks/Tests/Microsoft.Android.Build.Tasks.Tests/Microsoft.Android.Build.Tasks.Tests.csproj -c Debug --no-restore -m:1 -nr:false -p:UseSharedCompilation=false -v:q — passed, 0 errors, 0 warnings.
  • ./dotnet-local.sh test bin/TestDebug/net12.0/Microsoft.Android.Build.Tasks.Tests.dll --filter 'Name~NativeLinkBoundsOnlyPostExitOutputDrain|FullyQualifiedName~ExtractTypeMapKeysFromNativeAotObject' --logger 'trx;LogFileName=finite-restack-modern.trx' --results-directory /Users/simon/.copilot/session-state/46adb309-4abf-4756-91f7-16efbcc103f8/files — passed 205, failed 0, skipped 0. Covers actual 30-second inherited-EOF deadlines, 31-second active linking, real task cancellation with joined asynchronous cleanup, and metadata policies.
  • DOTNET_CLI_USE_MSBUILD_SERVER=0 MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Xamarin.Android.Build.Tests.csproj -c Debug --no-restore -m:1 -nr:false -p:UseSharedCompilation=false -v:q — passed, 0 errors, 27 warnings.
  • ./dotnet-local.sh test bin/TestDebug/net11.0/Xamarin.Android.Build.Tests.dll --filter 'FullyQualifiedName~GetAvailableAndroidDevicesTests|Name~CurrentSdkUsesConfiguredJavaVersionRange|Name~ResolveAndroidToolingCachesAapt2Version|Name~FailedNativeStripReportsBothStreamsAndRemovesPartialOutput' --logger 'trx;LogFileName=finite-restack-owner.trx' --results-directory /Users/simon/.copilot/session-state/46adb309-4abf-4756-91f7-16efbcc103f8/files — passed 64, failed 0, skipped 0. Includes retained raw unterminated strip diagnostics, discovery deadlines/cleanup and 100 actual discovery captures releasing both pipes without GC/finalization.
  • git diff --check — passed. git range-diff fc35a8078238ad4e4de9509f8e1539052f061a0e..c5fa145550a709e666926da983708f729667ee75 7b7fced0b208fd0570e2d622dcab46f7efedbfc3..b331dd5c7ef3cdbd91933097c1e0cc3b6c477e20 — all six patches unchanged; stable patch IDs match individually.

These are fresh current-head results, superseding the earlier 43-case fixture-only proof at 2ee131d2 and 63-case finite-policy proof at e321d01c.

Blocked Windows lint finding: downstream Azure build 1677420, run 46759822/result 100248, CheckLintConfigMerging(NativeAOT) fails with XALNT7017 in Process.ReadPipes → ReadAllText → RunAndCaptureText → Lint.GetLintVersion, before the actual lint invocation. The binlog records 35.703 seconds in Lint versus this layer's new 30-second whole-capture policy. This is a coupled failure, not classified as unrelated or flaky. Slow Java startup versus retained/inherited pipe EOF versus Windows native capture behavior remains unresolved; Windows reproduction with the pinned host is unavailable. The reader-ownership correction does not claim to fix this timeout: its 30-second policy is unchanged. Per user decision this stays blocked: no guessed deadline increase, suppression, or CI rerun. Detailed evidence.

Full SDK preparation remains blocked: make prepare failed with MSB3231 at src/androidsdk/androidsdk.targets:341 removing the nonempty shared sources/android-37.2.staging directory. Shared staging was not repaired or raced; full SDK/application-build and Windows/device coverage are not claimed. No device or ADB use. Protected external worktree dirt is untouched.

Copilot AI balanced review requested due to automatic review settings November 12, 2026 11:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Timed-out lint probes can leave child JVMs running on Windows, while undisposed redirected readers during native-library stripping risk exhausting file handles in large builds.

2 open issues
What changed in this PR

Retires the finite-process helper in .NET for Android build tooling, advancing #12981 while leaving persistent daemon and emulator execution unchanged.

Changes:

  • Removes MonoAndroidHelper.RunProcess and migrates callers to native process APIs with structured arguments and timeouts.
  • Preserves completed emulator names and strip diagnostics on inherited-pipe timeouts, and removes failed strip outputs.
  • Adds regression coverage for output draining, argument handling, timeouts, and cleanup.
File Description
src/​Xamarin.Android.Build.Tasks/​Utilities/​MonoAndroidHelper.cs Removes the process facade and migrates the uname probe.
src/​Xamarin.Android.Build.Tasks/​Tests/​Xamarin.Android.Build.Tests/​Utilities/​ResourceData.cs Uses bounded native capture for keytool.
src/​Xamarin.Android.Build.Tasks/​Tests/​Xamarin.Android.Build.Tests/​Tasks/​GetAvailableAndroidDevicesTests.cs Tests discovery output, structured arguments, and inherited pipes.
src/​Xamarin.Android.Build.Tasks/​Tests/​Xamarin.Android.Build.Tests/​NativeAotBuildTests.cs Tests strip diagnostics, timeouts, and partial-output removal.
src/​Xamarin.Android.Build.Tasks/​Tasks/​ValidateJavaVersion.cs Migrates Java version capture with a timeout.
src/​Xamarin.Android.Build.Tasks/​Tasks/​ResolveAndroidTooling.cs Migrates aapt2 version capture with a timeout.
src/​Xamarin.Android.Build.Tasks/​Tasks/​Lint.cs Migrates lint version capture while preserving environment overrides.
src/​Xamarin.Android.Build.Tasks/​Tasks/​GetAvailableAndroidDevices.cs Bounds discovery probes and preserves completed emulator names.
src/​Xamarin.Android.Build.Tasks/​Tasks/​CollectNativeFilesForArchive.cs Captures both strip streams concurrently and cleans up failed outputs.

Review effort: Balanced

};
foreach (var pair in environmentVariables)
startInfo.Environment [pair.Key] = pair.Value;
var result = Process.RunAndCaptureText (startInfo, TimeSpan.FromSeconds (30));
Comment on lines +160 to +161
var output = CaptureDiagnosticsAsync (process.StandardOutput, stdout);
var error = CaptureDiagnosticsAsync (process.StandardError, stderr);
@simonrozsival

Copy link
Copy Markdown
Member Author

Blocked validation finding: downstream Azure build 1677420, Windows MSBuild 7, run 46759822/result 100248 (CheckLintConfigMerging(NativeAOT)) fails with XALNT7017 in Process.ReadPipes → ReadAllText → RunAndCaptureText → Lint.GetLintVersion, before the actual lint invocation. The targeted binlog records Lint from 13:04:19.685 to 13:04:55.389 UTC (35.703 seconds). This layer introduced a 30-second whole-capture policy where the previous helper waited indefinitely, so this is a coupled failure, not classified as unrelated or flaky.

The available evidence cannot distinguish slow Java startup from retained/inherited pipe EOF or Windows native capture behavior. The lane used cmdline-tools 23.0, JDK 25.0.4.1 and .NET 12.0.0-alpha.1.26556.115. Windows reproduction with that pinned host remains unavailable. Per the user decision, this fix stays blocked: no speculative deadline increase, suppression, CI rerun, or finite source/ref changes.

Build: https://dev.azure.com/dnceng-public/public/_build/results?buildId=1677420 . Targeted original build.log/binlog are preserved in the owning session handoff as finite-lint-1677420-build.log and finite-lint-1677420-build.binlog.

simonrozsival and others added 2 commits November 12, 2026 17:27
`Xamarin.Android.Tasks.Aapt2Daemon` could block indefinitely while
reading a response, waiting for output, or shutting down.  A failed
worker also left queued jobs without a terminal result.

Keep the persistent protocol, but use cancellable async input and
stderr reads with a concurrent stdout drain.  Bound output-file waits
to 30 seconds, observe child exit independently, and terminate only
the owned process after a five-second graceful shutdown deadline.

Serialize queue/worker lifecycle changes, complete rejected and
pending jobs on failure/disposal, and replace stopped MSBuild cache
entries.  Cancellation restarts the affected daemon without
poisoning later commands.  Preserve UTF-8 input without a BOM and
deferred diagnostic classification.

Extend the existing Aapt2Tests owner with protocol/lifetime cases.
Both owner builds pass and all 33 Aapt2Tests pass.  Full `make all`
remains blocked by pre-existing generated Mono.Android binding
compile errors observed before this change.

Relates to #12981.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Context: #13515

The executing .NET 12.0.0-alpha.1.26556.115 runtime leaves accessed synchronous process readers caller-owned.  A process handle alone therefore cannot prove that a daemon's redirected pipe handles have closed.

Exercise the actual AAPT2 daemon through 100 start/stop cycles in a no-GC region, count real file descriptors, and ensure cleanup does not depend on reader finalization.  Warm the process path before measuring and allow a small fixed margin for test-host bookkeeping, rather than growth proportional to daemon launches.

The existing daemon already disposes both readers after its producer tasks join; no production fix is needed.  On the pinned macOS runtime, the isolated measurement stays at 113 descriptors.  Removing just the two reader ownership scopes as a temporary control makes the test fail with 315 descriptors; restoring them returns the measurement to 113.  The complete Aapt2Tests suite passes all 34 cases.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@simonrozsival
simonrozsival force-pushed the simonrozsival-aapt2-daemon-lifetime branch from fc35a80 to 7b7fced Compare November 12, 2026 16:29
simonrozsival and others added 6 commits November 12, 2026 17:33
Rely on WaitForExit to drain redirected streams and dispose the cancellation registration before diagnostics and process disposal. Cover output draining, cancellation, start failure, and registration retention on successful and exceptional exits.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Preserve the user-directed reduction to cancellation/draining and registration retention on normal and exceptional completion.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Move finite discovery, version, uname, keytool and native strip calls to native process capture APIs with structured arguments and bounded output collection. Preserve discovery partial results and XA0142 strip diagnostics, including timeout cleanup, without retaining unused event or cancellation callback contracts.

Replace obsolete helper lifecycle tests with focused discovery and strip policy regressions. The user-reduced original coverage remains preserved in the prior commits and safety branch.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Line-based capture discards unterminated diagnostics when an exited
strip process leaves inherited pipes open.  Capture both streams as
concurrent character chunks, retaining received text at the deadline.

Exercise the real timeout with an exited root and fixture-owned orphan
holding both streams, including unterminated stdout and stderr.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Wait for the killed inherited-output descendant before deleting its fixture directory.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Drain linker and metadata-tool output concurrently with an independent post-root deadline, preserving MSBuild-controlled active linking and failed-output cleanup. Join readers before disposal and bound live-root termination observation.

Use instance native capture with explicit ownership of both redirected readers at finite probe and keytool owners. Cover inherited EOF, real task cancellation, active linking beyond the drain deadline, and discovery pipe release without finalization.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@simonrozsival
simonrozsival force-pushed the simonrozsival-process-resource-cleanup branch from 2ee131d to b331dd5 Compare November 12, 2026 16:39
@simonrozsival
simonrozsival force-pushed the simonrozsival-aapt2-daemon-lifetime branch from 7b7fced to 8815d04 Compare November 13, 2026 09:26
@simonrozsival
simonrozsival removed this pull request from stack #13517 November 13, 2026 09:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants