]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
master: Split processes list to busy and idling processes
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Thu, 23 Mar 2023 13:09:43 +0000 (15:09 +0200)
committerTimo Sirainen <timo.sirainen@open-xchange.com>
Thu, 11 May 2023 09:12:56 +0000 (12:12 +0300)
Keep the idling processes sorted by idle_start time. This will be needed
by the next commit.

src/master/main.c
src/master/master-client.c
src/master/service-monitor.c
src/master/service-process.c
src/master/service-process.h
src/master/service.c
src/master/service.h

index 8c4c3e6518962a0e0723f1b031f92ef87f76824a..d7228e283f2cf2702d558797c2246e3cbbb45348 100644 (file)
@@ -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);
 
index 82389ec18eb5cd60bfa42bffb86c4bb41e6b1b82..39e87f7f54fb58cb302ef7cdaf999703e816c6e8 100644 (file)
@@ -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;
index c10e99c7ccfc007686a98299fcf8882073531f45..cb1fdbaf0fc8b4b5e0adf732a3b77ee488a4787d 100644 (file)
@@ -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)
 {
index 34d23ff55d41edb4b4ffbbb1261ed8a42fbf7da8..929255fb6d6eb266f7a31fe0d6f5fa8978155e74 100644 (file)
@@ -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)
index 3d55a685c81e9ef9810d9647e66ba7f2179c3b81..797f63db4619c859c9fccf540a29e3f47ec22f10 100644 (file)
@@ -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;
index 74e6fa5bb7231513cbcfe4a556d05ff1acf26e2b..e6b9eb6c3a91dfe38356e24dcebe2ec98fccf318 100644 (file)
@@ -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);
index 441264185eef00f9407c57545563c9e63501a26b..d422ecbb62955f6022afa6ab473fd3403b077d2b 100644 (file)
@@ -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;