--- /dev/null
+#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 <typename T>
+struct lld_node
+{
+ T * next = nullptr;
+ T * prev = nullptr;
+};
+
+template <typename T>
+struct lls_node
+{
+ T * next = nullptr;
+};
+
+// list is circular, so first->prev = tail.
+
+template <typename T, lld_node<T> &(*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 <typename T, lls_node<T> &(*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
#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,
// 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<ServiceRecord> console_queue_node;
// Propagation and start/stop queues
- ServiceRecord *next_in_prop_queue = nullptr;
- ServiceRecord *next_in_stop_queue = nullptr;
-
+ lls_node<ServiceRecord> prop_queue_node;
+ lls_node<ServiceRecord> stop_queue_node;
protected:
}
};
+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.
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<ServiceRecord, extract_console_queue> console_queue;
// Propagation and start/stop "queues" - list of services waiting for processing
- ServiceRecord * first_prop_queue = nullptr;
- ServiceRecord * first_stop_queue = nullptr;
+ slist<ServiceRecord, extract_prop_queue> prop_queue;
+ slist<ServiceRecord, extract_stop_queue> stop_queue;
// Private methods
// 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);
}
}
// 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);
}
}
// 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();
}
}
// 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)