From 025bbc26701b4e906745f91b6cf4927f3cd39b8f Mon Sep 17 00:00:00 2001 From: Dane Wagner Date: Wed, 15 Apr 2026 23:43:30 -0500 Subject: [PATCH] Shutdown cleanup --- src/core/main.c | 2 -- src/core/mqtt_client.c | 6 ++++++ src/core/shutdown_coordinator.c | 10 +++++++--- src/database/db_recordings_sync.c | 2 ++ src/telemetry/stream_metrics.c | 4 ++++ src/video/mp4_writer_thread.c | 4 ++-- src/web/api_handlers_health.c | 2 ++ 7 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/core/main.c b/src/core/main.c index 55e30d37c..3ac20afe7 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1308,7 +1308,6 @@ int main(int argc, char *argv[]) { usleep(1500000); // 1500ms // Finalize all MP4 recordings first before cleaning up the backend - log_info("Finalizing all MP4 recordings..."); close_all_mp4_writers(); // Update MP4 writer components state @@ -1360,7 +1359,6 @@ int main(int argc, char *argv[]) { usleep(200000); // 200ms (reduced from 1000ms) // Clean up all HLS writers first to ensure proper FFmpeg resource cleanup - log_info("Cleaning up all HLS writers..."); cleanup_all_hls_writers(); // Clean up HLS streaming backend diff --git a/src/core/mqtt_client.c b/src/core/mqtt_client.c index fc5801d86..4974bfc08 100644 --- a/src/core/mqtt_client.c +++ b/src/core/mqtt_client.c @@ -982,6 +982,9 @@ int mqtt_start_ha_services(void) { log_error("MQTT HA: Failed to create motion timeout thread"); // Signal any already-started HA service threads to stop ha_services_running = false; + pthread_kill(ha_snapshot_thread, SIGALRM); + sched_yield(); + // If the snapshot thread was started, wait for it to exit if (ha_snapshot_thread_started) { pthread_join(ha_snapshot_thread, NULL); @@ -1004,6 +1007,9 @@ void mqtt_stop_ha_services(void) { log_info("MQTT HA: Stopping background services..."); ha_services_running = false; + pthread_kill(ha_snapshot_thread, SIGALRM); + pthread_kill(ha_motion_thread, SIGALRM); + sched_yield(); // Wait for threads to finish (they check ha_services_running each second) if (ha_snapshot_thread_started) { diff --git a/src/core/shutdown_coordinator.c b/src/core/shutdown_coordinator.c index a98e8d896..0cfce051b 100644 --- a/src/core/shutdown_coordinator.c +++ b/src/core/shutdown_coordinator.c @@ -35,7 +35,8 @@ int init_shutdown_coordinator(void) { return -1; } - g_coordinator.all_components_stopped = false; + // Initially no components are running. The first to register will mark this false. + g_coordinator.all_components_stopped = true; log_info("Shutdown coordinator initialized"); return 0; @@ -115,6 +116,8 @@ int register_component(const char *name, component_type_t type, void *context, i component->context = context; component->priority = priority; + g_coordinator.all_components_stopped = false; + pthread_mutex_unlock(&g_coordinator.mutex); log_info("Registered component %s (ID: %d, type: %d, priority: %d)", @@ -268,8 +271,9 @@ bool wait_for_all_components_stopped(int timeout_seconds) { } // First, log which components are still not stopped - log_info("Waiting for components to stop (timeout: %d seconds):", timeout_seconds); - for (int i = 0; i < atomic_load(&g_coordinator.component_count); i++) { + int component_count = atomic_load(&g_coordinator.component_count); + log_info("Waiting for %d components to stop (timeout: %d seconds):", component_count, timeout_seconds); + for (int i = 0; i < component_count; i++) { component_state_t state = atomic_load(&g_coordinator.components[i].state); if (state != COMPONENT_STOPPED) { log_info("Component %s (ID: %d) is in state %d", diff --git a/src/database/db_recordings_sync.c b/src/database/db_recordings_sync.c index 08fc9f11c..e6951253d 100644 --- a/src/database/db_recordings_sync.c +++ b/src/database/db_recordings_sync.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -295,6 +296,7 @@ int stop_recording_sync_thread(void) { // Signal thread to stop sync_thread.running = false; pthread_mutex_unlock(&sync_thread.mutex); + pthread_kill(sync_thread.thread, SIGALRM); // Wait for thread to exit if (pthread_join(sync_thread.thread, NULL) != 0) { diff --git a/src/telemetry/stream_metrics.c b/src/telemetry/stream_metrics.c index 506a93869..4ed699aa1 100644 --- a/src/telemetry/stream_metrics.c +++ b/src/telemetry/stream_metrics.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -197,6 +198,9 @@ void metrics_shutdown(void) { /* Stop sampler thread */ if (g_sampler_running) { g_sampler_running = false; + pthread_kill(g_sampler_thread, SIGALRM); + sched_yield(); + pthread_join(g_sampler_thread, NULL); log_info("Metrics sampler thread stopped"); } diff --git a/src/video/mp4_writer_thread.c b/src/video/mp4_writer_thread.c index f282138fd..1e687117d 100644 --- a/src/video/mp4_writer_thread.c +++ b/src/video/mp4_writer_thread.c @@ -749,8 +749,8 @@ int mp4_writer_start_recording_thread(mp4_writer_t *writer, const char *rtsp_url if (is_shutdown_initiated()) { break; } - usleep(1000); // Sleep for 1ms - waited_ms += 1; + usleep(50000); // Sleep for 50ms + waited_ms += 50; } // If the thread did not report running within the timeout, treat as failure diff --git a/src/web/api_handlers_health.c b/src/web/api_handlers_health.c index c9520826b..6224ec209 100644 --- a/src/web/api_handlers_health.c +++ b/src/web/api_handlers_health.c @@ -594,6 +594,8 @@ void stop_health_check_thread(void) { log_info("Stopping health check thread..."); g_health_thread_running = false; + pthread_kill(g_health_check_thread, SIGALRM); + sched_yield(); // Use portable polling approach with timeout (5 seconds) // Poll every 50ms to check if thread has exited