base: exception safety during registry iteration

This patch fixes a problem in the non-const 'for_each' method of the
'Registry' data structure. If an exception was thrown from within the
functor of the 'for_each' operation, the not yet processed items of the
registry were dropped from the registry, which is not expected.
This commit is contained in:
Norman Feske 2018-08-21 21:15:07 +02:00 committed by Christian Helmuth
parent ccf6b237bb
commit c270e4fb30
5 changed files with 125 additions and 1 deletions

View File

@ -0,0 +1,27 @@
build "core init test/registry"
create_boot_directory
install_config {
<config>
<parent-provides>
<service name="LOG"/>
<service name="PD"/>
<service name="CPU"/>
<service name="ROM"/>
</parent-provides>
<default-route>
<any-service> <parent/> </any-service>
</default-route>
<default caps="50"/>
<start name="test-registry">
<resource name="RAM" quantum="10M"/>
</start>
</config>
}
build_boot_image "core ld.lib.so init test-registry"
append qemu_args "-nographic "
run_genode_until ".*child \"test-registry\" exited with exit value 0.*\n" 20

View File

@ -134,7 +134,21 @@ void Registry_base::_for_each(Untyped_functor &functor)
try { functor.call(e->_obj); }
/* propagate exceptions while keeping registry consistent */
catch (...) { at = _processed(notify, processed, *e, at); throw; }
catch (...) {
/* handle current element */
at = _processed(notify, processed, *e, at);
/* handle the remaining elements without invoking the functor */
while (Element *e = _elements.first()) {
_elements.remove(e);
at = _processed(notify, processed, *e, at);
};
_elements = processed;
/* propagate exception to caller */
throw;
}
at = _processed(notify, processed, *e, at);
}

View File

@ -0,0 +1,79 @@
/*
* \brief Test for 'Registry' data structure
* \author Norman Feske
* \date 2018-08-21
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/registry.h>
#include <base/component.h>
#include <base/log.h>
/*
* Check that an exception that occurs during the iteration over
* registry items does not affect the registry content.
*/
static void test_exception_during_for_each()
{
using namespace Genode;
struct Item : Interface
{
typedef String<10> Name;
Name const name;
Item(Name const &name) : name(name) { }
class Invalid : Exception { };
void visit()
{
if (name == "second")
throw Invalid();
}
};
Registry<Registered<Item> > items { };
Registered<Item> first (items, "first");
Registered<Item> second(items, "second");
Registered<Item> third (items, "third");
auto num_items = [&] () {
unsigned cnt = 0;
items.for_each([&] (Item &) { cnt++; });
return cnt;
};
unsigned const num_items_before_exception = num_items();
try {
items.for_each([&] (Item &item) {
/* second item throws an exception */
item.visit();
});
}
catch (Item::Invalid) { log("exception occurred (expected)"); };
unsigned const num_items_after_exception = num_items();
struct Failed : Exception { };
if (num_items_before_exception != num_items_after_exception)
throw Failed();
}
void Component::construct(Genode::Env &env)
{
test_exception_during_for_each();
env.parent().exit(0);
}

View File

@ -0,0 +1,3 @@
TARGET = test-registry
SRC_CC = main.cc
LIBS += base

View File

@ -71,6 +71,7 @@ pthread
python
ram_fs_chunk
reconstructible
registry
report_rom
resource_request
resource_yield