From a7245116b688753ef857a0d82dee32fe1c30cdfa Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Sun, 30 Jan 2022 20:42:49 -0800 Subject: [PATCH] [util] Add util::deque container Adding the util::deque container, implemented with the util::linked_list of arrays of items. Also, use the deque for a kobject's blocked thread list to maintain order instead of a vector using remove_swap(). --- src/kernel/objects/handle.h | 3 + src/kernel/objects/kobject.cpp | 59 +++++++++--- src/kernel/objects/kobject.h | 25 ++++-- src/libraries/util/include/util/deque.h | 114 ++++++++++++++++++++++++ 4 files changed, 182 insertions(+), 19 deletions(-) create mode 100644 src/libraries/util/include/util/deque.h diff --git a/src/kernel/objects/handle.h b/src/kernel/objects/handle.h index f77941d..0a58c78 100644 --- a/src/kernel/objects/handle.h +++ b/src/kernel/objects/handle.h @@ -23,6 +23,8 @@ struct handle static_cast(obj ? obj->get_type() : kobject::type::none) << type_shift; } + inline handle() : id {j6_handle_invalid}, object {nullptr} {} + inline handle(j6_handle_t in_id, kobject *in_obj, j6_cap_t caps) : id {make_id(in_id, caps, in_obj)}, object {in_obj} { if (object) object->handle_retain(); @@ -90,6 +92,7 @@ struct handle inline const kobject * as() const { return object; } j6_handle_t id; + uint64_t badge; kobject *object; }; diff --git a/src/kernel/objects/kobject.cpp b/src/kernel/objects/kobject.cpp index 32e64c1..fe7970a 100644 --- a/src/kernel/objects/kobject.cpp +++ b/src/kernel/objects/kobject.cpp @@ -37,32 +37,69 @@ kobject::koid_type(j6_koid_t koid) return static_cast((koid >> 48) & 0xffffull); } -void +j6_signal_t kobject::assert_signal(j6_signal_t s) { - m_signals |= s; + j6_signal_t old = + __atomic_fetch_or(&m_signals, s, __ATOMIC_SEQ_CST); notify_signal_observers(); + return old; +} + +j6_signal_t +kobject::deassert_signal(j6_signal_t s) +{ + return __atomic_fetch_and(&m_signals, ~s, __ATOMIC_SEQ_CST); } void -kobject::deassert_signal(j6_signal_t s) +kobject::compact_blocked_threads() { - m_signals &= ~s; + // Clean up what we can of the list + while (!m_blocked_threads.empty() && !m_blocked_threads.first()) + m_blocked_threads.pop_front(); + while (!m_blocked_threads.empty() && !m_blocked_threads.last()) + m_blocked_threads.pop_back(); } void kobject::notify_signal_observers() { - size_t i = 0; - while (i < m_blocked_threads.count()) { - thread *t = m_blocked_threads[i]; + for (auto &entry : m_blocked_threads) { + if (entry == nullptr) continue; + if (entry->wake_on_signals(this, m_signals)) + entry = nullptr; + } + compact_blocked_threads(); +} - if (t->wake_on_signals(this, m_signals)) { - m_blocked_threads.remove_swap_at(i); - } else { - ++i; +void +kobject::notify_object_observers(size_t count) +{ + if (!count) return; + + for (auto &entry : m_blocked_threads) { + if (entry == nullptr) continue; + if (entry->wake_on_object(this)) { + entry = nullptr; + if (--count) break; } } + compact_blocked_threads(); +} + +void +kobject::remove_blocked_thread(thread *t) +{ + // Can't really remove from a deque, so just + // null out removed entries. + for (auto &entry : m_blocked_threads) { + if (entry == t) { + entry = nullptr; + break; + } + } + compact_blocked_threads(); } void diff --git a/src/kernel/objects/kobject.h b/src/kernel/objects/kobject.h index 6160195..b719619 100644 --- a/src/kernel/objects/kobject.h +++ b/src/kernel/objects/kobject.h @@ -5,7 +5,7 @@ #include #include #include -#include +#include namespace obj { @@ -45,12 +45,14 @@ public: inline j6_koid_t koid() const { return m_koid; } /// Set the given signals active on this object - /// \arg s The set of signals to assert - void assert_signal(j6_signal_t s); + /// \arg s The set of signals to assert + /// \returns The previous state of the signals + j6_signal_t assert_signal(j6_signal_t s); /// Clear the given signals on this object - /// \arg s The set of signals to deassert - void deassert_signal(j6_signal_t s); + /// \arg s The set of signals to deassert + /// \returns The previous state of the signals + j6_signal_t deassert_signal(j6_signal_t s); /// Check if the given signals are set on this object /// \arg s The set of signals to check @@ -68,10 +70,10 @@ public: } /// Add the given thread to the list of threads waiting on this object. - inline void add_blocked_thread(thread *t) { m_blocked_threads.append(t); } + inline void add_blocked_thread(thread *t) { m_blocked_threads.push_back(t); } /// Remove the given thread from the list of threads waiting on this object. - inline void remove_blocked_thread(thread *t) { m_blocked_threads.remove_swap(t); } + void remove_blocked_thread(thread *t); /// Perform any cleanup actions necessary to mark this object closed virtual void close(); @@ -93,12 +95,19 @@ private: /// \arg result The result to pass to the observers void notify_signal_observers(); + /// Compact the blocked threads deque + void compact_blocked_threads(); + j6_koid_t m_koid; j6_signal_t m_signals; uint16_t m_handle_count; protected: - util::vector m_blocked_threads; + /// Notify threads waiting on this object + /// \arg count Number of observers to wake, or -1ull for all + void notify_object_observers(size_t count = -1ull); + + util::deque m_blocked_threads; }; } // namespace obj diff --git a/src/libraries/util/include/util/deque.h b/src/libraries/util/include/util/deque.h new file mode 100644 index 0000000..599e789 --- /dev/null +++ b/src/libraries/util/include/util/deque.h @@ -0,0 +1,114 @@ +#pragma once +/// \file linked_list.h +/// A generic templatized linked list. + +#include +#include + +namespace util { + +template +class deque +{ + struct node { T items[N]; }; + using list_type = linked_list; + using node_type = typename list_type::item_type; + +public: + class iterator + { + public: + iterator(node_type *n, size_t i) : node(n), index(i) {} + iterator(const iterator &o) : node(o.node), index(o.index) {} + inline T& operator*() { assert(node); return node->items[index]; } + inline iterator & operator++() { incr(); return *this; } + inline iterator & operator++(int) { iterator old {*this}; incr(); return old; } + inline bool operator!=(const iterator &o) const { return !(*this == o); } + inline bool operator==(const iterator &o) const { + if (!node && !o.node) return true; + return index == o.index && node == o.node; + } + private: + void incr() { if (++index >= N) { index = 0; node = node->next(); }} + node_type *node; + size_t index; + }; + + deque() : m_first(0), m_next(N) {} + ~deque() { + while (!m_list.empty()) + delete m_list.pop_front(); + } + + inline void push_front(const T& item) { + if (!m_first) { // need a new block at the start + m_list.push_front(new node_type); + m_first = N; + } + m_list.front()->items[--m_first] = item; + } + + inline void push_back(const T& item) { + if (m_next == N) { // need a new block at the end + m_list.push_back(new node_type); + m_next = 0; + } + m_list.back()->items[m_next++] = item; + } + + inline T pop_front() { + assert(!empty() && "Calling pop_front() on an empty deque"); + T value = m_list.front()->items[m_first++]; + if (m_first == N) { + delete m_list.pop_front(); + m_first = 0; + if (m_list.empty()) + m_next = N; + } + return value; + } + + inline T pop_back() { + assert(!empty() && "Calling pop_back() on an empty deque"); + T value = m_list.back()->items[--m_next]; + if (m_next == 0) { + delete m_list.pop_back(); + m_next = N; + if (m_list.empty()) + m_first = 0; + } + return value; + } + + inline bool empty() const { return m_list.empty(); } + + inline T& first() { + assert(!empty() && "Calling first() on an empty deque"); + return m_list.front()->items[m_first]; + } + + inline const T& first() const { + assert(!empty() && "Calling first() on an empty deque"); + return m_list.front()->items[m_first]; + } + + inline T& last() { + assert(!empty() && "Calling last() on an empty deque"); + return m_list.back()->items[m_next-1]; + } + + inline const T& last() const { + assert(!empty() && "Calling last() on an empty deque"); + return m_list.back()->items[m_next-1]; + } + + iterator begin() { return iterator {m_list.front(), m_first}; } + iterator end() { return iterator {m_list.back(), m_next}; } + +private: + unsigned m_first; // Index of the first item in the first node + unsigned m_next; // Index of the first empty item in the last node + list_type m_list; +}; + +} // namespace util