/*
 * \brief  Mechanism to deliver signals via core
 * \author Norman Feske
 * \date   2017-05-10
 */

/*
 * Copyright (C) 2017 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.
 */

#ifndef _CORE__INCLUDE__SIGNAL_DELIVERY_PROXY_H_
#define _CORE__INCLUDE__SIGNAL_DELIVERY_PROXY_H_

/* core includes */
#include <types.h>

namespace Core {
	struct Signal_delivery_proxy;
	struct Signal_delivery_proxy_component;
}


struct Core::Signal_delivery_proxy : Interface
{
	GENODE_RPC(Rpc_deliver, void, _deliver_from_ep, Signal_context_capability, unsigned);
	GENODE_RPC(Rpc_release, void, _release_from_ep, addr_t);
	GENODE_RPC_INTERFACE(Rpc_deliver, Rpc_release);
};


struct Core::Signal_delivery_proxy_component
:
	Rpc_object<Signal_delivery_proxy, Signal_delivery_proxy_component>
{
	Rpc_entrypoint &_ep;

	Capability<Signal_delivery_proxy> _proxy_cap;

	/**
	 * Constructor
	 *
	 * \param ep  entrypoint to be used as a proxy for delivering signals
	 *            as IPC-reply messages.
	 */
	Signal_delivery_proxy_component(Rpc_entrypoint &ep)
	: _ep(ep), _proxy_cap(_ep.manage(this)) { }

	~Signal_delivery_proxy_component()
	{
		if (_proxy_cap.valid())
			_ep.dissolve(this);
	}

	/**
	 * Signal_delivery_proxy RPC interface
	 *
	 * This method is executed in the context of the 'ep'. Hence, it
	 * can produce legitimate IPC reply messages to 'Signal_source'
	 * clients.
	 */
	void _deliver_from_ep(Signal_context_capability cap, unsigned cnt)
	{
		_ep.apply(cap, [&] (Signal_context_component *context) {
			if (context)
				context->source().submit(*context, cnt);
			else
				warning("invalid signal-context capability");
		});
	}

	void _release_from_ep(addr_t const context_addr)
	{
		Signal_context_component * context = reinterpret_cast<Signal_context_component *>(context_addr);
		if (context)
			context->source().release(*context);
	}

	/**
	 * Deliver signal via the proxy mechanism
	 *
	 * Since this method perform an RPC call to the 'ep' specified at the
	 * constructor, is must never be called from this ep.
	 *
	 * Called from threads other than 'ep'.
	 */
	void submit(Signal_context_capability cap, unsigned cnt) {
		_proxy_cap.call<Rpc_deliver>(cap, cnt); }

	/**
	 * Deliver signal via the proxy mechanism
	 *
	 * Since this method perform an RPC call to the 'ep' specified at the
	 * constructor, is must never be called from this ep.
	 *
	 * Called from threads other than 'ep'.
	 */
	void release(Signal_context_component &context) {
		_proxy_cap.call<Rpc_release>(reinterpret_cast<addr_t>(&context)); }
};

#endif /* _CORE__INCLUDE__SIGNLA_DELIVERY_PROXY_H_ */
