From f84b01e4d13d155bd0a8a429c5d18e855414de4b Mon Sep 17 00:00:00 2001 From: Kyle Walker Date: Thu, 30 Jun 2016 15:12:18 -0400 Subject: [PATCH] manager: Only invoke a single sigchld per unit within a cleanup cycle By default, each iteration of manager_dispatch_sigchld() results in a unit level sigchld event being invoked. For scope units, this results in a scope_sigchld_event() which can seemingly stall for workloads that have a large number of PIDs within the scope. The stall exhibits itself as a SIG_0 being initiated for each u->pids entry as a result of pid_is_unwaited(). v2: This patch resolves this condition by only paying to cost of a sigchld in the underlying scope unit once per sigchld iteration. A new "sigchldgen" member resides within the Unit struct. The Manager is incremented via the sd event loop, accessed via sd_event_get_iteration, and the Unit member is set to the same value as the manager each time that a sigchld event is invoked. If the Manager iteration value and Unit member match, the sigchld event is not invoked for that iteration. Cherry-picked from: 36f20ae3b2975e44b6ef17e453ae06a289e9a122 Resolves: #1342173 --- src/core/manager.c | 13 ++++++++++++- src/core/unit.c | 1 + src/core/unit.h | 3 +++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/core/manager.c b/src/core/manager.c index b6d76ca..f2f5c6a 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -1743,14 +1743,25 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t } static void invoke_sigchld_event(Manager *m, Unit *u, siginfo_t *si) { + uint64_t iteration; + assert(m); assert(u); assert(si); + sd_event_get_iteration(m->event, &iteration); + log_unit_debug(u->id, "Child "PID_FMT" belongs to %s", si->si_pid, u->id); unit_unwatch_pid(u, si->si_pid); - UNIT_VTABLE(u)->sigchld_event(u, si->si_pid, si->si_code, si->si_status); + + if (UNIT_VTABLE(u)->sigchld_event) { + if (set_size(u->pids) <= 1 || iteration != u->sigchldgen) { + UNIT_VTABLE(u)->sigchld_event(u, si->si_pid, si->si_code, si->si_status); + u->sigchldgen = iteration; + } else + log_debug("%s already issued a sigchld this iteration %llu, skipping. Pids still being watched %d", u->id, iteration, set_size(u->pids)); + } } static int manager_dispatch_sigchld(Manager *m) { diff --git a/src/core/unit.c b/src/core/unit.c index 4fb2fd3..5b2becc 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -94,6 +94,7 @@ Unit *unit_new(Manager *m, size_t size) { u->unit_file_state = _UNIT_FILE_STATE_INVALID; u->unit_file_preset = -1; u->on_failure_job_mode = JOB_REPLACE; + u->sigchldgen = 0; return u; } diff --git a/src/core/unit.h b/src/core/unit.h index 0eebc0b..d936457 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -167,6 +167,9 @@ struct Unit { * process SIGCHLD for */ Set *pids; + /* Used in sigchld event invocation to avoid repeat events being invoked */ + uint64_t sigchldgen; + /* Used during GC sweeps */ unsigned gc_marker;