/*
 * \brief  RAM management
 * \author Norman Feske
 * \date   2013-10-14
 */

/*
 * Copyright (C) 2013-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 _INCLUDE__CLI_MONITOR__RAM_H_
#define _INCLUDE__CLI_MONITOR__RAM_H_

/* Genode includes */
#include <pd_session/client.h>

namespace Cli_monitor { class Ram; }


class Cli_monitor::Ram
{
	private:

		typedef Genode::size_t size_t;

		Genode::Pd_session           &_pd;
		Genode::Pd_session_capability _pd_cap;

		Genode::Lock mutable _lock { };

		Genode::Signal_context_capability _yield_sigh;
		Genode::Signal_context_capability _resource_avail_sigh;

		size_t _preserve;

		void _validate_preservation()
		{
			if (_pd.avail_ram().value < _preserve)
				Genode::Signal_transmitter(_yield_sigh).submit();

			/* verify to answer outstanding resource requests too */
			if (_pd.avail_ram().value > _preserve)
				Genode::Signal_transmitter(_resource_avail_sigh).submit();
		}

	public:

		struct Status
		{
			size_t quota, used, avail, preserve;
			Status(size_t quota, size_t used, size_t avail, size_t preserve)
			: quota(quota), used(used), avail(avail), preserve(preserve) { }
		};

		Ram(Genode::Pd_session               &pd,
		    Genode::Pd_session_capability     pd_cap,
		    size_t                            preserve,
		    Genode::Signal_context_capability yield_sigh,
		    Genode::Signal_context_capability resource_avail_sigh)
		:
			_pd(pd), _pd_cap(pd_cap),
			_yield_sigh(yield_sigh),
			_resource_avail_sigh(resource_avail_sigh),
			_preserve(preserve)
		{ }

		size_t preserve() const
		{
			Genode::Lock::Guard guard(_lock);

			return _preserve;
		}

		void preserve(size_t preserve)
		{
			Genode::Lock::Guard guard(_lock);

			_preserve = preserve;

			_validate_preservation();
		}

		Status status() const
		{
			Genode::Lock::Guard guard(_lock);

			return Status(_pd.ram_quota().value, _pd.used_ram().value,
			              _pd.avail_ram().value, _preserve);
		}

		void validate_preservation()
		{
			Genode::Lock::Guard guard(_lock);

			_validate_preservation();
		}

		/**
		 * Exception type
		 */
		class Transfer_quota_failed { };

		/**
		 * \throw Transfer_quota_failed
		 */
		void withdraw_from(Genode::Pd_session_capability from, size_t amount)
		{
			using namespace Genode;

			Lock::Guard guard(_lock);

			try { Pd_session_client(from).transfer_quota(_pd_cap, Ram_quota{amount}); }
			catch (...) { throw Transfer_quota_failed(); }

			Signal_transmitter(_resource_avail_sigh).submit();
		}

		/**
		 * \throw Transfer_quota_failed
		 */
		void transfer_to(Genode::Pd_session_capability to, size_t amount)
		{
			Genode::Lock::Guard guard(_lock);

			if (_pd.avail_ram().value < (_preserve + amount)) {
				Genode::Signal_transmitter(_yield_sigh).submit();
				throw Transfer_quota_failed();
			}

			try { _pd.transfer_quota(to, Genode::Ram_quota{amount}); }
			catch (...) { throw Transfer_quota_failed(); }
		}

		size_t avail() const { return _pd.avail_ram().value; }
};

#endif /* _INCLUDE__CLI_MONITOR__RAM_H_ */