[kernel] Re-design thread blocking

In preparation for the new mailbox IPC model, blocking threads needed an
overhaul. The `wait_on_*` and `wake_on_*` methods are gone, and the
`block()` and `wake()` calls on threads now pass a value between the
waker and the blocked thread.

As part of this change, the concept of signals on the base kobject class
was removed, along with the queue of blocked threads waiting on any
given object. Signals are now exclusively the domain of the event object
type, and the new wait_queue utility class helps manage waiting threads
when an object does actually need this functionality. In some cases (eg,
logger) an event object is used instead of the lower-level wait_queue.

Since this change has a lot of ramifications, this large commit includes
the following additional changes:

- The j6_object_wait, j6_object_wait_many, and j6_thread_pause syscalls
  have been removed.
- The j6_event_clear syscall has been removed - events are "cleared" by
  reading them now. A new j6_event_wait syscall has been added to read
  events.
- The generic close() method on kobject has been removed.
- The on_no_handles() method on kobject now deletes the object by
  default, and needs to be overridden by classes that should not be.
- The j6_system_bind_irq syscall now takes an event handle, as well as a
  signal that the IRQ should set on the event. IRQs will cause a waiting
  thread to be woken with the appropriate bit set.
- Threads waking due to timeout is simplified to just having a
  wake_timeout() accessor that returns a timestamp.
- The new wait_queue uses util::deque, which caused the disovery of two
  bugs in the deque implementation: empty deques could still have a
  single array allocated and thus return true for empty(), and new
  arrays getting allocated were not being zeroed first.
- Exposed a new erase() method on util::map that takes a node pointer
  instead of a key, skipping lookup.
This commit is contained in:
Justin C. Miller
2022-02-21 19:16:20 -08:00
parent f93d80b8d2
commit f7ae2e2220
40 changed files with 419 additions and 628 deletions

View File

@@ -9,43 +9,38 @@
#include <j6/syscalls.h>
#include <j6/types.h>
j6_handle_t __handle_sys;
j6_handle_t __handle_self;
namespace {
constexpr size_t __static_arr_size = 4;
j6_handle_t __handle_array[__static_arr_size];
static j6_status_t
load_handles()
{
size_t count = __static_arr_size;
j6_handle_t *handles = __handle_array;
j6_status_t s = j6_handle_list(handles, &count);
if (s != j6_err_insufficient && s != j6_status_ok)
return s;
if (count > __static_arr_size)
count = __static_arr_size;
for (size_t i = 0; i < count; ++i) {
uint8_t type = (handles[i] >> 56);
if (type == j6_object_type_system && __handle_sys == j6_handle_invalid)
__handle_sys = handles[i];
else if (type == j6_object_type_process && __handle_self == j6_handle_invalid)
__handle_self = handles[i];
}
return s;
}
constexpr size_t static_arr_size = 8;
j6_handle_t handle_array[static_arr_size];
} // namespace
j6_handle_t
j6_find_first_handle(j6_object_type obj_type)
{
size_t count = static_arr_size;
j6_handle_t *handles = handle_array;
j6_status_t s = j6_handle_list(handles, &count);
if (s != j6_err_insufficient && s != j6_status_ok)
return j6_handle_invalid;
if (count > static_arr_size)
count = static_arr_size;
for (size_t i = 0; i < count; ++i) {
uint8_t type = (handles[i] >> 56);
if (type == obj_type) return handles[i];
}
return j6_handle_invalid;
}
extern "C" void
__init_libj6(uint64_t *rsp)
{
__handle_sys = __handle_self = j6_handle_invalid;
load_handles();
__handle_self = j6_find_first_handle(j6_object_type_process);
}
#endif // __j6kernel

View File

@@ -12,7 +12,6 @@ j6 = module("j6",
"j6/errors.h",
"j6/flags.h",
"j6/init.h",
"j6/signals.h",
"j6/syscalls.h.cog",
"j6/sysconf.h.cog",
"j6/types.h",

View File

@@ -1,37 +1,16 @@
#pragma once
/// \file init.h
/// Types used in process and thread initialization
/// Process initialization utility functions
#include <stdint.h>
#include <j6/types.h>
enum j6_init_type { // `value` is a:
j6_init_handle_self, // Handle to the system
j6_init_handle_other, // Handle to this process
j6_init_desc_framebuffer // Pointer to a j6_init_framebuffer descriptor
};
#ifdef __cplusplus
extern "C" {
#endif
struct j6_typed_handle {
enum j6_object_type type;
j6_handle_t handle;
};
/// Find the first handle of the given type held by this process
j6_handle_t j6_find_first_handle(j6_object_type obj_type);
struct j6_init_value {
enum j6_init_type type;
union {
struct j6_typed_handle handle;
void *data;
};
};
/// Structure defining a framebuffer.
/// `flags` has the following bits:
/// 0-3: Pixel layout. 0000: rgb8, 0001: bgr8
struct j6_init_framebuffer {
uintptr_t addr;
size_t size;
uint32_t vertical;
uint32_t horizontal;
uint32_t scanline;
uint32_t flags;
};
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -1,22 +0,0 @@
#pragma once
/// \file signals.h
/// Collection of constants for the j6_signal_t type
// Signals 56-63 are common to all types
#define j6_signal_no_handles (1ull << 56)
#define j6_signal_closed (1ull << 57)
#define j6_signal_global_mask 0xff00000000000000
// Signals 0-55 are defined per object type
// System signals
#define j6_signal_system_has_log (1ull << 0)
// Channel signals
#define j6_signal_channel_can_send (1ull << 0)
#define j6_signal_channel_can_recv (1ull << 1)
// Endpoint signals
#define j6_signal_endpoint_can_send (1ull << 0)
#define j6_signal_endpoint_can_recv (1ull << 1)

View File

@@ -3,6 +3,7 @@
/// A generic templatized linked list.
#include <assert.h>
#include <string.h>
#include <util/linked_list.h>
namespace util {
@@ -42,7 +43,9 @@ public:
inline void push_front(const T& item) {
if (!m_first) { // need a new block at the start
m_list.push_front(new node_type);
node_type *n = new node_type;
memset(n, 0, sizeof(node_type));
m_list.push_front(n);
m_first = N;
}
m_list.front()->items[--m_first] = item;
@@ -50,7 +53,9 @@ public:
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);
node_type *n = new node_type;
memset(n, 0, sizeof(node_type));
m_list.push_back(n);
m_next = 0;
}
m_list.back()->items[m_next++] = item;
@@ -80,7 +85,10 @@ public:
return value;
}
inline bool empty() const { return m_list.empty(); }
inline bool empty() const {
return m_list.empty() ||
m_list.length() == 1 && m_first == m_next;
}
inline T& first() {
assert(!empty() && "Calling first() on an empty deque");

View File

@@ -119,7 +119,22 @@ public:
{
node *n = lookup(k);
if (!n) return false;
erase(n);
return true;
}
inline size_t count() const { return m_count; }
inline size_t capacity() const { return m_capacity; }
inline size_t threshold() const { return (m_capacity * max_load) / 100; }
protected:
inline size_t mod(uint64_t i) const { return i & (m_capacity - 1); }
inline size_t offset(uint64_t h, size_t i) const {
return mod(i + m_capacity - mod(h));
}
void erase(node *n)
{
n->~node();
--m_count;
@@ -132,18 +147,6 @@ public:
m.~node();
i = mod(++i);
}
return true;
}
inline size_t count() const { return m_count; }
inline size_t capacity() const { return m_capacity; }
inline size_t threshold() const { return (m_capacity * max_load) / 100; }
protected:
inline size_t mod(uint64_t i) const { return i & (m_capacity - 1); }
inline size_t offset(uint64_t h, size_t i) const {
return mod(i + m_capacity - mod(h));
}
void set_capacity(size_t capacity) {
@@ -263,7 +266,8 @@ public:
V * find(const K &k) {
node *n = this->lookup(k);
return n ? &n->val : nullptr;
V *val = n ? &n->val : nullptr;
return val;
}
const V * find(const K &k) const {
@@ -285,6 +289,12 @@ public:
map(size_t capacity = 0) :
base(capacity) {}
V * find(const K &k) {
node *n = this->lookup(k);
V *val = n ? n->val : nullptr;
return val;
}
V * find(const K &k) const {
const node *n = this->lookup(k);
return n ? n->val : nullptr;