2011-12-22 16:19:25 +01:00
|
|
|
/*
|
|
|
|
* \brief Test for signalling framework
|
|
|
|
* \author Norman Feske
|
|
|
|
* \date 2008-09-06
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2013-01-10 21:44:47 +01:00
|
|
|
* Copyright (C) 2008-2013 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <base/printf.h>
|
|
|
|
#include <base/signal.h>
|
|
|
|
#include <base/thread.h>
|
|
|
|
#include <timer_session/connection.h>
|
|
|
|
#include <cap_session/connection.h>
|
|
|
|
#include <util/misc_math.h>
|
|
|
|
|
|
|
|
using namespace Genode;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Transmit signals in a periodic fashion
|
|
|
|
*/
|
|
|
|
class Sender : Thread<4096>
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
|
|
|
Signal_transmitter _transmitter;
|
|
|
|
Timer::Connection _timer; /* timer connection for local use */
|
|
|
|
unsigned _interval_ms; /* interval between signals in milliseconds */
|
|
|
|
bool _stop; /* state for destruction protocol */
|
|
|
|
unsigned _submit_cnt; /* statistics */
|
|
|
|
bool _idle; /* suppress the submission of signals */
|
|
|
|
bool _verbose; /* print activities */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sender thread submits signals every '_interval_ms' milliseconds
|
|
|
|
*/
|
|
|
|
void entry()
|
|
|
|
{
|
|
|
|
while (!_stop) {
|
|
|
|
|
|
|
|
if (!_idle) {
|
|
|
|
_submit_cnt++;
|
|
|
|
|
|
|
|
if (_verbose)
|
|
|
|
printf("submit signal %d\n", _submit_cnt);
|
|
|
|
|
|
|
|
_transmitter.submit();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_interval_ms)
|
|
|
|
_timer.msleep(_interval_ms);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*
|
|
|
|
* \param context signal destination
|
|
|
|
* \param interval_ms interval between signals
|
|
|
|
* \param verbose print status information
|
|
|
|
*/
|
|
|
|
Sender(Signal_context_capability context,
|
|
|
|
unsigned interval_ms, bool verbose = true)
|
|
|
|
:
|
|
|
|
_transmitter(context),
|
|
|
|
_interval_ms(interval_ms),
|
|
|
|
_stop(false),
|
|
|
|
_submit_cnt(0),
|
|
|
|
_idle(0),
|
|
|
|
_verbose(verbose)
|
|
|
|
{
|
|
|
|
/* start thread at 'entry' function */
|
|
|
|
start();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Destructor
|
|
|
|
*/
|
|
|
|
~Sender()
|
|
|
|
{
|
|
|
|
/* tell thread to stop iterating */
|
|
|
|
_stop = true;
|
|
|
|
|
|
|
|
/* wait for current 'msleep' call of the thread to finish */
|
|
|
|
_timer.msleep(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Suppress the transmission of further signals
|
|
|
|
*/
|
|
|
|
void idle(bool idle = true) { _idle = idle; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return total number of submitted notifications
|
|
|
|
*/
|
|
|
|
unsigned submit_cnt() { return _submit_cnt; }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Signal handler receives signals and takes some time to handle each
|
|
|
|
*/
|
|
|
|
class Handler : Thread<4096>
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
|
|
|
unsigned _dispatch_ms; /* time needed for dispatching a signal */
|
|
|
|
unsigned _id; /* unique ID of signal handler for debug output */
|
|
|
|
static unsigned _id_cnt; /* counter for producing unique IDs */
|
|
|
|
Signal_receiver *_receiver; /* signal endpoint */
|
|
|
|
Timer::Connection _timer; /* timer connection for local use */
|
|
|
|
bool _stop; /* state for destruction protocol */
|
|
|
|
unsigned _receive_cnt; /* number of received notifications */
|
|
|
|
unsigned _activation_cnt; /* number of invocations of the signal handler */
|
|
|
|
bool _idle; /* suppress the further handling of signals */
|
|
|
|
bool _verbose; /* print status information */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Signal handler needs '_dispatch_ms' milliseconds for each signal
|
|
|
|
*/
|
|
|
|
void entry()
|
|
|
|
{
|
|
|
|
while (!_stop) {
|
|
|
|
|
|
|
|
if (!_idle) {
|
|
|
|
Signal signal = _receiver->wait_for_signal();
|
|
|
|
|
|
|
|
if (_verbose)
|
2013-01-10 20:56:48 +01:00
|
|
|
printf("handler %d got %u signal%s with context %p\n",
|
2011-12-22 16:19:25 +01:00
|
|
|
_id,
|
|
|
|
signal.num(),
|
|
|
|
signal.num() == 1 ? "" : "s",
|
|
|
|
signal.context());
|
|
|
|
|
|
|
|
_receive_cnt += signal.num();
|
|
|
|
_activation_cnt++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_dispatch_ms)
|
|
|
|
_timer.msleep(_dispatch_ms);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*
|
|
|
|
* \param receiver receiver to request signals from
|
|
|
|
* \param dispatch_ms duration of signal-handler activity
|
|
|
|
* \param verbose print status information
|
|
|
|
*/
|
|
|
|
Handler(Signal_receiver *receiver, unsigned dispatch_ms, bool verbose = true)
|
|
|
|
:
|
|
|
|
_dispatch_ms(dispatch_ms),
|
|
|
|
_id(++_id_cnt),
|
|
|
|
_receiver(receiver),
|
|
|
|
_stop(false),
|
|
|
|
_receive_cnt(0),
|
|
|
|
_activation_cnt(0),
|
|
|
|
_idle(0),
|
|
|
|
_verbose(verbose)
|
|
|
|
{
|
|
|
|
start();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Destructor
|
|
|
|
*/
|
|
|
|
~Handler()
|
|
|
|
{
|
|
|
|
/* tell thread to stop iterating */
|
|
|
|
_stop = true;
|
|
|
|
|
|
|
|
/* wait for current 'msleep' call of the thread to finish */
|
|
|
|
_timer.msleep(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Suppress the handling of further signals
|
|
|
|
*/
|
|
|
|
void idle(bool idle = true) { _idle = idle; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return total number of received notifications
|
|
|
|
*/
|
2013-01-10 20:56:48 +01:00
|
|
|
unsigned receive_cnt() const { return _receive_cnt; }
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Return total number of signal-handler activations
|
|
|
|
*/
|
2013-01-10 20:56:48 +01:00
|
|
|
unsigned activation_cnt() const { return _activation_cnt; }
|
2011-12-22 16:19:25 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Counter for generating unique signal-handler IDs
|
|
|
|
*/
|
|
|
|
unsigned Handler::_id_cnt = 0;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Counter for enumerating the tests
|
|
|
|
*/
|
|
|
|
static unsigned test_cnt = 0;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Timer connection to be used by the main program
|
|
|
|
*/
|
|
|
|
static Timer::Connection timer;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Connection to CAP service used for allocating signal-context capabilities
|
|
|
|
*/
|
|
|
|
static Genode::Cap_connection cap;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Symbolic error codes
|
|
|
|
*/
|
|
|
|
class Test_failed { };
|
|
|
|
class Test_failed_with_unequal_sent_and_received_signals : public Test_failed { };
|
|
|
|
class Test_failed_with_unequal_activation_of_handlers : public Test_failed { };
|
|
|
|
|
|
|
|
|
|
|
|
class Id_signal_context : public Signal_context
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
|
|
|
int _id;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
Id_signal_context(int id) : _id(id) { }
|
|
|
|
|
2013-01-10 20:56:48 +01:00
|
|
|
int id() const { return _id; }
|
2011-12-22 16:19:25 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test for reliable notification delivery
|
|
|
|
*
|
|
|
|
* For this test, the produce more notification than that can be handled.
|
|
|
|
* Still, the total number of notifications gets transmitted because of the
|
|
|
|
* batching of notifications in one signal. This test fails if the number of
|
|
|
|
* submitted notifications on the sender side does not match the number of
|
|
|
|
* notifications received at the signal handler.
|
|
|
|
*/
|
|
|
|
static void fast_sender_test()
|
|
|
|
{
|
|
|
|
enum { SPEED = 10 };
|
|
|
|
enum { TEST_DURATION = 50*SPEED };
|
|
|
|
enum { HANDLER_INTERVAL = 10*SPEED };
|
|
|
|
enum { SENDER_INTERVAL = 2*SPEED };
|
|
|
|
enum { FINISH_IDLE_TIME = 2*HANDLER_INTERVAL };
|
|
|
|
|
|
|
|
printf("\n");
|
|
|
|
printf("TEST %d: one sender, one handler, sender is faster than handler\n", ++test_cnt);
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
Signal_receiver receiver;
|
|
|
|
Id_signal_context context_123(123);
|
|
|
|
|
|
|
|
Handler *handler = new (env()->heap()) Handler(&receiver, HANDLER_INTERVAL, false);
|
|
|
|
Sender *sender = new (env()->heap()) Sender(receiver.manage(&context_123),
|
|
|
|
SENDER_INTERVAL, false);
|
|
|
|
|
|
|
|
timer.msleep(TEST_DURATION);
|
|
|
|
|
|
|
|
/* stop emitting signals */
|
|
|
|
printf("deactivate sender\n");
|
|
|
|
sender->idle();
|
|
|
|
timer.msleep(FINISH_IDLE_TIME);
|
|
|
|
|
|
|
|
printf("\n");
|
|
|
|
printf("sender submitted a total of %d signals\n", sender->submit_cnt());
|
|
|
|
printf("handler received a total of %d signals\n", handler->receive_cnt());
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
if (sender->submit_cnt() != handler->receive_cnt())
|
|
|
|
throw Test_failed();
|
|
|
|
|
|
|
|
destroy(env()->heap(), sender);
|
|
|
|
destroy(env()->heap(), handler);
|
|
|
|
|
|
|
|
printf("TEST %d FINISHED\n", test_cnt);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fairness test if multiple signal-handler threads are present at one receiver
|
|
|
|
*
|
|
|
|
* We expect that all handler threads get activated in a fair manner. The test
|
|
|
|
* fails if the number of activations per handler differs by more than one.
|
|
|
|
* Furthermore, if operating in non-descrete mode, the total number of sent and
|
|
|
|
* handled notifications is checked.
|
|
|
|
*/
|
|
|
|
static void multiple_handlers_test()
|
|
|
|
{
|
|
|
|
enum { SPEED = 10 };
|
|
|
|
enum { TEST_DURATION = 50*SPEED };
|
|
|
|
enum { HANDLER_INTERVAL = 8*SPEED };
|
|
|
|
enum { SENDER_INTERVAL = 1*SPEED };
|
|
|
|
enum { FINISH_IDLE_TIME = 2*HANDLER_INTERVAL };
|
|
|
|
enum { NUM_HANDLERS = 4 };
|
|
|
|
|
|
|
|
printf("\n");
|
|
|
|
printf("TEST %d: one busy sender, %d handlers\n",
|
|
|
|
++test_cnt, NUM_HANDLERS);
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
Signal_receiver receiver;
|
|
|
|
|
|
|
|
Handler *handler[NUM_HANDLERS];
|
|
|
|
for (int i = 0; i < NUM_HANDLERS; i++)
|
|
|
|
handler[i] = new (env()->heap()) Handler(&receiver, HANDLER_INTERVAL);
|
|
|
|
|
|
|
|
Id_signal_context context_123(123);
|
|
|
|
Sender *sender = new (env()->heap()) Sender(receiver.manage(&context_123), SENDER_INTERVAL);
|
|
|
|
|
|
|
|
timer.msleep(TEST_DURATION);
|
|
|
|
|
|
|
|
/* stop emitting signals */
|
|
|
|
printf("stop generating new notifications\n");
|
|
|
|
sender->idle();
|
|
|
|
timer.msleep(FINISH_IDLE_TIME);
|
|
|
|
|
|
|
|
/* let handlers settle down */
|
|
|
|
for (int i = 0; i < NUM_HANDLERS; i++)
|
|
|
|
handler[i]->idle();
|
|
|
|
timer.msleep(FINISH_IDLE_TIME);
|
|
|
|
|
|
|
|
/* print signal delivery statistics */
|
|
|
|
printf("\n");
|
|
|
|
printf("sender submitted a total of %d signals\n",
|
|
|
|
sender->submit_cnt());
|
|
|
|
unsigned total_receive_cnt = 0;
|
|
|
|
for (int i = 0; i < NUM_HANDLERS; i++) {
|
|
|
|
printf("handler %d received a total of %d signals\n",
|
|
|
|
i, handler[i]->receive_cnt());
|
|
|
|
total_receive_cnt += handler[i]->receive_cnt();
|
|
|
|
}
|
|
|
|
printf("all handlers received a total of %d signals\n",
|
|
|
|
total_receive_cnt);
|
|
|
|
|
|
|
|
/* check if number of sent notifications match the received ones */
|
|
|
|
if (sender->submit_cnt() != total_receive_cnt)
|
|
|
|
throw Test_failed_with_unequal_sent_and_received_signals();
|
|
|
|
|
|
|
|
/* print activation statistics */
|
|
|
|
printf("\n");
|
|
|
|
for (int i = 0; i < NUM_HANDLERS; i++)
|
|
|
|
printf("handler %d was activated %d times\n",
|
|
|
|
i, handler[i]->activation_cnt());
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
/* check if handlers had been activated equally (tolerating a difference of one) */
|
|
|
|
for (int i = 0; i < NUM_HANDLERS; i++) {
|
|
|
|
|
|
|
|
int diff = handler[0]->activation_cnt()
|
|
|
|
- handler[(i + 1)/NUM_HANDLERS]->activation_cnt();
|
|
|
|
|
|
|
|
if (abs(diff) > 1)
|
|
|
|
throw Test_failed_with_unequal_activation_of_handlers();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* cleanup */
|
|
|
|
destroy(env()->heap(), sender);
|
|
|
|
for (int i = 0; i < NUM_HANDLERS; i++)
|
|
|
|
destroy(env()->heap(), handler[i]);
|
|
|
|
|
|
|
|
printf("TEST %d FINISHED\n", test_cnt);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stress test to estimate signal throughput
|
|
|
|
*
|
|
|
|
* For this test, we disable status output and any simulated wait times.
|
|
|
|
* We produce and handle notifications as fast as possible via spinning
|
|
|
|
* loops at the sender and handler side.
|
|
|
|
*/
|
|
|
|
static void stress_test()
|
|
|
|
{
|
|
|
|
enum { SPEED = 10 };
|
|
|
|
enum { DURATION_SECONDS = 5 };
|
|
|
|
enum { FINISH_IDLE_TIME = 100*SPEED };
|
|
|
|
|
|
|
|
printf("\n");
|
|
|
|
printf("TEST %d: stress test, busy signal transmission and handling\n", ++test_cnt);
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
Signal_receiver receiver;
|
|
|
|
Id_signal_context context_123(123);
|
|
|
|
|
|
|
|
Handler *handler = new (env()->heap()) Handler(&receiver, 0, false);
|
|
|
|
Sender *sender = new (env()->heap()) Sender(receiver.manage(&context_123),
|
|
|
|
0, false);
|
|
|
|
|
|
|
|
for (int i = 1; i <= DURATION_SECONDS; i++) {
|
|
|
|
printf("%d/%d\n", i, DURATION_SECONDS);
|
|
|
|
timer.msleep(1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* stop emitting signals */
|
|
|
|
printf("deactivate sender\n");
|
|
|
|
sender->idle();
|
2012-02-10 10:09:55 +01:00
|
|
|
|
|
|
|
while (handler->receive_cnt() < sender->submit_cnt()) {
|
|
|
|
printf("waiting for signals still in flight...");
|
|
|
|
timer.msleep(FINISH_IDLE_TIME);
|
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
printf("\n");
|
|
|
|
printf("sender submitted a total of %d signals\n", sender->submit_cnt());
|
|
|
|
printf("handler received a total of %d signals\n", handler->receive_cnt());
|
|
|
|
printf("\n");
|
|
|
|
printf("processed %d notifications per second\n",
|
|
|
|
(handler->receive_cnt())/DURATION_SECONDS);
|
|
|
|
printf("handler was activated %d times per second\n",
|
|
|
|
(handler->activation_cnt())/DURATION_SECONDS);
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
if (sender->submit_cnt() != handler->receive_cnt())
|
|
|
|
throw Test_failed_with_unequal_sent_and_received_signals();
|
|
|
|
|
|
|
|
destroy(env()->heap(), sender);
|
|
|
|
destroy(env()->heap(), handler);
|
|
|
|
|
|
|
|
printf("TEST %d FINISHED\n", test_cnt);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void lazy_receivers_test()
|
|
|
|
{
|
|
|
|
printf("\n");
|
|
|
|
printf("TEST %d: lazy and out-of-order signal reception test\n", ++test_cnt);
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
Signal_receiver rec_1, rec_2;
|
|
|
|
Signal_context rec_context_1, rec_context_2;
|
|
|
|
|
|
|
|
Signal_transmitter transmitter_1(rec_1.manage(&rec_context_1));
|
|
|
|
Signal_transmitter transmitter_2(rec_2.manage(&rec_context_2));
|
|
|
|
|
|
|
|
printf("submit and receive signals with multiple receivers in order\n");
|
|
|
|
transmitter_1.submit();
|
|
|
|
transmitter_2.submit();
|
|
|
|
|
|
|
|
Signal signal = rec_1.wait_for_signal();
|
|
|
|
printf("returned from wait_for_signal for receiver 1\n");
|
|
|
|
|
|
|
|
signal = rec_2.wait_for_signal();
|
|
|
|
printf("returned from wait_for_signal for receiver 2\n");
|
|
|
|
|
|
|
|
printf("submit and receive signals with multiple receivers out of order\n");
|
|
|
|
transmitter_1.submit();
|
|
|
|
transmitter_2.submit();
|
|
|
|
|
|
|
|
signal = rec_2.wait_for_signal();
|
|
|
|
printf("returned from wait_for_signal for receiver 2\n");
|
|
|
|
|
|
|
|
signal = rec_1.wait_for_signal();
|
|
|
|
printf("returned from wait_for_signal for receiver 1\n");
|
|
|
|
|
|
|
|
printf("TEST %d FINISHED\n", test_cnt);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Try correct initialization and cleanup of receiver/context
|
|
|
|
*/
|
|
|
|
static void check_context_management()
|
|
|
|
{
|
|
|
|
Id_signal_context *context;
|
|
|
|
Signal_receiver *rec;
|
|
|
|
Signal_context_capability cap;
|
|
|
|
|
|
|
|
/* setup receiver side */
|
|
|
|
context = new (env()->heap()) Id_signal_context(321);
|
|
|
|
rec = new (env()->heap()) Signal_receiver;
|
|
|
|
cap = rec->manage(context);
|
|
|
|
|
|
|
|
/* spawn sender */
|
|
|
|
Sender *sender = new (env()->heap()) Sender(cap, 500);
|
|
|
|
|
|
|
|
/* stop sender after timeout */
|
|
|
|
timer.msleep(1000);
|
|
|
|
printf("suspend sender\n");
|
|
|
|
sender->idle();
|
|
|
|
|
|
|
|
/* collect pending signals and dissolve context from receiver */
|
|
|
|
Signal signal = rec->wait_for_signal();
|
|
|
|
printf("got %d signal(s) from %p\n", signal.num(), signal.context());
|
|
|
|
rec->dissolve(context);
|
|
|
|
|
|
|
|
/* let sender spin for some time */
|
|
|
|
printf("resume sender\n");
|
|
|
|
sender->idle(false);
|
|
|
|
timer.msleep(1000);
|
|
|
|
printf("suspend sender\n");
|
|
|
|
sender->idle();
|
|
|
|
|
|
|
|
printf("destroy sender\n");
|
|
|
|
destroy(env()->heap(), sender);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Main program
|
|
|
|
*/
|
|
|
|
int main(int, char **)
|
|
|
|
{
|
|
|
|
printf("--- signalling test ---\n");
|
|
|
|
|
|
|
|
fast_sender_test();
|
|
|
|
multiple_handlers_test();
|
|
|
|
stress_test();
|
|
|
|
lazy_receivers_test();
|
|
|
|
check_context_management();
|
|
|
|
|
|
|
|
printf("--- signalling test finished ---\n");
|
|
|
|
return 0;
|
|
|
|
}
|