mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 00:14:32 -08:00
[kernel] Simplify mailbox code, and messages
A number of simplifications of mailboxes now that the interface is much simpler, and synchronous. * call and respond can now only transfer one handle at a time * mailbox objects got rid of the message queue, and just have wait_queues of blocked threads, and a reply_to map. * threads now have a message_data struct on them for use by mailboxes
This commit is contained in:
@@ -3,16 +3,8 @@
|
||||
#include "objects/mailbox.h"
|
||||
#include "objects/thread.h"
|
||||
|
||||
DEFINE_SLAB_ALLOCATOR(obj::mailbox::message);
|
||||
|
||||
namespace obj {
|
||||
|
||||
static_assert(mailbox::message::slab_size % sizeof(mailbox::message) == 0,
|
||||
"mailbox message size does not fit cleanly into N pages.");
|
||||
|
||||
constexpr uint64_t no_message = 0;
|
||||
constexpr uint64_t has_message = 1;
|
||||
|
||||
mailbox::mailbox() :
|
||||
kobject(kobject::type::mailbox),
|
||||
m_closed {false},
|
||||
@@ -28,91 +20,80 @@ mailbox::~mailbox()
|
||||
void
|
||||
mailbox::close()
|
||||
{
|
||||
util::scoped_lock lock {m_message_lock};
|
||||
|
||||
// If this was previously closed, we're done
|
||||
if (closed()) return;
|
||||
m_closed = true;
|
||||
bool was_closed = __atomic_exchange_n(&m_closed, true, __ATOMIC_ACQ_REL);
|
||||
if (was_closed) return;
|
||||
|
||||
while (!m_messages.empty()) {
|
||||
message *msg = m_messages.pop_front();
|
||||
delete msg;
|
||||
}
|
||||
|
||||
for (auto &p : m_pending) {
|
||||
delete p.val.msg;
|
||||
}
|
||||
m_queue.clear();
|
||||
m_callers.clear(j6_status_closed);
|
||||
m_responders.clear(j6_status_closed);
|
||||
}
|
||||
|
||||
bool
|
||||
mailbox::call(message *msg)
|
||||
j6_status_t
|
||||
mailbox::call()
|
||||
{
|
||||
uint16_t reply_tag = next_reply_tag();
|
||||
if (closed())
|
||||
return j6_status_closed;
|
||||
|
||||
util::scoped_lock lock {m_message_lock};
|
||||
|
||||
msg->reply_tag = reply_tag;
|
||||
thread ¤t = thread::current();
|
||||
m_pending.insert(reply_tag, {¤t, msg});
|
||||
m_callers.add_thread(¤t);
|
||||
|
||||
m_messages.push_back(msg);
|
||||
thread *responder = m_responders.pop_next();
|
||||
if (responder)
|
||||
responder->wake(j6_status_ok);
|
||||
|
||||
thread *t = m_queue.pop_next();
|
||||
|
||||
lock.release();
|
||||
if (t) t->wake(has_message);
|
||||
|
||||
uint64_t result = current.block();
|
||||
return result == has_message;
|
||||
return current.block();
|
||||
}
|
||||
|
||||
bool
|
||||
mailbox::receive(mailbox::message *&msg, bool block)
|
||||
j6_status_t
|
||||
mailbox::receive(thread::message_data &data, reply_tag_t &reply_tag, bool block)
|
||||
{
|
||||
util::scoped_lock lock {m_message_lock};
|
||||
if (closed())
|
||||
return j6_status_closed;
|
||||
|
||||
// This needs to be a loop because we're re-acquiring the lock
|
||||
// after waking up, and may have missed the message that woke us
|
||||
while (m_messages.empty()) {
|
||||
if (!block) {
|
||||
msg = nullptr;
|
||||
return false;
|
||||
}
|
||||
thread ¤t = thread::current();
|
||||
thread *caller = nullptr;
|
||||
|
||||
thread &cur = thread::current();
|
||||
m_queue.add_thread(&cur);
|
||||
while (true) {
|
||||
caller = m_callers.pop_next();
|
||||
if (caller)
|
||||
break;
|
||||
|
||||
lock.release();
|
||||
uint64_t result = cur.block();
|
||||
if (result == no_message)
|
||||
return false;
|
||||
lock.reacquire();
|
||||
if (!block)
|
||||
return j6_status_would_block;
|
||||
|
||||
m_responders.add_thread(¤t);
|
||||
j6_status_t s = current.block();
|
||||
if (s != j6_status_ok)
|
||||
return s;
|
||||
}
|
||||
|
||||
msg = m_messages.pop_front();
|
||||
return true;
|
||||
util::scoped_lock lock {m_reply_lock};
|
||||
reply_tag = ++m_next_reply_tag;
|
||||
m_reply_map.insert({ reply_tag, caller });
|
||||
lock.release();
|
||||
|
||||
data = caller->get_message_data();
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
mailbox::replyer
|
||||
mailbox::reply(uint16_t reply_tag)
|
||||
j6_status_t
|
||||
mailbox::reply(reply_tag_t reply_tag, const thread::message_data &data)
|
||||
{
|
||||
util::scoped_lock lock {m_message_lock};
|
||||
if (closed())
|
||||
return j6_status_closed;
|
||||
|
||||
pending *p = m_pending.find(reply_tag);
|
||||
if (!p) return {};
|
||||
util::scoped_lock lock {m_reply_lock};
|
||||
reply_to *rt = m_reply_map.find(reply_tag);
|
||||
if (!rt)
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
thread *caller = p->sender;
|
||||
message *msg = p->msg;
|
||||
m_pending.erase(reply_tag);
|
||||
thread *caller = rt->thread;
|
||||
m_reply_map.erase(reply_tag);
|
||||
lock.release();
|
||||
|
||||
return {msg, caller, has_message};
|
||||
}
|
||||
|
||||
mailbox::replyer::~replyer()
|
||||
{
|
||||
if (caller)
|
||||
caller->wake(status);
|
||||
caller->get_message_data() = data;
|
||||
caller->wake(j6_status_ok);
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
} // namespace obj
|
||||
|
||||
Reference in New Issue
Block a user