⎇ e2272c0ed3c87ef53fd5ea5c9e6fd52d4790dcb8
renaser/kernel/src/async_system/executor.rs
Ñawpa willay
Pi imata llamkarqan
ch'uya
feat: integra renaser (kernel SASOS bare-metal) al monorepo
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
10use alloc::collections::{BTreeMap, VecDeque};
11use alloc::sync::Arc;
12use core::future::Future;
13use core::task::{Context, Poll, Waker};
14
15use spin::Mutex;
16use x86_64::instructions::interrupts;
17
18use super::task::{Task, TaskId};
19use super::waker::{self, ColaListas};
20
21/// El ejecutor cooperativo de renaser.
22pub struct Executor {
23 /// Censo de todas las tareas vivas, indexadas por su identidad.
24 tareas: BTreeMap<TaskId, Task>,
25 /// Cola de tareas listas para su proximo avance.
26 cola_listas: ColaListas,
27 /// Cache de wakers: uno por tarea, reutilizado entre avances.
28 cache_wakers: BTreeMap<TaskId, Waker>,
29}
30
31impl Executor {
32 /// Crea un ejecutor vacio. Requiere que el heap ya este fundado.
33 pub fn nuevo() -> Executor {
34 Executor {
35 tareas: BTreeMap::new(),
36 cola_listas: Arc::new(Mutex::new(VecDeque::new())),
37 cache_wakers: BTreeMap::new(),
38 }
39 }
40
41 /// Da de alta una tarea nueva y la marca como lista para su primer avance.
42 pub fn spawn(&mut self, futuro: impl Future<Output = ()> + Send + 'static) {
43 let tarea = Task::nueva(futuro);
44 let id = tarea.id;
45 if self.tareas.insert(id, tarea).is_some() {
46 panic!("renaser :: TaskId duplicado — lo imposible ha ocurrido");
47 }
48 interrupts::without_interrupts(|| self.cola_listas.lock().push_back(id));
49 }
50
51 /// Avanza, hasta agotarla, la cola de tareas listas.
52 fn avanzar_listas(&mut self) {
53 loop {
54 // Sacar el siguiente id con las interrupciones acalladas: la cola
55 // es compartida con los wakers que se disparan desde las IRQ.
56 let siguiente = interrupts::without_interrupts(|| self.cola_listas.lock().pop_front());
57 let id = match siguiente {
58 Some(id) => id,
59 None => return,
60 };
61 let tarea = match self.tareas.get_mut(&id) {
62 Some(t) => t,
63 None => continue, // la tarea concluyo en una vuelta previa
64 };
65 // Un waker por tarea, reutilizado: reinyecta esta tarea en la cola.
66 let cola = self.cola_listas.clone();
67 let despertador = self
68 .cache_wakers
69 .entry(id)
70 .or_insert_with(|| waker::crear(id, cola));
71 let mut contexto = Context::from_waker(despertador);
72 if let Poll::Ready(()) = tarea.poll(&mut contexto) {
73 // La tarea termino: se retira del censo y se libera su waker.
74 self.tareas.remove(&id);
75 self.cache_wakers.remove(&id);
76 }
77 }
78 }
79
80 /// Si no queda trabajo, duerme la CPU hasta la proxima interrupcion. El
81 /// chequeo y el `hlt` se hacen con las interrupciones acalladas para que
82 /// ningun despertar se pierda en la rendija entre uno y otro.
83 fn dormir_si_inactivo(&self) {
84 interrupts::disable();
85 let hay_trabajo = !self.cola_listas.lock().is_empty();
86 if hay_trabajo {
87 // Llego trabajo justo ahora: reactivar y seguir sin dormir.
88 interrupts::enable();
89 } else {
90 // `sti; hlt` atomico: habilita y duerme sin condicion de carrera.
91 interrupts::enable_and_hlt();
92 }
93 }
94
95 /// Cede el hilo principal al reactor. No retorna jamas: desde aqui, renaser
96 /// vive del latir de sus interrupciones.
97 pub fn run(&mut self) -> ! {
98 loop {
99 self.avanzar_listas();
100 self.dormir_si_inactivo();
101 }
102 }
103}