Skip to content

FEATURE: Track resource usage in Landlock.capture - #5

Closed
tgxworld wants to merge 3 commits into
mainfrom
tgxworld/image-processing-resource-usage
Closed

FEATURE: Track resource usage in Landlock.capture#5
tgxworld wants to merge 3 commits into
mainfrom
tgxworld/image-processing-resource-usage

Conversation

@tgxworld

@tgxworld tgxworld commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Capture results previously exposed output and status without reporting how long the command ran or which CPU and memory resources it consumed. Callers could not measure sandboxed command cost, and process-wide accounting could not safely distinguish concurrent captures.

This commit reaps each capture's direct process with native wait4, collecting its matching status and rusage atomically. The native helper is replaced by the target command with exec, so the helper and target keep the same PID and are measured as one process. This accounting is isolated from unrelated children running concurrently; it is not a general process-tree profiler, and descendant accounting follows the operating system's wait4 semantics.

Native waits release the GVL and remain interruptible, while capture cleanup uses nonblocking polling so other Ruby threads can continue running.

CaptureResult now exposes monotonic elapsed time and an immutable ResourceUsage value for successful and failed commands, with Linux peak RSS normalized to bytes:

result = Landlock.capture(
  ["magick", input_path, "-resize", "800x800>", output_path],
  read: [input_path, "/usr", "/lib", "/lib64"],
  write: [output_path],
  execute: ["/usr", "/lib", "/lib64"]
)

usage = result.resource_usage

result.elapsed_seconds    # Wall-clock seconds
usage.user_seconds        # CPU seconds spent in user mode
usage.system_seconds      # CPU seconds spent in kernel mode
usage.cpu_seconds         # user_seconds + system_seconds
usage.max_rss_bytes       # Peak resident memory in bytes

For Landlock.capture! failures, the same measurements are available through error.result:

begin
  Landlock.capture!(command, rlimits: { cpu_seconds: 5 })
rescue Landlock::CommandError => error
  error.result.elapsed_seconds
  error.result.resource_usage.cpu_seconds
end

Once a process is reaped, retained descendant pipes cannot replace its status, usage, or completion time during timeout cleanup.

The existing three-value destructuring contract remains unchanged, and capture! errors continue to carry the complete result.

Capture results previously exposed output and status without reporting how long the child ran or which CPU and memory resources it consumed. Callers could not measure sandboxed command cost, and process-wide accounting could not safely distinguish concurrent children.

This commit reaps capture children with native `wait4`, collecting the matching status and `rusage` atomically. Native waits release the GVL and remain interruptible, while capture cleanup uses nonblocking polling so other Ruby threads can continue running.

`CaptureResult` now exposes monotonic elapsed time and an immutable `ResourceUsage` value for successful and failed commands, with Linux peak RSS normalized to bytes. Once a child is reaped, retained descendant pipes cannot replace its status, usage, or completion time during timeout cleanup.

The existing three-value destructuring contract remains unchanged, and `capture!` errors continue to carry the complete result.
@tgxworld
tgxworld marked this pull request as ready for review August 20, 2026 04:00
The changelog named the new measurements but did not show callers how to access them or clarify which process they describe.

This commit adds successful and failed capture examples and describes the accounting as belonging to the direct process launched by each capture.
@tgxworld tgxworld closed this Aug 20, 2026
@tgxworld
tgxworld deleted the tgxworld/image-processing-resource-usage branch August 20, 2026 07:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant