| 2032 | } |
| 2033 | |
| 2034 | static bool runtime_service_can_exit(cbm_daemon_runtime_service_t *service, uint64_t now_ms) { |
| 2035 | bool can_exit = false; |
| 2036 | cbm_mutex_lock(&service->mutex); |
| 2037 | if (service->state == CBM_DAEMON_RUNTIME_SERVICE_STOPPING && service->active_connections == 0) { |
| 2038 | bool drained = cbm_daemon_should_exit(service->coordinator, now_ms); |
| 2039 | if (!drained && service->activation_shutdown_requested) { |
| 2040 | /* Activation can arrive while the daemon has no committed |
| 2041 | * coordinator client, so no final disconnect exists to flip the |
| 2042 | * coordinator's terminal bit. Resource emptiness is equivalent |
| 2043 | * once the runtime has atomically stopped all new admission. */ |
| 2044 | drained = cbm_daemon_active_clients(service->coordinator) == 0 && |
| 2045 | cbm_daemon_active_jobs(service->coordinator) == 0 && |
| 2046 | cbm_daemon_active_watches(service->coordinator) == 0; |
| 2047 | } |
| 2048 | bool deadline_reached = |
| 2049 | service->stop_deadline_ms != 0 && now_ms >= service->stop_deadline_ms; |
| 2050 | can_exit = drained || deadline_reached || service->emergency_stop; |
| 2051 | if (can_exit) { |
| 2052 | service->state = CBM_DAEMON_RUNTIME_SERVICE_EXITED; |
| 2053 | } |
| 2054 | } |
| 2055 | cbm_mutex_unlock(&service->mutex); |
| 2056 | return can_exit; |
| 2057 | } |
| 2058 | |
| 2059 | static void *runtime_accept_loop(void *opaque) { |
| 2060 | cbm_daemon_runtime_service_t *service = opaque; |
no test coverage detected