genode/repos/os/src/test/signal/main.cc

461 lines
13 KiB
C++
Raw Normal View History

2011-12-22 16:19:25 +01:00
/*
* \brief Test for signalling framework
* \author Norman Feske
* \author Martin Stein
2011-12-22 16:19:25 +01:00
* \date 2008-09-06
*/
/*
* Copyright (C) 2008-2017 Genode Labs GmbH
2011-12-22 16:19:25 +01:00
*
* 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/component.h>
#include <base/heap.h>
2011-12-22 16:19:25 +01:00
#include <base/thread.h>
#include <base/registry.h>
2011-12-22 16:19:25 +01:00
#include <timer_session/connection.h>
using namespace Genode;
/**
* A thread that submits a signal context in a periodic fashion
2011-12-22 16:19:25 +01:00
*/
class Sender : Thread
2011-12-22 16:19:25 +01:00
{
private:
Timer::Connection _timer;
Signal_transmitter _transmitter;
unsigned const _interval_ms;
bool const _verbose;
bool _stop { false };
unsigned _submit_cnt { 0 };
bool _idle { false };
2011-12-22 16:19:25 +01:00
void entry()
{
while (!_stop) {
if (!_idle) {
_submit_cnt++;
if (_verbose) {
log("submit signal ", _submit_cnt); }
2011-12-22 16:19:25 +01:00
_transmitter.submit();
if (_interval_ms) {
_timer.msleep(_interval_ms); }
} else {
_timer.msleep(100); }
2011-12-22 16:19:25 +01:00
}
}
public:
Sender(Env &env,
Signal_context_capability context,
unsigned interval_ms,
bool verbose)
2011-12-22 16:19:25 +01:00
:
Thread(env, "sender", 4096 * sizeof(addr_t)), _timer(env),
_transmitter(context), _interval_ms(interval_ms), _verbose(verbose)
2011-12-22 16:19:25 +01:00
{
Thread::start();
2011-12-22 16:19:25 +01:00
}
/***************
** Accessors **
***************/
2011-12-22 16:19:25 +01:00
void idle(bool idle) { _idle = idle; }
unsigned submit_cnt() const { return _submit_cnt; }
2011-12-22 16:19:25 +01:00
};
/**
* A thread that receives signals and takes some time to handle each
2011-12-22 16:19:25 +01:00
*/
class Handler : Thread
2011-12-22 16:19:25 +01:00
{
private:
Timer::Connection _timer;
unsigned const _dispatch_ms;
unsigned const _id;
bool const _verbose;
Signal_receiver &_receiver;
bool _stop { false };
unsigned _receive_cnt { 0 };
unsigned _activation_cnt { 0 };
bool _idle { false };
2011-12-22 16:19:25 +01:00
void entry()
{
while (!_stop) {
if (!_idle) {
Signal signal = _receiver.wait_for_signal();
2011-12-22 16:19:25 +01:00
if (_verbose)
log("handler ", _id, " got ", signal.num(), " "
"signal", (signal.num() == 1 ? "" : "s"), " "
"with context ", signal.context());
2011-12-22 16:19:25 +01:00
_receive_cnt += signal.num();
_activation_cnt++;
}
if (_dispatch_ms)
_timer.msleep(_dispatch_ms);
}
}
public:
Handler(Env &env,
Signal_receiver &receiver,
unsigned dispatch_ms,
bool verbose,
unsigned id)
2011-12-22 16:19:25 +01:00
:
Thread(env, "handler", 4096 * sizeof(addr_t)), _timer(env),
_dispatch_ms(dispatch_ms), _id(id), _verbose(verbose),
_receiver(receiver)
2011-12-22 16:19:25 +01:00
{
Thread::start();
2011-12-22 16:19:25 +01:00
}
void print(Output &output) const { Genode::print(output, "handler ", _id); }
2011-12-22 16:19:25 +01:00
/***************
** Accessors **
***************/
2011-12-22 16:19:25 +01:00
void idle(bool idle) { _idle = idle; }
unsigned receive_cnt() const { return _receive_cnt; }
unsigned activation_cnt() const { return _activation_cnt; }
2011-12-22 16:19:25 +01:00
};
/**
* Base of all signalling tests
2011-12-22 16:19:25 +01:00
*/
struct Signal_test
2011-12-22 16:19:25 +01:00
{
enum { SPEED = 10 };
2011-12-22 16:19:25 +01:00
int id;
2011-12-22 16:19:25 +01:00
Signal_test(int id, char const *brief) : id(id) {
log("\nTEST ", id, ": ", brief, "\n"); }
2011-12-22 16:19:25 +01:00
~Signal_test() { log("\nTEST ", id, " finished\n"); }
2011-12-22 16:19:25 +01:00
};
struct Fast_sender_test : Signal_test
2011-12-22 16:19:25 +01:00
{
static constexpr char const *brief =
"reliable delivery if the sender is faster than the handlers";
2011-12-22 16:19:25 +01:00
enum { HANDLER_INTERVAL_MS = 10 * SPEED,
SENDER_INTERVAL_MS = 2 * SPEED,
DURATION_MS = 50 * SPEED,
FINISH_IDLE_MS = 2 * HANDLER_INTERVAL_MS };
struct Unequal_sent_and_received_signals : Exception { };
2011-12-22 16:19:25 +01:00
Env &env;
Timer::Connection timer { env };
Signal_context context;
Signal_receiver receiver;
Handler handler { env, receiver, HANDLER_INTERVAL_MS, false, 1 };
Sender sender { env, receiver.manage(&context),
SENDER_INTERVAL_MS, false };
2011-12-22 16:19:25 +01:00
Fast_sender_test(Env &env, int id) : Signal_test(id, brief), env(env)
{
timer.msleep(DURATION_MS);
2011-12-22 16:19:25 +01:00
/* stop emitting signals */
log("deactivate sender");
sender.idle(true);
timer.msleep(FINISH_IDLE_MS);
log("sender submitted a total of ", sender.submit_cnt(), " signals");
log("handler received a total of ", handler.receive_cnt(), " signals");
2011-12-22 16:19:25 +01:00
if (sender.submit_cnt() != handler.receive_cnt()) {
throw Unequal_sent_and_received_signals(); }
2011-12-22 16:19:25 +01:00
}
};
2011-12-22 16:19:25 +01:00
struct Multiple_handlers_test : Signal_test
2011-12-22 16:19:25 +01:00
{
static constexpr char const *brief =
"get multiple handlers at one sender activated in a fair manner";
enum { HANDLER_INTERVAL_MS = 8 * SPEED,
SENDER_INTERVAL_MS = 1 * SPEED,
FINISH_IDLE_MS = 2 * HANDLER_INTERVAL_MS,
DURATION_MS = 50 * SPEED,
NR_OF_HANDLERS = 4 };
struct Unequal_sent_and_received_signals : Exception { };
struct Unequal_activation_of_handlers : Exception { };
Env &env;
Heap heap { env.ram(), env.rm() };
Timer::Connection timer { env };
Signal_context context;
Signal_receiver receiver;
Registry<Registered<Handler> > handlers;
Sender sender { env, receiver.manage(&context),
SENDER_INTERVAL_MS, true};
Multiple_handlers_test(Env &env, int id) : Signal_test(id, brief), env(env)
{
for (unsigned i = 0; i < NR_OF_HANDLERS; i++)
new (heap) Registered<Handler>(handlers, env, receiver,
HANDLER_INTERVAL_MS, true, i);
timer.msleep(DURATION_MS);
/* stop emitting signals */
log("stop generating new signals");
sender.idle(true);
timer.msleep(FINISH_IDLE_MS);
/* let handlers settle down */
handlers.for_each([&] (Handler &handler) { handler.idle(true); });
timer.msleep(FINISH_IDLE_MS);
/* print statistics and clean up */
unsigned total_rcv = 0, max_act = 0, min_act = ~0;;
handlers.for_each([&] (Handler &handler) {
unsigned const rcv = handler.receive_cnt();
unsigned const act = handler.activation_cnt();
log(handler, " received ", rcv, " signals, was activated ", act, " times");
total_rcv += rcv;
if (act > max_act) { max_act = act; }
if (act < min_act) { min_act = act; }
destroy(heap, &handler);
});
log("sender submitted a total of ", sender.submit_cnt(), " signals");
log("handlers received a total of ", total_rcv, " signals");
/* check if number of sent signals match the received ones */
if (sender.submit_cnt() != total_rcv) {
throw Unequal_sent_and_received_signals(); }
/* check if handlers had been activated equally (tolerance of one) */
if (max_act - min_act > 1) {
throw Unequal_activation_of_handlers(); }
}
};
2011-12-22 16:19:25 +01:00
struct Stress_test : Signal_test
2011-12-22 16:19:25 +01:00
{
static constexpr char const *brief =
"throughput when submitting/handling as fast as possible";
2011-12-22 16:19:25 +01:00
enum { DURATION_SEC = 5 };
2011-12-22 16:19:25 +01:00
struct Unequal_sent_and_received_signals : Exception { };
2011-12-22 16:19:25 +01:00
Env &env;
Timer::Connection timer { env };
Signal_context context;
Signal_receiver receiver;
Handler handler { env, receiver, 0, false, 1 };
Sender sender { env, receiver.manage(&context), 0, false };
2011-12-22 16:19:25 +01:00
Stress_test(Env &env, int id) : Signal_test(id, brief), env(env)
{
for (unsigned i = 1; i <= DURATION_SEC; i++) {
log(i, "/", (unsigned)DURATION_SEC);
timer.msleep(1000);
}
log("deactivate sender");
sender.idle(true);
2011-12-22 16:19:25 +01:00
while (handler.receive_cnt() < sender.submit_cnt()) {
log("waiting for signals still in flight...");
timer.msleep(1000);
}
log("");
log("sender submitted a total of ", sender.submit_cnt(), " signals");
log("handler received a total of ", handler.receive_cnt(), " signals");
log("");
log("handler received ", handler.receive_cnt() / DURATION_SEC, " signals per second");
log("handler was activated ", handler.activation_cnt() / DURATION_SEC, " times per second");
log("");
if (sender.submit_cnt() != handler.receive_cnt())
throw Unequal_sent_and_received_signals();
}
};
2011-12-22 16:19:25 +01:00
struct Lazy_receivers_test : Signal_test
2011-12-22 16:19:25 +01:00
{
static constexpr char const *brief = "lazy and out-of-order signal reception";
2011-12-22 16:19:25 +01:00
Signal_context context_1, context_2;
Signal_receiver receiver_1, receiver_2;
Signal_transmitter transmitter_1 { receiver_1.manage(&context_1) };
Signal_transmitter transmitter_2 { receiver_2.manage(&context_2) };
Lazy_receivers_test(Env &env, int id) : Signal_test(id, brief)
{
log("submit and receive signals with multiple receivers in order");
transmitter_1.submit();
transmitter_2.submit();
{
Signal signal = receiver_1.wait_for_signal();
log("returned from wait_for_signal for receiver 1");
2011-12-22 16:19:25 +01:00
signal = receiver_2.wait_for_signal();
log("returned from wait_for_signal for receiver 2");
}
log("submit and receive signals with multiple receivers out of order");
transmitter_1.submit();
transmitter_2.submit();
{
Signal signal = receiver_2.wait_for_signal();
log("returned from wait_for_signal for receiver 2");
signal = receiver_1.wait_for_signal();
log("returned from wait_for_signal for receiver 1");
}
}
};
struct Context_management_test : Signal_test
{
static constexpr char const *brief =
"correct initialization and cleanup of receiver and context";
Env &env;
Timer::Connection timer { env };
Signal_context context;
Signal_receiver receiver;
Signal_context_capability context_cap { receiver.manage(&context) };
Sender sender { env, context_cap, 500, true };
Context_management_test(Env &env, int id) : Signal_test(id, brief), env(env)
{
/* stop sender after timeout */
timer.msleep(1000);
log("suspend sender");
sender.idle(true);
/* collect pending signals and dissolve context from receiver */
{
Signal signal = receiver.wait_for_signal();
log("got ", signal.num(), " signal(s) from ", signal.context());
}
receiver.dissolve(&context);
/* let sender spin for some time */
log("resume sender");
sender.idle(false);
timer.msleep(1000);
log("suspend sender");
sender.idle(true);
log("destroy sender");
}
};
struct Synchronized_destruction_test : Signal_test, Thread
{
static constexpr char const *brief =
"does 'dissolve' block as long as the signal context is referenced?";
struct Failed : Exception { };
Env &env;
Timer::Connection timer { env };
Heap heap { env.ram(), env.rm() };
Signal_context &context { *new (heap) Signal_context };
Signal_receiver receiver;
Signal_transmitter transmitter { receiver.manage(&context) };
bool destroyed { false };
void entry()
{
receiver.dissolve(&context);
log("dissolve finished");
destroyed = true;
destroy(heap, &context);
}
Synchronized_destruction_test(Env &env, int id)
: Signal_test(id, brief), Thread(env, "destroyer", 1024 * sizeof(addr_t)), env(env)
{
transmitter.submit();
{
Signal signal = receiver.wait_for_signal();
log("start dissolving");
Thread::start();
timer.msleep(2000);
Signal signal_copy_1 = signal;
Signal signal_copy_2 = signal;
signal_copy_1 = signal_copy_2;
if (destroyed) {
throw Failed(); }
log("destruct signal");
}
Thread::join();
}
};
struct Many_contexts_test : Signal_test
{
static constexpr char const *brief = "create and manage many contexts";
struct Manage_failed : Exception { };
Env &env;
Heap heap { env.ram(), env.rm() };
Registry<Registered<Signal_context> > contexts;
Many_contexts_test(Env &env, int id) : Signal_test(id, brief), env(env)
{
for (unsigned round = 0; round < 10; round++) {
unsigned const nr_of_contexts = 200 + 5 * round;
log("round ", round, ": manage ", nr_of_contexts, " contexts");
Signal_receiver receiver;
for (unsigned i = 0; i < nr_of_contexts; i++) {
if (!receiver.manage(new (heap) Registered<Signal_context>(contexts)).valid()) {
throw Manage_failed(); }
}
contexts.for_each([&] (Registered<Signal_context> &context) {
receiver.dissolve(&context);
destroy(heap, &context);
});
}
}
};
struct Main
2011-12-22 16:19:25 +01:00
{
Constructible<Fast_sender_test> test_1;
Constructible<Multiple_handlers_test> test_2;
Constructible<Stress_test> test_3;
Constructible<Lazy_receivers_test> test_4;
Constructible<Context_management_test> test_5;
Constructible<Synchronized_destruction_test> test_6;
Constructible<Many_contexts_test> test_7;
Main(Env &env)
{
log("--- Signalling test ---");
test_1.construct(env, 1); test_1.destruct();
test_2.construct(env, 2); test_2.destruct();
test_3.construct(env, 3); test_3.destruct();
test_4.construct(env, 4); test_4.destruct();
test_5.construct(env, 5); test_5.destruct();
test_6.construct(env, 6); test_6.destruct();
test_7.construct(env, 7); test_7.destruct();
log("--- Signalling test finished ---");
2011-12-22 16:19:25 +01:00
}
};
void Component::construct(Genode::Env &env) { static Main main(env); }