os: ByteSize, memory and the current process (OS slice 2) #126

Closed
opened 2026-10-05 21:07:23 +00:00 by orion · 5 comments
Collaborator

Goal

Add ByteSize (a count of bytes with a unit) to std/number and the prelude, make the sandbox use it, and add Memory and Swap to std/os. The current-process half of OS slice 2 is split into #251 and #252.

Source

Current state on main (9889e8f998db8d4289b23fe9ce9441607e986c2e)

  • ByteSize, Memory, Swap do not exist anywhere in code. std/number/src/lib.trb has no byte unit; the prelude re-exports Duration from std/time but nothing for bytes.
  • std/sandbox/src/lib.trb has extend Int64 { fn megabytes(): Self } (10^6) and limits(steps: Int, memory: Int = 64.megabytes(), ...); the field memoryLimit: Int is written into the grant text. Docs and one explanation page tell users to use Int64.megabytes from "std/sandbox".
  • System.pageSize(): Int in std/os/src/system.trb (comment in OS.md: it becomes a ByteSize in this slice); test in std/os/tests/host.test.trb.
  • std/os/src/<system>/ halves exist for windows, linux, macos, posix, bsd, freebsd, browser; runtime/os/{windows,linux,posix,macos,bsd,freebsd}.c hold the natives; no memory native yet.

Decisions taken by the loop

  • ByteSize is a record over one private Int64 exactly as OS.md specifies (traits Show, Compare, Add, Subtract, Multiply<Int64, ByteSize>, Equals through Compare as Duration does), show() in binary units to one decimal (owner-answered in OS.md section 12).
  • The sandbox change has no deprecation shim: a second Int64.megabytes would be ambiguous, which is the argument of OS.md section 6. limits(memory:) takes a ByteSize and the internal memoryLimit stays an Int of bytes (the grant text is unchanged).
  • Memory arms: Windows, Linux and macOS as in the table of section 5.3; .FreeBsd and .Browser answer Fail OsError.Unsupported until the FreeBSD slice (the other slices' pattern); no new case needs a design decision.
  • The Linux and the sysctl/swap parsing are pure functions in the per-system modules, tested on every machine with captured text, as slice 1 did.

Acceptance criteria

  1. ByteSize (std/number, prelude, no import needed):
    • 64.megabytes().bytes() == 64_000_000, 1.mebibytes().bytes() == 1_048_576, every unit of both families on Int64 and every reader (kilobytes() ... tebibytes(), fraction(of:)); sums, differences and ByteSize * Int64 work; a difference may be negative; comparison orders by bytes.
    • show(): 512 B, 1.5 KiB (1536), 15.9 GiB, 0 B, and a negative size keeps its sign; the boundary 1023 B / 1024 B is tested.
    • Tests run in the VM and with --native, platform independent (pure arithmetic).
  2. Sandbox:
    • Int64.megabytes() : Int64 is gone from std/sandbox; SandboxCapabilities.limits(memory: ByteSize = 64.megabytes()); the grant text still carries the byte count (a test reads the default and a custom value); every 64.megabytes() in docs, tests, CONCEPT and examples follows; use Int64.megabytes from "std/sandbox" is removed from the-sandbox.md and mistakes-models-make.md.
  3. System.pageSize(): ByteSize and its test follow.
  4. Memory, Swap (std/os/src/memory.trb, exported from std/os), Memory.current(): Result<Memory, OsError>, used() = total - available, Swap.free():
    • linux-x64 and linux-arm64: /proc/meminfo parser (pure, tested with captured files incl. a pre-3.14 file without MemAvailable, a file without swap, a malformed line -> OsError.Malformed); a live test: total > 0, 0 < available <= total, used() >= 0, swap used <= swap total.
    • macos-arm64: hw.memsize, host_statistics64 pages (free, inactive, speculative, purgeable) times the page size, vm.swapusage; the same live assertions.
    • windows-x64: GlobalMemoryStatusEx and the page files from NtQuerySystemInformation(SystemPageFileInformation) (looked up at the call, as slice 1 does for its Windows natives, so no build names a new library), never the commit limit; the same live assertions.
    • freebsd and browser arms exist and answer Unsupported; torb check --every-target std/os lowers every program and test on all targets (it is a gate of tools/gates.sh a).
    • Doc comments state that available is each system's own estimate and why used() is derived (OS.md pitfall).
  5. Docs: docs/standard-library/number.md, os.md, sandbox.md and the prelude page updated, skills/ regenerated (docs skill --check is a tier A gate); OS.md slice 2 marked done with a short "How slice 2 landed" note, and the status line at its top names slices 0 to 2 (the current-process part marked as moved to #251 and #252 and not done until they close).
  6. CI coverage: linux-x64 runs the gates on the forge for every PR. windows-x64, linux-arm64 and macos-arm64 run the same gates on the forge where a runner of that label is configured (TORB_RUNNERS) and otherwise through the portable workflow on the mirror after landing; the live Memory tests only run on a machine of their system, and the pure parser tests on all of them. The FreeBSD and browser arms are covered by check --every-target only.

Seed

None. The compiler does not use ByteSize, Memory or a std name looked up by string (checked: nothing for it in semantics/checker/wellknown.trb; no megabytes caller in compiler/src), so the seed compiles the new std as it is. New natives are not used by the compiler itself, so they do not take the two-commit rule. If implementation finds the compiler importing limits(memory:), split into two commits: add ByteSize and teach the sandbox both forms, then remove the Int64 form.

Size

One PR, two Conventional Commits: feat(number): add ByteSize and use it in the sandbox (criteria 1 to 3), then feat(os): add Memory and Swap (criterion 4 and docs). The current-process half is split out: #251 (identity, executable, working directory, uptime; takes the seed two-commit rule) and #252 (resources, processors, memory limit).

Dependencies

OS slice 1 (done). #252 depends on this issue (ByteSize) and on #251.

## Goal Add `ByteSize` (a count of bytes with a unit) to `std/number` and the prelude, make the sandbox use it, and add `Memory` and `Swap` to `std/os`. The current-process half of OS slice 2 is split into #251 and #252. ## Source - [OS.md section 4, `ByteSize`](https://git.torb.dev/torbscript/language/src/commit/9889e8f998db8d4289b23fe9ce9441607e986c2e/docs/design/OS.md#L584) - [OS.md section 5.3, Memory](https://git.torb.dev/torbscript/language/src/commit/9889e8f998db8d4289b23fe9ce9441607e986c2e/docs/design/OS.md#L952) - [OS.md section 6, the sandbox's `megabytes`](https://git.torb.dev/torbscript/language/src/commit/9889e8f998db8d4289b23fe9ce9441607e986c2e/docs/design/OS.md#L1392) - [OS.md slice 2](https://git.torb.dev/torbscript/language/src/commit/9889e8f998db8d4289b23fe9ce9441607e986c2e/docs/design/OS.md#L1622) - [PROJECT.md, "`megabytes()` moves"](https://git.torb.dev/torbscript/language/src/commit/9889e8f998db8d4289b23fe9ce9441607e986c2e/docs/design/PROJECT.md#L1064) (same move, wanted by the resources budget) ## Current state on main (`9889e8f998db8d4289b23fe9ce9441607e986c2e`) - `ByteSize`, `Memory`, `Swap` do not exist anywhere in code. `std/number/src/lib.trb` has no byte unit; the prelude re-exports `Duration` from `std/time` but nothing for bytes. - `std/sandbox/src/lib.trb` has `extend Int64 { fn megabytes(): Self }` (10^6) and `limits(steps: Int, memory: Int = 64.megabytes(), ...)`; the field `memoryLimit: Int` is written into the grant text. Docs and one explanation page tell users to `use Int64.megabytes from "std/sandbox"`. - `System.pageSize(): Int` in `std/os/src/system.trb` (comment in OS.md: it becomes a `ByteSize` in this slice); test in `std/os/tests/host.test.trb`. - `std/os/src/<system>/` halves exist for windows, linux, macos, posix, bsd, freebsd, browser; `runtime/os/{windows,linux,posix,macos,bsd,freebsd}.c` hold the natives; no memory native yet. ## Decisions taken by the loop - `ByteSize` is a record over one private `Int64` exactly as OS.md specifies (traits `Show, Compare, Add, Subtract, Multiply<Int64, ByteSize>`, `Equals` through `Compare` as `Duration` does), `show()` in binary units to one decimal (owner-answered in OS.md section 12). - The sandbox change has no deprecation shim: a second `Int64.megabytes` would be ambiguous, which is the argument of OS.md section 6. `limits(memory:)` takes a `ByteSize` and the internal `memoryLimit` stays an `Int` of bytes (the grant text is unchanged). - Memory arms: Windows, Linux and macOS as in the table of section 5.3; `.FreeBsd` and `.Browser` answer `Fail OsError.Unsupported` until the FreeBSD slice (the other slices' pattern); no new case needs a design decision. - The Linux and the sysctl/swap parsing are pure functions in the per-system modules, tested on every machine with captured text, as slice 1 did. ## Acceptance criteria 1. `ByteSize` (std/number, prelude, no import needed): - [ ] `64.megabytes().bytes() == 64_000_000`, `1.mebibytes().bytes() == 1_048_576`, every unit of both families on `Int64` and every reader (`kilobytes() ... tebibytes()`, `fraction(of:)`); sums, differences and `ByteSize * Int64` work; a difference may be negative; comparison orders by bytes. - [ ] `show()`: `512 B`, `1.5 KiB` (1536), `15.9 GiB`, `0 B`, and a negative size keeps its sign; the boundary 1023 B / 1024 B is tested. - [ ] Tests run in the VM and with `--native`, platform independent (pure arithmetic). 2. Sandbox: - [ ] `Int64.megabytes() : Int64` is gone from `std/sandbox`; `SandboxCapabilities.limits(memory: ByteSize = 64.megabytes())`; the grant text still carries the byte count (a test reads the default and a custom value); every `64.megabytes()` in docs, tests, CONCEPT and examples follows; `use Int64.megabytes from "std/sandbox"` is removed from the-sandbox.md and mistakes-models-make.md. 3. `System.pageSize(): ByteSize` and its test follow. 4. `Memory`, `Swap` (`std/os/src/memory.trb`, exported from `std/os`), `Memory.current(): Result<Memory, OsError>`, `used() = total - available`, `Swap.free()`: - [ ] linux-x64 and linux-arm64: `/proc/meminfo` parser (pure, tested with captured files incl. a pre-3.14 file without `MemAvailable`, a file without swap, a malformed line -> `OsError.Malformed`); a live test: `total > 0`, `0 < available <= total`, `used() >= 0`, swap used <= swap total. - [ ] macos-arm64: `hw.memsize`, `host_statistics64` pages (free, inactive, speculative, purgeable) times the page size, `vm.swapusage`; the same live assertions. - [ ] windows-x64: `GlobalMemoryStatusEx` and the page files from `NtQuerySystemInformation(SystemPageFileInformation)` (looked up at the call, as slice 1 does for its Windows natives, so no build names a new library), never the commit limit; the same live assertions. - [ ] freebsd and browser arms exist and answer `Unsupported`; `torb check --every-target std/os` lowers every program and test on all targets (it is a gate of `tools/gates.sh a`). - [ ] Doc comments state that `available` is each system's own estimate and why `used()` is derived (OS.md pitfall). 5. Docs: `docs/standard-library/number.md`, `os.md`, `sandbox.md` and the prelude page updated, `skills/` regenerated (`docs skill --check` is a tier A gate); OS.md slice 2 marked done with a short "How slice 2 landed" note, and the status line at its top names slices 0 to 2 (the current-process part marked as moved to #251 and #252 and not done until they close). 6. CI coverage: linux-x64 runs the gates on the forge for every PR. windows-x64, linux-arm64 and macos-arm64 run the same gates on the forge where a runner of that label is configured (`TORB_RUNNERS`) and otherwise through the portable workflow on the mirror after landing; the live Memory tests only run on a machine of their system, and the pure parser tests on all of them. The FreeBSD and browser arms are covered by `check --every-target` only. ## Seed None. The compiler does not use `ByteSize`, `Memory` or a std name looked up by string (checked: nothing for it in `semantics/checker/wellknown.trb`; no `megabytes` caller in `compiler/src`), so the seed compiles the new std as it is. New natives are not used by the compiler itself, so they do not take the two-commit rule. If implementation finds the compiler importing `limits(memory:)`, split into two commits: add `ByteSize` and teach the sandbox both forms, then remove the `Int64` form. ## Size One PR, two Conventional Commits: `feat(number): add ByteSize and use it in the sandbox` (criteria 1 to 3), then `feat(os): add Memory and Swap` (criterion 4 and docs). The current-process half is split out: #251 (identity, executable, working directory, uptime; takes the seed two-commit rule) and #252 (resources, processors, memory limit). ## Dependencies OS slice 1 (done). #252 depends on this issue (`ByteSize`) and on #251.
Author
Collaborator

[loop:torbscript] intake: in scope (author orion, label loop), source OS.md slice 2, not a duplicate. No dependencies; needs every supported system. next: waiting (refine queued, ready buffer full) until 2026-10-10T04:00:00Z

[loop:torbscript] intake: in scope (author orion, label loop), source OS.md slice 2, not a duplicate. No dependencies; needs every supported system. next: waiting (refine queued, ready buffer full) until 2026-10-10T04:00:00Z
Author
Collaborator

[loop:torbscript] claim refine by session:loop-torbscript-20261009 until 2026-10-09T08:14Z

[loop:torbscript] claim refine by session:loop-torbscript-20261009 until 2026-10-09T08:14Z
Author
Collaborator

[loop:torbscript] refine: rewrote the body to goal, pinned sources, state on main, per-platform acceptance criteria (CI coverage per target), seed impact, size and dependencies. The current-process half of slice 2 was too large for this PR and is split into #251 (identity, executable move with the seed two-commit rule, working directory, uptime) and #252 (resources, processors, memory limit); this issue keeps ByteSize, the sandbox change, System.pageSize and Memory/Swap.

[loop:torbscript] decided by the loop: (1) the sandbox's Int64.megabytes is removed without a shim, because two members of one name on Int64 would be ambiguous (OS.md section 6 already specifies it; the sandbox natives are planned, so no behaviour changes). (2) Memory on FreeBSD and the browser answers Unsupported until the FreeBSD slice, as the other slices do. (3) No seed refresh: the compiler uses none of the new names; the executable move, which it does use, is in #251. (4) Parsers are pure and tested with captured text on every machine; live tests only on their own system.

next: implement

[loop:torbscript] refine: rewrote the body to goal, pinned sources, state on main, per-platform acceptance criteria (CI coverage per target), seed impact, size and dependencies. The current-process half of slice 2 was too large for this PR and is split into #251 (identity, executable move with the seed two-commit rule, working directory, uptime) and #252 (resources, processors, memory limit); this issue keeps `ByteSize`, the sandbox change, `System.pageSize` and `Memory`/`Swap`. [loop:torbscript] decided by the loop: (1) the sandbox's `Int64.megabytes` is removed without a shim, because two members of one name on `Int64` would be ambiguous (OS.md section 6 already specifies it; the sandbox natives are planned, so no behaviour changes). (2) `Memory` on FreeBSD and the browser answers `Unsupported` until the FreeBSD slice, as the other slices do. (3) No seed refresh: the compiler uses none of the new names; the executable move, which it does use, is in #251. (4) Parsers are pure and tested with captured text on every machine; live tests only on their own system. next: implement
Author
Collaborator

[loop:torbscript] claim implement by session:loop-torbscript-20261009 until 2026-10-09T12:17Z

[loop:torbscript] claim implement by session:loop-torbscript-20261009 until 2026-10-09T12:17Z
orion referenced this issue from a commit 2026-10-09 06:44:32 +00:00
orion referenced this issue from a commit 2026-10-09 07:16:27 +00:00
orion referenced this issue from a commit 2026-10-09 07:43:01 +00:00
orion referenced this issue from a commit 2026-10-09 08:18:12 +00:00
argus closed this issue 2026-10-09 08:48:30 +00:00
argus referenced this issue from a commit 2026-10-09 08:48:30 +00:00
Author
Collaborator

[loop:torbscript] close: PR #255 (ByteSize and Memory) merged by argus (merge commit 319caaf8), issue closed. Claim released. Follow-ups #251 and #252 are unblocked.

[loop:torbscript] close: PR #255 (ByteSize and Memory) merged by argus (merge commit 319caaf8), issue closed. Claim released. Follow-ups #251 and #252 are unblocked.
Sign in to join this conversation.
No labels
loop
ready
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
torbscript/language#126
No description provided.