genode/base/src/test/lifetime/main.cc

279 lines
5.9 KiB
C++

/*
* \brief Test for lifetime-management utilities
* \author Norman Feske
* \date 2013-03-12
*/
/*
* Copyright (C) 2013 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 <base/printf.h>
#include <base/env.h>
#include <base/thread.h>
#include <timer_session/connection.h>
/* core includes */
#include <lifetime.h>
/********************************************************************
** Hooks for obtaining internal information of the tested classes **
********************************************************************/
static int weak_ptr_cnt;
void Genode::Volatile_object_base::debug_info() const
{
/* count number of weak pointers pointing to the object */
weak_ptr_cnt = 0;
for (Weak_ptr_base *curr = _list.first(); curr; curr = curr->next())
weak_ptr_cnt++;
}
static int weak_ptr_is_valid;
void Genode::Weak_ptr_base::debug_info() const
{
weak_ptr_is_valid = _valid;
}
struct Fatal_error { };
static void assert_weak_ptr_cnt(Genode::Volatile_object_base const *obj,
int expected_cnt)
{
obj->debug_info();
if (expected_cnt != weak_ptr_cnt) {
PERR("unexpected count, expected %d, got %d",
expected_cnt, weak_ptr_cnt);
throw Fatal_error();
}
}
static void assert_weak_ptr_valid(Genode::Weak_ptr_base const &ptr, bool valid)
{
ptr.debug_info();
if (weak_ptr_is_valid == valid)
return;
PERR("weak pointer unexpectedly %s", valid ? "valid" : "invalid");
throw Fatal_error();
}
/********************************************
** Test for the tracking of weak pointers **
********************************************/
static bool object_is_constructed;
struct Object : Genode::Volatile_object<Object>
{
Object() { object_is_constructed = true; }
~Object()
{
Volatile_object<Object>::lock_for_destruction();
object_is_constructed = false;
}
};
static void test_weak_pointer_tracking()
{
using namespace Genode;
PLOG("construct invalid weak pointer");
{
Weak_ptr<Object> ptr;
assert_weak_ptr_valid(ptr, false);
}
Object *obj = new (env()->heap()) Object;
Weak_ptr<Object> ptr_1 = obj->weak_ptr();
assert_weak_ptr_valid(ptr_1, true);
Weak_ptr<Object> ptr_2 = obj->weak_ptr();
assert_weak_ptr_valid(ptr_2, true);
assert_weak_ptr_cnt(obj, 2);
PLOG("test: assign weak pointer to itself");
ptr_2 = ptr_2;
assert_weak_ptr_cnt(obj, 2);
assert_weak_ptr_valid(ptr_2, true);
{
PLOG("test: assign weak pointer to another");
Weak_ptr<Object> ptr_3 = ptr_2;
assert_weak_ptr_cnt(obj, 3);
PLOG("test: destruct weak pointer");
/* 'ptr_3' gets destructed when leaving the scope */
}
assert_weak_ptr_cnt(obj, 2);
PLOG("destruct object");
destroy(env()->heap(), obj);
/*
* The destruction of the object should have invalidated all weak pointers
* pointing to the object.
*/
assert_weak_ptr_valid(ptr_1, false);
assert_weak_ptr_valid(ptr_2, false);
}
/*******************************************
** Test for deferring object destruction **
*******************************************/
struct Destruct_thread : Genode::Thread<4096>
{
Object *obj;
void entry()
{
using namespace Genode;
PLOG("thread: going to destroy object");
destroy(env()->heap(), obj);
PLOG("thread: destruction completed, job done");
}
Destruct_thread(Object *obj) : obj(obj) { }
};
static void assert_constructed(bool expect_constructed)
{
if (object_is_constructed == expect_constructed)
return;
PERR("object unexpectedly %sconstructed",
!object_is_constructed ? "not" : "");
throw Fatal_error();
}
static void test_deferred_destruction()
{
using namespace Genode;
static Timer::Connection timer;
Object *obj = new (env()->heap()) Object;
Weak_ptr<Object> ptr = obj->weak_ptr();
assert_weak_ptr_cnt(obj, 1);
assert_weak_ptr_valid(ptr, true);
assert_constructed(true);
/* create thread that will be used to destruct the object */
Destruct_thread destruct_thread(obj);
{
/* acquire possession over the object */
Locked_ptr<Object> locked_ptr(ptr);
/* start destruction using dedicated thread */
destruct_thread.start();
/* yield some time to the other thread */
timer.msleep(500);
/* even after the time period, the object should still be alive */
assert_constructed(true);
/* now, we release the locked pointer, the destruction can begin */
}
/*
* Now that the thread is expected to be unblocked, yield some time
* to actually do the destruction.
*/
timer.msleep(100);
assert_constructed(false);
destruct_thread.join();
}
/*******************************************************
** Test the failed aquisition of a destructed object **
*******************************************************/
static void test_acquisition_failure()
{
using namespace Genode;
PLOG("create object and weak pointer");
Object *obj = new (env()->heap()) Object;
Weak_ptr<Object> ptr = obj->weak_ptr();
PLOG("try to acquire possession over the object");
{
Locked_ptr<Object> locked_ptr(ptr);
if (!locked_ptr.is_valid()) {
PERR("locked pointer unexpectedly invalid");
throw Fatal_error();
}
/* release lock */
}
PLOG("destroy object");
destroy(env()->heap(), obj);
PLOG("try again, this time we should get an invalid pointer");
{
Locked_ptr<Object> locked_ptr(ptr);
if (locked_ptr.is_valid()) {
PERR("locked pointer unexpectedly valid");
throw Fatal_error();
}
}
}
/******************
** Main program **
******************/
int main(int argc, char **argv)
{
using namespace Genode;
printf("--- test-lifetime started ---\n");
printf("\n-- test tracking of weak pointers --\n");
test_weak_pointer_tracking();
printf("\n-- test deferred destruction --\n");
test_deferred_destruction();
printf("\n-- test acquisition failure --\n");
test_acquisition_failure();
printf("\n--- finished test-lifetime ---\n");
return 0;
}