implements PLN-0123 async work lane specs

This commit is contained in:
bQUARKz 2026-06-28 13:53:53 +01:00
parent 8f37d971cd
commit 54dad3b398
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
7 changed files with 183 additions and 6 deletions

View File

@ -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"}]}

View File

@ -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

View File

@ -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]

View File

@ -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.

View File

@ -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`

View File

@ -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:

View File

@ -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.