diff --git a/os/include/util/volatile_object.h b/os/include/util/volatile_object.h new file mode 100644 index 0000000000..f80ae84a5b --- /dev/null +++ b/os/include/util/volatile_object.h @@ -0,0 +1,167 @@ +/* + * \brief Utility for manual in-place construction of objects + * \author Norman Feske + * \date 2014-01-10 + */ + +/* + * Copyright (C) 2014 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__UTIL__VOLATILE_OBJECT_H_ +#define _INCLUDE__UTIL__VOLATILE_OBJECT_H_ + +#include +#include + +namespace Genode { + template class Volatile_object; + template class Lazy_volatile_object; +} + + +/** + * Place holder for an object to be repeatedly constructed and destructed + * + * This class template acts as a smart pointer that refers to an object + * contained within the smart pointer itself. The contained object may be + * repeatedly constructed and destructed while staying in the same place. This + * is useful for replacing aggregated members during the lifetime of a compound + * object. + * + * \param MT type + */ +template +class Genode::Volatile_object +{ + private: + + /** + * Utility to equip an existing type 'T' with a placement new operator + */ + template + struct Placeable : T + { + template + Placeable(ARGS &&... args) + : + T(args...) + { } + + void *operator new (size_t, void *ptr) { return ptr; } + }; + + /** + * Static reservation of memory for the embedded object + */ + char _space[sizeof(MT)]; + + /** + * True if the volatile object contains a constructed object + */ + bool _constructed = false; + + template void _do_construct(ARGS &&... args) + { + new (_space) Placeable(args...); + _constructed = true; + } + + MT *_ptr() { return reinterpret_cast(_space); } + MT const *_const_ptr() const { return reinterpret_cast(_space); } + + void _check_constructed() const + { + if (!_constructed) + throw Deref_unconstructed_object(); + } + + protected: + + /** + * Dummy type used as a hook for 'Lazy_volatile_object' to bypass the + * default constructor by invoking the 'Volatile_object(Lazy *)' + * constructor. + */ + struct Lazy { }; + + /** + * Constructor that omits the initial construction of the object + */ + Volatile_object(Lazy *) { } + + public: + + class Deref_unconstructed_object { }; + + /** + * Constructor + * + * The arguments are forwarded to the constructor of the embedded + * object. + */ + template + Volatile_object(ARGS &&... args) + { + _do_construct(args...); + } + + ~Volatile_object() { destruct(); } + + /** + * Construct new object in place + * + * If the 'Volatile_object' already hosts a constructed object, the old + * object will be destructed first. + */ + template + void construct(ARGS &&... args) + { + destruct(); + _do_construct(args...); + } + + /** + * Destruct object + */ + void destruct() + { + if (!_constructed) + return; + + /* invoke destructor */ + _ptr()->~MT(); + + _constructed = false; + } + + /** + * Return true of volatile object contains a constructed object + */ + bool is_constructed() const { return _constructed; } + + /** + * Access contained object + */ + MT *operator -> () { _check_constructed(); return _ptr(); } + MT const *operator -> () const { _check_constructed(); return _const_ptr(); } +}; + + +/** + * Volatile object that holds no initially constructed object + */ +template +struct Genode::Lazy_volatile_object : Volatile_object +{ + template + Lazy_volatile_object(ARGS... args) + : + Volatile_object((typename Volatile_object::Lazy *)0) + { } +}; + +#endif /* _INCLUDE__UTIL__VOLATILE_OBJECT_H_ */ diff --git a/os/run/volatile_object.run b/os/run/volatile_object.run new file mode 100644 index 0000000000..8bec6c38c5 --- /dev/null +++ b/os/run/volatile_object.run @@ -0,0 +1,64 @@ +build "core init test/volatile_object" + +create_boot_directory + +install_config { + + + + + + + + + + + + + + + + + + +} + +build_boot_image "core init test-volatile_object" + +append qemu_args "-nographic -m 64" + +run_genode_until {child exited with exit value 0.*\n} 10 + +grep_output {-> test-volatile_object} + +compare_output_to { +[init -> test-volatile_object] --- test-volatile_object started --- +[init -> test-volatile_object] construct Object 1 +[init -> test-volatile_object] construct Object 2 +[init -> test-volatile_object] -- create Compound object -- +[init -> test-volatile_object] construct Member_with_reference +[init -> test-volatile_object] construct Compound +[init -> test-volatile_object] compound.member.is_constructed returns 1 +[init -> test-volatile_object] compound.lazy_member.is_constructed returns 0 +[init -> test-volatile_object] -- construct lazy member -- +[init -> test-volatile_object] construct Member_with_reference +[init -> test-volatile_object] compound.lazy_member.is_constructed returns 1 +[init -> test-volatile_object] -- call method on member (with reference to Object 1) -- +[init -> test-volatile_object] const method called on Object 1 +[init -> test-volatile_object] -- reconstruct member with Object 2 as reference -- +[init -> test-volatile_object] destruct Member_with_reference +[init -> test-volatile_object] construct Member_with_reference +[init -> test-volatile_object] -- call method on member -- +[init -> test-volatile_object] const method called on Object 2 +[init -> test-volatile_object] -- destruct member -- +[init -> test-volatile_object] destruct Member_with_reference +[init -> test-volatile_object] -- try to call method on member, catch exception -- +[init -> test-volatile_object] got exception, as expected +[init -> test-volatile_object] -- destruct Compound and Objects 1 and 2 -- +[init -> test-volatile_object] destruct Compound +[init -> test-volatile_object] destruct Member_with_reference +[init -> test-volatile_object] destruct Object 2 +[init -> test-volatile_object] destruct Object 1 +[init -> test-volatile_object] --- test-volatile_object finished --- +} + diff --git a/os/src/test/volatile_object/main.cc b/os/src/test/volatile_object/main.cc new file mode 100644 index 0000000000..72a7119398 --- /dev/null +++ b/os/src/test/volatile_object/main.cc @@ -0,0 +1,131 @@ +/* + * \brief Test for 'Volatile_object' + * \author Norman Feske + * \date 2013-01-10 + */ + +/* + * Copyright (C) 2014 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. + */ + +/* Genode includes */ +#include +#include + +using Genode::Volatile_object; +using Genode::Lazy_volatile_object; + + +struct Object +{ + unsigned const id; + + Object(unsigned id) : id(id) + { + PLOG("construct Object %d", id); + } + + ~Object() + { + PLOG("destruct Object %d", id); + } + + void method() { PLOG("method called on Object %d", id); } + void const_method() const { PLOG("const method called on Object %d", id); } +}; + + +struct Member_with_reference +{ + Object &reference; + + const int c = 13; + + Member_with_reference(Object &reference) : reference(reference) + { + PLOG("construct Member_with_reference"); + } + + ~Member_with_reference() + { + PLOG("destruct Member_with_reference"); + } +}; + + +struct Compound +{ + Volatile_object member; + Lazy_volatile_object lazy_member; + + Compound(Object &object) + : + member(object) + { + PLOG("construct Compound"); + } + + ~Compound() + { + PLOG("destruct Compound"); + } +}; + + +static void call_const_method(Compound const &compound) +{ + compound.member->reference.const_method(); +} + + +int main(int, char **) +{ + using namespace Genode; + + printf("--- test-volatile_object started ---\n"); + + { + Object object_1(1); + Object object_2(2); + + printf("-- create Compound object --\n"); + Compound compound(object_1); + + PLOG("compound.member.is_constructed returns %d", + compound.member.is_constructed()); + PLOG("compound.lazy_member.is_constructed returns %d", + compound.lazy_member.is_constructed()); + + printf("-- construct lazy member --\n"); + compound.lazy_member.construct(object_2); + PLOG("compound.lazy_member.is_constructed returns %d", + compound.lazy_member.is_constructed()); + + printf("-- call method on member (with reference to Object 1) --\n"); + call_const_method(compound); + + printf("-- reconstruct member with Object 2 as reference --\n"); + compound.member.construct(object_2); + + printf("-- call method on member --\n"); + call_const_method(compound); + + printf("-- destruct member --\n"); + compound.member.destruct(); + + printf("-- try to call method on member, catch exception --\n"); + try { + call_const_method(compound); } + catch (typename Volatile_object::Deref_unconstructed_object) { + PLOG("got exception, as expected"); } + + printf("-- destruct Compound and Objects 1 and 2 --\n"); + } + + printf("--- test-volatile_object finished ---\n"); + + return 0; +} diff --git a/os/src/test/volatile_object/target.mk b/os/src/test/volatile_object/target.mk new file mode 100644 index 0000000000..18d4861ef9 --- /dev/null +++ b/os/src/test/volatile_object/target.mk @@ -0,0 +1,3 @@ +TARGET = test-volatile_object +SRC_CC = main.cc +LIBS = base