mirror of
https://github.com/genodelabs/genode.git
synced 2025-06-18 07:08:18 +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
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