You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Fix orphaned requirement and bound clang subprocess timeout - #49
Fixes a reqstream traceability error: ApiMarkDotNet-DotNetEmitter-RenderConstantFieldValue was missing from the system-level requirement's children list, so
eqstream --enforce reported it as orphaned.
Fixes a CI reliability issue: ClangAstParser.RunProcess had no timeout on the clang/xcrun subprocess wait, so a hung invocation (e.g. Render compile-time constant values in C# and C++ emitters #48's macOS job, cancelled after 47 minutes) could block the entire CI job indefinitely.
Details
Requirement traceability
Added \ApiMarkDotNet-DotNetEmitter-RenderConstantFieldValue\ to the \children\ list of \ApiMarkDotNet-GenerateDocumentationFromAssembliesAndXml\ in \docs/reqstream/api-mark-dot-net.yaml. Verified with \dotnet reqstream --requirements requirements.yaml --enforce\ — no orphan/error output.
Clang subprocess timeout
\RunProcess\ now bounds \WaitForExit\ to 120 seconds. On timeout it kills the process tree (reusing the existing \TryKillProcess\ helper used by the xcrun discovery probe) and throws \InvalidOperationException\ carrying:
OS description/architecture
Process ID
Elapsed time
Full command line
Partial stdout/stderr already produced (bounded to 2000 chars, captured with its own grace period so a dead pipe can't re-introduce a hang)
Testing
\dotnet reqstream --requirements requirements.yaml --enforce\ — passes, no orphans.
\dotnet test test\ApiMark.Cpp.Tests\ApiMark.Cpp.Tests.csproj -c Release -f net8.0\ with a real LLVM 22.1.8 install on PATH: 177 passed, 0 failed, 14 skipped (unchanged from baseline without this change).
\ix.ps1\ auto-fix pass run — no additional changes needed.
The ApiMarkDotNet system-level requirement's children list was missing
ApiMarkDotNet-DotNetEmitter-RenderConstantFieldValue, leaving it
unreachable from any requirement tagged system/quality and causing
reqstream --enforce to report it as orphaned.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ClangAstParser.RunProcess called Process.WaitForExit() with no timeout,
so a hung clang/xcrun invocation (observed on macOS CI, e.g. run 37162361228
which had to be manually cancelled after 47 minutes) would block the
calling test indefinitely, and transitively the whole CI job.
RunProcess now waits up to 120 seconds and, on timeout, kills the process
tree (reusing the existing TryKillProcess helper) and throws an
InvalidOperationException carrying the OS description/architecture, PID,
elapsed time, full command line, and any partial stdout/stderr the process
had already produced -- so a future hang can be diagnosed from CI output
alone.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Addresses PR review feedback on #49: the timeout behavior added in
e42b871 had no automated test coverage. ClangAstParser.RunProcess now
reads a thread-local TimeoutOverrideMillisecondsForTests field (reset
by the test in a finally block to avoid thread-pool reuse leaking it
into unrelated tests) so a test can exercise the kill/diagnostics path
with a short wait instead of the real 120-second production timeout.
Added ClangAstParser_Parse_ClangHangs_ThrowsWithDiagnosticsAndKillsProcess,
which points ClangPath at a small platform-appropriate script that
ignores its arguments and sleeps far longer than the test timeout, then
asserts the thrown InvalidOperationException carries each diagnostic
field (OS, PID, elapsed time, command line, partial stdout/stderr).
Re: the reported 'invalid string.Contains overload for net8.0' finding —
verified this is a false positive. Contains(char, StringComparison) has
been part of the BCL since .NET 5 and ApiMark.Cpp targets net8.0 only
(confirmed via a clean --no-incremental rebuild with 0 warnings/errors,
TreatWarningsAsErrors is enabled for this project).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds CppGeneratorOptions.ClangTimeoutMilliseconds, --clang-timeout-ms CLI flag, ApiMarkClangTimeoutMs MSBuild property, and APIMARK_CLANG_TIMEOUT_MS env var override for the clang AST-dump subprocess timeout introduced in PR #49. Precedence: test seam > explicit option > env var > hard-coded 120000ms default. Includes tests, reqstream/design/verification docs, and user guide updates.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…, drop real-clang dependency
- Add ClangEnvironmentCollection to serialize ClangAstParserTests and
CppGeneratorTests (both mutate/depend on APIMARK_CLANG_TIMEOUT_MS and
invoke real clang), preventing cross-test races on the process-wide
environment.
- Restore the original APIMARK_CLANG_TIMEOUT_MS value in test finally
blocks instead of unconditionally clearing it, so a pre-set value on
a CI runner is never lost.
- Rewrite the invalid-timeout-value test to use the hanging-script
ClangPath seam instead of BuildOptions()/real fixture headers, since
ResolveClangTimeoutMilliseconds runs before the clang process is ever
started; the test no longer depends on clang being installed and no
longer needs an IsClangAvailable() skip guard.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ReadToEndAsync buffers unbounded stdout and stderr
src/ApiMark.Cpp/CppAst/ClangAstParser.cs:843
Truncate limits only the exception text, not the ReadToEndAsync buffers that have already accumulated the complete stream. A hung/chatty clang process can therefore keep allocating stdout/stderr data for the entire timeout (especially when the timeout is raised), potentially exhausting CI memory before this line runs. Drain each pipe continuously while retaining only the first 2000 characters (and a truncation flag) for diagnostics.
Grandchild processes survive when parent exits during pipe draining
src/ApiMark.Cpp/CppAst/ClangAstParser.cs:934
When the parent exits but a grandchild keeps a redirected pipe open, this helper throws after the grace period without terminating that grandchild; RunProcess only calls TryKillProcess in the parent-timeout branch, and the parent is already exited here. A wrapper around clang can therefore leave an indefinitely running child behind on this failure path. Track and terminate inherited pipe holders before throwing, or otherwise make the wrapper/process-tree cleanup cover this case.
…ze DocumentationCoverageCheckerTests
The macOS CI run for 2c6342f surfaced two real bugs not caught locally:
- GetOutputOrThrow used the same 2s PartialOutputGraceMilliseconds grace
period as the post-kill partial-output path, but that budget is only
appropriate once a process is already known to be stuck. On the
success path (process exited within its timeout), a real clang
invocation legitimately draining a large AST dump under CI load can
take longer than 2s to finish flushing its redirected stdout pipe,
which was incorrectly reported as a hung grandchild process. Split
this into a new, much larger SuccessPathOutputGraceMilliseconds
(30s) used only on the success path; the short 2s grace remains for
the post-kill case where fast-fail matters.
- DocumentationCoverageCheckerTests invokes CppGenerator.Parse (real
clang) directly but was not part of the ClangEnvironment collection,
so it could run concurrently with ClangAstParserTests setting
APIMARK_CLANG_TIMEOUT_MS to an intentionally invalid test value,
poisoning its clang invocation. Added it to the collection.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
eqstream --enforce reported it as orphaned.
Details
Requirement traceability
Added \ApiMarkDotNet-DotNetEmitter-RenderConstantFieldValue\ to the \children\ list of \ApiMarkDotNet-GenerateDocumentationFromAssembliesAndXml\ in \docs/reqstream/api-mark-dot-net.yaml. Verified with \dotnet reqstream --requirements requirements.yaml --enforce\ — no orphan/error output.
Clang subprocess timeout
\RunProcess\ now bounds \WaitForExit\ to 120 seconds. On timeout it kills the process tree (reusing the existing \TryKillProcess\ helper used by the xcrun discovery probe) and throws \InvalidOperationException\ carrying:
Testing
Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com