#include "clock.h" #include "device_manager.h" #include "objects/endpoint.h" #include "objects/process.h" #include "objects/thread.h" #include "vm_space.h" endpoint::endpoint() : kobject {kobject::type::endpoint} {} endpoint::~endpoint() { if (!check_signal(j6_signal_closed)) close(); } void endpoint::close() { kobject::close(); for (auto &data : m_blocked) { if (data.th) data.th->wake_on_result(this, j6_status_closed); } device_manager::get().unbind_irqs(this); } j6_status_t endpoint::send(j6_tag_t tag, const void *data, size_t data_len) { thread_data sender = { &thread::current(), data }; sender.len = data_len; sender.tag = tag; if (!check_signal(j6_signal_endpoint_can_send)) { assert_signal(j6_signal_endpoint_can_recv); m_blocked.append(sender); sender.th->wait_on_object(this); // we woke up having already finished the send // because it happened in the receiver return sender.th->get_wait_result(); } thread_data receiver = m_blocked.pop_front(); if (m_blocked.count() == 0) deassert_signal(j6_signal_endpoint_can_send); j6_status_t status = do_message_copy(sender, receiver); receiver.th->wake_on_result(this, status); return status; } j6_status_t endpoint::receive(j6_tag_t *tag, void *data, size_t *data_len, uint64_t timeout) { thread_data receiver = { &thread::current(), data }; receiver.tag_p = tag; receiver.len_p = data_len; // Timeout is a duration, but wait_on_* calls need a time if (timeout) timeout += clock::get().value(); if (!check_signal(j6_signal_endpoint_can_recv)) { assert_signal(j6_signal_endpoint_can_send); m_blocked.append(receiver); receiver.th->wait_on_object(this, timeout); // we woke up having already finished the recv // because it happened in the sender return receiver.th->get_wait_result(); } thread_data sender = m_blocked.pop_front(); if (m_blocked.count() == 0) deassert_signal(j6_signal_endpoint_can_recv); // TODO: don't pop sender on some errors j6_status_t status = do_message_copy(sender, receiver); if (sender.th) sender.th->wake_on_result(this, status); return status; } void endpoint::signal_irq(unsigned irq) { j6_tag_t tag = j6_tag_from_irq(irq); if (!check_signal(j6_signal_endpoint_can_send)) { assert_signal(j6_signal_endpoint_can_recv); for (auto &blocked : m_blocked) if (blocked.tag == tag) return; thread_data sender = { nullptr, nullptr }; sender.tag = tag; m_blocked.append(sender); return; } thread_data receiver = m_blocked.pop_front(); if (m_blocked.count() == 0) deassert_signal(j6_signal_endpoint_can_send); *receiver.len_p = 0; *receiver.tag_p = tag; receiver.th->wake_on_result(this, j6_status_ok); } j6_status_t endpoint::do_message_copy(const endpoint::thread_data &sender, endpoint::thread_data &receiver) { if (sender.len > *receiver.len_p) return j6_err_insufficient; if (sender.len) { vm_space &source = sender.th->parent().space(); vm_space &dest = receiver.th->parent().space(); vm_space::copy(source, dest, sender.data, receiver.buffer, sender.len); } *receiver.len_p = sender.len; *receiver.tag_p = sender.tag; // TODO: this will not work if non-contiguous pages are mapped!! return j6_status_ok; }