mirror of
https://github.com/genodelabs/genode.git
synced 2025-06-18 15:18:20 +00:00
base: new 'Registry' data structure
This data structure is meant as a safe alternative for a list wherever the list is solely used to remember objects and iterate through them in an unspecified order. One use case is the 'Service_registry'.
This commit is contained in:
committed by
Christian Helmuth
parent
513a3f1d37
commit
baf61df0fd
160
repos/base/include/base/registry.h
Normal file
160
repos/base/include/base/registry.h
Normal file
@ -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 <util/list.h>
|
||||||
|
#include <base/lock.h>
|
||||||
|
|
||||||
|
namespace Genode {
|
||||||
|
|
||||||
|
class Registry_base;
|
||||||
|
template <typename T> struct Registry;
|
||||||
|
template <typename T> class Registered;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Genode::Registry_base
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
enum Keep { KEEP, DISCARD };
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
class Element : public List<Element>::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<Element> _elements;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Element currently processed by '_for_each'
|
||||||
|
*/
|
||||||
|
Element const *_curr = nullptr;
|
||||||
|
|
||||||
|
void _insert(Element &);
|
||||||
|
void _remove(Element &);
|
||||||
|
|
||||||
|
Element *_processed(Keep, List<Element> &, Element &, Element *);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
struct Untyped_functor { virtual void call(void *obj_ptr) = 0; };
|
||||||
|
|
||||||
|
void _for_each(Untyped_functor &);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
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 <typename FUNC>
|
||||||
|
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<T *>(obj_ptr);
|
||||||
|
_fn(obj);
|
||||||
|
}
|
||||||
|
} untyped_functor(fn);
|
||||||
|
|
||||||
|
Registry_base::_for_each(untyped_functor);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FUNC>
|
||||||
|
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<Element const &>(*e);
|
||||||
|
T const &obj = *reinterpret_cast<T const *>(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<Child_service>'.
|
||||||
|
* Objects of this type can be kept in a 'Registry<Registered<Child_service>
|
||||||
|
* >'. 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 <typename T>
|
||||||
|
class Genode::Registered : public T
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
typename Registry<Registered<T> >::Element _element;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
template <typename... ARGS>
|
||||||
|
Registered(Registry<Registered<T> > ®istry, ARGS &&... args)
|
||||||
|
: T(args...), _element(registry, *this) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _INCLUDE__BASE__REGISTRY_H_ */
|
@ -10,6 +10,7 @@ SRC_CC += avl_tree.cc
|
|||||||
SRC_CC += slab.cc
|
SRC_CC += slab.cc
|
||||||
SRC_CC += allocator_avl.cc
|
SRC_CC += allocator_avl.cc
|
||||||
SRC_CC += heap.cc sliced_heap.cc
|
SRC_CC += heap.cc sliced_heap.cc
|
||||||
|
SRC_CC += registry.cc
|
||||||
SRC_CC += console.cc
|
SRC_CC += console.cc
|
||||||
SRC_CC += output.cc
|
SRC_CC += output.cc
|
||||||
SRC_CC += child.cc
|
SRC_CC += child.cc
|
||||||
|
132
repos/base/src/lib/base/registry.cc
Normal file
132
repos/base/src/lib/base/registry.cc
Normal file
@ -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 <base/registry.h>
|
||||||
|
|
||||||
|
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<Element> &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<Element> 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;
|
||||||
|
}
|
Reference in New Issue
Block a user