Conversation
The blocking and async clients were two near-copies of the same ~980 lines, and they had drifted. Async `ps` folded stderr into the JSON it then parsed; async `run`/`start` returned `Result<()>` where the blocking client returned `Result<Response>`; `create` reported an IO setup failure as two different error variants depending on the flavor; only one side had `events()` or honored `THP_DISABLED`. Async temp-file cleanup was a hand-rolled `tc!` macro that leaked the spec file whenever a future was dropped. Write the client once instead. `runc_impl!` is invoked with the `async` keyword or with nothing, and `maybe_await!` expands to `.await` or to nothing, so the whole of `impl Runc` -- all sixteen commands plus the launch path -- lives in lib.rs with no `cfg` and nothing boxed. The macro interpolates the keyword rather than parsing signatures, so generics, where-clauses and lifetimes pass through untouched. Only `Spawner`, `Io` and `Pipe` remain flavor-specific, since their signatures genuinely differ. Alongside that: - Fold `command()` into `launch_io`, so assembling argv, wiring up the Io driver and spawning are one function. Restoring `THP_DISABLED` moves there too, which means it now applies to every `Spawner` rather than only `DefaultExecutor` -- `ShimExecutor`, the one the shim actually installs, had silently stopped restoring it. - Read `THP_DISABLED` in the parent and hand the `pre_exec` closure only a `bool`. The hook previously called `env::var` and `log::debug!` post-fork, neither of which is async-signal-safe: a fork racing another thread's `env::set_var` would deadlock the child. Register the hook only when the knob is set, so the common path keeps the `posix_spawn` fast path. - Replace the `combined_output` bool with an `Output` enum, and fix `list`, `state` and `stats`, which deserialized JSON out of stdout+stderr. - Give both builds one RAII temp-file helper, dropping the `tc!` macro, the async `write_value_to_temp_file` and the `uuid` dependency. - Merge the two test modules into one covering the union of what each had, so the blocking client gains `start` and the async client gains `exec`. Async `run` and `start` now return `Result<Response>`, matching the blocking client; the crate version should be bumped before the next publish. Signed-off-by: Maksym Pavlenko <pavlenko.maksym@gmail.com>
There was a problem hiding this comment.
Pull request overview
Refactors the runc crate to share a single Runc client implementation between the synchronous and asynchronous builds, reducing drift and aligning behavior across both flavors.
Changes:
- Centralizes the
Runccommand execution path inlib.rsviarunc_impl!+maybe_await!, including consistent stdout/stderr handling through anOutputenum. - Unifies spec/resource temp-file creation into a single RAII helper and removes the
uuiddependency. - Consolidates sync/async tests into one shared test module expanded with the appropriate test attribute.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| crates/runc/src/utils.rs | Switches temp-file creation to RAII NamedTempFile for both builds and adds a THP_DISABLED restore hook builder. |
| crates/runc/src/lib.rs | Introduces the shared sync/async Runc implementation, common launch path, output mode enum, and unified tests. |
| crates/runc/src/synchronous/runc.rs | Removes the duplicate sync Runc implementation, leaving only sync Spawner code. |
| crates/runc/src/asynchronous/runc.rs | Removes the duplicate async Runc implementation and temp-file cleanup macro, leaving only async Spawner code. |
| crates/runc/Cargo.toml | Drops the uuid dependency now that temp-file naming uses tempfile’s randomness. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: mxpv <865334+mxpv@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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
The blocking and async clients were two near-copies of the same ~980 lines, and they had drifted. Async
psfolded stderr into the JSON it then parsed; asyncrun/startreturnedResult<()>where the blocking client returnedResult<Response>;createreported an IO setup failure as two different error variants depending on the flavor; only one side hadevents()or honoredTHP_DISABLED. Async temp-file cleanup was a hand-rolledtc!macro that leaked the spec file whenever a future was dropped.Write the client once instead.
runc_impl!is invoked with theasynckeyword or with nothing, andmaybe_await!expands to.awaitor to nothing, so the whole ofimpl Runc-- all sixteen commands plus the launch path -- lives in lib.rs with nocfgand nothing boxed. The macro interpolates the keyword rather than parsing signatures, so generics, where-clauses and lifetimes pass through untouched. OnlySpawner,IoandPiperemain flavor-specific, since their signatures genuinely differ.Alongside that:
Fold
command()intolaunch_io, so assembling argv, wiring up the Io driver and spawning are one function. RestoringTHP_DISABLEDmoves there too, which means it now applies to everySpawnerrather than onlyDefaultExecutor--ShimExecutor, the one the shim actually installs, had silently stopped restoring it.Read
THP_DISABLEDin the parent and hand thepre_execclosure only abool. The hook previously calledenv::varandlog::debug!post-fork, neither of which is async-signal-safe: a fork racing another thread'senv::set_varwould deadlock the child. Register the hook only when the knob is set, so the common path keeps theposix_spawnfast path.Replace the
combined_outputbool with anOutputenum, and fixlist,stateandstats, which deserialized JSON out of stdout+stderr.Give both builds one RAII temp-file helper, dropping the
tc!macro, the asyncwrite_value_to_temp_fileand theuuiddependency.Merge the two test modules into one covering the union of what each had, so the blocking client gains
startand the async client gainsexec.Async
runandstartnow returnResult<Response>, matching the blocking client; the crate version should be bumped before the next publish.