There has been a global clock object for a while now, but scheduler was never using it, instead still using its simple increment clock. Now it uses the hpet clock.
138 lines
3.5 KiB
C++
138 lines
3.5 KiB
C++
#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;
|
|
}
|