feat(renaser): Fase 7c — persistencia inter-sesión por-app
Cada app tiene ahora su propia ranura de estado en el Manifiesto de Génesis (EntradaApp.estado): guarda y recobra lo suyo, sobrevive al reinicio, y no pisa a ninguna otra app. - apps/memoriosa: app WASM interactiva nueva. Cuenta las teclas pulsadas y persiste el recuento; al reiniciar despierta con su cuenta intacta. Reemplaza la 2a instancia de hola en la genesis. - kernel: capacidades sys_estado_cargar / sys_estado_guardar. El kernel custodia un manifiesto VIVO (Mutex<Manifiesto>); fijar_estado lo muta, lo re-graba en el grafo y lo re-ancla. ContextoCapacidades.indice_app da a cada app su identidad — su ranura, jamas la de otra. - cargar_userspace instala el manifiesto vivo antes de instanciar las apps: el init de una app ya consulta su estado al despertar. Verificado en QEMU (screendump + sendkey): disco virgen -> memoriosa con 0 celdas, testigo verde; 5 pulsaciones -> 5 celdas; reinicio -> 5 celdas intactas, testigo ambar (init releyo el estado del grafo). Cierra la Fase 7 — el userspace nace del grafo, completa. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -7,17 +7,24 @@
|
||||
// cuota, en que region— lo dicta este Manifiesto de Genesis, que tambien
|
||||
// habita el grafo. El superbloque guarda su hash en un ancla propia.
|
||||
//
|
||||
// ESTADO: Fase 7b. El kernel ya NO empotra una sola app. La siembra de la
|
||||
// imagen —grabar los objetos de bytecode y el manifiesto en un disco virgen—
|
||||
// la hace por completo el constructor de imagen `boot`, en el anfitrion. Este
|
||||
// modulo se reduce a su esencia: LEER el manifiesto del grafo al arrancar.
|
||||
// ESTADO: Fase 7c. El manifiesto deja de ser de solo lectura. El kernel
|
||||
// conserva una copia VIVA —`VIVO`, un `Mutex<Manifiesto>`—; cuando una app
|
||||
// persiste su estado (`sys_estado_guardar`), el kernel actualiza la ranura
|
||||
// `estado` de su `EntradaApp`, re-graba el manifiesto en el grafo y lo
|
||||
// re-ancla. Asi el estado de cada app sobrevive, por separado, a un reinicio.
|
||||
//
|
||||
// Los tipos `Manifiesto` / `EntradaApp` y su (de)serializacion viven en la
|
||||
// crate `formato`, el nucleo `no_std` compartido con `boot`. Aqui solo queda
|
||||
// lo que es del kernel: recuperar el manifiesto del grafo y traducir las
|
||||
// regiones de su formato en disco (`u32`) a la `RegionPantalla` del kernel.
|
||||
// lo que es del kernel: cargar el manifiesto, traducir regiones, custodiar la
|
||||
// copia viva y mutarla cuando una app graba su estado.
|
||||
// =============================================================================
|
||||
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use spin::{Mutex, Once};
|
||||
|
||||
use formato::Hash;
|
||||
|
||||
use crate::almacen;
|
||||
use crate::grafico::RegionPantalla;
|
||||
|
||||
@@ -25,6 +32,12 @@ use crate::grafico::RegionPantalla;
|
||||
// resto del kernel los nombre `manifiesto::EntradaApp` / `manifiesto::Manifiesto`.
|
||||
pub use formato::{EntradaApp, Manifiesto};
|
||||
|
||||
/// El manifiesto VIVO del kernel: la copia en memoria, mutable, del Manifiesto
|
||||
/// de Genesis. Las apps la actualizan al persistir su estado (Fase 7c); el
|
||||
/// kernel la re-graba en el grafo y la re-ancla en el superbloque. Se instala
|
||||
/// una sola vez, en el arranque, con [`instalar`].
|
||||
static VIVO: Once<Mutex<Manifiesto>> = Once::new();
|
||||
|
||||
/// Traduce la sub-region de una `EntradaApp` —campos `u32` de ancho fijo, el
|
||||
/// formato en disco— a la `RegionPantalla` que el kernel entiende (`usize`,
|
||||
/// ancho de plataforma). El puente entre lo que el disco guarda y lo que el
|
||||
@@ -54,3 +67,45 @@ pub fn cargar() -> Result<Option<Manifiesto>, &'static str> {
|
||||
let manifiesto = Manifiesto::deserializar(&objeto.datos)?;
|
||||
Ok(Some(manifiesto))
|
||||
}
|
||||
|
||||
/// Instala el manifiesto recien cargado como el manifiesto VIVO del kernel. Se
|
||||
/// invoca una sola vez, en el arranque, ANTES de instanciar las apps — el
|
||||
/// `init` de cada app ya consulta su estado persistido, y eso lee de aqui.
|
||||
pub fn instalar(manifiesto: Manifiesto) {
|
||||
VIVO.call_once(|| Mutex::new(manifiesto));
|
||||
}
|
||||
|
||||
/// El hash del estado persistido de la app `indice`, si tiene uno anclado. Lo
|
||||
/// consulta la capacidad `sys_estado_cargar` cuando una app despierta.
|
||||
pub fn estado_de(indice: usize) -> Option<Hash> {
|
||||
VIVO.get()
|
||||
.and_then(|vivo| vivo.lock().apps.get(indice).and_then(|app| app.estado))
|
||||
}
|
||||
|
||||
/// Registra `hash` como el nuevo estado persistido de la app `indice`: muta el
|
||||
/// manifiesto vivo, lo re-serializa, lo graba como un objeto NUEVO del grafo y
|
||||
/// lo re-ancla en el superbloque. Desde esta llamada, el estado de esa app
|
||||
/// sobrevive a un reinicio. La invoca la capacidad `sys_estado_guardar`.
|
||||
pub fn fijar_estado(indice: usize, hash: Hash) -> Result<(), &'static str> {
|
||||
let vivo = VIVO.get().ok_or("manifiesto :: no hay manifiesto vivo")?;
|
||||
let mut manifiesto = vivo.lock();
|
||||
let entrada = manifiesto
|
||||
.apps
|
||||
.get_mut(indice)
|
||||
.ok_or("manifiesto :: indice de app fuera de rango")?;
|
||||
entrada.estado = Some(hash);
|
||||
|
||||
// Re-grabar el manifiesto mutado. Sus `hijos` son, como en la siembra de
|
||||
// `boot`, los objetos de bytecode deduplicados: el grafo lo sigue leyendo
|
||||
// como el nodo padre del userspace. El objeto nuevo se ancla en el
|
||||
// superbloque; el viejo queda en el log, inerte e inofensivo.
|
||||
let bytes = manifiesto.serializar()?;
|
||||
let mut hijos: Vec<Hash> = Vec::new();
|
||||
for app in &manifiesto.apps {
|
||||
if !hijos.contains(&app.bytecode) {
|
||||
hijos.push(app.bytecode);
|
||||
}
|
||||
}
|
||||
let nuevo = almacen::almacenar(bytes, hijos)?;
|
||||
almacen::fijar_manifiesto(nuevo)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user