From 513a3f1d37d287db308cdd1ab12c85a2601e5ae1 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Sun, 6 Nov 2016 14:23:28 +0100 Subject: [PATCH] base: new 'Id_space' data structure This data structure allows the association of objects with IDs. IDs are kept in an AVL tree. So in contrast to a bit allocator, the ID space can be sparsely populated and does not need to be dimensioned. The lifetime of an ID is bound to an 'Element' object, which relieves the programmer from manually allocating/deallocating IDs for objects. Issue #2120 --- repos/base/include/base/id_space.h | 245 +++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 repos/base/include/base/id_space.h 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_ */