From 54dad3b398af8bfa37565e0a0438f7f04a3f2dbb Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Sun, 28 Jun 2026 13:53:53 +0100 Subject: [PATCH] implements PLN-0123 async work lane specs --- discussion/index.ndjson | 2 +- ...p-home-filesystem-surface-and-semantics.md | 5 + ...ync-work-lane-specification-propagation.md | 2 +- .../runtime/09-events-and-concurrency.md | 28 +++- docs/specs/runtime/15-asset-management.md | 126 +++++++++++++++++- .../specs/runtime/16-host-abi-and-syscalls.md | 18 +++ docs/specs/runtime/16a-syscall-policies.md | 8 ++ 7 files changed, 183 insertions(+), 6 deletions(-) diff --git a/discussion/index.ndjson b/discussion/index.ndjson index eb2097e5..8ac7a833 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -18,7 +18,7 @@ {"type":"discussion","id":"DSC-0006","status":"open","ticket":"vm-owned-random-service","title":"Agenda - VM-Owned Random Service","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0005","file":"AGD-0005-vm-owned-random-service.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0007","status":"open","ticket":"app-home-filesystem-surface-and-semantics","title":"Agenda - App Home Filesystem Surface and Semantics","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0006","file":"AGD-0006-app-home-filesystem-surface-and-semantics.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0008","status":"done","ticket":"perf-runtime-telemetry-hot-path","title":"Agenda - [PERF] Runtime Telemetry Hot Path","created_at":"2026-03-27","updated_at":"2026-06-04","tags":[],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0026","file":"discussion/lessons/DSC-0008-perf-runtime-telemetry-hot-path/LSN-0026-push-based-telemetry-model.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} -{"type":"discussion","id":"DSC-0009","status":"in_progress","ticket":"perf-async-background-work-lanes-for-assets-and-fs","title":"Agenda - [PERF] Async Background Work Lanes for Assets and FS","created_at":"2026-03-27","updated_at":"2026-06-28","tags":["perf","asset","fs","async","scheduler","runtime"],"agendas":[{"id":"AGD-0008","file":"AGD-0008-perf-async-background-work-lanes-for-assets-and-fs.md","status":"accepted","created_at":"2026-03-27","updated_at":"2026-06-28"}],"decisions":[{"id":"DEC-0034","file":"DEC-0034-async-work-lane-and-asset-backlog-contract.md","status":"accepted","created_at":"2026-06-28","updated_at":"2026-06-28","ref_agenda":"AGD-0008"}],"plans":[{"id":"PLN-0123","file":"PLN-0123-async-work-lane-specification-propagation.md","status":"open","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]},{"id":"PLN-0124","file":"PLN-0124-async-work-lane-runtime-infrastructure.md","status":"open","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]},{"id":"PLN-0125","file":"PLN-0125-asset-backlog-and-stable-slot-handles.md","status":"open","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]},{"id":"PLN-0126","file":"PLN-0126-asset-backlog-public-api-and-status-surface.md","status":"open","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]},{"id":"PLN-0127","file":"PLN-0127-async-lane-memcard-and-fs-integration-boundaries.md","status":"open","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]},{"id":"PLN-0128","file":"PLN-0128-async-asset-pipeline-contract-tests-and-telemetry.md","status":"open","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]}],"lessons":[]} +{"type":"discussion","id":"DSC-0009","status":"in_progress","ticket":"perf-async-background-work-lanes-for-assets-and-fs","title":"Agenda - [PERF] Async Background Work Lanes for Assets and FS","created_at":"2026-03-27","updated_at":"2026-06-28","tags":["perf","asset","fs","async","scheduler","runtime"],"agendas":[{"id":"AGD-0008","file":"AGD-0008-perf-async-background-work-lanes-for-assets-and-fs.md","status":"accepted","created_at":"2026-03-27","updated_at":"2026-06-28"}],"decisions":[{"id":"DEC-0034","file":"DEC-0034-async-work-lane-and-asset-backlog-contract.md","status":"accepted","created_at":"2026-06-28","updated_at":"2026-06-28","ref_agenda":"AGD-0008"}],"plans":[{"id":"PLN-0123","file":"PLN-0123-async-work-lane-specification-propagation.md","status":"done","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]},{"id":"PLN-0124","file":"PLN-0124-async-work-lane-runtime-infrastructure.md","status":"open","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]},{"id":"PLN-0125","file":"PLN-0125-asset-backlog-and-stable-slot-handles.md","status":"open","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]},{"id":"PLN-0126","file":"PLN-0126-asset-backlog-public-api-and-status-surface.md","status":"open","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]},{"id":"PLN-0127","file":"PLN-0127-async-lane-memcard-and-fs-integration-boundaries.md","status":"open","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]},{"id":"PLN-0128","file":"PLN-0128-async-asset-pipeline-contract-tests-and-telemetry.md","status":"open","created_at":"2026-06-28","updated_at":"2026-06-28","ref_decisions":["DEC-0034"]}],"lessons":[]} {"type":"discussion","id":"DSC-0010","status":"done","ticket":"perf-host-desktop-frame-pacing-and-presentation","title":"Agenda - [PERF] Host Desktop Frame Pacing and Presentation","created_at":"2026-03-27","updated_at":"2026-04-20","tags":[],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0036","file":"discussion/lessons/DSC-0010-perf-host-desktop-frame-pacing-and-presentation/LSN-0036-frame-publication-and-host-invalidation-must-be-separate.md","status":"done","created_at":"2026-04-20","updated_at":"2026-04-20"}]} {"type":"discussion","id":"DSC-0011","status":"abandoned","ticket":"perf-gfx-render-pipeline-and-dirty-regions","title":"Agenda - [PERF] GFX Render Pipeline and Dirty Regions","created_at":"2026-03-27","updated_at":"2026-06-28","tags":[],"agendas":[{"id":"AGD-0010","file":"AGD-0010-perf-gfx-render-pipeline-and-dirty-regions.md","status":"abandoned","created_at":"2026-03-27","updated_at":"2026-06-28","_override_reason":"User explicitly chose to remove this agenda because it no longer applies to the current render scenario; future optimizations can be discussed when needed."}],"decisions":[],"plans":[],"lessons":[],"_override_reason":"User explicitly chose to abandon this discussion because it no longer applies to the current render scenario; future optimizations can be discussed when needed."} {"type":"discussion","id":"DSC-0012","status":"done","ticket":"perf-runtime-introspection-syscalls","title":"Agenda - [PERF] Runtime Introspection Syscalls","created_at":"2026-03-27","updated_at":"2026-04-19","tags":["perf","runtime","syscall","telemetry","debug","asset"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0034","file":"discussion/lessons/DSC-0012-perf-runtime-introspection-syscalls/LSN-0034-host-owned-debug-boundaries.md","status":"done","created_at":"2026-04-19","updated_at":"2026-04-19"}]} diff --git a/discussion/workflow/agendas/AGD-0006-app-home-filesystem-surface-and-semantics.md b/discussion/workflow/agendas/AGD-0006-app-home-filesystem-surface-and-semantics.md index 7ff84a23..01bf40cc 100644 --- a/discussion/workflow/agendas/AGD-0006-app-home-filesystem-surface-and-semantics.md +++ b/discussion/workflow/agendas/AGD-0006-app-home-filesystem-surface-and-semantics.md @@ -22,6 +22,9 @@ Sem um contrato claro de `home` por app, a API tende a crescer com semantica inc 1. Todo `app` acessa somente sua `home` logica. 2. Nunca ha acesso direto ao filesystem global do host pela userland. 3. O runtime `fs` interno continua cobrindo tanto `game` quanto `app`. +4. Existe uma async IO lane compartilhavel por assets, memcard e FS. Esta + agenda deve decidir a API publica de FS considerando essa lane, mas a + existencia da lane ja esta fechada por `DEC-0034`. ## Alvo da Discussao @@ -116,6 +119,8 @@ No perfil `app` (`home` sandbox), esta agenda passa a ser a fonte normativa para 2. `rename` entra no MVP ou pode ficar para fase seguinte? 3. Qual conjunto minimo de metadados garante portabilidade real entre hosts? 4. Qual grau de atomicidade e obrigatorio para escrita de arquivo no v1? +5. Quais operacoes de FS devem consumir a async IO lane e quais permanecem + sincrono-aparentes para a userland? ## Dependencias diff --git a/discussion/workflow/plans/PLN-0123-async-work-lane-specification-propagation.md b/discussion/workflow/plans/PLN-0123-async-work-lane-specification-propagation.md index f3277ed8..9fa544cf 100644 --- a/discussion/workflow/plans/PLN-0123-async-work-lane-specification-propagation.md +++ b/discussion/workflow/plans/PLN-0123-async-work-lane-specification-propagation.md @@ -2,7 +2,7 @@ id: PLN-0123 ticket: perf-async-background-work-lanes-for-assets-and-fs title: Async Work Lane Specification Propagation -status: open +status: done created: 2026-06-28 completed: ref_decisions: [DEC-0034] diff --git a/docs/specs/runtime/09-events-and-concurrency.md b/docs/specs/runtime/09-events-and-concurrency.md index 9337ad90..497c25c0 100644 --- a/docs/specs/runtime/09-events-and-concurrency.md +++ b/docs/specs/runtime/09-events-and-concurrency.md @@ -127,7 +127,31 @@ Shutdown is explicit and bounded. A shutdown request wakes a waiting worker, causes pending work that will not be consumed to be discarded, and reports a typed failure if the worker cannot join within the configured timeout. -## 8 Determinism and Best Practices +## 8 Async Asset and IO Work Lane + +The asset/IO async work lane is not a machine-visible event source and does not +introduce guest callbacks. It is an implementation-side lane for asset +IO/decode/materialization and compatible persistence IO work. + +The lane is serial: + +- it has at most one active job; +- it keeps an ordered backlog; +- it is separate from the render worker; +- it must not create one OS thread per guest-visible asset request. + +Asset jobs are keyed by target `bank_type/slot`. A newer request for the same +target supersedes the older request. Superseding is an operational status and +does not execute guest code. + +Asset install/commit into resident banks happens on the main runtime lane at +predictable ownership points. The async lane prepares materialized results; it +does not publish resident graphics/audio/scene state directly. + +FS and game persistence services may consume this lane for IO-style work, but +public FS API shape is defined by the FS/app-home contract, not by this chapter. + +## 9 Determinism and Best Practices PROMETEU encourages: @@ -142,7 +166,7 @@ PROMETEU discourages: - hidden timing channels; - ambiguous out-of-band execution. -## 9 Relationship to Other Specs +## 10 Relationship to Other Specs - [`09a-coroutines-and-cooperative-scheduling.md`](09a-coroutines-and-cooperative-scheduling.md) defines coroutine lifecycle and scheduling behavior. - [`10-debug-inspection-and-profiling.md`](10-debug-inspection-and-profiling.md) defines observability and diagnostics surfaces. diff --git a/docs/specs/runtime/15-asset-management.md b/docs/specs/runtime/15-asset-management.md index d2e52306..699c499d 100644 --- a/docs/specs/runtime/15-asset-management.md +++ b/docs/specs/runtime/15-asset-management.md @@ -393,14 +393,129 @@ Fault boundary: Rules: -- `handle` is valid only when `load` status is `OK`; +- `handle` is returned when `load` status is `OK`; +- `handle` represents a stable bank slot target, not a worker thread; +- a known handle remains queryable even when the slot is empty, has no active + request, or has a superseded request; - failed `load` returns `handle = 0`; - `commit` and `cancel` must not be silent no-op for unknown/invalid handle state. - `asset.load` resolves the target bank type from `asset_table` using `asset_id`; - public callers must not supply `asset_name` or `bank_type` to `asset.load`; - slot validation and residency/lifecycle rejection remain in `asset` status space and are not delegated to `bank`. -### 11.2 Minimum status tables +### 11.2 Async work lane and backlog + +Asset loading uses the runtime async work lane. This lane is separate from the +VM/main lane and separate from the render worker lane. + +The asset async lane is serial: + +- it executes at most one active asset job at a time; +- it keeps an ordered backlog of pending requests; +- it must not create one OS thread per `asset.load` request. + +Asset requests are keyed by the target `bank_type/slot`. + +Rules: + +- each `bank_type/slot` has at most one current request; +- a newer request for the same `bank_type/slot` supersedes the previous request; +- if the previous request is queued, it is removed from the backlog; +- if the previous request is active, the lane should cancel cooperatively when + the current phase supports cheap cancellation; +- if active work cannot stop cheaply, it may finish, but the result must be + discarded when its generation no longer matches the target request generation; +- if the target already contains the requested `asset_id` as a valid resident + asset, `asset.load` returns a ready handle without adding a backlog entry. + +The effective backlog size is bounded by the sum of targetable bank slots, +because only one current request can exist per `bank_type/slot`. The runtime +does not expose a guest-visible `queue_full` status for the normal asset +backlog path. + +### 11.3 Handle state + +An asset handle observes one stable bank slot target. Its observable state has +two parts: + +```text +handle: + bank_type + slot + +slot_state: + loaded_asset_id + resident_state + slot_generation + +request_state: + requested_asset_id + request_generation + state + backlog_position + progress +``` + +`slot_state` describes what is currently resident in the target slot. +`request_state` describes the current or most recent request for that target. + +Mutating operations such as commit, cancel, promote, demote, and move must act +on the current request generation. They must not accidentally mutate a newer +request through an older handle view. + +### 11.4 Backlog inspection and ordering + +The asset backlog surface may expose these status-first operations: + +- `asset.backlog_info() -> (status, pending_count, active_handle, active_asset_id, active_bank_type, active_slot, active_progress)` +- `asset.backlog_position(handle) -> (status, state, position, progress)` +- `asset.backlog_move(handle, new_position) -> status` +- `asset.backlog_promote(handle) -> status` +- `asset.backlog_demote(handle) -> status` +- `asset.target_status(bank_type, slot) -> (status, asset_id, handle, state, position, progress)` + +`asset.backlog_promote(handle)` is a shortcut for moving a queued request to +position `1`, the first pending position after the active job. + +`asset.backlog_demote(handle)` is a shortcut for moving a queued request to the +end of the pending backlog. + +### 11.5 Progress and telemetry + +Asset progress uses integer progress, not floating point. The preferred scale is +`0..10000`. + +The initial phase model is: + +```text +queued -> 0 +read -> 0..4000 +decode -> 4000..9000 +stage -> 9000..10000 +ready -> 10000 +``` + +If a phase cannot report internal progress, it keeps the previous progress mark +and advances at phase completion. The runtime must not invent false precision +for non-linear decode phases. + +Minimum telemetry: + +- current backlog depth; +- target/request position; +- active job progress; +- jobs submitted; +- jobs completed; +- jobs failed; +- jobs canceled; +- jobs superseded; +- job duration; +- percentiles by `bank_type`; +- lightweight percentiles or small-window samples by `asset_id`. + +Percentiles are updated when a job closes, not inside the inner decode loop. + +### 11.6 Minimum status tables `asset.load` request statuses: @@ -418,9 +533,16 @@ Rules: - `4` = `CANCELED` - `5` = `ERROR` - `6` = `UNKNOWN_HANDLE` +- `7` = `QUEUED` +- `8` = `ACTIVE` +- `9` = `SUPERSEDED` +- `10` = `EMPTY` +- `11` = `INVALID` +- `12` = `BACKEND_UNAVAILABLE` `asset.commit` and `asset.cancel` operation statuses: - `0` = `OK` - `1` = `UNKNOWN_HANDLE` - `2` = `INVALID_STATE` +- `3` = `SUPERSEDED` diff --git a/docs/specs/runtime/16-host-abi-and-syscalls.md b/docs/specs/runtime/16-host-abi-and-syscalls.md index 34ac736f..5944b4c4 100644 --- a/docs/specs/runtime/16-host-abi-and-syscalls.md +++ b/docs/specs/runtime/16-host-abi-and-syscalls.md @@ -203,6 +203,12 @@ Canonical operations in v1 are: - `asset.status(handle) -> status` - `asset.commit(handle) -> status` - `asset.cancel(handle) -> status` +- `asset.backlog_info() -> (status, pending_count, active_handle, active_asset_id, active_bank_type, active_slot, active_progress)` +- `asset.backlog_position(handle) -> (status, state, position, progress)` +- `asset.backlog_move(handle, new_position) -> status` +- `asset.backlog_promote(handle) -> status` +- `asset.backlog_demote(handle) -> status` +- `asset.target_status(bank_type, slot) -> (status, asset_id, handle, state, position, progress)` For `asset.load`: @@ -210,6 +216,18 @@ For `asset.load`: - `slot` is the target slot index; - bank kind is resolved from `asset_table` by `asset_id`, not supplied by the caller. +Asset handles represent stable bank slot targets. A handle can be queried even +when its slot has no resident asset or active request. Internally the handle +state separates resident `slot_state` from current `request_state`. + +The asset backlog is keyed by `bank_type/slot`. New requests for the same target +supersede older requests for that target. `superseded` is an operational status, +not a structural trap. + +`asset.backlog_promote(handle)` and `asset.backlog_demote(handle)` are +convenience operations over backlog movement. They do not introduce a second +ordering model. + ### Bank diagnostics surface (`bank`, v1) `DEC-0009` narrows the public bank contract: diff --git a/docs/specs/runtime/16a-syscall-policies.md b/docs/specs/runtime/16a-syscall-policies.md index 1efb10ef..71e58c45 100644 --- a/docs/specs/runtime/16a-syscall-policies.md +++ b/docs/specs/runtime/16a-syscall-policies.md @@ -44,6 +44,9 @@ Normal operational success and operational failure conditions should be represen Examples: - asset not yet loaded; +- asset request queued or active; +- asset request superseded by a newer request for the same bank slot; +- asset backend unavailable; - audio voice unavailable; - persistent storage full. @@ -107,6 +110,11 @@ Game memcard operations (`mem.*`) are status-first and use `fs` capability in v1 `mem` remains layered on runtime `fs`; no parallel persistence channel is introduced. Domain surface, status catalog and slot semantics are defined in [`08-save-memory-and-memcard.md`](08-save-memory-and-memcard.md). +Asset backlog operations are status-first. `queued`, `active`, `ready`, +`canceled`, `superseded`, `empty`, `invalid`, decode failure, and backend +unavailability are operational results. They must not be reclassified as `Trap` +unless the caller violates the structural ABI contract. + ## 3 Interaction with the Garbage Collector The VM heap and host-managed memory are separate.