El ciclo de vida del proceso y la persistencia en sesión
Mecanismos del sistema operativo para la ejecución en segundo plano: desacoplamiento entre ventana y proceso, integración con el system tray, servicios de autostart y contratos de ciclo de vida en macOS, Windows y Linux.
El proceso desacoplado de la ventana gráfica
Una aplicación de escritorio Tauri se compone de un proceso anfitrión en Rust que inicializa un bucle de eventos del sistema (a través de la biblioteca tao) y renderiza la interfaz en ventanas Webview (mediante wry).
A nivel del kernel, el proceso existe desde que el cargador de ejecutables asigna su espacio de direcciones virtuales y arranca el hilo principal. Cerrar una ventana destruye únicamente el contexto de la Webview y la estructura asociada del gestor de ventanas del SO, pero no implica necesariamente la terminación del proceso.
// Intercepción explícita del ciclo de vida en Tauri v2use tauri::{Manager, WindowEvent};pub fn configurar_ciclo_vida(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<tauri::Wry> { builder.on_window_event(|window, event| { if let WindowEvent::CloseRequested { api, .. } = event { // Cancela la terminación por defecto del proceso api.prevent_close(); // Oculta la ventana gráfica manteniendo el proceso activo en segundo plano let _ = window.hide(); } })}Al invocar api.prevent_close(), el proceso Core intercepta la señal de cierre antes de que el bucle de eventos propague la salida. La aplicación continúa viva, consumiendo memoria asignada y ejecutando tareas asíncronas en el pool de hilos de Tokio.
La divergencia de plataformas en el cierre de aplicaciones
Cada familia de sistemas operativos implementa un contrato histórico distinto para la terminación de programas gráficos:
[Divergencia del bucle de eventos ante el cierre de ventana]macOS (Cocoa): [Última Ventana Cerrada] ──> Proceso sigue vivo en Dock/MenúBar ──> Cmd+Q (terminate:)Windows (Win32): [Ventana Principal Cerrada] ──> WM_CLOSE ──> WM_DESTROY ──> PostQuitMessage(0) ──> Proceso muereLinux (X11 / Wayland): [Cierre de Ventana] ──> Señal DeleteWindow ──> Destrucción del bucle si no hay System Tray activo- macOS (
NSApplication): En macOS, las aplicaciones se conceptualizan como entidades a nivel de sistema (NSApplicationActivationPolicyRegular). La barra de menú superior pertenece a la aplicación, no a la ventana. El comportamiento nativo de la plataforma es que al cerrar la última ventana (applicationShouldTerminateAfterLastWindowClosed:devolviendofalse), la aplicación continúe viva en la barra de menú superior y en el Dock, lista para instanciar una ventana nueva ante un clic del usuario. - Windows (
Win32): En Windows, el modelo tradicional ata el bucle de mensajes a la ventana principal. El mensajeWM_CLOSEdesencadena la destrucción del manejadorHWNDy despachaWM_QUIT, culminando el hilo principal. Si la aplicación pretende permanecer viva sin ventanas visibles, debe mantener un icono activo en la bandeja de notificación (Notification Area) antes de destruir o esconder la ventana. - Linux (
FreeDesktop): En entornos de escritorio Linux (GNOME, KDE Plasma), las aplicaciones suelen terminarse al cerrar su última ventana salvo que expongan un servicio en D-Bus mediante la especificaciónorg.kde.StatusNotifierItem(System Tray).
Mecanismos del sistema operativo para persistencia en sesión
Cuando una aplicación requiere arrancar automáticamente con la sesión del usuario o vigilar eventos del sistema de forma desatendida, no recurre a técnicas web sino a subsistemas nativos del host:
La responsabilidad del consumo de hardware en background
Tener un proceso en ejecución continua fuera de la supervisión del usuario impone estrictas restricciones de arquitectura:
// Antipatrón: Polling ocupado en background (quema ciclos y drena batería)// loop { comprobar_servidor(); std::thread::sleep(Duration::from_millis(500)); }// Patrón correcto: Suspensión cooperativa guiada por eventosuse tauri::async_runtime;use tokio::time::{sleep, Duration};pub fn iniciar_vigilante_background(app_handle: tauri::AppHandle) { async_runtime::spawn(async move { let mut intervalo = tokio::time::interval(Duration::from_secs(60)); loop { intervalo.tick().await; // El hilo duerme en el runtime de Tokio; 0% CPU if let Err(err) = sincronizar_datos(&app_handle).await { eprintln!("Fallo en sincronización en background: {err}"); } } });}En dispositivos portátiles, un proceso en background que despierta la CPU cada pocos milisegundos impide que el procesador entre en estados de bajo consumo de energía (C-states en arquitecturas x86/ARM), degradando notablemente la autonomía de la batería. Las tareas en background de Tauri deben ser guiadas por eventos (event-driven): reaccionar a sockets de red o eventos de disco (notify), nunca a bucles de sondeo activo (busy polling).