Cancelar y apagar tareas
Un manejador que se descarta no para nada, y una tarea que nadie espera puede morir a medias. Los dos mecanismos —el testigo y el manejador— y el registro de trabajos que hace posible apagar bien.
Dos mecanismos, dos cosas distintas
De ahí sale la corrección del registro de trabajos del nivel 21.3: no basta con guardar el testigo, hay que guardar también el manejador.
El registro de trabajos
pub struct Trabajo { pub testigo: CancellationToken, pub manejador: JoinHandle<()>,}pub struct Trabajos(pub Mutex<HashMap<TrabajoId, Trabajo>>);impl Trabajos { pub fn registrar(&self, id: TrabajoId, t: Trabajo) { self.0.lock().unwrap().insert(id, t); } pub fn cancelar(&self, id: TrabajoId) { if let Some(t) = self.0.lock().unwrap().get(&id) { t.testigo.cancel(); } } /// Pide a todos que paren y devuelve los manejadores para esperarlos. pub fn cancelar_todos(&self) -> Vec<JoinHandle<()>> { let mut g = self.0.lock().unwrap(); for t in g.values() { t.testigo.cancel(); } g.drain().map(|(_, t)| t.manejador).collect() }}El último método es el que hace posible el nivel 25.4: primero se pide a todos que paren, y después se espera a todos. Al revés —esperar a uno, cancelar el siguiente— multiplica el tiempo por el número de tareas.
Y fíjate en que devuelve los manejadores en vez de esperarlos dentro: esperar exige un punto de espera, y ahí está el cerrojo tomado. Es la regla del nivel 16.5 otra vez.
Que la tarea se dé de baja
Falta la otra mitad: si una tarea termina sola, su entrada se queda en el registro para siempre. Con el tiempo, el mapa se llena de trabajos muertos y cancelar_todos espera a manejadores de tareas que acabaron hace horas.
La corrección es que la tarea se limpie a sí misma:
let trabajos = app.state::<Trabajos>().inner().clone();let manejador = tauri::async_runtime::spawn(async move { hacer_el_trabajo(testigo_clon).await; trabajos.dar_de_baja(id); // pase lo que pase, se quita del registro});Con un detalle: la baja tiene que ocurrir también si la tarea entra en pánico. La forma robusta es un guardián que se dé de baja al destruirse, de modo que ocurra por el camino normal y por el del pánico.
El tiempo de espera
Al apagar hay que poner un límite, porque una tarea que no comprueba su testigo con la frecuencia suficiente puede tardar. Y hay que decidir qué pasa cuando se agota:
Salir igual es lo correcto. Una aplicación que no se cierra porque una tarea no responde es peor que una que cierra dejando trabajo a medias — siempre que lo que importaba ya se hubiera escrito, que es la política del nivel 22.5.