From 12f659e6fe2429618e7fc1c780a82ab0c9271195 Mon Sep 17 00:00:00 2001 From: Davin McCall Date: Mon, 12 Jun 2017 18:50:25 +0100 Subject: [PATCH] Convert various queues to instantiations of generic queue type --- src/dinit-ll.h | 131 +++++++++++++++++++++++++++++++++++++++++++++++++ src/service.h | 81 +++++++++++++----------------- 2 files changed, 166 insertions(+), 46 deletions(-) create mode 100644 src/dinit-ll.h diff --git a/src/dinit-ll.h b/src/dinit-ll.h new file mode 100644 index 0000000..6abb8d3 --- /dev/null +++ b/src/dinit-ll.h @@ -0,0 +1,131 @@ +#ifndef DINIT_LL_INCLUDED +#define DINIT_LL_INCLUDED 1 + +// Simple doubly-linked list implementation, where the contained element includes the +// list node. This allows a single item to be a member of several different kinds of +// list, without requiring dynamicall allocation of nodes. + +template +struct lld_node +{ + T * next = nullptr; + T * prev = nullptr; +}; + +template +struct lls_node +{ + T * next = nullptr; +}; + +// list is circular, so first->prev = tail. + +template &(*E)(T *)> +class dlist +{ + T * first; + // E extractor; + + public: + dlist() noexcept : first(nullptr) { } + + bool is_queued(T *e) noexcept + { + auto &node = E(e); + return node.next != nullptr; + } + + void append(T *e) noexcept + { + auto &node = E(e); + if (first == nullptr) { + first = e; + node.next = e; + node.prev = e; + } + else { + node.next = first; + node.prev = E(first).prev; + E(E(first).prev).next = e; + E(first).prev = e; + } + } + + T * tail() noexcept + { + if (first == nullptr) { + return nullptr; + } + else { + return E(first).prev; + } + } + + bool is_empty() noexcept + { + return first == nullptr; + } + + T * pop_front() noexcept + { + auto r = first; + auto &first_node = E(first); + if (first_node.next == first) { + // Only one node in the queue: + first_node.next = nullptr; + first_node.prev = nullptr; + first = nullptr; + } + else { + // Unlink first node: + auto &node = E(first_node.next); + node.prev = first_node.prev; + E(node.prev).next = first_node.next; + first = first_node.next; + // Return original first node: + first_node.next = nullptr; + first_node.prev = nullptr; + } + return r; + } +}; + +template &(*E)(T *)> +class slist +{ + T * first; + // E extractor; + + public: + slist() noexcept : first(nullptr) { } + + bool is_queued(T *e) noexcept + { + auto &node = E(e); + return node.next != nullptr && first != e; + } + + void insert(T *e) noexcept + { + auto &node = E(e); + node.next = first; + first = e; + } + + bool is_empty() noexcept + { + return first == nullptr; + } + + T * pop_front() noexcept + { + T * r = first; + auto &node = E(r); + first = node.next; + node.next = nullptr; + return r; + } +}; + + +#endif diff --git a/src/service.h b/src/service.h index 5385788..4ba8ab3 100644 --- a/src/service.h +++ b/src/service.h @@ -12,6 +12,7 @@ #include "control.h" #include "service-listener.h" #include "service-constants.h" +#include "dinit-ll.h" /* * This header defines ServiceRecord, a data record maintaining information about a service, @@ -315,13 +316,12 @@ class ServiceRecord // Data for use by ServiceSet public: - // Next service (after this one) in the queue for the console. Intended to only be used by ServiceSet class. - ServiceRecord *next_for_console; + // Console queue. + lld_node console_queue_node; // Propagation and start/stop queues - ServiceRecord *next_in_prop_queue = nullptr; - ServiceRecord *next_in_stop_queue = nullptr; - + lls_node prop_queue_node; + lls_node stop_queue_node; protected: @@ -725,6 +725,20 @@ class scripted_service : public base_process_service } }; +inline auto extract_prop_queue(ServiceRecord *sr) -> decltype(sr->prop_queue_node) & +{ + return sr->prop_queue_node; +} + +inline auto extract_stop_queue(ServiceRecord *sr) -> decltype(sr->stop_queue_node) & +{ + return sr->stop_queue_node; +} + +inline auto extract_console_queue(ServiceRecord *sr) -> decltype(sr->console_queue_node) & +{ + return sr->console_queue_node; +} /* * A ServiceSet, as the name suggests, manages a set of services. @@ -753,12 +767,12 @@ class ServiceSet ShutdownType shutdown_type = ShutdownType::CONTINUE; // Shutdown type, if stopping - ServiceRecord * console_queue_head = nullptr; // first record in console queue - ServiceRecord * console_queue_tail = nullptr; // last record in console queue + // Services waiting for exclusive access to the console + dlist console_queue; // Propagation and start/stop "queues" - list of services waiting for processing - ServiceRecord * first_prop_queue = nullptr; - ServiceRecord * first_stop_queue = nullptr; + slist prop_queue; + slist stop_queue; // Private methods @@ -817,9 +831,8 @@ class ServiceSet // do_propagation() method called when the queue is processed. void addToPropQueue(ServiceRecord *service) noexcept { - if (service->next_in_prop_queue == nullptr && first_prop_queue != service) { - service->next_in_prop_queue = first_prop_queue; - first_prop_queue = service; + if (! prop_queue.is_queued(service)) { + prop_queue.insert(service); } } @@ -835,9 +848,8 @@ class ServiceSet // execute_transition() method called when the queue is processed. void addToStopQueue(ServiceRecord *service) noexcept { - if (service->next_in_stop_queue == nullptr && first_stop_queue != service) { - service->next_in_stop_queue = first_stop_queue; - first_stop_queue = service; + if (! stop_queue.is_queued(service)) { + stop_queue.insert(service); } } @@ -845,17 +857,13 @@ class ServiceSet // TODO remove the pointless parameter void processQueues(bool ignoredparam = false) noexcept { - while (first_stop_queue != nullptr || first_prop_queue != nullptr) { - while (first_prop_queue != nullptr) { - auto next = first_prop_queue; - first_prop_queue = next->next_in_prop_queue; - next->next_in_prop_queue = nullptr; + while (! stop_queue.is_empty() || ! prop_queue.is_empty()) { + while (! prop_queue.is_empty()) { + auto next = prop_queue.pop_front(); next->do_propagation(); } - while (first_stop_queue != nullptr) { - auto next = first_stop_queue; - first_stop_queue = next->next_in_stop_queue; - next->next_in_stop_queue = nullptr; + while (! stop_queue.is_empty()) { + auto next = stop_queue.pop_front(); next->execute_transition(); } } @@ -864,34 +872,15 @@ class ServiceSet // Set the console queue tail (returns previous tail) ServiceRecord * append_console_queue(ServiceRecord * newTail) noexcept { - auto prev_tail = console_queue_tail; - console_queue_tail = newTail; - newTail->next_for_console = nullptr; - if (! prev_tail) { - console_queue_head = newTail; - enable_console_log(false); - } - else { - prev_tail->next_for_console = newTail; - } + auto prev_tail = console_queue.tail(); + console_queue.append(newTail); return prev_tail; } // Retrieve the current console queue head and remove it from the queue ServiceRecord * pullConsoleQueue() noexcept { - auto prev_head = console_queue_head; - if (prev_head) { - prev_head->acquiredConsole(); - console_queue_head = prev_head->next_for_console; - if (! console_queue_head) { - console_queue_tail = nullptr; - } - } - else { - enable_console_log(true); - } - return prev_head; + return console_queue.pop_front(); } // Notification from service that it is active (state != STOPPED) -- 2.25.1