/* * \brief Backend for end points of synchronous interprocess communication * \author Martin Stein * \author Stefan Kalkowski * \date 2012-11-30 */ /* * Copyright (C) 2012-2019 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. */ /* Genode includes */ #include /* base-internal includes */ #include /* core includes */ #include #include #include using namespace Kernel; void Ipc_node::_receive_request(Ipc_node &caller) { _thread.ipc_copy_msg(caller._thread); _caller = &caller; _state = INACTIVE; } void Ipc_node::_receive_reply(Ipc_node &callee) { _thread.ipc_copy_msg(callee._thread); _state = INACTIVE; _thread.ipc_send_request_succeeded(); } void Ipc_node::_announce_request(Ipc_node &node) { /* directly receive request if we've awaited it */ if (_state == AWAIT_REQUEST) { _receive_request(node); _thread.ipc_await_request_succeeded(); return; } /* cannot receive yet, so queue request */ _request_queue.enqueue(node._request_queue_item); } void Ipc_node::_cancel_request_queue() { _request_queue.dequeue_all([] (Queue_item &item) { Ipc_node &node { item.object() }; node._outbuf_request_cancelled(); }); } void Ipc_node::_cancel_outbuf_request() { if (_callee) { _callee->_announced_request_cancelled(*this); _callee = nullptr; } } void Ipc_node::_cancel_inbuf_request() { if (_caller) { _caller->_outbuf_request_cancelled(); _caller = nullptr; } } void Ipc_node::_announced_request_cancelled(Ipc_node &node) { if (_caller == &node) _caller = nullptr; else _request_queue.remove(node._request_queue_item); } void Ipc_node::_outbuf_request_cancelled() { if (_callee == nullptr) return; _callee = nullptr; _state = INACTIVE; _thread.ipc_send_request_failed(); } bool Ipc_node::_helps_outbuf_dst() { return (_state == AWAIT_REPLY) && _help; } bool Ipc_node::can_send_request() { return _state == INACTIVE; } void Ipc_node::send_request(Ipc_node &callee, bool help) { _state = AWAIT_REPLY; _callee = &callee; _help = false; /* announce request */ _callee->_announce_request(*this); _help = help; } Thread &Ipc_node::helping_sink() { return _helps_outbuf_dst() ? _callee->helping_sink() : _thread; } bool Ipc_node::can_await_request() { return _state == INACTIVE; } void Ipc_node::await_request() { _state = AWAIT_REQUEST; _request_queue.dequeue([&] (Queue_item &item) { _receive_request(item.object()); }); } void Ipc_node::send_reply() { /* reply to the last request if we have to */ if (_state == INACTIVE && _caller) { _caller->_receive_reply(*this); _caller = nullptr; } } void Ipc_node::cancel_waiting() { switch (_state) { case AWAIT_REPLY: _cancel_outbuf_request(); _state = INACTIVE; _thread.ipc_send_request_failed(); break; case AWAIT_REQUEST: _state = INACTIVE; _thread.ipc_await_request_failed(); break; return; default: return; } } Ipc_node::Ipc_node(Thread &thread) : _thread(thread) { } Ipc_node::~Ipc_node() { _cancel_request_queue(); _cancel_inbuf_request(); _cancel_outbuf_request(); }