diff --git a/repos/base/include/base/id_space.h b/repos/base/include/base/id_space.h new file mode 100644 index 0000000000..6e65c8ce73 --- /dev/null +++ b/repos/base/include/base/id_space.h @@ -0,0 +1,245 @@ +/* + * \brief ID name space + * \author Norman Feske + * \date 2016-10-10 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__ID_SPACE_H_ +#define _INCLUDE__BASE__ID_SPACE_H_ + +#include +#include +#include +#include + +namespace Genode { template class Id_space; } + + +template +class Genode::Id_space : public Noncopyable +{ + public: + + struct Id + { + unsigned long value; + + bool operator == (Id const &other) const { return value == other.value; } + + void print(Output &out) const { Genode::print(out, value); } + }; + + class Out_of_ids : Exception { }; + class Conflicting_id : Exception { }; + + class Element : public Avl_node, Noncopyable + { + private: + + T &_obj; + Id_space &_id_space; + Id _id { 0 }; + + friend class Id_space; + + /** + * Search the tree for the element with the given ID + */ + Element *_lookup(Id id) + { + if (id.value == _id.value) return this; + + Element *e = Avl_node::child(id.value > _id.value); + + return e ? e->_lookup(id) : 0; + } + + template + void _for_each(FUNC const &fn) const + { + if (Avl_node::child(Avl_node_base::LEFT)) + Avl_node::child(Avl_node_base::LEFT)->_for_each(fn); + + fn(static_cast(_obj)); + + if (Avl_node::child(Avl_node_base::RIGHT)) + Avl_node::child(Avl_node_base::RIGHT)->_for_each(fn); + } + + public: + + /** + * Constructor + * + * \throw Out_of_ids ID space is exhausted + */ + Element(T &obj, Id_space &id_space) + : + _obj(obj), _id_space(id_space) + { + Lock::Guard guard(_id_space._lock); + _id = id_space._unused_id(*this); + _id_space._elements.insert(this); + } + + /** + * Constructor + * + * \throw Conflicting_id 'id' is already present in ID space + */ + Element(T &obj, Id_space &id_space, Id_space::Id id) + : + _obj(obj), _id_space(id_space), _id(id) + { + Lock::Guard guard(_id_space._lock); + _id_space._check_conflict(*this, id); + _id_space._elements.insert(this); + } + + ~Element() + { + Lock::Guard guard(_id_space._lock); + _id_space._elements.remove(this); + } + + /** + * Avl_node interface + */ + bool higher(Element *other) { return other->_id.value > _id.value; } + + Id id() const { return _id; } + + void print(Output &out) const { Genode::print(out, _id); } + }; + + private: + + Lock mutable _lock; /* protect '_elements' and '_cnt' */ + Avl_tree _elements; + unsigned long _cnt = 0; + + /** + * Return ID that does not exist within the ID space + * + * \return ID assigned to the element within the ID space + * \throw Out_of_ids + */ + Id _unused_id(Element &e) + { + unsigned long _attempts = 0; + for (; _attempts < ~0UL; _attempts++, _cnt++) { + + Id const id { _cnt }; + + /* another attempt if is already in use */ + if (_elements.first() && _elements.first()->_lookup(id)) + continue; + + return id; + } + throw Out_of_ids(); + } + + /** + * Check if ID is already in use + * + * \throw Conflicting_id + */ + void _check_conflict(Element &e, Id id) + { + if (_elements.first() && _elements.first()->_lookup(id)) + throw Conflicting_id(); + } + + public: + + class Unknown_id : Exception { }; + + /** + * Apply functor 'fn' to each ID present in the ID space + * + * \param ARG argument type passed to 'fn', must be convertible + * from 'T' via a 'static_cast' + * + * This function is called with the ID space locked. Hence, it is not + * possible to modify the ID space from within 'fn'. + */ + template + void for_each(FUNC const &fn) const + { + Lock::Guard guard(_lock); + + if (_elements.first()) + _elements.first()->template _for_each(fn); + } + + /** + * Apply functor 'fn' to object with given ID + * + * See 'for_each' for a description of the 'ARG' argument. + * + * \throw Unknown_id + */ + template + void apply(Id id, FUNC const &fn) + { + T *obj = nullptr; + { + Lock::Guard guard(_lock); + + if (!_elements.first()) + throw Unknown_id(); + + if (Element *e = _elements.first()->_lookup(id)) + obj = &e->_obj; + } + if (obj) + fn(static_cast(*obj)); + else + throw Unknown_id(); + } + + /** + * Apply functor 'fn' to an arbitrary ID present in the ID space + * + * See 'for_each' for a description of the 'ARG' argument. + * + * The functor is called with a reference to the managed object as + * argument. This method is designated for the destruction of ID + * spaces. It allows the caller to remove all IDs by subseqently + * calling this function and destructing the object in 'fn'. + * + * \return true if 'fn' was applied, or + * false if the ID space is empty. + */ + template + bool apply_any(FUNC const &fn) + { + T *obj = nullptr; + { + Lock::Guard guard(_lock); + + if (_elements.first()) + obj = &_elements.first()->_obj; + else + return false; + } + fn(static_cast(*obj)); + return true; + } + + ~Id_space() + { + if (_elements.first()) + error("ID space not empty at destruction time"); + } +}; + +#endif /* _INCLUDE__BASE__ID_SPACE_H_ */