This will be nice to have. On Mon, Apr 20, 2015 at 10:56 AM WaLyong Cho <walyong....@samsung.com> wrote:
> If a service does not consume CPU during some time(can be configured > by ExitOnIdleSec=) and set to stopped on idle state(ExitOnIdle=), the > service will be stopped. This can be useful if the service provides > some of activation methods. > --- > src/core/load-fragment-gperf.gperf.m4 | 2 + > src/core/service.c | 97 > +++++++++++++++++++++++++++++++++++ > src/core/service.h | 5 ++ > src/core/unit.h | 1 + > 4 files changed, 105 insertions(+) > > diff --git a/src/core/load-fragment-gperf.gperf.m4 > b/src/core/load-fragment-gperf.gperf.m4 > index 5305984..60d573e 100644 > --- a/src/core/load-fragment-gperf.gperf.m4 > +++ b/src/core/load-fragment-gperf.gperf.m4 > @@ -229,6 +229,8 @@ Service.BusName, > config_parse_bus_name, 0, > Service.FileDescriptorStoreMax, config_parse_unsigned, 0, > offsetof(Service, n_fd_store_max) > Service.NotifyAccess, config_parse_notify_access, 0, > offsetof(Service, notify_access) > Service.Sockets, config_parse_service_sockets, 0, > 0 > +Service.ExitOnIdle, config_parse_bool, 0, > offsetof(Service, exit_on_idle) > +Service.ExitOnIdleSec, config_parse_sec, 0, > offsetof(Service, exit_on_idle_usec) > m4_ifdef(`ENABLE_KDBUS', > `Service.BusPolicy, config_parse_bus_endpoint_policy, 0, > offsetof(Service, exec_context)', > `Service.BusPolicy, config_parse_warn_compat, > DISABLED_EXPERIMENTAL, 0') > diff --git a/src/core/service.c b/src/core/service.c > index fa818fc..c8752ae 100644 > --- a/src/core/service.c > +++ b/src/core/service.c > @@ -91,6 +91,7 @@ static const UnitActiveState > state_translation_table_idle[_SERVICE_STATE_MAX] = > static int service_dispatch_io(sd_event_source *source, int fd, uint32_t > events, void *userdata); > static int service_dispatch_timer(sd_event_source *source, usec_t usec, > void *userdata); > static int service_dispatch_watchdog(sd_event_source *source, usec_t > usec, void *userdata); > +static int service_dispatch_exit_on_idle_timer(sd_event_source *source, > usec_t usec, void *userdata); > > static void service_enter_signal(Service *s, ServiceState state, > ServiceResult f); > static void service_enter_reload_by_notify(Service *s); > @@ -108,6 +109,8 @@ static void service_init(Unit *u) { > s->socket_fd = -1; > s->bus_endpoint_fd = -1; > s->guess_main_pid = true; > + s->exit_on_idle = false; > + s->exit_on_idle_usec = 0; > > RATELIMIT_INIT(s->start_limit, > u->manager->default_start_limit_interval, > u->manager->default_start_limit_burst); > > @@ -279,6 +282,56 @@ static void service_release_resources(Unit *u) { > assert(s->n_fd_store == 0); > } > > +static void service_stop_exit_on_idle_timer(Service *s) { > + assert(s); > + > + s->exit_on_idle_event_source = > sd_event_source_unref(s->exit_on_idle_event_source); > + s->exit_on_idle_timestamp = DUAL_TIMESTAMP_NULL; > +} > + > +static void service_start_exit_on_idle_timer(Service *s) { > + int r; > + > + assert(s); > + > + if (s->exit_on_idle_usec <= 0) > + return; > + > + if (s->exit_on_idle_event_source) { > + r = > sd_event_source_set_time(s->exit_on_idle_event_source, > s->exit_on_idle_timestamp.monotonic + s->exit_on_idle_usec); > + if (r < 0) { > + log_unit_warning(UNIT(s)->id, "%s failed to reset > exit-on-idle timer: %s", UNIT(s)->id, strerror(-r)); > + return; > + } > + > + r = > sd_event_source_set_enabled(s->exit_on_idle_event_source, SD_EVENT_ON); > + } else { > + r = sd_event_add_time( > + UNIT(s)->manager->event, > + &s->exit_on_idle_event_source, > + CLOCK_MONOTONIC, > + s->exit_on_idle_timestamp.monotonic + > s->exit_on_idle_usec, 0, > + service_dispatch_exit_on_idle_timer, s); > + if (r < 0) { > + log_unit_warning(UNIT(s)->id, "%s failed to add > exit-on-idle timer: %s", UNIT(s)->id, strerror(-r)); > + return; > + } > + > + r = > sd_event_source_set_priority(s->exit_on_idle_event_source, > SD_EVENT_PRIORITY_IDLE); > + } > + > + if (r < 0) > + log_unit_warning(UNIT(s)->id, "%s failed to install > exit-on-idle timer: %s", UNIT(s)->id, strerror(-r)); > + return; > +} > + > +static void service_reset_exit_on_idle_timer(Service *s) { > + assert(s); > + > + dual_timestamp_get(&s->exit_on_idle_timestamp); > + service_start_exit_on_idle_timer(s); > +} > + > static void service_done(Unit *u) { > Service *s = SERVICE(u); > > @@ -518,6 +571,7 @@ static void service_fix_output(Service *s) { > } > > static int service_add_extras(Service *s) { > + CGroupContext *cc; > int r; > > assert(s); > @@ -571,6 +625,15 @@ static int service_add_extras(Service *s) { > return r; > } > > + if (s->exit_on_idle && s->exit_on_idle_usec <= 0) > + s->exit_on_idle_usec = USEC_PER_MINUTE; > + > + if (s->exit_on_idle) { > + cc = unit_get_cgroup_context(UNIT(s)); > + if (cc) > + cc->cpu_accounting = true; > + } > + > if (UNIT(s)->default_dependencies) { > r = service_add_default_dependencies(s); > if (r < 0) > @@ -850,6 +913,12 @@ static void service_set_state(Service *s, > ServiceState state) { > if (!IN_SET(state, SERVICE_START_POST, SERVICE_RUNNING, > SERVICE_RELOAD)) > service_stop_watchdog(s); > > + if (IN_SET(state, SERVICE_RUNNING)) > + service_start_exit_on_idle_timer(s); > + > + if (IN_SET(state, SERVICE_DEAD)) > + service_stop_exit_on_idle_timer(s); > + > /* For the inactive states unit_notify() will trim the cgroup, > * but for exit we have to do that ourselves... */ > if (state == SERVICE_EXITED && UNIT(s)->manager->n_reloading <= 0) > @@ -2770,6 +2839,34 @@ static int > service_dispatch_watchdog(sd_event_source *source, usec_t usec, void > format_timespan(t, sizeof(t), s->watchdog_usec, > 1)); > > service_enter_signal(s, SERVICE_STOP_SIGABRT, > SERVICE_FAILURE_WATCHDOG); > + return 0; > +} > + > +static int service_dispatch_exit_on_idle_timer(sd_event_source *source, > usec_t usec, void *userdata) { > + Service *s = SERVICE(userdata); > + nsec_t usage; > + int r; > + > + assert(s); > + assert(source == s->exit_on_idle_event_source); > + > + r = unit_get_cpu_usage(UNIT(s), &usage); > + if (r < 0) { > + log_unit_error_errno(UNIT(s)->id, r, "%s failed to get > cpu usage: %m", UNIT(s)->id); > + return 0; > + } > + > + if (usage == UNIT(s)->cpuacct_usage) { > + log_unit_info(UNIT(s)->id, "%s is in idle state, > stopping...", UNIT(s)->id); > + r = manager_add_job(UNIT(s)->manager, JOB_STOP, UNIT(s), > JOB_FAIL, true, NULL, NULL); > + if (r < 0) { > + log_unit_error_errno(UNIT(s)->id, r, "%s failed > to enqueue stopping job: %m", UNIT(s)->id); > + return r; > + } > + } else { > + UNIT(s)->cpuacct_usage = usage; > + service_reset_exit_on_idle_timer(s); > + } > > return 0; > } > diff --git a/src/core/service.h b/src/core/service.h > index 7da0a93..909908e 100644 > --- a/src/core/service.h > +++ b/src/core/service.h > @@ -212,6 +212,11 @@ struct Service { > ServiceFDStore *fd_store; > unsigned n_fd_store; > unsigned n_fd_store_max; > + > + bool exit_on_idle; > + dual_timestamp exit_on_idle_timestamp; > + usec_t exit_on_idle_usec; > + sd_event_source *exit_on_idle_event_source; > }; > > extern const UnitVTable service_vtable; > diff --git a/src/core/unit.h b/src/core/unit.h > index 11242c2..84c0cd0 100644 > --- a/src/core/unit.h > +++ b/src/core/unit.h > @@ -178,6 +178,7 @@ struct Unit { > > /* Where the cpuacct.usage cgroup counter was at the time the > unit was started */ > nsec_t cpuacct_usage_base; > + nsec_t cpuacct_usage; > > /* Counterparts in the cgroup filesystem */ > char *cgroup_path; > -- > 1.9.3 > > _______________________________________________ > systemd-devel mailing list > systemd-devel@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/systemd-devel >
_______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel