tawasuyu

tawasuyu/brahman

Rust 582TOML 222Markdown 83JSON 198635 Definiciones

⎇ 07ab095d4202aa5ff30d9a109e610d517a434e74 renaser/kernel/src/async_system/executor.rs Rust · 161 líneas · 6.5 KB Historia Quién tocó qué crudo

feat(renaser): Fase 10 — alta y baja de aplicaciones en vivo 089afccbb · sergio ·

1// =============================================================================
2//  renaser :: async_system/executor.rs — Fase 3 :: el reactor cooperativo
3// -----------------------------------------------------------------------------
4//  El ejecutor es el corazon reactivo de renaser. Mantiene el censo de tareas
5//  vivas y una cola de las que estan listas para avanzar. Cuando no queda nada
6//  por hacer, no malgasta la CPU en un bucle ocupado: la duerme con `hlt`
7//  hasta que el proximo impulso de hardware la despierte.
8//
9//  FASE 10 :: el censo deja de ser inmutable tras el arranque. Una cola de
10//  NACIMIENTOS permite engendrar tareas EN VIVO —con el reactor ya en marcha—:
11//  el orquestador deposita un futuro y el ejecutor lo adopta en su proxima
12//  vuelta. Asi el userspace puede crecer despues del arranque.
13// =============================================================================
14
15use alloc::boxed::Box;
16use alloc::collections::{BTreeMap, VecDeque};
17use alloc::sync::Arc;
18use alloc::vec::Vec;
19use core::future::Future;
20use core::pin::Pin;
21use core::task::{Context, Poll, Waker};
22
23use spin::{Mutex, Once};
24use x86_64::instructions::interrupts;
25
26use super::task::{Task, TaskId};
27use super::waker::{self, ColaListas};
28
29/// Un futuro de tarea ya anclado en el heap: la moneda de los nacimientos.
30pub type FuturoTarea = Pin<Box<dyn Future<Output = ()> + Send + 'static>>;
31
32/// Cola de NACIMIENTOS: tareas engendradas EN VIVO, mientras el reactor ya
33/// corre. Una tarea cooperativa deposita aqui un futuro; el ejecutor lo recoge
34/// al inicio de su proxima vuelta y lo da de alta. No la toca ningun manejador
35/// de IRQ —solo tareas cooperativas y el propio ejecutor—, asi que un `Mutex`
36/// llano basta: en un solo nucleo cooperativo nadie disputa el cerrojo.
37static NACIMIENTOS: Once<Mutex<Vec<FuturoTarea>>> = Once::new();
38
39/// Engendra una tarea nueva mientras el reactor ya corre (Fase 10). El ejecutor
40/// la adoptara en su proxima vuelta. La invocan los orquestadores del kernel
41/// —jamas una IRQ—.
42pub fn engendrar(futuro: FuturoTarea) {
43    if let Some(nacimientos) = NACIMIENTOS.get() {
44        nacimientos.lock().push(futuro);
45    }
46}
47
48/// El ejecutor cooperativo de renaser.
49pub struct Executor {
50    /// Censo de todas las tareas vivas, indexadas por su identidad.
51    tareas: BTreeMap<TaskId, Task>,
52    /// Cola de tareas listas para su proximo avance.
53    cola_listas: ColaListas,
54    /// Cache de wakers: uno por tarea, reutilizado entre avances.
55    cache_wakers: BTreeMap<TaskId, Waker>,
56}
57
58impl Executor {
59    /// Crea un ejecutor vacio. Requiere que el heap ya este fundado.
60    pub fn nuevo() -> Executor {
61        NACIMIENTOS.call_once(|| Mutex::new(Vec::new()));
62        Executor {
63            tareas: BTreeMap::new(),
64            cola_listas: Arc::new(Mutex::new(VecDeque::new())),
65            cache_wakers: BTreeMap::new(),
66        }
67    }
68
69    /// Da de alta una tarea nueva y la marca como lista para su primer avance.
70    pub fn spawn(&mut self, futuro: impl Future<Output = ()> + Send + 'static) {
71        self.alistar(Task::nueva(futuro));
72    }
73
74    /// Inscribe una tarea ya construida en el censo y la encola para su primer
75    /// avance. Es la via comun de `spawn` y de la recoleccion de nacimientos.
76    fn alistar(&mut self, tarea: Task) {
77        let id = tarea.id;
78        if self.tareas.insert(id, tarea).is_some() {
79            panic!("renaser :: TaskId duplicado — lo imposible ha ocurrido");
80        }
81        interrupts::without_interrupts(|| self.cola_listas.lock().push_back(id));
82    }
83
84    /// Recoge las tareas engendradas EN VIVO desde la ultima vuelta y las da de
85    /// alta. Se invoca al inicio de cada ciclo del reactor (Fase 10).
86    fn recoger_nacimientos(&mut self) {
87        let Some(nacimientos) = NACIMIENTOS.get() else {
88            return;
89        };
90        let recien: Vec<FuturoTarea> = {
91            let mut cola = nacimientos.lock();
92            if cola.is_empty() {
93                return;
94            }
95            core::mem::take(&mut *cola)
96        };
97        for futuro in recien {
98            self.alistar(Task::adoptar(futuro));
99        }
100    }
101
102    /// Avanza, hasta agotarla, la cola de tareas listas.
103    fn avanzar_listas(&mut self) {
104        loop {
105            // Sacar el siguiente id con las interrupciones acalladas: la cola
106            // es compartida con los wakers que se disparan desde las IRQ.
107            let siguiente = interrupts::without_interrupts(|| self.cola_listas.lock().pop_front());
108            let id = match siguiente {
109                Some(id) => id,
110                None => return,
111            };
112            let tarea = match self.tareas.get_mut(&id) {
113                Some(t) => t,
114                None => continue, // la tarea concluyo en una vuelta previa
115            };
116            // Un waker por tarea, reutilizado: reinyecta esta tarea en la cola.
117            let cola = self.cola_listas.clone();
118            let despertador = self
119                .cache_wakers
120                .entry(id)
121                .or_insert_with(|| waker::crear(id, cola));
122            let mut contexto = Context::from_waker(despertador);
123            if let Poll::Ready(()) = tarea.poll(&mut contexto) {
124                // La tarea termino: se retira del censo y se libera su waker.
125                self.tareas.remove(&id);
126                self.cache_wakers.remove(&id);
127            }
128        }
129    }
130
131    /// Si no queda trabajo, duerme la CPU hasta la proxima interrupcion. El
132    /// chequeo y el `hlt` se hacen con las interrupciones acalladas para que
133    /// ningun despertar se pierda en la rendija entre uno y otro. Una tarea
134    /// recien engendrada cuenta como trabajo: no se duerme con nacimientos
135    /// pendientes de adoptar.
136    fn dormir_si_inactivo(&self) {
137        interrupts::disable();
138        let hay_listas = !self.cola_listas.lock().is_empty();
139        let hay_nacimientos = NACIMIENTOS
140            .get()
141            .map(|nacimientos| !nacimientos.lock().is_empty())
142            .unwrap_or(false);
143        if hay_listas || hay_nacimientos {
144            // Llego trabajo justo ahora: reactivar y seguir sin dormir.
145            interrupts::enable();
146        } else {
147            // `sti; hlt` atomico: habilita y duerme sin condicion de carrera.
148            interrupts::enable_and_hlt();
149        }
150    }
151
152    /// Cede el hilo principal al reactor. No retorna jamas: desde aqui, renaser
153    /// vive del latir de sus interrupciones.
154    pub fn run(&mut self) -> ! {
155        loop {
156            self.recoger_nacimientos();
157            self.avanzar_listas();
158            self.dormir_si_inactivo();
159        }
160    }
161}