From: Timo Sirainen Date: Thu, 23 Mar 2023 13:09:43 +0000 (+0200) Subject: master: Split processes list to busy and idling processes X-Git-Tag: 2.3.21~84 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a4e9e0c9b333d6ecf430c9e12ff2c19be7f65a68;p=thirdparty%2Fdovecot%2Fcore.git master: Split processes list to busy and idling processes Keep the idling processes sorted by idle_start time. This will be needed by the next commit. --- diff --git a/src/master/main.c b/src/master/main.c index 8c4c3e6518..d7228e283f 100644 --- a/src/master/main.c +++ b/src/master/main.c @@ -429,8 +429,10 @@ sig_settings_reload(const siginfo_t *si ATTR_UNUSED, /* anvil never dies. it just gets moved to the new services list */ service = service_lookup_type(services, SERVICE_TYPE_ANVIL); if (service != NULL) { - while (service->processes != NULL) - service_process_destroy(service->processes); + while (service->busy_processes != NULL) + service_process_destroy(service->busy_processes); + while (service->idle_processes_head != NULL) + service_process_destroy(service->idle_processes_head); } services_destroy(services, FALSE); diff --git a/src/master/master-client.c b/src/master/master-client.c index 82389ec18e..39e87f7f54 100644 --- a/src/master/master-client.c +++ b/src/master/master-client.c @@ -61,23 +61,35 @@ master_client_process_output(string_t *str, (long)process->last_kill_sent); } +static void +master_client_process_status_list(struct master_client *client, + struct service_process *processes, + string_t *str) +{ + struct service_process *p; + + for (p = processes; p != NULL; p = p->next) { + str_truncate(str, 0); + master_client_process_output(str, p); + o_stream_nsend(client->conn.output, + str_data(str), str_len(str)); + } +} + static int master_client_process_status(struct master_client *client, const char *const *args) { struct service *service; - struct service_process *p; string_t *str = t_str_new(128); array_foreach_elem(&services->services, service) { if (args[0] != NULL && !str_array_find(args, service->set->name)) continue; - for (p = service->processes; p != NULL; p = p->next) { - str_truncate(str, 0); - master_client_process_output(str, p); - o_stream_nsend(client->conn.output, - str_data(str), str_len(str)); - } + master_client_process_status_list(client, + service->busy_processes, str); + master_client_process_status_list(client, + service->idle_processes_head, str); } o_stream_nsend_str(client->conn.output, "\n"); return 1; diff --git a/src/master/service-monitor.c b/src/master/service-monitor.c index c10e99c7cc..cb1fdbaf0f 100644 --- a/src/master/service-monitor.c +++ b/src/master/service-monitor.c @@ -4,6 +4,7 @@ #include "array.h" #include "ioloop.h" #include "hash.h" +#include "llist.h" #include "str.h" #include "safe-mkstemp.h" #include "time-util.h" @@ -65,9 +66,15 @@ static void service_status_more(struct service_process *process, { struct service *service = process->service; + if (process->idle_start != 0) { + /* idling process became busy */ + DLLIST2_REMOVE(&service->idle_processes_head, + &service->idle_processes_tail, process); + DLLIST_PREPEND(&service->busy_processes, process); + process->idle_start = 0; + } process->total_count += process->available_count - status->available_count; - process->idle_start = 0; timeout_remove(&process->to_idle); @@ -94,7 +101,20 @@ static void service_check_idle(struct service_process *process) if (process->available_count != service->client_limit) return; + + if (process->idle_start == 0) { + /* busy process started idling */ + DLLIST_REMOVE(&service->busy_processes, process); + } else { + /* Idling process updated its status again to be idling. Maybe + it was busy for a little bit? Update its idle_start time. */ + DLLIST2_REMOVE(&service->idle_processes_head, + &service->idle_processes_tail, process); + } + DLLIST2_APPEND(&service->idle_processes_head, + &service->idle_processes_tail, process); process->idle_start = ioloop_time; + if (service->process_avail > service->set->process_min_avail && process->to_idle == NULL && service->idle_kill != UINT_MAX) { @@ -614,9 +634,11 @@ static void services_monitor_wait(struct service_list *service_list) } } -static bool service_processes_close_listeners(struct service *service) +static bool +service_processes_list_close_listeners(struct service *service, + struct service_process *processes) { - struct service_process *process = service->processes; + struct service_process *process = processes; bool ret = FALSE; for (; process != NULL; process = process->next) { @@ -630,6 +652,19 @@ static bool service_processes_close_listeners(struct service *service) return ret; } +static bool service_processes_close_listeners(struct service *service) +{ + bool ret = FALSE; + + if (service_processes_list_close_listeners(service, + service->busy_processes)) + ret = TRUE; + if (service_processes_list_close_listeners(service, + service->idle_processes_head)) + ret = TRUE; + return ret; +} + static bool service_list_processes_close_listeners(struct service_list *service_list) { diff --git a/src/master/service-process.c b/src/master/service-process.c index 34d23ff55d..929255fb6d 100644 --- a/src/master/service-process.c +++ b/src/master/service-process.c @@ -370,10 +370,12 @@ struct service_process *service_process_create(struct service *service) } process->available_count = service->client_limit; + process->idle_start = ioloop_time; service->process_count_total++; service->process_count++; service->process_avail++; - DLLIST_PREPEND(&service->processes, process); + DLLIST2_APPEND(&service->idle_processes_head, + &service->idle_processes_tail, process); service_list_ref(service->list); hash_table_insert(service_pids, POINTER_CAST(process->pid), process); @@ -388,7 +390,12 @@ void service_process_destroy(struct service_process *process) struct service *service = process->service; struct service_list *service_list = service->list; - DLLIST_REMOVE(&service->processes, process); + if (process->idle_start == 0) + DLLIST_REMOVE(&service->busy_processes, process); + else { + DLLIST2_REMOVE(&service->idle_processes_head, + &service->idle_processes_tail, process); + } hash_table_remove(service_pids, POINTER_CAST(process->pid)); if (process->available_count > 0) diff --git a/src/master/service-process.h b/src/master/service-process.h index 3d55a685c8..797f63db46 100644 --- a/src/master/service-process.h +++ b/src/master/service-process.h @@ -19,7 +19,12 @@ struct service_process { smaller than the correct value. */ unsigned int total_count; - /* time when process started idling, or 0 if we're not idling */ + /* Time when process started idling, or 0 if we're not idling. This is + updated when the process sends a notification via its status pipe + about the number of clients it is processing. + + This field also determines whether the process is in the service's + "busy" or "idle" processes linked list. */ time_t idle_start; /* kill process if it hits idle timeout */ struct timeout *to_idle; diff --git a/src/master/service.c b/src/master/service.c index 74e6fa5bb7..e6b9eb6c3a 100644 --- a/src/master/service.c +++ b/src/master/service.c @@ -496,20 +496,21 @@ int services_create(const struct master_settings *set, return 0; } -unsigned int service_signal(struct service *service, int signo, - unsigned int *uninitialized_count_r) +static unsigned int +service_signal_processes(struct service *service, int signo, + struct service_process *processes, + unsigned int *uninitialized_count) { - struct service_process *process = service->processes; + struct service_process *process; unsigned int count = 0; - *uninitialized_count_r = 0; - for (; process != NULL; process = process->next) { + for (process = processes; process != NULL; process = process->next) { i_assert(process->service == service); if (!SERVICE_PROCESS_IS_INITIALIZED(process) && signo != SIGKILL) { /* too early to signal it */ - *uninitialized_count_r += 1; + *uninitialized_count += 1; continue; } @@ -528,6 +529,21 @@ unsigned int service_signal(struct service *service, int signo, return count; } +unsigned int service_signal(struct service *service, int signo, + unsigned int *uninitialized_count_r) +{ + unsigned int count = 0; + + *uninitialized_count_r = 0; + count = service_signal_processes(service, signo, + service->busy_processes, + uninitialized_count_r); + count += service_signal_processes(service, signo, + service->idle_processes_head, + uninitialized_count_r); + return count; +} + static void service_login_notify_send(struct service *service) { unsigned int uninitialized_count; @@ -713,7 +729,8 @@ void service_throttle(struct service *service, unsigned int msecs) if (service->to_throttle != NULL || service->list->destroyed) return; - if (service->processes == NULL) + if (service->busy_processes == NULL && + service->idle_processes_head == NULL) service_drop_listener_connections(service); service_monitor_listen_stop(service); diff --git a/src/master/service.h b/src/master/service.h index 441264185e..d422ecbb62 100644 --- a/src/master/service.h +++ b/src/master/service.h @@ -58,8 +58,12 @@ struct service { /* all listeners, even those that aren't currently listening */ ARRAY(struct service_listener *) listeners; - /* linked list of all processes belonging to this service */ - struct service_process *processes; + /* linked list of processes belonging to this service, which have + idle_start == 0. */ + struct service_process *busy_processes; + /* linked list of processes belonging to this service, which have + ldle_start != 0. */ + struct service_process *idle_processes_head, *idle_processes_tail; /* number of processes currently created for this service */ unsigned int process_count;