implements PLN-0127 async io lane boundaries
This commit is contained in:
parent
961557583b
commit
8747c4f4cf
@ -10,9 +10,10 @@ pub use prometeu_hal::{RenderWorkerBackend, RenderWorkerFrameSink};
|
||||
pub use services::async_work::{
|
||||
AsyncWorkActiveJob, AsyncWorkCancelToken, AsyncWorkJobContext, AsyncWorkJobId,
|
||||
AsyncWorkJobKind, AsyncWorkJobOutcome, AsyncWorkLane, AsyncWorkLaneConfig,
|
||||
AsyncWorkLaneController, AsyncWorkLaneError, AsyncWorkLaneTelemetry,
|
||||
AsyncWorkLaneController, AsyncWorkLaneError, AsyncWorkLaneTelemetry, AsyncWorkPriority,
|
||||
};
|
||||
pub use services::fs;
|
||||
pub use services::memcard::MemcardAsyncLaneOperation;
|
||||
pub use services::process;
|
||||
pub use services::task;
|
||||
pub use services::vm_runtime::{
|
||||
|
||||
@ -29,6 +29,36 @@ pub enum AsyncWorkJobKind {
|
||||
Test,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum AsyncWorkPriority {
|
||||
MemcardCommitWrite,
|
||||
FsWriteConfig,
|
||||
AssetLoad,
|
||||
NonCriticalReadList,
|
||||
}
|
||||
|
||||
impl AsyncWorkPriority {
|
||||
const fn rank(self) -> u8 {
|
||||
match self {
|
||||
Self::MemcardCommitWrite => 0,
|
||||
Self::FsWriteConfig => 1,
|
||||
Self::AssetLoad => 2,
|
||||
Self::NonCriticalReadList => 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWorkJobKind {
|
||||
pub const fn default_priority(self) -> AsyncWorkPriority {
|
||||
match self {
|
||||
Self::Memcard => AsyncWorkPriority::MemcardCommitWrite,
|
||||
Self::Fs => AsyncWorkPriority::FsWriteConfig,
|
||||
Self::Asset => AsyncWorkPriority::AssetLoad,
|
||||
Self::Test => AsyncWorkPriority::NonCriticalReadList,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum AsyncWorkJobOutcome {
|
||||
Completed,
|
||||
@ -59,6 +89,7 @@ impl Default for AsyncWorkLaneConfig {
|
||||
pub struct AsyncWorkActiveJob {
|
||||
pub id: AsyncWorkJobId,
|
||||
pub kind: AsyncWorkJobKind,
|
||||
pub priority: AsyncWorkPriority,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
@ -131,6 +162,7 @@ impl AsyncWorkCancelToken {
|
||||
pub struct AsyncWorkJobContext {
|
||||
pub job_id: AsyncWorkJobId,
|
||||
pub kind: AsyncWorkJobKind,
|
||||
pub priority: AsyncWorkPriority,
|
||||
cancel_token: AsyncWorkCancelToken,
|
||||
}
|
||||
|
||||
@ -145,6 +177,7 @@ type AsyncWorkFn = Box<dyn FnOnce(AsyncWorkJobContext) -> AsyncWorkJobOutcome +
|
||||
struct PendingAsyncWorkJob {
|
||||
id: AsyncWorkJobId,
|
||||
kind: AsyncWorkJobKind,
|
||||
priority: AsyncWorkPriority,
|
||||
cancel_token: AsyncWorkCancelToken,
|
||||
work: AsyncWorkFn,
|
||||
}
|
||||
@ -152,6 +185,7 @@ struct PendingAsyncWorkJob {
|
||||
struct RunningAsyncWorkJob {
|
||||
id: AsyncWorkJobId,
|
||||
kind: AsyncWorkJobKind,
|
||||
priority: AsyncWorkPriority,
|
||||
cancel_token: AsyncWorkCancelToken,
|
||||
work: AsyncWorkFn,
|
||||
}
|
||||
@ -177,6 +211,15 @@ impl AsyncWorkLane {
|
||||
&self,
|
||||
kind: AsyncWorkJobKind,
|
||||
work: impl FnOnce(AsyncWorkJobContext) -> AsyncWorkJobOutcome + Send + 'static,
|
||||
) -> Result<AsyncWorkJobId, AsyncWorkLaneError> {
|
||||
self.submit_with_priority(kind, kind.default_priority(), work)
|
||||
}
|
||||
|
||||
pub fn submit_with_priority(
|
||||
&self,
|
||||
kind: AsyncWorkJobKind,
|
||||
priority: AsyncWorkPriority,
|
||||
work: impl FnOnce(AsyncWorkJobContext) -> AsyncWorkJobOutcome + Send + 'static,
|
||||
) -> Result<AsyncWorkJobId, AsyncWorkLaneError> {
|
||||
let mut state = self.state.lock().map_err(|_| AsyncWorkLaneError::InternalFailure)?;
|
||||
if state.shutdown_requested {
|
||||
@ -186,18 +229,52 @@ impl AsyncWorkLane {
|
||||
state.next_job_id += 1;
|
||||
let id = AsyncWorkJobId::new(state.next_job_id);
|
||||
let cancel_token = AsyncWorkCancelToken::new();
|
||||
state.pending.push_back(PendingAsyncWorkJob {
|
||||
id,
|
||||
kind,
|
||||
cancel_token,
|
||||
work: Box::new(work),
|
||||
});
|
||||
let job = PendingAsyncWorkJob { id, kind, priority, cancel_token, work: Box::new(work) };
|
||||
let insert_at = state
|
||||
.pending
|
||||
.iter()
|
||||
.position(|pending| priority.rank() < pending.priority.rank())
|
||||
.unwrap_or(state.pending.len());
|
||||
state.pending.insert(insert_at, job);
|
||||
let pending_depth = state.pending.len();
|
||||
state.telemetry.record_submitted(id, pending_depth);
|
||||
self.ready.notify_one();
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
pub fn submit_memcard_commit_write(
|
||||
&self,
|
||||
work: impl FnOnce(AsyncWorkJobContext) -> AsyncWorkJobOutcome + Send + 'static,
|
||||
) -> Result<AsyncWorkJobId, AsyncWorkLaneError> {
|
||||
self.submit_with_priority(
|
||||
AsyncWorkJobKind::Memcard,
|
||||
AsyncWorkPriority::MemcardCommitWrite,
|
||||
work,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn submit_fs_write_config(
|
||||
&self,
|
||||
work: impl FnOnce(AsyncWorkJobContext) -> AsyncWorkJobOutcome + Send + 'static,
|
||||
) -> Result<AsyncWorkJobId, AsyncWorkLaneError> {
|
||||
self.submit_with_priority(AsyncWorkJobKind::Fs, AsyncWorkPriority::FsWriteConfig, work)
|
||||
}
|
||||
|
||||
pub fn submit_asset_load(
|
||||
&self,
|
||||
work: impl FnOnce(AsyncWorkJobContext) -> AsyncWorkJobOutcome + Send + 'static,
|
||||
) -> Result<AsyncWorkJobId, AsyncWorkLaneError> {
|
||||
self.submit_with_priority(AsyncWorkJobKind::Asset, AsyncWorkPriority::AssetLoad, work)
|
||||
}
|
||||
|
||||
pub fn submit_non_critical_read_list(
|
||||
&self,
|
||||
kind: AsyncWorkJobKind,
|
||||
work: impl FnOnce(AsyncWorkJobContext) -> AsyncWorkJobOutcome + Send + 'static,
|
||||
) -> Result<AsyncWorkJobId, AsyncWorkLaneError> {
|
||||
self.submit_with_priority(kind, AsyncWorkPriority::NonCriticalReadList, work)
|
||||
}
|
||||
|
||||
pub fn request_cancel(&self, job_id: AsyncWorkJobId) -> bool {
|
||||
let state = self.state.lock().unwrap();
|
||||
if state.active.is_some_and(|active| active.id == job_id) {
|
||||
@ -253,7 +330,11 @@ impl AsyncWorkLane {
|
||||
}
|
||||
|
||||
if let Some(pending) = state.pending.pop_front() {
|
||||
let active = AsyncWorkActiveJob { id: pending.id, kind: pending.kind };
|
||||
let active = AsyncWorkActiveJob {
|
||||
id: pending.id,
|
||||
kind: pending.kind,
|
||||
priority: pending.priority,
|
||||
};
|
||||
state.active = Some(active);
|
||||
state.active_cancel_token = Some(pending.cancel_token.clone());
|
||||
let pending_depth = state.pending.len();
|
||||
@ -261,6 +342,7 @@ impl AsyncWorkLane {
|
||||
return Some(RunningAsyncWorkJob {
|
||||
id: pending.id,
|
||||
kind: pending.kind,
|
||||
priority: pending.priority,
|
||||
cancel_token: pending.cancel_token,
|
||||
work: pending.work,
|
||||
});
|
||||
@ -332,8 +414,12 @@ impl Drop for AsyncWorkLaneController {
|
||||
|
||||
fn run_async_work_loop(lane: Arc<AsyncWorkLane>) {
|
||||
while let Some(job) = lane.wait_take() {
|
||||
let context =
|
||||
AsyncWorkJobContext { job_id: job.id, kind: job.kind, cancel_token: job.cancel_token };
|
||||
let context = AsyncWorkJobContext {
|
||||
job_id: job.id,
|
||||
kind: job.kind,
|
||||
priority: job.priority,
|
||||
cancel_token: job.cancel_token,
|
||||
};
|
||||
let result = catch_unwind(AssertUnwindSafe(|| (job.work)(context)))
|
||||
.unwrap_or(AsyncWorkJobOutcome::Failed);
|
||||
lane.record_finished(job.id, result);
|
||||
@ -379,6 +465,60 @@ mod tests {
|
||||
assert_eq!(telemetry.completed_jobs, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn async_work_lane_runs_higher_priority_io_before_assets() {
|
||||
let lane = Arc::new(AsyncWorkLane::default());
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
lane.submit_asset_load({
|
||||
let tx = tx.clone();
|
||||
move |ctx| {
|
||||
tx.send((ctx.kind, ctx.priority)).unwrap();
|
||||
AsyncWorkJobOutcome::Completed
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
lane.submit_non_critical_read_list(AsyncWorkJobKind::Fs, {
|
||||
let tx = tx.clone();
|
||||
move |ctx| {
|
||||
tx.send((ctx.kind, ctx.priority)).unwrap();
|
||||
AsyncWorkJobOutcome::Completed
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
lane.submit_memcard_commit_write({
|
||||
let tx = tx.clone();
|
||||
move |ctx| {
|
||||
tx.send((ctx.kind, ctx.priority)).unwrap();
|
||||
AsyncWorkJobOutcome::Completed
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
lane.submit_fs_write_config({
|
||||
let tx = tx.clone();
|
||||
move |ctx| {
|
||||
tx.send((ctx.kind, ctx.priority)).unwrap();
|
||||
AsyncWorkJobOutcome::Completed
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let mut controller =
|
||||
AsyncWorkLaneController::start(AsyncWorkLaneConfig::default(), Arc::clone(&lane));
|
||||
|
||||
assert_eq!(
|
||||
rx.recv().unwrap(),
|
||||
(AsyncWorkJobKind::Memcard, AsyncWorkPriority::MemcardCommitWrite)
|
||||
);
|
||||
assert_eq!(rx.recv().unwrap(), (AsyncWorkJobKind::Fs, AsyncWorkPriority::FsWriteConfig));
|
||||
assert_eq!(rx.recv().unwrap(), (AsyncWorkJobKind::Asset, AsyncWorkPriority::AssetLoad));
|
||||
assert_eq!(
|
||||
rx.recv().unwrap(),
|
||||
(AsyncWorkJobKind::Fs, AsyncWorkPriority::NonCriticalReadList)
|
||||
);
|
||||
controller.stop().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn async_work_lane_has_one_active_job() {
|
||||
let lane = Arc::new(AsyncWorkLane::default());
|
||||
|
||||
@ -8,4 +8,4 @@ pub use fs_backend::FsBackend;
|
||||
pub use fs_entry::FsEntry;
|
||||
pub use fs_error::FsError;
|
||||
pub use fs_state::FsState;
|
||||
pub use virtual_fs::VirtualFS;
|
||||
pub use virtual_fs::{FsAsyncLaneOperation, VirtualFS};
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use crate::fs::{FsBackend, FsEntry, FsError};
|
||||
use crate::services::async_work::{AsyncWorkJobKind, AsyncWorkPriority};
|
||||
|
||||
/// Virtual Filesystem (VFS) interface for Prometeu.
|
||||
///
|
||||
@ -13,6 +14,29 @@ pub struct VirtualFS {
|
||||
backend: Option<Box<dyn FsBackend>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum FsAsyncLaneOperation {
|
||||
Write,
|
||||
Delete,
|
||||
Config,
|
||||
Read,
|
||||
ListDir,
|
||||
Exists,
|
||||
}
|
||||
|
||||
impl FsAsyncLaneOperation {
|
||||
pub const fn job_kind(self) -> AsyncWorkJobKind {
|
||||
AsyncWorkJobKind::Fs
|
||||
}
|
||||
|
||||
pub const fn priority(self) -> AsyncWorkPriority {
|
||||
match self {
|
||||
Self::Write | Self::Delete | Self::Config => AsyncWorkPriority::FsWriteConfig,
|
||||
Self::Read | Self::ListDir | Self::Exists => AsyncWorkPriority::NonCriticalReadList,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for VirtualFS {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
@ -242,4 +266,18 @@ mod tests {
|
||||
assert!(matches!(vfs.delete("/"), Err(FsError::PermissionDenied)));
|
||||
assert_eq!(calls.delete.load(Ordering::Relaxed), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fs_async_lane_operations_classify_priority_without_public_api_changes() {
|
||||
assert_eq!(FsAsyncLaneOperation::Write.job_kind(), AsyncWorkJobKind::Fs);
|
||||
assert_eq!(FsAsyncLaneOperation::Write.priority(), AsyncWorkPriority::FsWriteConfig);
|
||||
assert_eq!(FsAsyncLaneOperation::Delete.priority(), AsyncWorkPriority::FsWriteConfig);
|
||||
assert_eq!(FsAsyncLaneOperation::Config.priority(), AsyncWorkPriority::FsWriteConfig);
|
||||
assert_eq!(FsAsyncLaneOperation::Read.priority(), AsyncWorkPriority::NonCriticalReadList);
|
||||
assert_eq!(
|
||||
FsAsyncLaneOperation::ListDir.priority(),
|
||||
AsyncWorkPriority::NonCriticalReadList
|
||||
);
|
||||
assert_eq!(FsAsyncLaneOperation::Exists.priority(), AsyncWorkPriority::NonCriticalReadList);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use crate::fs::{FsError, VirtualFS};
|
||||
use crate::services::async_work::{AsyncWorkJobKind, AsyncWorkPriority};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub const MEMCARD_SLOT_COUNT: usize = 32;
|
||||
@ -50,6 +51,30 @@ pub struct MemcardWriteResult {
|
||||
pub bytes_written: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum MemcardAsyncLaneOperation {
|
||||
SlotWrite,
|
||||
SlotCommit,
|
||||
SlotClear,
|
||||
SlotRead,
|
||||
SlotStat,
|
||||
}
|
||||
|
||||
impl MemcardAsyncLaneOperation {
|
||||
pub const fn job_kind(self) -> AsyncWorkJobKind {
|
||||
AsyncWorkJobKind::Memcard
|
||||
}
|
||||
|
||||
pub const fn priority(self) -> AsyncWorkPriority {
|
||||
match self {
|
||||
Self::SlotWrite | Self::SlotCommit | Self::SlotClear => {
|
||||
AsyncWorkPriority::MemcardCommitWrite
|
||||
}
|
||||
Self::SlotRead | Self::SlotStat => AsyncWorkPriority::NonCriticalReadList,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct SlotImage {
|
||||
payload: Vec<u8>,
|
||||
@ -428,4 +453,29 @@ mod tests {
|
||||
let stat = mem.slot_stat(&fs, 7, 2);
|
||||
assert_eq!(stat.state, MemcardSlotState::Corrupt);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn memcard_async_lane_operations_classify_priority_without_changing_public_api() {
|
||||
assert_eq!(MemcardAsyncLaneOperation::SlotWrite.job_kind(), AsyncWorkJobKind::Memcard);
|
||||
assert_eq!(
|
||||
MemcardAsyncLaneOperation::SlotWrite.priority(),
|
||||
AsyncWorkPriority::MemcardCommitWrite
|
||||
);
|
||||
assert_eq!(
|
||||
MemcardAsyncLaneOperation::SlotCommit.priority(),
|
||||
AsyncWorkPriority::MemcardCommitWrite
|
||||
);
|
||||
assert_eq!(
|
||||
MemcardAsyncLaneOperation::SlotClear.priority(),
|
||||
AsyncWorkPriority::MemcardCommitWrite
|
||||
);
|
||||
assert_eq!(
|
||||
MemcardAsyncLaneOperation::SlotRead.priority(),
|
||||
AsyncWorkPriority::NonCriticalReadList
|
||||
);
|
||||
assert_eq!(
|
||||
MemcardAsyncLaneOperation::SlotStat.priority(),
|
||||
AsyncWorkPriority::NonCriticalReadList
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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":"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":"done","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":"done","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":"done","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":"done","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":"done","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":"done","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":"done","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"}]}
|
||||
|
||||
@ -26,6 +26,16 @@ Sem um contrato claro de `home` por app, a API tende a crescer com semantica inc
|
||||
agenda deve decidir a API publica de FS considerando essa lane, mas a
|
||||
existencia da lane ja esta fechada por `DEC-0034`.
|
||||
|
||||
## Fronteira com a Async IO Lane
|
||||
|
||||
`DEC-0034` fecha a existencia de uma lane serial para trabalho async de IO. FS
|
||||
pode consumir essa lane internamente para escrita, configuracao, leitura ou
|
||||
listagem, conforme prioridade operacional definida pelo runtime.
|
||||
|
||||
Esta agenda continua dona da decisao sobre API publica de FS para `app home`.
|
||||
Portanto, a existencia da lane nao implica criar agora request handles, polling
|
||||
publico ou novas syscalls de FS. Esses shapes permanecem em aberto aqui.
|
||||
|
||||
## Alvo da Discussao
|
||||
|
||||
Fechar a superficie e semantica funcional de `fs` para perfil `app`, com sandbox por `app_id` e transporte de bytes via decisao `003`.
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
id: PLN-0127
|
||||
ticket: perf-async-background-work-lanes-for-assets-and-fs
|
||||
title: Async Lane Memcard and FS Integration Boundaries
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-06-28
|
||||
completed:
|
||||
ref_decisions: [DEC-0034]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user