diff --git a/repos/base/include/base/registry.h b/repos/base/include/base/registry.h new file mode 100644 index 0000000000..7af32fc408 --- /dev/null +++ b/repos/base/include/base/registry.h @@ -0,0 +1,160 @@ +/* + * \brief Thread-safe object registry + * \author Norman Feske + * \date 2016-11-06 + */ + +/* + * 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__REGISTRY_H_ +#define _INCLUDE__BASE__REGISTRY_H_ + +#include +#include + +namespace Genode { + + class Registry_base; + template struct Registry; + template class Registered; +} + + +class Genode::Registry_base +{ + private: + + enum Keep { KEEP, DISCARD }; + + protected: + + class Element : public List::Element + { + private: + + friend class Registry_base; + + Registry_base &_registry; + + /** + * Protect '_reinsert_ptr' + */ + Lock _lock; + + /* + * Assigned by 'Registry::_for_each' + */ + Keep *_keep_ptr = nullptr; + + protected: + + void * const _obj; + + public: + + Element(Registry_base &, void *); + + ~Element(); + }; + + protected: + + Lock mutable _lock; /* protect '_elements' */ + List _elements; + + private: + + /** + * Element currently processed by '_for_each' + */ + Element const *_curr = nullptr; + + void _insert(Element &); + void _remove(Element &); + + Element *_processed(Keep, List &, Element &, Element *); + + protected: + + struct Untyped_functor { virtual void call(void *obj_ptr) = 0; }; + + void _for_each(Untyped_functor &); +}; + + +template +struct Genode::Registry : private Registry_base +{ + struct Element : Registry_base::Element + { + friend class Registry; /* allow 'for_each' to access '_obj' */ + + Element(Registry ®istry, T &obj) + : Registry_base::Element(registry, &obj) { } + }; + + template + void for_each(FUNC const &fn) + { + struct Typed_functor : Registry_base::Untyped_functor + { + FUNC const &_fn; + Typed_functor(FUNC const &fn) : _fn(fn) { } + + void call(void *obj_ptr) override + { + T &obj = *reinterpret_cast(obj_ptr); + _fn(obj); + } + } untyped_functor(fn); + + Registry_base::_for_each(untyped_functor); + } + + template + void for_each(FUNC const &fn) const + { + Lock::Guard lock_guard(_lock); + + Registry_base::Element const *e = _elements.first(), *next = nullptr; + for ( ; e; e = next) { + next = e->next(); + Element const &typed_element = static_cast(*e); + T const &obj = *reinterpret_cast(typed_element._obj); + fn(obj); + } + } +}; + + +/** + * Convenience helper to equip a type 'T' with a 'Registry::Element' + * + * Using this helper, an arbitrary type can be turned into a registry element + * type. E.g., in order to keep 'Child_service' objects in a registry, a new + * registry-compatible type can be created via 'Registered'. + * Objects of this type can be kept in a 'Registry + * >'. The constructor of such "registered" objects expect the registry as the + * first argument. The other arguments are forwarded to the constructor of the + * enclosed type. + */ +template +class Genode::Registered : public T +{ + private: + + typename Registry >::Element _element; + + public: + + template + Registered(Registry > ®istry, ARGS &&... args) + : T(args...), _element(registry, *this) { } +}; + +#endif /* _INCLUDE__BASE__REGISTRY_H_ */ diff --git a/repos/base/lib/mk/base-common.inc b/repos/base/lib/mk/base-common.inc index 465fa68330..b74b9fec45 100644 --- a/repos/base/lib/mk/base-common.inc +++ b/repos/base/lib/mk/base-common.inc @@ -10,6 +10,7 @@ SRC_CC += avl_tree.cc SRC_CC += slab.cc SRC_CC += allocator_avl.cc SRC_CC += heap.cc sliced_heap.cc +SRC_CC += registry.cc SRC_CC += console.cc SRC_CC += output.cc SRC_CC += child.cc diff --git a/repos/base/src/lib/base/registry.cc b/repos/base/src/lib/base/registry.cc new file mode 100644 index 0000000000..5749d63a7c --- /dev/null +++ b/repos/base/src/lib/base/registry.cc @@ -0,0 +1,132 @@ +/* + * \brief Thread-safe object registry + * \author Norman Feske + * \date 2016-11-06 + */ + +/* + * 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. + */ + +#include + +using namespace Genode; + + +Registry_base::Element::Element(Registry_base ®istry, void *obj) +: + _registry(registry), _obj(obj) +{ + _registry._insert(*this); +} + + +Registry_base::Element::~Element() +{ + { + Lock::Guard guard(_lock); + if (_keep_ptr && _registry._curr == this) { + + /* + * The destructor is called from the functor of a + * 'Registry::for_each' loop with the element temporarily dequeued. + * We flag the element to be not re-inserted into the list. + */ + *_keep_ptr = DISCARD; + return; + } + } + _registry._remove(*this); +} + + +void Registry_base::_insert(Element &element) +{ + Lock::Guard lock_guard(_lock); + + _elements.insert(&element); +} + + +void Registry_base::_remove(Element &element) +{ + Lock::Guard lock_guard(_lock); + + _elements.remove(&element); +} + + +Registry_base::Element *Registry_base::_processed(Keep const keep, + List &processed, + Element &e, Element *at) +{ + _curr = nullptr; + + /* if 'e' was dropped from the list, keep the current re-insert position */ + if (keep == DISCARD) + return at; + + /* make sure that the critical section of '~Element' is completed */ + Lock::Guard guard(e._lock); + + /* + * If '~Element' was preempted between the condition check and the + * assignment of keep = DISCARD, the above check would miss the DISCARD + * flag. Now, with the acquired lock, we know that the 'keep' value is + * up to date. + */ + if (keep == DISCARD) + return at; + + /* here we know that 'e' still exists */ + e._keep_ptr = nullptr; + + /* insert 'e' into the list of processed elements */ + processed.insert(&e, at); + + /* advance insert position to 'e' */ + return &e; +} + + +void Registry_base::_for_each(Untyped_functor &functor) +{ + Lock::Guard lock_guard(_lock); + + /* insert position in list of processed elements */ + Element *at = nullptr; + + List processed; + + while (Element *e = _elements.first()) { + + Keep keep = KEEP; + { + /* tell the element where to report its status */ + Lock::Guard guard(e->_lock); + _curr = e; + e->_keep_ptr = &keep; + } + + /* + * Remove the element from the list. Depending on the behavior of 'fn' + * (whether it destroys 'e' or not, we will insert it into the + * 'processed' list afterwards. + */ + _elements.remove(e); + + /* the element may disappear during the call of 'fn' */ + try { functor.call(e->_obj); } + + /* propagate exceptions while keeping registry consistent */ + catch (...) { at = _processed(keep, processed, *e, at); throw; } + + at = _processed(keep, processed, *e, at); + } + + /* use list of processed elements as '_elements' list */ + _elements = processed; +}