⎇ 71ebcea899d0ac71f959ad5461d830d952087735
renaser/kernel/src/async_system/executor.rs
Historia
Quién tocó qué
crudo
feat(renaser): Fase 10 — alta y baja de aplicaciones en vivo
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}