diff --git a/src/core/cgroup.c b/src/core/cgroup.c index b3cd12000e..47a3b6d3a2 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -1827,6 +1827,31 @@ static int unit_watch_pids_in_path(Unit *u, const char *path) { return ret; } +int unit_synthesize_cgroup_empty_event(Unit *u) { + int r; + + assert(u); + + /* Enqueue a synthetic cgroup empty event if this unit doesn't watch any PIDs anymore. This is compatibility + * support for non-unified systems where notifications aren't reliable, and hence need to take whatever we can + * get as notification source as soon as we stopped having any useful PIDs to watch for. */ + + if (!u->cgroup_path) + return -ENOENT; + + r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER); + if (r < 0) + return r; + if (r > 0) /* On unified we have reliable notifications, and don't need this */ + return 0; + + if (!set_isempty(u->pids)) + return 0; + + unit_add_to_cgroup_empty_queue(u); + return 0; +} + int unit_watch_all_pids(Unit *u) { int r; diff --git a/src/core/cgroup.h b/src/core/cgroup.h index 0c5bb4a2c8..d5c0bd4311 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -192,6 +192,8 @@ Unit* manager_get_unit_by_pid(Manager *m, pid_t pid); int unit_search_main_pid(Unit *u, pid_t *ret); int unit_watch_all_pids(Unit *u); +int unit_synthesize_cgroup_empty_event(Unit *u); + int unit_get_memory_current(Unit *u, uint64_t *ret); int unit_get_tasks_current(Unit *u, uint64_t *ret); int unit_get_cpu_usage(Unit *u, nsec_t *ret); diff --git a/src/core/scope.c b/src/core/scope.c index 10454d56b0..468dd81217 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -472,19 +472,16 @@ static void scope_notify_cgroup_empty_event(Unit *u) { static void scope_sigchld_event(Unit *u, pid_t pid, int code, int status) { - /* If we get a SIGCHLD event for one of the processes we were - interested in, then we look for others to watch, under the - assumption that we'll sooner or later get a SIGCHLD for - them, as the original process we watched was probably the - parent of them, and they are hence now our children. */ + assert(u); + /* If we get a SIGCHLD event for one of the processes we were interested in, then we look for others to + * watch, under the assumption that we'll sooner or later get a SIGCHLD for them, as the original + * process we watched was probably the parent of them, and they are hence now our children. */ unit_tidy_watch_pids(u, 0, 0); unit_watch_all_pids(u); - /* If the PID set is empty now, then let's finish this off - (On unified we use proper notifications) */ - if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) == 0 && set_isempty(u->pids)) - scope_notify_cgroup_empty_event(u); + /* If the PID set is empty now, then let's finish this off. */ + unit_synthesize_cgroup_empty_event(u); } static int scope_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) { diff --git a/src/core/service.c b/src/core/service.c index 445f3ff604..124b334e3e 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -3301,18 +3301,14 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { if (notify_dbus) unit_add_to_dbus_queue(u); - /* We got one SIGCHLD for the service, let's watch all - * processes that are now running of the service, and watch - * that. Among the PIDs we then watch will be children - * reassigned to us, which hopefully allows us to identify - * when all children are gone */ + /* If we get a SIGCHLD event for one of the processes we were interested in, then we look for others to watch, + * under the assumption that we'll sooner or later get a SIGCHLD for them, as the original process we watched + * was probably the parent of them, and they are hence now our children. */ unit_tidy_watch_pids(u, s->main_pid, s->control_pid); unit_watch_all_pids(u); - /* If the PID set is empty now, then let's finish this off - (On unified we use proper notifications) */ - if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) == 0 && set_isempty(u->pids)) - unit_add_to_cgroup_empty_queue(u); + /* If the PID set is empty now, then let's check if the cgroup is empty too and finish off the unit. */ + unit_synthesize_cgroup_empty_event(u); } static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) {