New utility for managing volatile class members

Fixes #1025
This commit is contained in:
Norman Feske 2014-01-10 17:50:34 +01:00 committed by Christian Helmuth
parent 3394be9464
commit 865f2b263f
4 changed files with 365 additions and 0 deletions

View File

@ -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 <base/printf.h>
#include <base/stdint.h>
namespace Genode {
template<typename> class Volatile_object;
template<typename> 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 <typename MT>
class Genode::Volatile_object
{
private:
/**
* Utility to equip an existing type 'T' with a placement new operator
*/
template <typename T>
struct Placeable : T
{
template <typename... ARGS>
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 <typename... ARGS> void _do_construct(ARGS &&... args)
{
new (_space) Placeable<MT>(args...);
_constructed = true;
}
MT *_ptr() { return reinterpret_cast<MT *>(_space); }
MT const *_const_ptr() const { return reinterpret_cast<MT const *>(_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 <typename... ARGS>
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 <typename... ARGS>
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 <typename MT>
struct Genode::Lazy_volatile_object : Volatile_object<MT>
{
template <typename... ARGS>
Lazy_volatile_object(ARGS... args)
:
Volatile_object<MT>((typename Volatile_object<MT>::Lazy *)0)
{ }
};
#endif /* _INCLUDE__UTIL__VOLATILE_OBJECT_H_ */

View File

@ -0,0 +1,64 @@
build "core init test/volatile_object"
create_boot_directory
install_config {
<config>
<parent-provides>
<service name="ROM"/>
<service name="RAM"/>
<service name="CPU"/>
<service name="RM"/>
<service name="CAP"/>
<service name="PD"/>
<service name="SIGNAL"/>
<service name="LOG"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<start name="test-volatile_object">
<resource name="RAM" quantum="1M"/>
</start>
</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 ---
}

View File

@ -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 <util/volatile_object.h>
#include <base/printf.h>
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_with_reference> member;
Lazy_volatile_object<Member_with_reference> 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<Member_with_reference>::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;
}

View File

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