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:
@@ -587,3 +587,46 @@ poblado. El binario del kernel ya no carga ni un solo `.wasm`.
|
|||||||
por `boot`.
|
por `boot`.
|
||||||
- `cargo build -p boot` y `cargo kernel` compilan limpios; `cargo test` de
|
- `cargo build -p boot` y `cargo kernel` compilan limpios; `cargo test` de
|
||||||
`formato` — 5/5 en verde.
|
`formato` — 5/5 en verde.
|
||||||
|
|
||||||
|
## Fase 7c — Persistencia inter-sesión: cada app recuerda lo suyo — 2026-05-22
|
||||||
|
|
||||||
|
La cronista dejaba huella, pero en la RAÍZ del grafo — un ancla única que sólo
|
||||||
|
una app puede usar. La 7c estrena la persistencia POR-APP: cada `EntradaApp`
|
||||||
|
del Manifiesto de Génesis tiene su propia ranura `estado`, y una app guarda y
|
||||||
|
recobra lo suyo sin pisar a nadie. El estado de cada aplicación sobrevive, por
|
||||||
|
separado, a un reinicio.
|
||||||
|
|
||||||
|
### Añadido
|
||||||
|
- **`apps/memoriosa`** — nueva app WASM interactiva. Cuenta las teclas pulsadas
|
||||||
|
en toda su historia y persiste el recuento; al reiniciar, despierta con su
|
||||||
|
cuenta intacta. Una celda violeta por pulsación; el testigo de la esquina es
|
||||||
|
verde si nació limpia, ámbar si despertó con memoria de una vida anterior.
|
||||||
|
- `kernel` — dos capacidades del host nuevas:
|
||||||
|
- `sys_estado_cargar(salida, capacidad)` — copia el estado persistido de la
|
||||||
|
app que llama en su memoria lineal; 0 si aún no tiene estado previo.
|
||||||
|
- `sys_estado_guardar(datos, datos_len)` — graba `datos` como objeto del
|
||||||
|
grafo y ancla su hash en la `EntradaApp` de la app, re-grabando y
|
||||||
|
re-anclando el manifiesto.
|
||||||
|
- `manifiesto.rs` — el manifiesto VIVO: `VIVO`, un `Mutex<Manifiesto>` que el
|
||||||
|
kernel custodia. `instalar` (en el arranque), `estado_de` (lee la ranura de
|
||||||
|
una app) y `fijar_estado` (la muta, re-graba el manifiesto y lo re-ancla).
|
||||||
|
- `ContextoCapacidades` gana `indice_app`: la identidad de cada app en el
|
||||||
|
manifiesto, con la que las capacidades de estado hallan SU ranura — jamás la
|
||||||
|
de otra.
|
||||||
|
|
||||||
|
### Cambiado
|
||||||
|
- `AplicacionWasm::cargar` y `encender_app` reciben el `indice_app` de la app.
|
||||||
|
- `cargar_userspace` instala el manifiesto VIVO ANTES de instanciar las apps:
|
||||||
|
el `init` de una app ya consulta su estado persistido al despertar.
|
||||||
|
- `almacen::fijar_manifiesto` deja de ser `dead_code` — lo invoca `fijar_estado`.
|
||||||
|
- La génesis (`boot`) sustituye la segunda instancia de `hola` por `memoriosa`
|
||||||
|
(región 700,120 de 360×80).
|
||||||
|
|
||||||
|
### Verificado
|
||||||
|
- QEMU (captura headless + `sendkey` por el monitor):
|
||||||
|
- **Disco virgen** — memoriosa arranca con 0 celdas y el testigo verde.
|
||||||
|
- **Cinco pulsaciones** (`sendkey`) — memoriosa pinta 5 celdas violetas y
|
||||||
|
persiste el recuento; `sys_estado_guardar` re-ancla el manifiesto.
|
||||||
|
- **Reinicio** — `boot` respeta el disco; memoriosa despierta con las 5
|
||||||
|
celdas intactas y el testigo en ámbar: su `init` releyó el estado del
|
||||||
|
grafo. El estado por-app sobrevivió al apagón.
|
||||||
|
|||||||
+9
-7
@@ -23,12 +23,15 @@ Verificación headless (sin ventana): añadir
|
|||||||
`-- -display none -monitor unix:/tmp/q.sock,server,nowait` y capturar con
|
`-- -display none -monitor unix:/tmp/q.sock,server,nowait` y capturar con
|
||||||
`screendump` por el socket del monitor.
|
`screendump` por el socket del monitor.
|
||||||
|
|
||||||
Reconstruir la app WASM del userspace tras tocar `apps/hello_wasm`:
|
Reconstruir una app WASM del userspace tras tocarla. Los `.wasm` viven en
|
||||||
|
`kernel/assets/` —de ahí los lee `boot` al sembrar la imagen (Fase 7b)—; el
|
||||||
|
modulo `hello_wasm` se copia como `app.wasm`, el resto conserva su nombre:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd apps/hello_wasm
|
cd apps/<app> # hello_wasm, discola, glotona, cronista, memoriosa
|
||||||
cargo build --target wasm32-unknown-unknown --release
|
cargo build --target wasm32-unknown-unknown --release
|
||||||
cp target/wasm32-unknown-unknown/release/hello_wasm.wasm ../../kernel/assets/app.wasm
|
cp target/wasm32-unknown-unknown/release/<app>.wasm ../../kernel/assets/<app>.wasm
|
||||||
|
# (hello_wasm es la excepcion: su destino es kernel/assets/app.wasm)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Estructura del espacio de trabajo
|
## Estructura del espacio de trabajo
|
||||||
@@ -70,10 +73,9 @@ QEMU 11, OVMF en `/usr/share/edk2/x64/OVMF.4m.fd` (sin módulo KVM → TCG).
|
|||||||
|
|
||||||
## Estado
|
## Estado
|
||||||
|
|
||||||
Fases 1 a 5, 6.0, 6.1 y 6.2 completas; y la Fase 7 —el userspace nace del
|
Fases 1 a 5, 6.0, 6.1, 6.2 y la Fase 7 COMPLETA —el userspace nace del grafo
|
||||||
grafo de objetos— en sus sub-fases 7a (Manifiesto de Génesis, carga desde el
|
de objetos: Manifiesto de Génesis (7a), imagen sembrada por `boot` (7b) y
|
||||||
grafo) y 7b (`boot` siembra la imagen, muere el `include_bytes!` del kernel).
|
persistencia inter-sesión por-app (7c)—. Todo verificado en QEMU. Ver
|
||||||
Todo verificado en QEMU. Pendiente: 7c (persistencia inter-sesión). Ver
|
|
||||||
`ROADMAP.md`.
|
`ROADMAP.md`.
|
||||||
|
|
||||||
## Flujo de trabajo
|
## Flujo de trabajo
|
||||||
|
|||||||
@@ -292,6 +292,32 @@ Hay una sola lengua.
|
|||||||
A los ojos, otra vez, casi nada cambió: las mismas cinco ventanas. Pero la
|
A los ojos, otra vez, casi nada cambió: las mismas cinco ventanas. Pero la
|
||||||
maleta del kernel, esta vez sí, está del todo vacía.
|
maleta del kernel, esta vez sí, está del todo vacía.
|
||||||
|
|
||||||
|
## Cada quien, su cajón de recuerdos
|
||||||
|
|
||||||
|
Había una inquilina —la cronista— que sabía dejar huella. Cada vez que la casa
|
||||||
|
despertaba, ella anotaba el número del despertar y lo guardaba donde nada se
|
||||||
|
pierde. Pero lo guardaba en un único cajón, el cajón de la casa: si otro
|
||||||
|
inquilino hubiera querido recordar algo suyo, habría tenido que borrar lo de
|
||||||
|
ella. Memoria había, sí, pero una sola, y a empujones.
|
||||||
|
|
||||||
|
Hoy eso cambió. El cuaderno de la casa —el que dice quién vive dónde— ganó, en
|
||||||
|
cada página, un margen en blanco: un cajoncito propio para cada inquilino,
|
||||||
|
donde guardar lo que quiera sin tocar lo ajeno. Mil inquilinos, mil cajones;
|
||||||
|
ninguno pisa al otro.
|
||||||
|
|
||||||
|
La primera en estrenarlo es una recién llegada que hace honor a su nombre:
|
||||||
|
*memoriosa*. Su oficio es sencillo y entrañable — cuenta las teclas que se
|
||||||
|
pulsan. Cada vez que alguien teclea, ella suma uno y lo apunta en su cajón. Y
|
||||||
|
cuando la casa se apaga del todo y vuelve a encenderse, memoriosa no empieza de
|
||||||
|
cero: abre su cajón, lee la cuenta donde la dejó, y continúa. En su rincón
|
||||||
|
pinta una marca por cada tecla de toda su vida; y una lucecita —ámbar— confiesa
|
||||||
|
que ha despertado recordando.
|
||||||
|
|
||||||
|
No es poca cosa. Una máquina que se apaga suele olvidarlo todo; renaser, no.
|
||||||
|
Sus inquilinos cierran los ojos en el apagón y los abren, al volver, justo
|
||||||
|
donde los cerraron. La casa ya no sólo perdura: recuerda, uno por uno, a los
|
||||||
|
suyos.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*El diario continúa. La próxima página la escribirá la próxima jornada.*
|
*El diario continúa. La próxima página la escribirá la próxima jornada.*
|
||||||
|
|||||||
+1
-1
@@ -65,7 +65,7 @@ ataca **incremental**, como las Fases 6.1a/b/c.
|
|||||||
- El kernel pierde los `include_bytes!` del userspace; `sembrar_genesis` se
|
- El kernel pierde los `include_bytes!` del userspace; `sembrar_genesis` se
|
||||||
retira o queda como camino vacío de respaldo.
|
retira o queda como camino vacío de respaldo.
|
||||||
|
|
||||||
### 7c — Persistencia inter-sesión
|
### 7c — Persistencia inter-sesión — ✅ HECHA
|
||||||
|
|
||||||
- Una app (cronista evolucionada, o una nueva interactiva) guarda su estado
|
- Una app (cronista evolucionada, o una nueva interactiva) guarda su estado
|
||||||
mutado como un objeto nuevo del grafo al recibir teclado.
|
mutado como un objeto nuevo del grafo al recibir teclado.
|
||||||
|
|||||||
+6
-4
@@ -97,7 +97,7 @@ gobierna, no por el IOAPIC — basta leer la línea que el firmware ya asignó.
|
|||||||
Verificado en QEMU: el disco se enruta a la IRQ 11; una tarea-sonda del reactor
|
Verificado en QEMU: el disco se enruta a la IRQ 11; una tarea-sonda del reactor
|
||||||
lee un bloque de forma asíncrona mientras las apps siguen pintando.
|
lee un bloque de forma asíncrona mientras las apps siguen pintando.
|
||||||
|
|
||||||
## Fase 7 — el userspace nace del Grafo de Objetos
|
## Fase 7 — el userspace nace del Grafo de Objetos (completada)
|
||||||
|
|
||||||
Hasta la Fase 6, el userspace venía **empotrado en el binario del kernel**:
|
Hasta la Fase 6, el userspace venía **empotrado en el binario del kernel**:
|
||||||
cuatro `include_bytes!` de `.wasm` y regiones escritas a mano. La Fase 7 lo
|
cuatro `include_bytes!` de `.wasm` y regiones escritas a mano. La Fase 7 lo
|
||||||
@@ -114,9 +114,11 @@ destierra — las aplicaciones pasan a ser objetos del grafo, gobernadas por un
|
|||||||
por el kernel y el constructor de imagen `boot`. `boot` siembra el disco
|
por el kernel y el constructor de imagen `boot`. `boot` siembra el disco
|
||||||
virgen con el grafo ya poblado —bytecode y manifiesto—; el kernel pierde
|
virgen con el grafo ya poblado —bytecode y manifiesto—; el kernel pierde
|
||||||
todo `include_bytes!` del userspace. Su binario ya no carga ni un `.wasm`.
|
todo `include_bytes!` del userspace. Su binario ya no carga ni un `.wasm`.
|
||||||
- **7c — persistencia inter-sesión (pendiente).** Una app guarda su estado
|
- **7c — persistencia inter-sesión (completada).** La app `memoriosa` graba
|
||||||
mutado como un objeto nuevo del grafo; el campo `EntradaApp.estado` lo
|
su estado como un objeto del grafo; el kernel lo ancla en la ranura
|
||||||
ancla. Al despertar, la app retoma donde quedó.
|
`EntradaApp.estado` y re-graba el manifiesto. Al despertar, `init` lo relee
|
||||||
|
y la app retoma donde quedó. Capacidades `sys_estado_cargar` /
|
||||||
|
`sys_estado_guardar`; el kernel custodia un manifiesto VIVO y mutable.
|
||||||
|
|
||||||
Líneas abiertas posteriores: más capacidades del host (temporización, audio);
|
Líneas abiertas posteriores: más capacidades del host (temporización, audio);
|
||||||
la Fase 8 — el compositor sobre `mirada-layout`.
|
la Fase 8 — el compositor sobre `mirada-layout`.
|
||||||
|
|||||||
Generated
+7
@@ -0,0 +1,7 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memoriosa"
|
||||||
|
version = "0.1.0"
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# renaser :: apps/memoriosa — Fase 7c :: la app que recuerda entre vidas
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Un modulo WebAssembly interactivo. Cuenta las teclas que se pulsan y graba
|
||||||
|
# ese recuento como SU estado persistido —anclado en su `EntradaApp` del
|
||||||
|
# Manifiesto de Genesis—. Al reiniciar la maquina, despierta justo donde se
|
||||||
|
# quedo: la cuenta no vive en la RAM, vive en el grafo.
|
||||||
|
# Tiene su propio `[workspace]`: queda fuera del espacio de trabajo del kernel.
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "memoriosa"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
description = "renaser :: app WASM memoriosa — persistencia inter-sesion por-app"
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
|
||||||
|
# `cdylib` produce un modulo `.wasm` que exporta funciones — el formato que
|
||||||
|
# wasmi instancia.
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
panic = "abort"
|
||||||
|
opt-level = "s"
|
||||||
|
lto = true
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// renaser :: apps/memoriosa — Fase 7c :: la app que recuerda entre vidas
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// La cronista deja huella, sí — pero en la RAIZ del grafo, un ancla unica que
|
||||||
|
// solo una app puede usar. `memoriosa` estrena la persistencia POR-APP: cada
|
||||||
|
// aplicacion tiene, en su `EntradaApp` del Manifiesto de Genesis, una ranura
|
||||||
|
// propia —`estado`— donde guardar lo suyo. Mil apps, mil memorias; ninguna
|
||||||
|
// pisa a otra.
|
||||||
|
//
|
||||||
|
// Lo que memoriosa recuerda es simple y visible: cuantas teclas se han pulsado
|
||||||
|
// EN TODA SU HISTORIA. En cada `tick` drena su canal de teclado; cada
|
||||||
|
// pulsacion suma uno y graba el recuento con `sys_estado_guardar`. El kernel lo
|
||||||
|
// ancla en el manifiesto. Al reiniciar, `init` lo relee con `sys_estado_cargar`
|
||||||
|
// y la app despierta con su cuenta intacta — una celda por tecla, como las dejo.
|
||||||
|
//
|
||||||
|
// El testigo de la esquina lo cuenta todo de un vistazo: verde si nacio limpia
|
||||||
|
// —disco recien sembrado—, ambar si desperto con memoria de vidas pasadas.
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
// --- Las capacidades que el kernel `renaser` inyecta a esta aplicacion. ---
|
||||||
|
#[link(wasm_import_module = "renaser")]
|
||||||
|
extern "C" {
|
||||||
|
/// Compone un bufer de pixeles (de ESTA memoria lineal) en la region que el
|
||||||
|
/// kernel asigno a esta aplicacion.
|
||||||
|
fn sys_render_frame(ptr: u32, len: u32);
|
||||||
|
|
||||||
|
/// Extrae, sin bloquear, el siguiente scancode del canal de teclado propio
|
||||||
|
/// de esta app. Devuelve 0 si el canal esta vacio.
|
||||||
|
fn sys_get_scancode() -> u32;
|
||||||
|
|
||||||
|
/// Copia el estado persistido de ESTA app en `salida`. Devuelve los bytes
|
||||||
|
/// copiados, 0 si no hay estado previo, o un valor negativo si fallo.
|
||||||
|
fn sys_estado_cargar(salida: u32, capacidad: u32) -> i32;
|
||||||
|
|
||||||
|
/// Graba `datos` como el nuevo estado persistido de ESTA app. El kernel lo
|
||||||
|
/// ancla en el Manifiesto de Genesis: sobrevivira al reinicio. Devuelve 0
|
||||||
|
/// si todo fue bien, un valor negativo si fallo.
|
||||||
|
fn sys_estado_guardar(datos: u32, datos_len: u32) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sin sistema operativo bajo nosotros, un panico solo puede detenerse en seco.
|
||||||
|
#[panic_handler]
|
||||||
|
fn al_fallar(_: &core::panic::PanicInfo) -> ! {
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Geometria de la escena. El ancho y el alto DEBEN coincidir con la region
|
||||||
|
// que el kernel asigna a esta app. ---
|
||||||
|
const ANCHO: usize = 360;
|
||||||
|
const ALTO: usize = 80;
|
||||||
|
|
||||||
|
/// Lado de paso de la rejilla de celdas, en pixeles.
|
||||||
|
const PASO: usize = 20;
|
||||||
|
/// Lado de una celda, en pixeles.
|
||||||
|
const LADO: usize = 16;
|
||||||
|
/// Celdas que caben en una fila.
|
||||||
|
const POR_FILA: usize = ANCHO / PASO;
|
||||||
|
/// Celdas que caben en la rejilla entera — el techo de lo que se pinta.
|
||||||
|
const MAX_CELDAS: usize = POR_FILA * (ALTO / PASO);
|
||||||
|
|
||||||
|
/// Indigo casi negro: el fondo del lienzo de memoriosa.
|
||||||
|
const FONDO: u32 = 0x10_0E_22;
|
||||||
|
/// Violeta: una celda por cada tecla pulsada en toda la historia de la app.
|
||||||
|
const CELDA: u32 = 0x8C_6E_F2;
|
||||||
|
/// Verde: la app nacio limpia — disco recien sembrado, sin estado previo.
|
||||||
|
const TESTIGO_FRESCO: u32 = 0x35_C4_6A;
|
||||||
|
/// Ambar: la app desperto con memoria — leyo su estado de una vida anterior.
|
||||||
|
const TESTIGO_RECORDADO: u32 = 0xF2_B2_33;
|
||||||
|
|
||||||
|
/// El lienzo de la aplicacion, en SU propia memoria lineal.
|
||||||
|
static mut LIENZO: [u32; ANCHO * ALTO] = [0; ANCHO * ALTO];
|
||||||
|
|
||||||
|
/// Los ocho bytes del recuento, de ida y de vuelta de la ranura de estado.
|
||||||
|
static mut ESTADO_IO: [u8; 8] = [0; 8];
|
||||||
|
|
||||||
|
/// El recuento vivo de pulsaciones — el estado que memoriosa persiste.
|
||||||
|
static mut CONTADOR: u64 = 0;
|
||||||
|
|
||||||
|
/// ¿Desperto la app con un estado previo? Decide el color del testigo.
|
||||||
|
static mut RECORDADO: bool = false;
|
||||||
|
|
||||||
|
/// Preparacion: el kernel la invoca UNA sola vez. memoriosa relee aqui su
|
||||||
|
/// estado persistido —el recuento de teclas de vidas anteriores— y pinta la
|
||||||
|
/// rejilla tal como la dejo la ultima vez que la maquina estuvo viva.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn init() {
|
||||||
|
// Cargar el estado persistido. `8` => habia un recuento anclado; la app
|
||||||
|
// despierta con memoria. `0` => disco recien sembrado, sin estado previo.
|
||||||
|
let leidos = unsafe { sys_estado_cargar(core::ptr::addr_of_mut!(ESTADO_IO) as u32, 8) };
|
||||||
|
if leidos == 8 {
|
||||||
|
// SEGURIDAD: lectura de un escalar `Copy` de un estatico propio.
|
||||||
|
unsafe {
|
||||||
|
CONTADOR = u64::from_le_bytes(*core::ptr::addr_of!(ESTADO_IO));
|
||||||
|
RECORDADO = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pintar();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Un fotograma de trabajo. Drena el canal de teclado: cada tecla pulsada suma
|
||||||
|
/// uno al recuento. Si hubo alguna pulsacion, persiste el nuevo total —el
|
||||||
|
/// kernel lo ancla en el manifiesto— y redibuja la rejilla.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn tick() {
|
||||||
|
let mut cambio = false;
|
||||||
|
loop {
|
||||||
|
let scancode = unsafe { sys_get_scancode() };
|
||||||
|
if scancode == 0 {
|
||||||
|
break; // Canal vacio: nada mas que atender este fotograma.
|
||||||
|
}
|
||||||
|
// Make codes del set 1: `0x01..=0x7F` (bit 7 a cero) — una tecla
|
||||||
|
// PULSADA. Los break codes —tecla soltada— llevan el bit 7 y se
|
||||||
|
// ignoran: asi cada pulsacion fisica cuenta exactamente una vez.
|
||||||
|
if (1..=0x7F).contains(&scancode) {
|
||||||
|
// SEGURIDAD: incremento de un escalar estatico propio; el kernel
|
||||||
|
// jamas reentra el modulo mientras `tick` corre.
|
||||||
|
unsafe {
|
||||||
|
CONTADOR += 1;
|
||||||
|
}
|
||||||
|
cambio = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cambio {
|
||||||
|
// Persistir el nuevo recuento. El kernel lo graba como un objeto del
|
||||||
|
// grafo y reescribe el manifiesto: sobrevivira al proximo apagon.
|
||||||
|
// SEGURIDAD: escritura de un escalar `Copy` a un estatico propio.
|
||||||
|
unsafe {
|
||||||
|
*core::ptr::addr_of_mut!(ESTADO_IO) = CONTADOR.to_le_bytes();
|
||||||
|
sys_estado_guardar(core::ptr::addr_of!(ESTADO_IO) as u32, 8);
|
||||||
|
}
|
||||||
|
pintar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pinta la cronica de pulsaciones: el fondo, una celda violeta por cada tecla
|
||||||
|
/// pulsada en toda la historia de la app y, en la esquina, el testigo que
|
||||||
|
/// delata si la app desperto con memoria o nacio limpia.
|
||||||
|
fn pintar() {
|
||||||
|
// SEGURIDAD: el kernel jamas reentra el modulo mientras `init` o `tick`
|
||||||
|
// corren; esta es la unica via de acceso a LIENZO durante esa ventana.
|
||||||
|
let lienzo: &mut [u32] = unsafe { &mut *core::ptr::addr_of_mut!(LIENZO) };
|
||||||
|
for pixel in lienzo.iter_mut() {
|
||||||
|
*pixel = FONDO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Una celda violeta por cada pulsacion registrada, dispuestas en rejilla.
|
||||||
|
// SEGURIDAD: lectura de escalares `Copy` de estaticos propios.
|
||||||
|
let contador = unsafe { *core::ptr::addr_of!(CONTADOR) };
|
||||||
|
let celdas = (contador as usize).min(MAX_CELDAS);
|
||||||
|
for i in 0..celdas {
|
||||||
|
let x = (i % POR_FILA) * PASO + 2;
|
||||||
|
let y = (i / POR_FILA) * PASO + 2;
|
||||||
|
rellenar(lienzo, x, y, LADO, LADO, CELDA);
|
||||||
|
}
|
||||||
|
|
||||||
|
// El testigo: ambar si la app leyo un estado de una vida anterior, verde
|
||||||
|
// si nacio en un disco recien sembrado, sin memoria que heredar.
|
||||||
|
let recordado = unsafe { *core::ptr::addr_of!(RECORDADO) };
|
||||||
|
let testigo = if recordado {
|
||||||
|
TESTIGO_RECORDADO
|
||||||
|
} else {
|
||||||
|
TESTIGO_FRESCO
|
||||||
|
};
|
||||||
|
rellenar(lienzo, ANCHO - 14, 4, 10, 10, testigo);
|
||||||
|
|
||||||
|
// SEGURIDAD: `sys_render_frame` es una capacidad del host; el (ptr, len)
|
||||||
|
// describe nuestra propia memoria lineal y el host lo verifica sin piedad.
|
||||||
|
unsafe {
|
||||||
|
sys_render_frame(lienzo.as_ptr() as u32, (ANCHO * ALTO * 4) as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rellena un rectangulo, recortado con firmeza a los limites del lienzo.
|
||||||
|
fn rellenar(lienzo: &mut [u32], x: usize, y: usize, ancho: usize, alto: usize, color: u32) {
|
||||||
|
let x1 = (x + ancho).min(ANCHO);
|
||||||
|
let y1 = (y + alto).min(ALTO);
|
||||||
|
let mut fila = y;
|
||||||
|
while fila < y1 {
|
||||||
|
let base = fila * ANCHO;
|
||||||
|
let mut col = x;
|
||||||
|
while col < x1 {
|
||||||
|
lienzo[base + col] = color;
|
||||||
|
col += 1;
|
||||||
|
}
|
||||||
|
fila += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -108,12 +108,12 @@ struct AppGenesis {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// El userspace de genesis — las cinco aplicaciones que pueblan un disco recien
|
/// El userspace de genesis — las cinco aplicaciones que pueblan un disco recien
|
||||||
/// forjado, con las regiones de la Fase 6.2. `app.wasm` aparece dos veces —dos
|
/// forjado. Un saludo (`hola`), la `memoriosa` interactiva que recuerda entre
|
||||||
/// instancias del mismo bytecode—; el grafo, direccionado por contenido, guarda
|
/// sesiones (Fase 7c), y tres demos de los guardarrailes del kernel: `discola`
|
||||||
/// su objeto una sola vez.
|
/// (combustible), `glotona` (memoria) y `cronista` (la cronica de los arranques).
|
||||||
const GENESIS: [AppGenesis; 5] = [
|
const GENESIS: [AppGenesis; 5] = [
|
||||||
AppGenesis { nombre: "hola-izq", archivo: "app.wasm", region: (100, 120, 480, 560) },
|
AppGenesis { nombre: "hola", archivo: "app.wasm", region: (100, 120, 480, 560) },
|
||||||
AppGenesis { nombre: "hola-der", archivo: "app.wasm", region: (700, 120, 480, 560) },
|
AppGenesis { nombre: "memoriosa", archivo: "memoriosa.wasm", region: (700, 120, 360, 80) },
|
||||||
AppGenesis { nombre: "discola", archivo: "discola.wasm", region: (60, 700, 360, 80) },
|
AppGenesis { nombre: "discola", archivo: "discola.wasm", region: (60, 700, 360, 80) },
|
||||||
AppGenesis { nombre: "glotona", archivo: "glotona.wasm", region: (460, 700, 360, 80) },
|
AppGenesis { nombre: "glotona", archivo: "glotona.wasm", region: (460, 700, 360, 80) },
|
||||||
AppGenesis { nombre: "cronista", archivo: "cronista.wasm", region: (860, 700, 360, 80) },
|
AppGenesis { nombre: "cronista", archivo: "cronista.wasm", region: (860, 700, 360, 80) },
|
||||||
|
|||||||
Executable
BIN
Binary file not shown.
@@ -258,8 +258,8 @@ pub fn manifiesto() -> Option<Hash> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Ancla un objeto como el Manifiesto de Genesis y graba el cambio en el
|
/// Ancla un objeto como el Manifiesto de Genesis y graba el cambio en el
|
||||||
/// superbloque. Gemelo de [`fijar_raiz`].
|
/// superbloque. Gemelo de [`fijar_raiz`]. La Fase 7c lo invoca cada vez que una
|
||||||
#[allow(dead_code)] // Lo usara la Fase 7c (persistencia inter-sesion).
|
/// app persiste su estado y el manifiesto debe re-anclarse.
|
||||||
pub fn fijar_manifiesto(hash: Hash) -> Result<(), &'static str> {
|
pub fn fijar_manifiesto(hash: Hash) -> Result<(), &'static str> {
|
||||||
let mutex = ALMACEN.get().ok_or("almacen no inicializado")?;
|
let mutex = ALMACEN.get().ok_or("almacen no inicializado")?;
|
||||||
let mut almacen = mutex.lock();
|
let mut almacen = mutex.lock();
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ async fn tarea_sonda_disco() {
|
|||||||
/// despacha como tarea cooperativa del reactor. Si el bytecode falta, esta
|
/// despacha como tarea cooperativa del reactor. Si el bytecode falta, esta
|
||||||
/// corrupto, o la carga fracasa, se salda pintando la region de la app con
|
/// corrupto, o la carga fracasa, se salda pintando la region de la app con
|
||||||
/// la baliza de desalojo — el kernel no se inmuta y sigue con las demas.
|
/// la baliza de desalojo — el kernel no se inmuta y sigue con las demas.
|
||||||
fn encender_app(ejecutor: &mut Executor, entrada: &manifiesto::EntradaApp) {
|
fn encender_app(ejecutor: &mut Executor, indice: usize, entrada: &manifiesto::EntradaApp) {
|
||||||
let region = manifiesto::region(entrada);
|
let region = manifiesto::region(entrada);
|
||||||
// Recuperar el bytecode del grafo. `recuperar` recomputa el hash del
|
// Recuperar el bytecode del grafo. `recuperar` recomputa el hash del
|
||||||
// objeto y verifica su integridad: un bytecode corrupto se delata aqui
|
// objeto y verifica su integridad: un bytecode corrupto se delata aqui
|
||||||
@@ -145,7 +145,9 @@ fn encender_app(ejecutor: &mut Executor, entrada: &manifiesto::EntradaApp) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match wasm::AplicacionWasm::cargar(&bytecode, region, entrada.techo_memoria as usize) {
|
// `indice` es la identidad de la app en el manifiesto: las capacidades de
|
||||||
|
// estado persistido (Fase 7c) la usan para hallar SU ranura `estado`.
|
||||||
|
match wasm::AplicacionWasm::cargar(&bytecode, region, entrada.techo_memoria as usize, indice) {
|
||||||
Ok(app) => ejecutor.spawn(tarea_aplicacion(app)),
|
Ok(app) => ejecutor.spawn(tarea_aplicacion(app)),
|
||||||
Err(_) => consola::pintar_desalojo(region, Color::DESALOJO),
|
Err(_) => consola::pintar_desalojo(region, Color::DESALOJO),
|
||||||
}
|
}
|
||||||
@@ -163,10 +165,11 @@ fn reportar(linea: &str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// FASE 7 :: puebla el userspace DESDE EL GRAFO. Carga el Manifiesto de
|
/// FASE 7 :: puebla el userspace DESDE EL GRAFO. Carga el Manifiesto de
|
||||||
/// Genesis que `boot` sembro en la imagen de disco y, por cada `EntradaApp`,
|
/// Genesis que `boot` sembro en la imagen de disco, lo instala como el
|
||||||
/// enciende su aplicacion. Toda falla se reporta a la consola y NO detiene el
|
/// manifiesto VIVO del kernel y, por cada `EntradaApp`, enciende su
|
||||||
/// arranque: el kernel se levanta con las apps que pueda — o con ninguna, si
|
/// aplicacion. Toda falla se reporta a la consola y NO detiene el arranque: el
|
||||||
/// el grafo no tiene userspace.
|
/// kernel se levanta con las apps que pueda — o con ninguna, si el grafo no
|
||||||
|
/// tiene userspace.
|
||||||
fn cargar_userspace(ejecutor: &mut Executor) {
|
fn cargar_userspace(ejecutor: &mut Executor) {
|
||||||
let manifiesto = match manifiesto::cargar() {
|
let manifiesto = match manifiesto::cargar() {
|
||||||
Ok(Some(m)) => Some(m),
|
Ok(Some(m)) => Some(m),
|
||||||
@@ -192,8 +195,13 @@ fn cargar_userspace(ejecutor: &mut Executor) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(m) = manifiesto {
|
if let Some(m) = manifiesto {
|
||||||
for entrada in &m.apps {
|
// Instalar el manifiesto VIVO ANTES de instanciar las apps: el `init`
|
||||||
encender_app(ejecutor, entrada);
|
// de cada app puede consultar su estado persistido (Fase 7c), y esa
|
||||||
|
// consulta lee del manifiesto vivo. Se instala una copia; la otra se
|
||||||
|
// itera para encender cada app con su indice — su identidad.
|
||||||
|
manifiesto::instalar(m.clone());
|
||||||
|
for (indice, entrada) in m.apps.iter().enumerate() {
|
||||||
|
encender_app(ejecutor, indice, entrada);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,17 +7,24 @@
|
|||||||
// cuota, en que region— lo dicta este Manifiesto de Genesis, que tambien
|
// cuota, en que region— lo dicta este Manifiesto de Genesis, que tambien
|
||||||
// habita el grafo. El superbloque guarda su hash en un ancla propia.
|
// 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
|
// ESTADO: Fase 7c. El manifiesto deja de ser de solo lectura. El kernel
|
||||||
// imagen —grabar los objetos de bytecode y el manifiesto en un disco virgen—
|
// conserva una copia VIVA —`VIVO`, un `Mutex<Manifiesto>`—; cuando una app
|
||||||
// la hace por completo el constructor de imagen `boot`, en el anfitrion. Este
|
// persiste su estado (`sys_estado_guardar`), el kernel actualiza la ranura
|
||||||
// modulo se reduce a su esencia: LEER el manifiesto del grafo al arrancar.
|
// `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
|
// Los tipos `Manifiesto` / `EntradaApp` y su (de)serializacion viven en la
|
||||||
// crate `formato`, el nucleo `no_std` compartido con `boot`. Aqui solo queda
|
// 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
|
// lo que es del kernel: cargar el manifiesto, traducir regiones, custodiar la
|
||||||
// regiones de su formato en disco (`u32`) a la `RegionPantalla` del kernel.
|
// 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::almacen;
|
||||||
use crate::grafico::RegionPantalla;
|
use crate::grafico::RegionPantalla;
|
||||||
|
|
||||||
@@ -25,6 +32,12 @@ use crate::grafico::RegionPantalla;
|
|||||||
// resto del kernel los nombre `manifiesto::EntradaApp` / `manifiesto::Manifiesto`.
|
// resto del kernel los nombre `manifiesto::EntradaApp` / `manifiesto::Manifiesto`.
|
||||||
pub use formato::{EntradaApp, 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
|
/// Traduce la sub-region de una `EntradaApp` —campos `u32` de ancho fijo, el
|
||||||
/// formato en disco— a la `RegionPantalla` que el kernel entiende (`usize`,
|
/// 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
|
/// 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)?;
|
let manifiesto = Manifiesto::deserializar(&objeto.datos)?;
|
||||||
Ok(Some(manifiesto))
|
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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,7 +11,9 @@
|
|||||||
// * sys_object_datos — leer la carga util de un objeto;
|
// * sys_object_datos — leer la carga util de un objeto;
|
||||||
// * sys_object_hijo — recorrer las aristas del DAG;
|
// * sys_object_hijo — recorrer las aristas del DAG;
|
||||||
// * sys_object_raiz — leer la raiz del grafo;
|
// * sys_object_raiz — leer la raiz del grafo;
|
||||||
// * sys_object_fijar_raiz — coronar un objeto como raiz.
|
// * sys_object_fijar_raiz — coronar un objeto como raiz;
|
||||||
|
// * sys_estado_cargar — leer el estado persistido de la app (Fase 7c);
|
||||||
|
// * sys_estado_guardar — anclar el estado persistido de la app (Fase 7c).
|
||||||
//
|
//
|
||||||
// GUARDARRAIL: el kernel valida MATEMATICAMENTE todo puntero que el modulo le
|
// GUARDARRAIL: el kernel valida MATEMATICAMENTE todo puntero que el modulo le
|
||||||
// entrega contra los limites reales de su memoria lineal. No se confia en que
|
// entrega contra los limites reales de su memoria lineal. No se confia en que
|
||||||
@@ -40,6 +42,10 @@ pub(crate) struct ContextoCapacidades {
|
|||||||
/// El techo de recursos de la aplicacion — hoy, su memoria lineal maxima.
|
/// El techo de recursos de la aplicacion — hoy, su memoria lineal maxima.
|
||||||
/// `wasmi` lo consulta en cada `memory.grow` via `Store::limiter`.
|
/// `wasmi` lo consulta en cada `memory.grow` via `Store::limiter`.
|
||||||
pub(crate) limites: StoreLimits,
|
pub(crate) limites: StoreLimits,
|
||||||
|
/// El indice de esta app en el Manifiesto de Genesis — su identidad. Las
|
||||||
|
/// capacidades de estado (Fase 7c) lo usan para hallar la `EntradaApp`
|
||||||
|
/// correcta: cada app persiste en SU ranura, jamas en la de otra.
|
||||||
|
pub(crate) indice_app: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recupera la memoria lineal exportada por el modulo. Que no la exporte es un
|
/// Recupera la memoria lineal exportada por el modulo. Que no la exporte es un
|
||||||
@@ -345,5 +351,89 @@ pub(crate) fn enlazar_capacidades(
|
|||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// --- CAPACIDAD 8 :: sys_estado_cargar(salida, capacidad) -> i32 ---
|
||||||
|
// Copia el estado persistido de ESTA app —el objeto que su `EntradaApp` del
|
||||||
|
// manifiesto tiene anclado— en `salida`. Devuelve el numero de bytes
|
||||||
|
// copiados, 0 si la app no tiene estado previo, -1 si el objeto anclado no
|
||||||
|
// existe, -2 si `capacidad` no basta, -3 si el almacenamiento fallo.
|
||||||
|
enlazador.func_wrap(
|
||||||
|
"renaser",
|
||||||
|
"sys_estado_cargar",
|
||||||
|
|mut caller: Caller<'_, ContextoCapacidades>,
|
||||||
|
salida: u32,
|
||||||
|
capacidad: u32|
|
||||||
|
-> Result<i32, Error> {
|
||||||
|
let indice = caller.data().indice_app;
|
||||||
|
// El hash del estado de esta app, segun el manifiesto vivo.
|
||||||
|
let hash = match crate::manifiesto::estado_de(indice) {
|
||||||
|
Some(hash) => hash,
|
||||||
|
None => return Ok(0), // Sin estado previo: nada que cargar.
|
||||||
|
};
|
||||||
|
let objeto = match crate::almacen::recuperar(&hash) {
|
||||||
|
Ok(Some(objeto)) => objeto,
|
||||||
|
Ok(None) => return Ok(-1),
|
||||||
|
Err(_) => return Ok(-3),
|
||||||
|
};
|
||||||
|
if objeto.datos.len() > capacidad as usize {
|
||||||
|
return Ok(-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
let memoria = obtener_memoria(&caller)?;
|
||||||
|
// Verificar que el destino cabe, y solo entonces copiar.
|
||||||
|
{
|
||||||
|
let m = memoria.data(&caller);
|
||||||
|
rango(
|
||||||
|
m,
|
||||||
|
salida,
|
||||||
|
objeto.datos.len(),
|
||||||
|
"WASM :: sys_estado_cargar desbordo la memoria lineal (salida)",
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
let n = objeto.datos.len();
|
||||||
|
let m = memoria.data_mut(&mut caller);
|
||||||
|
m[salida as usize..salida as usize + n].copy_from_slice(&objeto.datos);
|
||||||
|
Ok(n as i32)
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// --- CAPACIDAD 9 :: sys_estado_guardar(datos, datos_len) -> i32 ---
|
||||||
|
// Graba `datos` como el estado persistido de ESTA app: el kernel lo
|
||||||
|
// almacena como un objeto del grafo y ancla su hash en la `EntradaApp` de
|
||||||
|
// la app, re-grabando y re-anclando el manifiesto. El estado sobrevivira al
|
||||||
|
// reinicio. Devuelve 0 si se logro, -3 si el almacenamiento fallo.
|
||||||
|
enlazador.func_wrap(
|
||||||
|
"renaser",
|
||||||
|
"sys_estado_guardar",
|
||||||
|
|caller: Caller<'_, ContextoCapacidades>,
|
||||||
|
datos_ptr: u32,
|
||||||
|
datos_len: u32|
|
||||||
|
-> Result<i32, Error> {
|
||||||
|
let indice = caller.data().indice_app;
|
||||||
|
let memoria = obtener_memoria(&caller)?;
|
||||||
|
// Leer el estado de la memoria lineal, con limites firmes.
|
||||||
|
let datos = {
|
||||||
|
let m = memoria.data(&caller);
|
||||||
|
rango(
|
||||||
|
m,
|
||||||
|
datos_ptr,
|
||||||
|
datos_len as usize,
|
||||||
|
"WASM :: sys_estado_guardar desbordo la memoria lineal (datos)",
|
||||||
|
)?
|
||||||
|
.to_vec()
|
||||||
|
};
|
||||||
|
// Grabar el objeto de estado. Un fallo del almacen NO es culpa de
|
||||||
|
// la app: se le devuelve -3, y ella decide que hacer.
|
||||||
|
let hash = match crate::almacen::almacenar(datos, alloc::vec::Vec::new()) {
|
||||||
|
Ok(hash) => hash,
|
||||||
|
Err(_) => return Ok(-3),
|
||||||
|
};
|
||||||
|
// Anclarlo: muta el manifiesto vivo, lo re-graba y lo re-ancla.
|
||||||
|
match crate::manifiesto::fijar_estado(indice, hash) {
|
||||||
|
Ok(()) => Ok(0),
|
||||||
|
Err(_) => Ok(-3),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,11 +85,14 @@ impl AplicacionWasm {
|
|||||||
/// el reactor en cada pulso del reloj.
|
/// el reactor en cada pulso del reloj.
|
||||||
///
|
///
|
||||||
/// `techo_memoria` es la cuota de memoria lineal de ESTA app, en bytes —
|
/// `techo_memoria` es la cuota de memoria lineal de ESTA app, en bytes —
|
||||||
/// desde la Fase 7 la dicta su `EntradaApp` del manifiesto.
|
/// desde la Fase 7 la dicta su `EntradaApp` del manifiesto. `indice_app` es
|
||||||
|
/// su posicion en el manifiesto: su identidad para las capacidades de
|
||||||
|
/// estado persistido (Fase 7c).
|
||||||
pub fn cargar(
|
pub fn cargar(
|
||||||
bytecode: &[u8],
|
bytecode: &[u8],
|
||||||
region: RegionPantalla,
|
region: RegionPantalla,
|
||||||
techo_memoria: usize,
|
techo_memoria: usize,
|
||||||
|
indice_app: usize,
|
||||||
) -> Result<AplicacionWasm, FallaApp> {
|
) -> Result<AplicacionWasm, FallaApp> {
|
||||||
// 1. El motor, con metricas de combustible y compilacion ANTICIPADA: la
|
// 1. El motor, con metricas de combustible y compilacion ANTICIPADA: la
|
||||||
// traduccion del modulo ocurre ahora, de modo que el `fuel` mida
|
// traduccion del modulo ocurre ahora, de modo que el `fuel` mida
|
||||||
@@ -119,6 +122,7 @@ impl AplicacionWasm {
|
|||||||
region,
|
region,
|
||||||
canal,
|
canal,
|
||||||
limites,
|
limites,
|
||||||
|
indice_app,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
// Ligar el limitador de recursos: `wasmi` lo consultara en cada
|
// Ligar el limitador de recursos: `wasmi` lo consultara en cada
|
||||||
|
|||||||
Reference in New Issue
Block a user