[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:
@@ -3,18 +3,20 @@ object event : object {
|
|||||||
|
|
||||||
capabilities [
|
capabilities [
|
||||||
signal
|
signal
|
||||||
|
wait
|
||||||
]
|
]
|
||||||
|
|
||||||
method create [constructor]
|
method create [constructor]
|
||||||
|
|
||||||
# Assert signals on the event from the given bitset
|
# Signal events on this object
|
||||||
method signal [cap:signal] {
|
method signal [cap:signal] {
|
||||||
param signals uint64
|
param signals uint64 # A bitset of which events to signal
|
||||||
}
|
}
|
||||||
|
|
||||||
# De-assert signals on the event from the given bitset
|
# Wait for signaled events on this object
|
||||||
method clear [cap:signal] {
|
method wait [cap:wait] {
|
||||||
param mask uint64
|
param signals uint64 [out] # A bitset of which events were signaled
|
||||||
|
param timeout uint64 # Wait timeout in nanoseconds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,20 +11,4 @@ object object [virtual] {
|
|||||||
method koid {
|
method koid {
|
||||||
param koid uint64 [out]
|
param koid uint64 [out]
|
||||||
}
|
}
|
||||||
|
|
||||||
# Block the current thread waiting for an object to assert
|
|
||||||
# one of a set of signals
|
|
||||||
method wait {
|
|
||||||
param mask uint64 # Bitmap of which signals to wait for
|
|
||||||
param signals uint64 [out] # Returns the state of the signals
|
|
||||||
}
|
|
||||||
|
|
||||||
# Block the current thread waiting for an one of multiple
|
|
||||||
# objects to assert one of a set of signals
|
|
||||||
method wait_many [static] {
|
|
||||||
param handles ref object [list] # The objects to wait on
|
|
||||||
param mask uint64 # Bitmap of which signals to wait for
|
|
||||||
param handle ref object [out] # Returns the object that signalled
|
|
||||||
param signals uint64 [out] # Returns the state of the signals
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,9 @@ object system : object {
|
|||||||
# Ask the kernel to send this process messages whenever
|
# Ask the kernel to send this process messages whenever
|
||||||
# the given IRQ fires
|
# the given IRQ fires
|
||||||
method bind_irq [cap:bind_irq] {
|
method bind_irq [cap:bind_irq] {
|
||||||
param dest ref endpoint # Endpoint that will receive messages
|
param dest ref event # Event object that will receive messages
|
||||||
param irq uint # IRQ number to bind
|
param irq uint # IRQ number to bind
|
||||||
|
param signal uint # Signal number on the event to bind to
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create a VMA and map an area of physical memory into it,
|
# Create a VMA and map an area of physical memory into it,
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ object thread : object {
|
|||||||
param status int32
|
param status int32
|
||||||
}
|
}
|
||||||
|
|
||||||
method pause [static]
|
|
||||||
method sleep [static] {
|
method sleep [static] {
|
||||||
param until uint64
|
param until uint64
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,10 @@
|
|||||||
#include "interrupts.h"
|
#include "interrupts.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "objects/endpoint.h"
|
#include "objects/event.h"
|
||||||
|
|
||||||
|
|
||||||
static obj::endpoint * const ignore_endpoint = reinterpret_cast<obj::endpoint*>(-1ull);
|
static obj::event * const ignore_event = reinterpret_cast<obj::event*>(-1ull);
|
||||||
|
|
||||||
static const char expected_signature[] = "RSD PTR ";
|
static const char expected_signature[] = "RSD PTR ";
|
||||||
|
|
||||||
@@ -58,9 +58,9 @@ device_manager::device_manager() :
|
|||||||
m_irqs.ensure_capacity(32);
|
m_irqs.ensure_capacity(32);
|
||||||
m_irqs.set_size(16);
|
m_irqs.set_size(16);
|
||||||
for (int i = 0; i < 16; ++i)
|
for (int i = 0; i < 16; ++i)
|
||||||
m_irqs[i] = nullptr;
|
m_irqs[i] = {nullptr, 0};
|
||||||
|
|
||||||
m_irqs[2] = ignore_endpoint;
|
m_irqs[2] = {ignore_event, 0};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> static const T *
|
template <typename T> static const T *
|
||||||
@@ -381,32 +381,32 @@ device_manager::dispatch_irq(unsigned irq)
|
|||||||
if (irq >= m_irqs.count())
|
if (irq >= m_irqs.count())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
obj::endpoint *e = m_irqs[irq];
|
irq_binding &binding = m_irqs[irq];
|
||||||
if (!e || e == ignore_endpoint)
|
if (!binding.target || binding.target == ignore_event)
|
||||||
return e == ignore_endpoint;
|
return binding.target == ignore_event;
|
||||||
|
|
||||||
e->signal_irq(irq);
|
binding.target->signal(1 << binding.signal);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
device_manager::bind_irq(unsigned irq, obj::endpoint *target)
|
device_manager::bind_irq(unsigned irq, obj::event *target, unsigned signal)
|
||||||
{
|
{
|
||||||
// TODO: grow if under max size
|
// TODO: grow if under max size
|
||||||
if (irq >= m_irqs.count())
|
if (irq >= m_irqs.count())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_irqs[irq]= target;
|
m_irqs[irq] = {target, signal};
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
device_manager::unbind_irqs(obj::endpoint *target)
|
device_manager::unbind_irqs(obj::event *target)
|
||||||
{
|
{
|
||||||
const size_t count = m_irqs.count();
|
const size_t count = m_irqs.count();
|
||||||
for (size_t i = 0; i < count; ++i) {
|
for (size_t i = 0; i < count; ++i) {
|
||||||
if (m_irqs[i] == target)
|
if (m_irqs[i].target == target)
|
||||||
m_irqs[i] = nullptr;
|
m_irqs[i] = {nullptr, 0};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ struct acpi_table_header;
|
|||||||
class block_device;
|
class block_device;
|
||||||
|
|
||||||
namespace obj {
|
namespace obj {
|
||||||
class endpoint;
|
class event;
|
||||||
}
|
}
|
||||||
|
|
||||||
using irq_callback = void (*)(void *);
|
using irq_callback = void (*)(void *);
|
||||||
@@ -45,12 +45,13 @@ public:
|
|||||||
/// Bind an IRQ to an endpoint
|
/// Bind an IRQ to an endpoint
|
||||||
/// \arg irq The IRQ number to bind
|
/// \arg irq The IRQ number to bind
|
||||||
/// \arg target The endpoint to recieve messages when the IRQ is signalled
|
/// \arg target The endpoint to recieve messages when the IRQ is signalled
|
||||||
|
/// \arg signal The event's signal number to bind to
|
||||||
/// \returns True on success
|
/// \returns True on success
|
||||||
bool bind_irq(unsigned irq, obj::endpoint *target);
|
bool bind_irq(unsigned irq, obj::event *target, unsigned signal);
|
||||||
|
|
||||||
/// Remove IRQ bindings for an endpoint
|
/// Remove IRQ bindings for an endpoint
|
||||||
/// \arg target The endpoint to remove
|
/// \arg target The endpoint to remove
|
||||||
void unbind_irqs(obj::endpoint *target);
|
void unbind_irqs(obj::event *target);
|
||||||
|
|
||||||
/// Allocate an MSI IRQ for a device
|
/// Allocate an MSI IRQ for a device
|
||||||
/// \arg name Name of the interrupt, for display to user
|
/// \arg name Name of the interrupt, for display to user
|
||||||
@@ -164,7 +165,12 @@ private:
|
|||||||
util::vector<pci_group> m_pci;
|
util::vector<pci_group> m_pci;
|
||||||
util::vector<pci_device> m_devices;
|
util::vector<pci_device> m_devices;
|
||||||
|
|
||||||
util::vector<obj::endpoint*> m_irqs;
|
struct irq_binding
|
||||||
|
{
|
||||||
|
obj::event *target = nullptr;
|
||||||
|
unsigned signal = 0;
|
||||||
|
};
|
||||||
|
util::vector<irq_binding> m_irqs;
|
||||||
|
|
||||||
util::vector<block_device *> m_blockdevs;
|
util::vector<block_device *> m_blockdevs;
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ kernel = module("kernel",
|
|||||||
"msr.cpp",
|
"msr.cpp",
|
||||||
"objects/channel.cpp",
|
"objects/channel.cpp",
|
||||||
"objects/endpoint.cpp",
|
"objects/endpoint.cpp",
|
||||||
|
"objects/event.cpp",
|
||||||
"objects/kobject.cpp",
|
"objects/kobject.cpp",
|
||||||
"objects/thread.cpp",
|
"objects/thread.cpp",
|
||||||
"objects/process.cpp",
|
"objects/process.cpp",
|
||||||
@@ -64,6 +65,7 @@ kernel = module("kernel",
|
|||||||
"task.s",
|
"task.s",
|
||||||
"tss.cpp",
|
"tss.cpp",
|
||||||
"vm_space.cpp",
|
"vm_space.cpp",
|
||||||
|
"wait_queue.cpp",
|
||||||
])
|
])
|
||||||
|
|
||||||
from glob import glob
|
from glob import glob
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "objects/system.h"
|
#include "objects/system.h"
|
||||||
|
#include "objects/thread.h"
|
||||||
#include "printf/printf.h"
|
#include "printf/printf.h"
|
||||||
|
|
||||||
static uint8_t log_buffer[128 * 1024];
|
static uint8_t log_buffer[128 * 1024];
|
||||||
@@ -87,13 +88,14 @@ logger::output(level severity, logs area, const char *fmt, va_list args)
|
|||||||
memcpy(out, buffer, n);
|
memcpy(out, buffer, n);
|
||||||
m_buffer.commit(n);
|
m_buffer.commit(n);
|
||||||
|
|
||||||
obj::system &sys = obj::system::get();
|
m_event.signal(1);
|
||||||
sys.assert_signal(j6_signal_system_has_log);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
logger::get_entry(void *buffer, size_t size)
|
logger::get_entry(void *buffer, size_t size)
|
||||||
{
|
{
|
||||||
|
m_event.wait();
|
||||||
|
|
||||||
util::scoped_lock lock {m_lock};
|
util::scoped_lock lock {m_lock};
|
||||||
|
|
||||||
void *out;
|
void *out;
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
#include <util/bip_buffer.h>
|
#include <util/bip_buffer.h>
|
||||||
#include <util/spinlock.h>
|
#include <util/spinlock.h>
|
||||||
|
|
||||||
|
#include "objects/event.h"
|
||||||
|
|
||||||
enum class logs : uint8_t {
|
enum class logs : uint8_t {
|
||||||
#define LOG(name, lvl) name,
|
#define LOG(name, lvl) name,
|
||||||
#include <j6/tables/log_areas.inc>
|
#include <j6/tables/log_areas.inc>
|
||||||
@@ -107,6 +109,8 @@ private:
|
|||||||
return m_levels[static_cast<unsigned>(area)];
|
return m_levels[static_cast<unsigned>(area)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
obj::event m_event;
|
||||||
|
|
||||||
level m_levels[areas_count];
|
level m_levels[areas_count];
|
||||||
immediate_cb m_immediate;
|
immediate_cb m_immediate;
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <bootproto/kernel.h>
|
#include <bootproto/kernel.h>
|
||||||
#include <j6/signals.h>
|
|
||||||
#include <util/vector.h>
|
#include <util/vector.h>
|
||||||
|
|
||||||
#include "assert.h"
|
#include "assert.h"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ channel::channel() :
|
|||||||
m_len(0),
|
m_len(0),
|
||||||
m_data(g_kernel_buffers.get_section()),
|
m_data(g_kernel_buffers.get_section()),
|
||||||
m_buffer(reinterpret_cast<uint8_t*>(m_data), buffer_bytes),
|
m_buffer(reinterpret_cast<uint8_t*>(m_data), buffer_bytes),
|
||||||
kobject(kobject::type::channel, j6_signal_channel_can_send)
|
kobject(kobject::type::channel)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,10 +41,10 @@ channel::enqueue(const util::buffer &data)
|
|||||||
m_buffer.commit(len);
|
m_buffer.commit(len);
|
||||||
|
|
||||||
if (len)
|
if (len)
|
||||||
assert_signal(j6_signal_channel_can_recv);
|
m_can_recv = true;
|
||||||
|
|
||||||
if (m_buffer.free_space() == 0)
|
if (m_buffer.free_space() == 0)
|
||||||
deassert_signal(j6_signal_channel_can_send);
|
m_can_send = false;
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
@@ -64,10 +64,10 @@ channel::dequeue(util::buffer buffer)
|
|||||||
m_buffer.consume(len);
|
m_buffer.consume(len);
|
||||||
|
|
||||||
if (len)
|
if (len)
|
||||||
assert_signal(j6_signal_channel_can_send);
|
m_can_send = true;
|
||||||
|
|
||||||
if (m_buffer.size() == 0)
|
if (m_buffer.size() == 0)
|
||||||
deassert_signal(j6_signal_channel_can_recv);
|
m_can_recv = false;
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
@@ -76,16 +76,8 @@ void
|
|||||||
channel::close()
|
channel::close()
|
||||||
{
|
{
|
||||||
util::scoped_lock lock {m_close_lock};
|
util::scoped_lock lock {m_close_lock};
|
||||||
|
|
||||||
kobject::close();
|
|
||||||
g_kernel_buffers.return_section(m_data);
|
g_kernel_buffers.return_section(m_data);
|
||||||
}
|
m_closed = true;
|
||||||
|
|
||||||
void
|
|
||||||
channel::on_no_handles()
|
|
||||||
{
|
|
||||||
kobject::on_no_handles();
|
|
||||||
delete this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace obj
|
} // namespace obj
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
/// Definition of channel objects and related functions
|
/// Definition of channel objects and related functions
|
||||||
|
|
||||||
#include <j6/caps.h>
|
#include <j6/caps.h>
|
||||||
#include <j6/signals.h>
|
|
||||||
#include <util/bip_buffer.h>
|
#include <util/bip_buffer.h>
|
||||||
#include <util/counted.h>
|
#include <util/counted.h>
|
||||||
#include <util/spinlock.h>
|
#include <util/spinlock.h>
|
||||||
@@ -26,10 +25,10 @@ public:
|
|||||||
static constexpr kobject::type type = kobject::type::channel;
|
static constexpr kobject::type type = kobject::type::channel;
|
||||||
|
|
||||||
/// Check if the channel has space for a message to be sent
|
/// Check if the channel has space for a message to be sent
|
||||||
inline bool can_send() const { return check_signal(j6_signal_channel_can_send); }
|
inline bool can_send() const { return m_can_send; }
|
||||||
|
|
||||||
/// Check if the channel has a message wiating already
|
/// Check if the channel has a message wiating already
|
||||||
inline bool can_receive() const { return check_signal(j6_signal_channel_can_recv); }
|
inline bool can_receive() const { return m_can_recv; }
|
||||||
|
|
||||||
/// Put a message into the channel
|
/// Put a message into the channel
|
||||||
/// \arg data Buffer of data to write
|
/// \arg data Buffer of data to write
|
||||||
@@ -43,14 +42,17 @@ public:
|
|||||||
|
|
||||||
/// Mark this channel as closed, all future calls to enqueue or
|
/// Mark this channel as closed, all future calls to enqueue or
|
||||||
/// dequeue messages will fail with j6_status_closed.
|
/// dequeue messages will fail with j6_status_closed.
|
||||||
virtual void close() override;
|
void close();
|
||||||
|
|
||||||
protected:
|
/// Check if this channel has been closed.
|
||||||
virtual void on_no_handles() override;
|
inline bool closed() const { return m_closed; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t m_len;
|
size_t m_len;
|
||||||
uintptr_t m_data;
|
uintptr_t m_data;
|
||||||
|
bool m_closed;
|
||||||
|
bool m_can_send;
|
||||||
|
bool m_can_recv;
|
||||||
util::bip_buffer m_buffer;
|
util::bip_buffer m_buffer;
|
||||||
util::spinlock m_close_lock;
|
util::spinlock m_close_lock;
|
||||||
};
|
};
|
||||||
|
|||||||
48
src/kernel/objects/event.cpp
Normal file
48
src/kernel/objects/event.cpp
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#include "objects/event.h"
|
||||||
|
#include "objects/thread.h"
|
||||||
|
|
||||||
|
namespace obj {
|
||||||
|
|
||||||
|
event::event() :
|
||||||
|
kobject {type::event},
|
||||||
|
m_signals {0}
|
||||||
|
{}
|
||||||
|
|
||||||
|
event::~event() {}
|
||||||
|
|
||||||
|
void
|
||||||
|
event::signal(uint64_t s)
|
||||||
|
{
|
||||||
|
uint64_t after = __atomic_or_fetch(&m_signals, s, __ATOMIC_SEQ_CST);
|
||||||
|
if (after) wake_observer();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t
|
||||||
|
event::wait()
|
||||||
|
{
|
||||||
|
// If events are pending, grab those and return immediately
|
||||||
|
uint64_t value = read();
|
||||||
|
if (value)
|
||||||
|
return value;
|
||||||
|
|
||||||
|
// Wait for event::signal() to wake us with a value
|
||||||
|
thread ¤t = thread::current();
|
||||||
|
m_queue.add_thread(¤t);
|
||||||
|
return current.block();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
event::wake_observer()
|
||||||
|
{
|
||||||
|
util::scoped_lock lock {m_queue.get_lock()};
|
||||||
|
thread *t = m_queue.get_next_unlocked();
|
||||||
|
if (!t) return;
|
||||||
|
|
||||||
|
uint64_t value = read();
|
||||||
|
if (value) {
|
||||||
|
m_queue.pop_next_unlocked();
|
||||||
|
t->wake(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace obj
|
||||||
@@ -2,21 +2,48 @@
|
|||||||
/// \file event.h
|
/// \file event.h
|
||||||
/// Definition of event kobject types
|
/// Definition of event kobject types
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <j6/caps.h>
|
||||||
|
|
||||||
#include "objects/kobject.h"
|
#include "objects/kobject.h"
|
||||||
|
#include "wait_queue.h"
|
||||||
|
|
||||||
namespace obj {
|
namespace obj {
|
||||||
|
|
||||||
|
class thread;
|
||||||
|
|
||||||
class event :
|
class event :
|
||||||
public kobject
|
public kobject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Capabilities on a newly constructed event handle
|
/// Capabilities on a newly constructed event handle
|
||||||
constexpr static j6_cap_t creation_caps = 0;
|
constexpr static j6_cap_t creation_caps = j6_cap_event_all;
|
||||||
|
|
||||||
event() :
|
|
||||||
kobject(type::event) {}
|
|
||||||
|
|
||||||
static constexpr kobject::type type = kobject::type::event;
|
static constexpr kobject::type type = kobject::type::event;
|
||||||
|
|
||||||
|
event();
|
||||||
|
~event();
|
||||||
|
|
||||||
|
/// Signal the given event(s).
|
||||||
|
/// \arg s The events to signal, as a bitset
|
||||||
|
void signal(uint64_t s);
|
||||||
|
|
||||||
|
/// Wait for events to be signalled. If events have already
|
||||||
|
/// been signaled since the last call to wait, return them
|
||||||
|
/// immediately.
|
||||||
|
/// \returns The events which have been signalled, as a bitset
|
||||||
|
uint64_t wait();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Read and consume the current signaled events
|
||||||
|
inline uint64_t read() {
|
||||||
|
return __atomic_exchange_n(&m_signals, 0, __ATOMIC_SEQ_CST);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wake an observer to handle an incoming event
|
||||||
|
void wake_observer();
|
||||||
|
|
||||||
|
uint64_t m_signals;
|
||||||
|
wait_queue m_queue;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace obj
|
} // namespace obj
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#include <j6/errors.h>
|
#include <j6/errors.h>
|
||||||
#include <j6/signals.h>
|
|
||||||
#include <j6/types.h>
|
#include <j6/types.h>
|
||||||
|
|
||||||
#include "assert.h"
|
#include "assert.h"
|
||||||
@@ -10,17 +9,12 @@ namespace obj {
|
|||||||
|
|
||||||
static j6_koid_t next_koids [static_cast<size_t>(kobject::type::max)] = { 0 };
|
static j6_koid_t next_koids [static_cast<size_t>(kobject::type::max)] = { 0 };
|
||||||
|
|
||||||
kobject::kobject(type t, j6_signal_t signals) :
|
kobject::kobject(type t) :
|
||||||
m_koid(koid_generate(t)),
|
m_koid(koid_generate(t)),
|
||||||
m_signals(signals),
|
|
||||||
m_handle_count(0)
|
m_handle_count(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
kobject::~kobject()
|
kobject::~kobject() {}
|
||||||
{
|
|
||||||
for (auto *t : m_blocked_threads)
|
|
||||||
t->wake_on_result(this, j6_status_destroyed);
|
|
||||||
}
|
|
||||||
|
|
||||||
j6_koid_t
|
j6_koid_t
|
||||||
kobject::koid_generate(type t)
|
kobject::koid_generate(type t)
|
||||||
@@ -37,81 +31,10 @@ kobject::koid_type(j6_koid_t koid)
|
|||||||
return static_cast<type>((koid >> 48) & 0xffffull);
|
return static_cast<type>((koid >> 48) & 0xffffull);
|
||||||
}
|
}
|
||||||
|
|
||||||
j6_signal_t
|
|
||||||
kobject::assert_signal(j6_signal_t 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::compact_blocked_threads()
|
|
||||||
{
|
|
||||||
// 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()
|
|
||||||
{
|
|
||||||
for (auto &entry : m_blocked_threads) {
|
|
||||||
if (entry == nullptr) continue;
|
|
||||||
if (entry->wake_on_signals(this, m_signals))
|
|
||||||
entry = nullptr;
|
|
||||||
}
|
|
||||||
compact_blocked_threads();
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
kobject::close()
|
|
||||||
{
|
|
||||||
assert_signal(j6_signal_closed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
kobject::on_no_handles()
|
kobject::on_no_handles()
|
||||||
{
|
{
|
||||||
assert_signal(j6_signal_no_handles);
|
delete this;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace obj
|
} // namespace obj
|
||||||
|
|||||||
@@ -3,9 +3,7 @@
|
|||||||
/// Definition of base type for user-interactable kernel objects
|
/// Definition of base type for user-interactable kernel objects
|
||||||
|
|
||||||
#include <j6/errors.h>
|
#include <j6/errors.h>
|
||||||
#include <j6/signals.h>
|
|
||||||
#include <j6/types.h>
|
#include <j6/types.h>
|
||||||
#include <util/deque.h>
|
|
||||||
|
|
||||||
namespace obj {
|
namespace obj {
|
||||||
|
|
||||||
@@ -25,7 +23,7 @@ public:
|
|||||||
max
|
max
|
||||||
};
|
};
|
||||||
|
|
||||||
kobject(type t, j6_signal_t signals = 0ull);
|
kobject(type t);
|
||||||
virtual ~kobject();
|
virtual ~kobject();
|
||||||
|
|
||||||
/// Generate a new koid for a given type
|
/// Generate a new koid for a given type
|
||||||
@@ -44,23 +42,6 @@ public:
|
|||||||
/// Get this object's koid
|
/// Get this object's koid
|
||||||
inline j6_koid_t koid() const { return m_koid; }
|
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
|
|
||||||
/// \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
|
|
||||||
/// \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
|
|
||||||
inline bool check_signal(j6_signal_t s) const { return (m_signals & s) == s; }
|
|
||||||
|
|
||||||
/// Get the current object signal state
|
|
||||||
inline j6_signal_t signals() const { return m_signals; }
|
|
||||||
|
|
||||||
/// Increment the handle refcount
|
/// Increment the handle refcount
|
||||||
inline void handle_retain() { ++m_handle_count; }
|
inline void handle_retain() { ++m_handle_count; }
|
||||||
|
|
||||||
@@ -69,45 +50,21 @@ public:
|
|||||||
if (--m_handle_count == 0) on_no_handles();
|
if (--m_handle_count == 0) on_no_handles();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add the given thread to the list of threads waiting on this object.
|
|
||||||
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.
|
|
||||||
void remove_blocked_thread(thread *t);
|
|
||||||
|
|
||||||
/// Perform any cleanup actions necessary to mark this object closed
|
|
||||||
virtual void close();
|
|
||||||
|
|
||||||
/// Check if this object has been closed
|
|
||||||
inline bool closed() const { return check_signal(j6_signal_closed); }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Interface for subclasses to handle when all handles are closed. Subclasses
|
/// Interface for subclasses to handle when all handles are closed.
|
||||||
/// should either call the base version, or assert j6_signal_no_handles.
|
/// Default implementation deletes the object.
|
||||||
virtual void on_no_handles();
|
virtual void on_no_handles();
|
||||||
|
|
||||||
|
/// Get the current number of handles to this object
|
||||||
|
inline uint16_t handle_count() const { return m_handle_count; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
kobject() = delete;
|
kobject() = delete;
|
||||||
kobject(const kobject &other) = delete;
|
kobject(const kobject &other) = delete;
|
||||||
kobject(const kobject &&other) = delete;
|
kobject(const kobject &&other) = delete;
|
||||||
|
|
||||||
/// Notifiy observers of this object
|
|
||||||
/// \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_koid_t m_koid;
|
||||||
j6_signal_t m_signals;
|
|
||||||
uint16_t m_handle_count;
|
uint16_t m_handle_count;
|
||||||
|
|
||||||
protected:
|
|
||||||
/// 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
|
} // namespace obj
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ process::exit(int32_t code)
|
|||||||
// TODO: make this thread-safe
|
// TODO: make this thread-safe
|
||||||
m_state = state::exited;
|
m_state = state::exited;
|
||||||
m_return_code = code;
|
m_return_code = code;
|
||||||
close();
|
|
||||||
|
|
||||||
for (auto *thread : m_threads) {
|
for (auto *thread : m_threads) {
|
||||||
thread->exit(code);
|
thread->exit(code);
|
||||||
|
|||||||
@@ -103,6 +103,11 @@ public:
|
|||||||
/// \returns The kernel process object
|
/// \returns The kernel process object
|
||||||
static process * create_kernel_process(page_table *pml4);
|
static process * create_kernel_process(page_table *pml4);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// Don't delete a process on no handles, the scheduler takes
|
||||||
|
/// care of that.
|
||||||
|
virtual void on_no_handles() override {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// This constructor is called by create_kernel_process
|
// This constructor is called by create_kernel_process
|
||||||
process(page_table *kpml4);
|
process(page_table *kpml4);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#include <j6/signals.h>
|
|
||||||
#include <util/pointers.h>
|
#include <util/pointers.h>
|
||||||
|
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
@@ -15,15 +14,12 @@ extern obj::vm_area_guarded &g_kernel_stacks;
|
|||||||
|
|
||||||
namespace obj {
|
namespace obj {
|
||||||
|
|
||||||
static constexpr j6_signal_t thread_default_signals = 0;
|
|
||||||
|
|
||||||
thread::thread(process &parent, uint8_t pri, uintptr_t rsp0) :
|
thread::thread(process &parent, uint8_t pri, uintptr_t rsp0) :
|
||||||
kobject(kobject::type::thread, thread_default_signals),
|
kobject {kobject::type::thread},
|
||||||
m_parent(parent),
|
m_parent {parent},
|
||||||
m_state(state::none),
|
m_state {state::none},
|
||||||
m_wait_type(wait_type::none),
|
m_wake_value {0},
|
||||||
m_wait_data(0),
|
m_wake_timeout {0}
|
||||||
m_wait_obj(0)
|
|
||||||
{
|
{
|
||||||
parent.space().initialize_tcb(m_tcb);
|
parent.space().initialize_tcb(m_tcb);
|
||||||
m_tcb.priority = pri;
|
m_tcb.priority = pri;
|
||||||
@@ -45,129 +41,20 @@ thread::~thread()
|
|||||||
|
|
||||||
thread & thread::current() { return *current_cpu().thread; }
|
thread & thread::current() { return *current_cpu().thread; }
|
||||||
|
|
||||||
j6_status_t
|
uint64_t
|
||||||
thread::wait_on_signals(j6_signal_t signals)
|
|
||||||
{
|
|
||||||
util::scoped_lock lock {m_wait_lock};
|
|
||||||
|
|
||||||
m_wait_type = wait_type::signal;
|
|
||||||
m_wait_data = signals;
|
|
||||||
|
|
||||||
lock.release();
|
|
||||||
block();
|
|
||||||
|
|
||||||
return m_wait_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
j6_status_t
|
|
||||||
thread::wait_on_time(uint64_t t)
|
|
||||||
{
|
|
||||||
util::scoped_lock lock {m_wait_lock};
|
|
||||||
|
|
||||||
m_wait_type = wait_type::time;
|
|
||||||
m_wait_time = t;
|
|
||||||
|
|
||||||
lock.release();
|
|
||||||
block();
|
|
||||||
|
|
||||||
return m_wait_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
j6_status_t
|
|
||||||
thread::wait_on_object(void *o, uint64_t t)
|
|
||||||
{
|
|
||||||
util::scoped_lock lock {m_wait_lock};
|
|
||||||
|
|
||||||
m_wait_type = wait_type::object;
|
|
||||||
m_wait_data = reinterpret_cast<uint64_t>(o);
|
|
||||||
|
|
||||||
if (t) {
|
|
||||||
m_wait_type |= wait_type::time;
|
|
||||||
m_wait_time = t;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock.release();
|
|
||||||
block();
|
|
||||||
return m_wait_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
thread::wake_on_signals(kobject *obj, j6_signal_t signals)
|
|
||||||
{
|
|
||||||
util::scoped_lock lock {m_wait_lock};
|
|
||||||
|
|
||||||
if (!(m_wait_type & wait_type::signal) ||
|
|
||||||
(signals & m_wait_data) == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
m_wait_type = wait_type::none;
|
|
||||||
m_wait_result = j6_status_ok;
|
|
||||||
m_wait_data = signals;
|
|
||||||
m_wait_obj = obj->koid();
|
|
||||||
set_state(state::ready);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
thread::wake_on_time(uint64_t now)
|
|
||||||
{
|
|
||||||
util::scoped_lock lock {m_wait_lock};
|
|
||||||
|
|
||||||
if (!(m_wait_type & wait_type::time) ||
|
|
||||||
now < m_wait_time)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!(m_wait_type & ~wait_type::none))
|
|
||||||
m_wait_result = j6_status_ok;
|
|
||||||
else
|
|
||||||
m_wait_result = j6_err_timed_out;
|
|
||||||
|
|
||||||
m_wait_type = wait_type::none;
|
|
||||||
m_wait_data = now;
|
|
||||||
m_wait_obj = 0;
|
|
||||||
set_state(state::ready);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
thread::wake_on_object(void *o, uint64_t id)
|
|
||||||
{
|
|
||||||
util::scoped_lock lock {m_wait_lock};
|
|
||||||
|
|
||||||
if (!(m_wait_type & wait_type::object) ||
|
|
||||||
reinterpret_cast<uint64_t>(o) != m_wait_data)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
m_wait_type = wait_type::none;
|
|
||||||
m_wait_result = j6_status_ok;
|
|
||||||
m_wait_obj = id;
|
|
||||||
set_state(state::ready);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
thread::wake_on_result(kobject *obj, j6_status_t result)
|
|
||||||
{
|
|
||||||
util::scoped_lock lock {m_wait_lock};
|
|
||||||
|
|
||||||
m_wait_type = wait_type::none;
|
|
||||||
m_wait_result = result;
|
|
||||||
m_wait_data = 0;
|
|
||||||
m_wait_obj = obj->koid();
|
|
||||||
set_state(state::ready);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
thread::block()
|
thread::block()
|
||||||
{
|
{
|
||||||
clear_state(state::ready);
|
clear_state(state::ready);
|
||||||
if (current_cpu().thread == this)
|
if (current_cpu().thread == this)
|
||||||
scheduler::get().schedule();
|
scheduler::get().schedule();
|
||||||
|
return m_wake_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
thread::wake()
|
thread::wake(uint64_t value)
|
||||||
{
|
{
|
||||||
|
m_wake_value = value;
|
||||||
|
m_wake_timeout = 0;
|
||||||
set_state(state::ready);
|
set_state(state::ready);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,8 +62,8 @@ void
|
|||||||
thread::exit(int32_t code)
|
thread::exit(int32_t code)
|
||||||
{
|
{
|
||||||
m_return_code = code;
|
m_return_code = code;
|
||||||
|
m_wake_timeout = 0;
|
||||||
set_state(state::exited);
|
set_state(state::exited);
|
||||||
close();
|
|
||||||
block();
|
block();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,14 +29,14 @@ struct TCB
|
|||||||
obj::thread* thread;
|
obj::thread* thread;
|
||||||
|
|
||||||
uint8_t priority;
|
uint8_t priority;
|
||||||
|
|
||||||
// note: 3 bytes padding
|
// note: 3 bytes padding
|
||||||
|
|
||||||
// TODO: move state into TCB?
|
// TODO: move state into TCB?
|
||||||
|
|
||||||
uintptr_t kernel_stack;
|
|
||||||
|
|
||||||
uint32_t time_left;
|
uint32_t time_left;
|
||||||
uint64_t last_ran;
|
uint64_t last_ran;
|
||||||
|
|
||||||
|
uintptr_t kernel_stack;
|
||||||
};
|
};
|
||||||
|
|
||||||
using tcb_list = util::linked_list<TCB>;
|
using tcb_list = util::linked_list<TCB>;
|
||||||
@@ -90,6 +90,10 @@ public:
|
|||||||
/// \returns True if the thread has constant priority.
|
/// \returns True if the thread has constant priority.
|
||||||
inline bool constant() const { return has_state(state::constant); }
|
inline bool constant() const { return has_state(state::constant); }
|
||||||
|
|
||||||
|
/// Get the `exited` state of the thread.
|
||||||
|
/// \returns True if the thread has exited.
|
||||||
|
inline bool exited() const { return has_state(state::exited); }
|
||||||
|
|
||||||
/// Get the thread priority.
|
/// Get the thread priority.
|
||||||
inline uint8_t priority() const { return m_tcb.priority; }
|
inline uint8_t priority() const { return m_tcb.priority; }
|
||||||
|
|
||||||
@@ -97,58 +101,21 @@ public:
|
|||||||
/// \arg p The new thread priority
|
/// \arg p The new thread priority
|
||||||
inline void set_priority(uint8_t p) { if (!constant()) m_tcb.priority = p; }
|
inline void set_priority(uint8_t p) { if (!constant()) m_tcb.priority = p; }
|
||||||
|
|
||||||
/// Block the thread, waiting an object's signals.
|
/// Block this thread, waiting for a value
|
||||||
/// \arg signals Mask of signals to wait for
|
/// \returns The value passed to wake()
|
||||||
/// \returns j6_status_ok on success
|
uint64_t block();
|
||||||
j6_status_t wait_on_signals(j6_signal_t signals);
|
|
||||||
|
|
||||||
/// Block the thread, waiting for a given clock value
|
/// Wake this thread, giving it a value
|
||||||
/// \arg t Clock value to wait for
|
/// \arg value The value that block() should return
|
||||||
/// \returns j6_status_ok on success
|
void wake(uint64_t value = 0);
|
||||||
j6_status_t wait_on_time(uint64_t t);
|
|
||||||
|
|
||||||
/// Block the thread, waiting on the given object
|
/// Set a timeout to unblock this thread
|
||||||
/// \arg o The object that should wake this thread
|
/// \arg time The clock time at which to wake. 0 for no timeout.
|
||||||
/// \arg t The timeout clock value to wait for
|
inline void set_wake_timeout(uint64_t time) { m_wake_timeout = time; }
|
||||||
/// \returns j6_status_ok on success
|
|
||||||
j6_status_t wait_on_object(void *o, uint64_t t = 0);
|
|
||||||
|
|
||||||
/// Wake the thread if it is waiting on signals.
|
/// Get the timeout at which to unblock this thread
|
||||||
/// \arg obj Object that changed signals
|
/// \returns The clock time at which to wake. 0 for no timeout.
|
||||||
/// \arg signals Signal state of the object
|
inline uint64_t wake_timeout() const { return m_wake_timeout; }
|
||||||
/// \returns True if this action unblocked the thread
|
|
||||||
bool wake_on_signals(kobject *obj, j6_signal_t signals);
|
|
||||||
|
|
||||||
/// Wake the thread if it is waiting on the clock.
|
|
||||||
/// \arg now Current clock value
|
|
||||||
/// \returns True if this action unblocked the thread
|
|
||||||
bool wake_on_time(uint64_t now);
|
|
||||||
|
|
||||||
/// Wake the thread if it is waiting on the given object.
|
|
||||||
/// \arg o Object trying to wake the thread
|
|
||||||
/// \arg id Id of the object trying to wake the thread
|
|
||||||
/// \returns True if this action unblocked the thread
|
|
||||||
bool wake_on_object(void *o, uint64_t id = 0);
|
|
||||||
|
|
||||||
/// Wake the thread with a given result code.
|
|
||||||
/// \arg obj Object that changed signals
|
|
||||||
/// \arg result Result code to return to the thread
|
|
||||||
void wake_on_result(kobject *obj, j6_status_t result);
|
|
||||||
|
|
||||||
/// Get the result status code from the last blocking operation
|
|
||||||
j6_status_t get_wait_result() const { return m_wait_result; }
|
|
||||||
|
|
||||||
/// Get the current blocking opreation's wait data
|
|
||||||
uint64_t get_wait_data() const { return m_wait_data; }
|
|
||||||
|
|
||||||
/// Get the current blocking operation's wait ojbect (as a handle)
|
|
||||||
j6_koid_t get_wait_object() const { return m_wait_obj; }
|
|
||||||
|
|
||||||
/// Primitive thread blocking, instead of using wait framework
|
|
||||||
void block();
|
|
||||||
|
|
||||||
/// Primitive thread unblocking, instead of using wait framework
|
|
||||||
void wake();
|
|
||||||
|
|
||||||
inline bool has_state(state s) const {
|
inline bool has_state(state s) const {
|
||||||
return static_cast<uint8_t>(m_state) & static_cast<uint8_t>(s);
|
return static_cast<uint8_t>(m_state) & static_cast<uint8_t>(s);
|
||||||
@@ -189,6 +156,11 @@ public:
|
|||||||
/// \arg rsp The existing stack for the idle thread
|
/// \arg rsp The existing stack for the idle thread
|
||||||
static thread * create_idle_thread(process &kernel, uint8_t pri, uintptr_t rsp);
|
static thread * create_idle_thread(process &kernel, uint8_t pri, uintptr_t rsp);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// Don't delete a thread on no handles, the scheduler takes
|
||||||
|
/// care of that.
|
||||||
|
virtual void on_no_handles() override {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
thread() = delete;
|
thread() = delete;
|
||||||
thread(const thread &other) = delete;
|
thread(const thread &other) = delete;
|
||||||
@@ -210,16 +182,13 @@ private:
|
|||||||
thread *m_creator;
|
thread *m_creator;
|
||||||
|
|
||||||
state m_state;
|
state m_state;
|
||||||
wait_type m_wait_type;
|
|
||||||
// There should be 1 byte of padding here
|
// There should be 3 bytes of padding here
|
||||||
|
|
||||||
int32_t m_return_code;
|
int32_t m_return_code;
|
||||||
|
|
||||||
uint64_t m_wait_data;
|
uint64_t m_wake_value;
|
||||||
uint64_t m_wait_time;
|
uint64_t m_wake_timeout;
|
||||||
j6_status_t m_wait_result;
|
|
||||||
uint64_t m_wait_obj;
|
|
||||||
util::spinlock m_wait_lock;
|
|
||||||
|
|
||||||
j6_handle_t m_self_handle;
|
j6_handle_t m_self_handle;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -35,15 +35,13 @@ void
|
|||||||
vm_area::remove_from(vm_space *space)
|
vm_area::remove_from(vm_space *space)
|
||||||
{
|
{
|
||||||
m_spaces.remove_swap(space);
|
m_spaces.remove_swap(space);
|
||||||
if (!m_spaces.count() &&
|
if (!m_spaces.count() && !handle_count())
|
||||||
check_signal(j6_signal_no_handles))
|
|
||||||
delete this;
|
delete this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
vm_area::on_no_handles()
|
vm_area::on_no_handles()
|
||||||
{
|
{
|
||||||
kobject::on_no_handles();
|
|
||||||
if (!m_spaces.count())
|
if (!m_spaces.count())
|
||||||
delete this;
|
delete this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <j6/caps.h>
|
#include <j6/caps.h>
|
||||||
#include <j6/signals.h>
|
|
||||||
#include <util/vector.h>
|
#include <util/vector.h>
|
||||||
#include <util/enum_bitfields.h>
|
#include <util/enum_bitfields.h>
|
||||||
|
|
||||||
@@ -76,7 +75,10 @@ public:
|
|||||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys) = 0;
|
virtual bool get_page(uintptr_t offset, uintptr_t &phys) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
/// A VMA is not deleted until both no handles remain AND it's not
|
||||||
|
/// mapped by any VM space.
|
||||||
virtual void on_no_handles() override;
|
virtual void on_no_handles() override;
|
||||||
|
|
||||||
bool can_resize(size_t size);
|
bool can_resize(size_t size);
|
||||||
|
|
||||||
size_t m_size;
|
size_t m_size;
|
||||||
|
|||||||
@@ -126,13 +126,15 @@ scheduler::prune(run_queue &queue, uint64_t now)
|
|||||||
thread *th = tcb->thread;
|
thread *th = tcb->thread;
|
||||||
uint8_t priority = tcb->priority;
|
uint8_t priority = tcb->priority;
|
||||||
|
|
||||||
|
uint64_t timeout = th->wake_timeout();
|
||||||
|
if (timeout && timeout <= now)
|
||||||
|
th->wake();
|
||||||
|
|
||||||
bool ready = th->has_state(thread::state::ready);
|
bool ready = th->has_state(thread::state::ready);
|
||||||
bool exited = th->has_state(thread::state::exited);
|
bool exited = th->has_state(thread::state::exited);
|
||||||
bool constant = th->has_state(thread::state::constant);
|
bool constant = th->has_state(thread::state::constant);
|
||||||
bool current = tcb == queue.current;
|
bool current = tcb == queue.current;
|
||||||
|
|
||||||
ready |= th->wake_on_time(now);
|
|
||||||
|
|
||||||
auto *remove = tcb;
|
auto *remove = tcb;
|
||||||
tcb = tcb->next();
|
tcb = tcb->next();
|
||||||
if (!exited && !ready)
|
if (!exited && !ready)
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#include <j6/errors.h>
|
#include <j6/errors.h>
|
||||||
#include <j6/signals.h>
|
|
||||||
|
|
||||||
|
#include "clock.h"
|
||||||
#include "objects/event.h"
|
#include "objects/event.h"
|
||||||
|
#include "objects/thread.h"
|
||||||
#include "syscalls/helpers.h"
|
#include "syscalls/helpers.h"
|
||||||
|
|
||||||
using namespace obj;
|
using namespace obj;
|
||||||
@@ -18,17 +19,21 @@ event_create(j6_handle_t *self)
|
|||||||
j6_status_t
|
j6_status_t
|
||||||
event_signal(event *self, j6_signal_t signals)
|
event_signal(event *self, j6_signal_t signals)
|
||||||
{
|
{
|
||||||
if (signals & j6_signal_global_mask)
|
self->signal(signals);
|
||||||
return j6_err_invalid_arg;
|
|
||||||
|
|
||||||
self->assert_signal(signals);
|
|
||||||
return j6_status_ok;
|
return j6_status_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
j6_status_t
|
j6_status_t
|
||||||
event_clear(event *self, j6_signal_t mask)
|
event_wait(event *self, j6_signal_t *signals, uint64_t timeout)
|
||||||
{
|
{
|
||||||
self->deassert_signal(~mask);
|
thread& t = thread::current();
|
||||||
|
|
||||||
|
if (timeout) {
|
||||||
|
timeout += clock::get().value();
|
||||||
|
t.set_wake_timeout(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
*signals = self->wait();
|
||||||
return j6_status_ok;
|
return j6_status_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#include <j6/errors.h>
|
#include <j6/errors.h>
|
||||||
#include <j6/signals.h>
|
|
||||||
#include <j6/types.h>
|
#include <j6/types.h>
|
||||||
#include <util/vector.h>
|
#include <util/vector.h>
|
||||||
|
|
||||||
@@ -19,81 +18,4 @@ object_koid(kobject *self, j6_koid_t *koid)
|
|||||||
return j6_status_ok;
|
return j6_status_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
j6_status_t
|
|
||||||
object_wait(kobject *self, j6_signal_t mask, j6_signal_t *sigs)
|
|
||||||
{
|
|
||||||
j6_signal_t current = self->signals();
|
|
||||||
if ((current & mask) != 0) {
|
|
||||||
*sigs = current;
|
|
||||||
return j6_status_ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
thread &th = thread::current();
|
|
||||||
self->add_blocked_thread(&th);
|
|
||||||
th.wait_on_signals(mask);
|
|
||||||
|
|
||||||
j6_status_t result = th.get_wait_result();
|
|
||||||
if (result == j6_status_ok) {
|
|
||||||
*sigs = th.get_wait_data();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
j6_status_t
|
|
||||||
object_wait_many(j6_handle_t * handles, size_t handles_count, uint64_t mask, j6_handle_t * woken, uint64_t * signals)
|
|
||||||
{
|
|
||||||
util::vector<kobject*> objects {uint32_t(handles_count)};
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < handles_count; ++i) {
|
|
||||||
j6_handle_t hid = handles[i];
|
|
||||||
if (hid == j6_handle_invalid)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
handle *h = get_handle<kobject>(hid);
|
|
||||||
if (!h || !h->object)
|
|
||||||
return j6_err_invalid_arg;
|
|
||||||
kobject *obj = h->object;
|
|
||||||
|
|
||||||
j6_signal_t current = obj->signals();
|
|
||||||
if ((current & mask) != 0) {
|
|
||||||
*signals = current;
|
|
||||||
*woken = hid;
|
|
||||||
return j6_status_ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
objects.append(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
thread &th = thread::current();
|
|
||||||
for (auto *obj : objects)
|
|
||||||
obj->add_blocked_thread(&th);
|
|
||||||
|
|
||||||
th.wait_on_signals(mask);
|
|
||||||
|
|
||||||
j6_status_t result = th.get_wait_result();
|
|
||||||
if (result != j6_status_ok)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
*woken = j6_handle_invalid;
|
|
||||||
*signals = th.get_wait_data();
|
|
||||||
j6_koid_t koid = th.get_wait_object();
|
|
||||||
for (unsigned i = 0; i < handles_count; ++i) {
|
|
||||||
if (koid == objects[i]->koid())
|
|
||||||
*woken = handles[i];
|
|
||||||
else
|
|
||||||
objects[i]->remove_blocked_thread(&th);
|
|
||||||
}
|
|
||||||
|
|
||||||
kassert(*woken != j6_handle_invalid,
|
|
||||||
"Somehow woke on a handle that was not waited on");
|
|
||||||
return j6_status_ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
j6_status_t
|
|
||||||
object_close(kobject *self)
|
|
||||||
{
|
|
||||||
self->close();
|
|
||||||
return j6_status_ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace syscalls
|
} // namespace syscalls
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#include "frame_allocator.h"
|
#include "frame_allocator.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "objects/endpoint.h"
|
#include "objects/event.h"
|
||||||
#include "objects/thread.h"
|
#include "objects/thread.h"
|
||||||
#include "objects/system.h"
|
#include "objects/system.h"
|
||||||
#include "objects/vm_area.h"
|
#include "objects/vm_area.h"
|
||||||
@@ -49,16 +49,13 @@ system_get_log(system *self, void *buffer, size_t *buffer_len)
|
|||||||
{
|
{
|
||||||
size_t orig_size = *buffer_len;
|
size_t orig_size = *buffer_len;
|
||||||
*buffer_len = g_logger.get_entry(buffer, *buffer_len);
|
*buffer_len = g_logger.get_entry(buffer, *buffer_len);
|
||||||
if (!g_logger.has_log())
|
|
||||||
self->deassert_signal(j6_signal_system_has_log);
|
|
||||||
|
|
||||||
return (*buffer_len > orig_size) ? j6_err_insufficient : j6_status_ok;
|
return (*buffer_len > orig_size) ? j6_err_insufficient : j6_status_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
j6_status_t
|
j6_status_t
|
||||||
system_bind_irq(system *self, endpoint *endp, unsigned irq)
|
system_bind_irq(system *self, event *dest, unsigned irq, unsigned signal)
|
||||||
{
|
{
|
||||||
if (device_manager::get().bind_irq(irq, endp))
|
if (device_manager::get().bind_irq(irq, dest, signal))
|
||||||
return j6_status_ok;
|
return j6_status_ok;
|
||||||
|
|
||||||
return j6_err_invalid_arg;
|
return j6_err_invalid_arg;
|
||||||
|
|||||||
@@ -46,21 +46,14 @@ thread_kill(thread *self)
|
|||||||
return j6_status_ok;
|
return j6_status_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
j6_status_t
|
|
||||||
thread_pause()
|
|
||||||
{
|
|
||||||
thread &th = thread::current();
|
|
||||||
th.wait_on_signals(-1ull);
|
|
||||||
return j6_status_ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
j6_status_t
|
j6_status_t
|
||||||
thread_sleep(uint64_t til)
|
thread_sleep(uint64_t til)
|
||||||
{
|
{
|
||||||
thread &th = thread::current();
|
thread &th = thread::current();
|
||||||
log::debug(logs::task, "Thread %llx sleeping until %llu", th.koid(), til);
|
log::debug(logs::task, "Thread %llx sleeping until %llu", th.koid(), til);
|
||||||
|
|
||||||
th.wait_on_time(til);
|
th.set_wake_timeout(til);
|
||||||
|
th.block();
|
||||||
return j6_status_ok;
|
return j6_status_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
50
src/kernel/wait_queue.cpp
Normal file
50
src/kernel/wait_queue.cpp
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#include "objects/thread.h"
|
||||||
|
#include "wait_queue.h"
|
||||||
|
|
||||||
|
wait_queue::~wait_queue()
|
||||||
|
{
|
||||||
|
util::scoped_lock lock {m_lock};
|
||||||
|
for (auto *t : m_threads) {
|
||||||
|
if (!t->exited()) t->wake();
|
||||||
|
t->handle_release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
wait_queue::add_thread(obj::thread *t)
|
||||||
|
{
|
||||||
|
util::scoped_lock lock {m_lock};
|
||||||
|
t->handle_retain();
|
||||||
|
m_threads.push_back(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
wait_queue::pop_exited()
|
||||||
|
{
|
||||||
|
while (!m_threads.empty()) {
|
||||||
|
obj::thread *t = m_threads.first();
|
||||||
|
if (!t->exited())
|
||||||
|
break;
|
||||||
|
|
||||||
|
m_threads.pop_front();
|
||||||
|
t->handle_release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
obj::thread *
|
||||||
|
wait_queue::get_next_unlocked()
|
||||||
|
{
|
||||||
|
pop_exited();
|
||||||
|
if (m_threads.empty())
|
||||||
|
return nullptr;
|
||||||
|
return m_threads.first();
|
||||||
|
}
|
||||||
|
|
||||||
|
obj::thread *
|
||||||
|
wait_queue::pop_next_unlocked()
|
||||||
|
{
|
||||||
|
pop_exited();
|
||||||
|
if (m_threads.empty())
|
||||||
|
return nullptr;
|
||||||
|
return m_threads.pop_front();
|
||||||
|
}
|
||||||
48
src/kernel/wait_queue.h
Normal file
48
src/kernel/wait_queue.h
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#pragma once
|
||||||
|
/// \file wait_queue.h
|
||||||
|
/// Class and related defintions for keeping a queue of waiting threads
|
||||||
|
|
||||||
|
#include <util/deque.h>
|
||||||
|
#include <util/spinlock.h>
|
||||||
|
|
||||||
|
namespace obj {
|
||||||
|
class thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
class wait_queue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Wake all threads when destructing
|
||||||
|
~wait_queue();
|
||||||
|
|
||||||
|
/// Add the given thread to the queue. Locks the
|
||||||
|
/// queue lock.
|
||||||
|
void add_thread(obj::thread *t);
|
||||||
|
|
||||||
|
/// Pops the next waiting thread off the queue.
|
||||||
|
/// Locks the queue lock.
|
||||||
|
inline obj::thread * pop_next() {
|
||||||
|
util::scoped_lock lock {m_lock};
|
||||||
|
return pop_next_unlocked();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the next waiting thread. Does not lock the
|
||||||
|
/// queue lock.
|
||||||
|
obj::thread * get_next_unlocked();
|
||||||
|
|
||||||
|
/// Pop the next thread off the queue. Does not
|
||||||
|
/// lock the queue lock.
|
||||||
|
obj::thread * pop_next_unlocked();
|
||||||
|
|
||||||
|
/// Get the spinlock to lock this queue
|
||||||
|
util::spinlock & get_lock() { return m_lock; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Get rid of any exited threads that are next
|
||||||
|
/// in the queue. Caller must hold the queue lock.
|
||||||
|
void pop_exited();
|
||||||
|
|
||||||
|
util::spinlock m_lock;
|
||||||
|
util::deque<obj::thread*, 8> m_threads;
|
||||||
|
};
|
||||||
|
|
||||||
@@ -9,43 +9,38 @@
|
|||||||
#include <j6/syscalls.h>
|
#include <j6/syscalls.h>
|
||||||
#include <j6/types.h>
|
#include <j6/types.h>
|
||||||
|
|
||||||
j6_handle_t __handle_sys;
|
|
||||||
j6_handle_t __handle_self;
|
j6_handle_t __handle_self;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr size_t __static_arr_size = 4;
|
constexpr size_t static_arr_size = 8;
|
||||||
j6_handle_t __handle_array[__static_arr_size];
|
j6_handle_t handle_array[static_arr_size];
|
||||||
|
} // namespace
|
||||||
|
|
||||||
static j6_status_t
|
j6_handle_t
|
||||||
load_handles()
|
j6_find_first_handle(j6_object_type obj_type)
|
||||||
{
|
{
|
||||||
size_t count = __static_arr_size;
|
size_t count = static_arr_size;
|
||||||
j6_handle_t *handles = __handle_array;
|
j6_handle_t *handles = handle_array;
|
||||||
j6_status_t s = j6_handle_list(handles, &count);
|
j6_status_t s = j6_handle_list(handles, &count);
|
||||||
|
|
||||||
if (s != j6_err_insufficient && s != j6_status_ok)
|
if (s != j6_err_insufficient && s != j6_status_ok)
|
||||||
return s;
|
return j6_handle_invalid;
|
||||||
|
|
||||||
if (count > __static_arr_size)
|
if (count > static_arr_size)
|
||||||
count = __static_arr_size;
|
count = static_arr_size;
|
||||||
|
|
||||||
for (size_t i = 0; i < count; ++i) {
|
for (size_t i = 0; i < count; ++i) {
|
||||||
uint8_t type = (handles[i] >> 56);
|
uint8_t type = (handles[i] >> 56);
|
||||||
if (type == j6_object_type_system && __handle_sys == j6_handle_invalid)
|
if (type == obj_type) return handles[i];
|
||||||
__handle_sys = handles[i];
|
|
||||||
else if (type == j6_object_type_process && __handle_self == j6_handle_invalid)
|
|
||||||
__handle_self = handles[i];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return s;
|
return j6_handle_invalid;
|
||||||
}
|
}
|
||||||
} // namespace
|
|
||||||
|
|
||||||
extern "C" void
|
extern "C" void
|
||||||
__init_libj6(uint64_t *rsp)
|
__init_libj6(uint64_t *rsp)
|
||||||
{
|
{
|
||||||
__handle_sys = __handle_self = j6_handle_invalid;
|
__handle_self = j6_find_first_handle(j6_object_type_process);
|
||||||
load_handles();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // __j6kernel
|
#endif // __j6kernel
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ j6 = module("j6",
|
|||||||
"j6/errors.h",
|
"j6/errors.h",
|
||||||
"j6/flags.h",
|
"j6/flags.h",
|
||||||
"j6/init.h",
|
"j6/init.h",
|
||||||
"j6/signals.h",
|
|
||||||
"j6/syscalls.h.cog",
|
"j6/syscalls.h.cog",
|
||||||
"j6/sysconf.h.cog",
|
"j6/sysconf.h.cog",
|
||||||
"j6/types.h",
|
"j6/types.h",
|
||||||
|
|||||||
@@ -1,37 +1,16 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
/// \file init.h
|
/// \file init.h
|
||||||
/// Types used in process and thread initialization
|
/// Process initialization utility functions
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <j6/types.h>
|
#include <j6/types.h>
|
||||||
|
|
||||||
enum j6_init_type { // `value` is a:
|
#ifdef __cplusplus
|
||||||
j6_init_handle_self, // Handle to the system
|
extern "C" {
|
||||||
j6_init_handle_other, // Handle to this process
|
#endif
|
||||||
j6_init_desc_framebuffer // Pointer to a j6_init_framebuffer descriptor
|
|
||||||
};
|
|
||||||
|
|
||||||
struct j6_typed_handle {
|
/// Find the first handle of the given type held by this process
|
||||||
enum j6_object_type type;
|
j6_handle_t j6_find_first_handle(j6_object_type obj_type);
|
||||||
j6_handle_t handle;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct j6_init_value {
|
#ifdef __cplusplus
|
||||||
enum j6_init_type type;
|
} // extern "C"
|
||||||
union {
|
#endif
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -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)
|
|
||||||
|
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
/// A generic templatized linked list.
|
/// A generic templatized linked list.
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
#include <util/linked_list.h>
|
#include <util/linked_list.h>
|
||||||
|
|
||||||
namespace util {
|
namespace util {
|
||||||
@@ -42,7 +43,9 @@ public:
|
|||||||
|
|
||||||
inline void push_front(const T& item) {
|
inline void push_front(const T& item) {
|
||||||
if (!m_first) { // need a new block at the start
|
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_first = N;
|
||||||
}
|
}
|
||||||
m_list.front()->items[--m_first] = item;
|
m_list.front()->items[--m_first] = item;
|
||||||
@@ -50,7 +53,9 @@ public:
|
|||||||
|
|
||||||
inline void push_back(const T& item) {
|
inline void push_back(const T& item) {
|
||||||
if (m_next == N) { // need a new block at the end
|
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_next = 0;
|
||||||
}
|
}
|
||||||
m_list.back()->items[m_next++] = item;
|
m_list.back()->items[m_next++] = item;
|
||||||
@@ -80,7 +85,10 @@ public:
|
|||||||
return value;
|
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() {
|
inline T& first() {
|
||||||
assert(!empty() && "Calling first() on an empty deque");
|
assert(!empty() && "Calling first() on an empty deque");
|
||||||
|
|||||||
@@ -119,7 +119,22 @@ public:
|
|||||||
{
|
{
|
||||||
node *n = lookup(k);
|
node *n = lookup(k);
|
||||||
if (!n) return false;
|
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();
|
n->~node();
|
||||||
--m_count;
|
--m_count;
|
||||||
|
|
||||||
@@ -132,18 +147,6 @@ public:
|
|||||||
m.~node();
|
m.~node();
|
||||||
i = mod(++i);
|
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) {
|
void set_capacity(size_t capacity) {
|
||||||
@@ -263,7 +266,8 @@ public:
|
|||||||
|
|
||||||
V * find(const K &k) {
|
V * find(const K &k) {
|
||||||
node *n = this->lookup(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 {
|
const V * find(const K &k) const {
|
||||||
@@ -285,6 +289,12 @@ public:
|
|||||||
map(size_t capacity = 0) :
|
map(size_t capacity = 0) :
|
||||||
base(capacity) {}
|
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 {
|
V * find(const K &k) const {
|
||||||
const node *n = this->lookup(k);
|
const node *n = this->lookup(k);
|
||||||
return n ? n->val : nullptr;
|
return n ? n->val : nullptr;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#include <j6/errors.h>
|
#include <j6/errors.h>
|
||||||
#include <j6/flags.h>
|
#include <j6/flags.h>
|
||||||
#include <j6/signals.h>
|
#include <j6/init.h>
|
||||||
#include <j6/syscalls.h>
|
#include <j6/syscalls.h>
|
||||||
#include <j6/sysconf.h>
|
#include <j6/sysconf.h>
|
||||||
#include <j6/types.h>
|
#include <j6/types.h>
|
||||||
@@ -19,7 +19,7 @@ extern "C" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern j6_handle_t __handle_self;
|
extern j6_handle_t __handle_self;
|
||||||
extern j6_handle_t __handle_sys;
|
j6_handle_t g_handle_sys = j6_handle_invalid;
|
||||||
|
|
||||||
struct entry
|
struct entry
|
||||||
{
|
{
|
||||||
@@ -76,13 +76,13 @@ log_pump_proc()
|
|||||||
void *message_buffer = nullptr;
|
void *message_buffer = nullptr;
|
||||||
char stringbuf[300];
|
char stringbuf[300];
|
||||||
|
|
||||||
j6_status_t result = j6_system_request_iopl(__handle_sys, 3);
|
j6_status_t result = j6_system_request_iopl(g_handle_sys, 3);
|
||||||
if (result != j6_status_ok)
|
if (result != j6_status_ok)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
size_t size = buffer_size;
|
size_t size = buffer_size;
|
||||||
j6_status_t s = j6_system_get_log(__handle_sys, message_buffer, &size);
|
j6_status_t s = j6_system_get_log(g_handle_sys, message_buffer, &size);
|
||||||
|
|
||||||
if (s == j6_err_insufficient) {
|
if (s == j6_err_insufficient) {
|
||||||
free(message_buffer);
|
free(message_buffer);
|
||||||
@@ -94,12 +94,6 @@ log_pump_proc()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size == 0) {
|
|
||||||
j6_signal_t sigs = 0;
|
|
||||||
j6_object_wait(__handle_sys, j6_signal_system_has_log, &sigs);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const entry *e = reinterpret_cast<entry*>(message_buffer);
|
const entry *e = reinterpret_cast<entry*>(message_buffer);
|
||||||
|
|
||||||
const char *area_name = area_names[e->area];
|
const char *area_name = area_names[e->area];
|
||||||
@@ -118,22 +112,26 @@ main(int argc, const char **argv)
|
|||||||
{
|
{
|
||||||
j6_log("uart driver starting");
|
j6_log("uart driver starting");
|
||||||
|
|
||||||
j6_handle_t endp = j6_handle_invalid;
|
j6_handle_t event = j6_handle_invalid;
|
||||||
j6_status_t result = j6_status_ok;
|
j6_status_t result = j6_status_ok;
|
||||||
|
|
||||||
result = j6_system_request_iopl(__handle_sys, 3);
|
g_handle_sys = j6_find_first_handle(j6_object_type_system);
|
||||||
|
if (g_handle_sys == j6_handle_invalid)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
result = j6_system_request_iopl(g_handle_sys, 3);
|
||||||
if (result != j6_status_ok)
|
if (result != j6_status_ok)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
result = j6_endpoint_create(&endp);
|
result = j6_event_create(&event);
|
||||||
if (result != j6_status_ok)
|
if (result != j6_status_ok)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
result = j6_system_bind_irq(__handle_sys, endp, 3);
|
result = j6_system_bind_irq(g_handle_sys, event, 3, 0);
|
||||||
if (result != j6_status_ok)
|
if (result != j6_status_ok)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
result = j6_system_bind_irq(__handle_sys, endp, 4);
|
result = j6_system_bind_irq(g_handle_sys, event, 4, 1);
|
||||||
if (result != j6_status_ok)
|
if (result != j6_status_ok)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
@@ -159,8 +157,8 @@ main(int argc, const char **argv)
|
|||||||
|
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
uint64_t tag = 0;
|
uint64_t signals = 0;
|
||||||
result = j6_endpoint_receive(endp, &tag, nullptr, &len, 10000);
|
result = j6_event_wait(event, &signals, 1000);
|
||||||
if (result == j6_err_timed_out) {
|
if (result == j6_err_timed_out) {
|
||||||
com1.handle_interrupt();
|
com1.handle_interrupt();
|
||||||
com2.handle_interrupt();
|
com2.handle_interrupt();
|
||||||
@@ -172,22 +170,10 @@ main(int argc, const char **argv)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!j6_tag_is_irq(tag)) {
|
if (signals & (1<<0))
|
||||||
j6_log("uart driver got non-irq waiting for irq");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned irq = j6_tag_to_irq(tag);
|
|
||||||
switch (irq) {
|
|
||||||
case 3:
|
|
||||||
com2.handle_interrupt();
|
com2.handle_interrupt();
|
||||||
break;
|
if (signals & (1<<1))
|
||||||
case 4:
|
|
||||||
com1.handle_interrupt();
|
com1.handle_interrupt();
|
||||||
break;
|
|
||||||
default:
|
|
||||||
j6_log("uart driver got unknown irq waiting for irq");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
j6_log("uart driver somehow got to the end of main");
|
j6_log("uart driver somehow got to the end of main");
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <j6/init.h>
|
#include <j6/init.h>
|
||||||
#include <j6/errors.h>
|
#include <j6/errors.h>
|
||||||
#include <j6/flags.h>
|
#include <j6/flags.h>
|
||||||
#include <j6/signals.h>
|
|
||||||
#include <j6/syscalls.h>
|
#include <j6/syscalls.h>
|
||||||
#include <j6/types.h>
|
#include <j6/types.h>
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ using bootproto::module_flags;
|
|||||||
using bootproto::module_program;
|
using bootproto::module_program;
|
||||||
|
|
||||||
extern j6_handle_t __handle_self;
|
extern j6_handle_t __handle_self;
|
||||||
extern j6_handle_t __handle_sys;
|
|
||||||
|
|
||||||
constexpr uintptr_t load_addr = 0xf8000000;
|
constexpr uintptr_t load_addr = 0xf8000000;
|
||||||
constexpr size_t stack_size = 0x10000;
|
constexpr size_t stack_size = 0x10000;
|
||||||
@@ -28,7 +27,7 @@ load_program(const module_program &prog, j6_handle_t sys, char *err_msg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
j6_handle_t elf_vma = j6_handle_invalid;
|
j6_handle_t elf_vma = j6_handle_invalid;
|
||||||
j6_status_t res = j6_system_map_phys(__handle_sys, &elf_vma, prog.base_address, prog.size, 0);
|
j6_status_t res = j6_system_map_phys(sys, &elf_vma, prog.base_address, prog.size, 0);
|
||||||
if (res != j6_status_ok) {
|
if (res != j6_status_ok) {
|
||||||
sprintf(err_msg, " ** error loading program '%s': creating physical vma: %lx", prog.filename, res);
|
sprintf(err_msg, " ** error loading program '%s': creating physical vma: %lx", prog.filename, res);
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <j6/caps.h>
|
#include <j6/caps.h>
|
||||||
#include <j6/errors.h>
|
#include <j6/errors.h>
|
||||||
|
#include <j6/init.h>
|
||||||
#include <j6/syscalls.h>
|
#include <j6/syscalls.h>
|
||||||
#include <j6/types.h>
|
#include <j6/types.h>
|
||||||
#include <bootproto/init.h>
|
#include <bootproto/init.h>
|
||||||
@@ -21,21 +22,34 @@ extern "C" {
|
|||||||
uintptr_t _arg_modules_phys; // This gets filled in in _start
|
uintptr_t _arg_modules_phys; // This gets filled in in _start
|
||||||
|
|
||||||
extern j6_handle_t __handle_self;
|
extern j6_handle_t __handle_self;
|
||||||
extern j6_handle_t __handle_sys;
|
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, const char **argv)
|
main(int argc, const char **argv)
|
||||||
{
|
{
|
||||||
|
j6_status_t s;
|
||||||
|
|
||||||
|
j6_handle_t sys = j6_handle_invalid;
|
||||||
|
j6_handle_t sys_child = j6_handle_invalid;
|
||||||
|
|
||||||
j6_log("srv.init starting");
|
j6_log("srv.init starting");
|
||||||
|
|
||||||
modules mods = modules::load_modules(_arg_modules_phys, __handle_sys, __handle_self);
|
sys = j6_find_first_handle(j6_object_type_system);
|
||||||
|
if (sys == j6_handle_invalid)
|
||||||
|
return 1;
|
||||||
|
|
||||||
j6_handle_t drv_sys_handle = j6_handle_invalid;
|
s = j6_handle_clone(sys, &sys_child,
|
||||||
j6_status_t s = j6_handle_clone(__handle_sys, &drv_sys_handle,
|
j6_cap_system_bind_irq |
|
||||||
j6_cap_system_bind_irq | j6_cap_system_map_phys | j6_cap_system_change_iopl);
|
j6_cap_system_get_log |
|
||||||
|
j6_cap_system_map_phys |
|
||||||
|
j6_cap_system_change_iopl);
|
||||||
if (s != j6_status_ok)
|
if (s != j6_status_ok)
|
||||||
return s;
|
return s;
|
||||||
|
|
||||||
|
if (s != j6_status_ok)
|
||||||
|
return s;
|
||||||
|
|
||||||
|
modules mods = modules::load_modules(_arg_modules_phys, sys, __handle_self);
|
||||||
|
|
||||||
for (auto &mod : mods.of_type(module_type::program)) {
|
for (auto &mod : mods.of_type(module_type::program)) {
|
||||||
auto &prog = static_cast<const module_program&>(mod);
|
auto &prog = static_cast<const module_program&>(mod);
|
||||||
|
|
||||||
@@ -43,7 +57,7 @@ main(int argc, const char **argv)
|
|||||||
sprintf(message, " loading program module '%s' at %lx", prog.filename, prog.base_address);
|
sprintf(message, " loading program module '%s' at %lx", prog.filename, prog.base_address);
|
||||||
j6_log(message);
|
j6_log(message);
|
||||||
|
|
||||||
if (!load_program(prog, __handle_sys, message)) {
|
if (!load_program(prog, sys_child, message)) {
|
||||||
j6_log(message);
|
j6_log(message);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
#include <j6/types.h>
|
#include <j6/types.h>
|
||||||
#include <j6/errors.h>
|
#include <j6/errors.h>
|
||||||
#include <j6/flags.h>
|
#include <j6/flags.h>
|
||||||
#include <j6/signals.h>
|
|
||||||
#include <j6/syscalls.h>
|
#include <j6/syscalls.h>
|
||||||
|
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
|
|||||||
Reference in New Issue
Block a user