mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 00:14:32 -08:00
[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().
This commit is contained in:
@@ -23,6 +23,8 @@ struct handle
|
||||
static_cast<j6_handle_t>(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<kobject>() const { return object; }
|
||||
|
||||
j6_handle_t id;
|
||||
uint64_t badge;
|
||||
kobject *object;
|
||||
};
|
||||
|
||||
|
||||
@@ -37,32 +37,69 @@ kobject::koid_type(j6_koid_t koid)
|
||||
return static_cast<type>((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
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <j6/errors.h>
|
||||
#include <j6/signals.h>
|
||||
#include <j6/types.h>
|
||||
#include <util/vector.h>
|
||||
#include <util/deque.h>
|
||||
|
||||
namespace obj {
|
||||
|
||||
@@ -46,11 +46,13 @@ public:
|
||||
|
||||
/// Set the given signals active on this object
|
||||
/// \arg s The set of signals to assert
|
||||
void assert_signal(j6_signal_t s);
|
||||
/// \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);
|
||||
/// \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<thread*> 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<thread*, 8> m_blocked_threads;
|
||||
};
|
||||
|
||||
} // namespace obj
|
||||
|
||||
114
src/libraries/util/include/util/deque.h
Normal file
114
src/libraries/util/include/util/deque.h
Normal file
@@ -0,0 +1,114 @@
|
||||
#pragma once
|
||||
/// \file linked_list.h
|
||||
/// A generic templatized linked list.
|
||||
|
||||
#include <assert.h>
|
||||
#include <util/linked_list.h>
|
||||
|
||||
namespace util {
|
||||
|
||||
template <typename T, unsigned N = 16>
|
||||
class deque
|
||||
{
|
||||
struct node { T items[N]; };
|
||||
using list_type = linked_list<node>;
|
||||
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
|
||||
Reference in New Issue
Block a user