Remove base/timed_semaphore.h from API

The former 'Genode::Timed_semaphore' mechanism is moved to the private
part of the two remaining users, namely dde_rump and the libc. Note
there are now two private copies of 'timed_semaphore.h'. This should be
regarded as an interim step until the use of this mechanism is removed
from both users.

This patch also cleans up the mechanism from legacy Genode API calls and
global side effects (alarm-thread singleton). The test/timed_semaphore
is now located at the libports repository as it now tests a mechanism of
the libc. The former timed_semaphore library is no more.

Fixes #3121
This commit is contained in:
Norman Feske 2019-01-18 11:40:23 +01:00
parent 7b37546a4d
commit af146e7dcd
25 changed files with 365 additions and 130 deletions

View File

@ -1,4 +0,0 @@
SRC_CC = timed_semaphore.cc
LIBS = alarm
vpath timed_semaphore.cc $(REP_DIR)/src/lib/timed_semaphore

View File

@ -4,7 +4,7 @@
# These static libraries are filtered out when linking dynamically linked
# binaries.
#
BASE_LIBS += cxx timed_semaphore alarm
BASE_LIBS += cxx alarm
#
# Name of Genode's dynamic linker

View File

@ -1 +0,0 @@
2019-01-13 fda13bc38b2fc67126485df088b8a84d0a3e8ff7

View File

@ -19,7 +19,6 @@
#include <util/string.h>
#include <base/thread.h>
#include <base/heap.h>
#include <base/timed_semaphore.h>
/* base-internal includes */
#include <base/internal/unmanaged_singleton.h>
@ -711,9 +710,6 @@ void Component::construct(Genode::Env &env)
Link_map::dump();
/* FIXME: remove 'Timeout_thread' from the base library */
Timeout_thread::env(env);
binary_ready_hook_for_gdb();
/* start binary */

View File

@ -1,35 +0,0 @@
/*
* \brief Semaphore implementation with timeout facility.
* \author Stefan Kalkowski
* \date 2010-03-05
*/
/*
* Copyright (C) 2010-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <base/timed_semaphore.h>
Genode::Env *Genode::Timeout_thread::_env = nullptr;
void Genode::Timeout_thread::entry()
{
while (true) {
Signal s = _receiver.wait_for_signal();
/* handle timouts of this point in time */
Genode::Alarm_scheduler::handle(_timer.elapsed_ms());
}
}
Genode::Timeout_thread *Genode::Timeout_thread::alarm_timer()
{
static Timeout_thread _alarm_timer;
return &_alarm_timer;
}

View File

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

View File

@ -18,6 +18,7 @@
#include <base/env.h>
#include <base/heap.h>
#include <util/reconstructible.h>
#include <rump/timed_semaphore.h>
namespace Rump {
class Env;
@ -32,6 +33,7 @@ class Rump::Env
private:
Genode::Env &_env;
Timeout_entrypoint _timeout_ep { _env };
Genode::Heap _heap { _env.ram(), _env.rm() };
Genode::Attached_rom_dataspace _config { _env, "config" };
@ -40,6 +42,7 @@ class Rump::Env
Env(Genode::Env &env) : _env(env) { }
Genode::Env &env() { return _env; }
Timeout_entrypoint &timeout_ep() { return _timeout_ep; }
Genode::Heap &heap() { return _heap; }
Genode::Attached_rom_dataspace &config_rom() { return _config; }
};

View File

@ -0,0 +1,256 @@
/*
* \brief Semaphore implementation with timeout facility
* \author Stefan Kalkowski
* \date 2010-03-05
*
* This semaphore implementation allows to block on a semaphore for a
* given time instead of blocking indefinetely.
*
* For the timeout functionality the alarm framework is used.
*/
/*
* Copyright (C) 2010-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__RUMP__TIMED_SEMAPHORE_H_
#define _INCLUDE__RUMP__TIMED_SEMAPHORE_H_
#include <base/thread.h>
#include <base/semaphore.h>
#include <base/alarm.h>
#include <timer_session/connection.h>
using Genode::Exception;
using Genode::Entrypoint;
using Genode::Alarm;
using Genode::Alarm_scheduler;
using Genode::Semaphore;
using Genode::Signal_handler;
/**
* Exception types
*/
class Timeout_exception : public Exception { };
class Nonblocking_exception : public Exception { };
/**
* Alarm thread, which counts jiffies and triggers timeout events.
*/
class Timeout_entrypoint : private Entrypoint
{
private:
enum { JIFFIES_STEP_MS = 10 };
Alarm_scheduler _alarm_scheduler { };
Timer::Connection _timer;
Signal_handler<Timeout_entrypoint> _timer_handler;
void _handle_timer() { _alarm_scheduler.handle(_timer.elapsed_ms()); }
static Genode::size_t constexpr STACK_SIZE = 2048*sizeof(long);
public:
Timeout_entrypoint(Genode::Env &env)
:
Entrypoint(env, STACK_SIZE, "alarm-timer", Genode::Affinity::Location()),
_timer(env),
_timer_handler(*this, *this, &Timeout_entrypoint::_handle_timer)
{
_timer.sigh(_timer_handler);
_timer.trigger_periodic(JIFFIES_STEP_MS*1000);
}
Alarm::Time time(void) { return _timer.elapsed_ms(); }
void schedule_absolute(Alarm &alarm, Alarm::Time timeout)
{
_alarm_scheduler.schedule_absolute(&alarm, timeout);
}
void discard(Alarm &alarm) { _alarm_scheduler.discard(&alarm); }
};
/**
* Semaphore with timeout on down operation.
*/
class Timed_semaphore : public Semaphore
{
private:
typedef Semaphore::Element Element;
Timeout_entrypoint &_timeout_ep;
/**
* Aborts blocking on the semaphore, raised when a timeout occured.
*
* \param element the waiting-queue element associated with a timeout.
* \return true if a thread was aborted/woken up
*/
bool _abort(Element &element)
{
Genode::Lock::Guard lock_guard(Semaphore::_meta_lock);
/* potentially, the queue is empty */
if (++Semaphore::_cnt <= 0) {
/*
* Iterate through the queue and find the thread,
* with the corresponding timeout.
*/
Element *first = Semaphore::_queue.dequeue();
Element *e = first;
while (true) {
/*
* Wakeup the thread.
*/
if (&element == e) {
e->wake_up();
return true;
}
/*
* Noninvolved threads are enqueued again.
*/
Semaphore::_queue.enqueue(e);
e = Semaphore::_queue.dequeue();
/*
* Maybe, the alarm was triggered just after the corresponding
* thread was already dequeued, that's why we have to track
* whether we processed the whole queue.
*/
if (e == first)
break;
}
}
/* The right element was not found, so decrease counter again */
--Semaphore::_cnt;
return false;
}
/**
* Represents a timeout associated with the blocking
* operation on a semaphore.
*/
class Timeout : public Alarm
{
private:
Timed_semaphore &_sem; /* semaphore we block on */
Element &_element; /* queue element timeout belongs to */
bool _triggered { false };
Time const _start;
public:
Timeout(Time start, Timed_semaphore &s, Element &e)
: _sem(s), _element(e), _triggered(false), _start(start)
{ }
bool triggered(void) { return _triggered; }
Time start() { return _start; }
protected:
bool on_alarm(unsigned) override
{
_triggered = _sem._abort(_element);
return false;
}
};
public:
/**
* Constructor
*
* \param n initial counter value of the semphore
*/
Timed_semaphore(Timeout_entrypoint &timeout_ep, int n = 0)
: Semaphore(n), _timeout_ep(timeout_ep) { }
/**
* Decrements semaphore and blocks when it's already zero.
*
* \param t after t milliseconds of blocking a Timeout_exception is thrown.
* if t is zero do not block, instead raise an
* Nonblocking_exception.
* \return milliseconds the caller was blocked
*/
Alarm::Time down(Alarm::Time t)
{
Semaphore::_meta_lock.lock();
if (--Semaphore::_cnt < 0) {
/* If t==0 we shall not block */
if (t == 0) {
++_cnt;
Semaphore::_meta_lock.unlock();
throw Nonblocking_exception();
}
/*
* Create semaphore queue element representing the thread
* in the wait queue.
*/
Element queue_element;
Semaphore::_queue.enqueue(&queue_element);
Semaphore::_meta_lock.unlock();
/* Create the timeout */
Alarm::Time const curr_time = _timeout_ep.time();
Timeout timeout(curr_time, *this, queue_element);
_timeout_ep.schedule_absolute(timeout, curr_time + t);
/*
* The thread is going to block on a local lock now,
* waiting for getting waked from another thread
* calling 'up()'
* */
queue_element.block();
/* Deactivate timeout */
_timeout_ep.discard(timeout);
/*
* When we were only woken up, because of a timeout,
* throw an exception.
*/
if (timeout.triggered())
throw Timeout_exception();
/* return blocking time */
return _timeout_ep.time() - timeout.start();
} else {
Semaphore::_meta_lock.unlock();
}
return 0;
}
/********************************
** Base class implementations **
********************************/
void down() { Semaphore::down(); }
void up() { Semaphore::up(); }
};
#endif /* _INCLUDE__RUMP__TIMED_SEMAPHORE_H_ */

View File

@ -14,7 +14,7 @@
#include <rump/env.h>
/*
* Genode nviroment instance
* Genode enviroment instance
*/
static Genode::Constructible<Rump::Env> _env;

View File

@ -16,7 +16,6 @@
#include <base/log.h>
#include <base/sleep.h>
#include <base/timed_semaphore.h>
#include <rump/env.h>
#include <util/allocator_fap.h>
#include <util/random.h>
@ -127,13 +126,6 @@ int rumpuser_init(int version, const struct rumpuser_hyperup *hyp)
main_context()->thread(Genode::Thread::myself());
Hard_context_registry::r().insert(main_context());
/*
* Start 'Timeout_thread' so it does not get constructed concurrently (which
* causes one thread to spin in cxa_guard_aqcuire), making emulation *really*
* slow
*/
Genode::Timeout_thread::alarm_timer();
return 0;
}

View File

@ -17,7 +17,6 @@ extern "C" {
}
#include <base/lock.h>
#include <util/fifo.h>
#include <base/timed_semaphore.h>
#include <rump/env.h>
#include "sched.h"
@ -215,7 +214,7 @@ struct Cond
int num_waiters;
int num_signallers;
Genode::Lock counter_lock;
Genode::Timed_semaphore signal_sem;
Timed_semaphore signal_sem { Rump::env().timeout_ep() };
Genode::Semaphore handshake_sem;
Cond() : num_waiters(0), num_signallers(0) { }

View File

@ -0,0 +1 @@
2019-01-21 fe6ffef91c2261bc4f7476c9ef842c86178ddef3

View File

@ -1,7 +1,7 @@
SRC_DIR = src/test/timed_semaphore src/lib/timed_semaphore src/lib/alarm
SRC_DIR = src/test/timed_semaphore
include $(GENODE_DIR)/repos/base/recipes/src/content.inc
MIRROR_FROM_REP_DIR := lib/mk/timed_semaphore.mk lib/mk/alarm.mk
MIRROR_FROM_REP_DIR := src/lib/libc/timed_semaphore.h
content: $(MIRROR_FROM_REP_DIR)

View File

@ -0,0 +1 @@
2019-01-21 ca889298ade8aca17f07ae66e5200b642f531f28

View File

@ -87,7 +87,7 @@ set config {
<provides> <service name="Timer"/> </provides>
</start>
<start name="test-libc_vfs" caps="200">
<resource name="RAM" quantum="8M"/>
<resource name="RAM" quantum="10M"/>
<config>
<iterations value="1"/>}
append_if [have_include "power_on/qemu"] config {

View File

@ -15,7 +15,6 @@
#include <base/log.h>
#include <base/sleep.h>
#include <base/thread.h>
#include <base/timed_semaphore.h>
#include <util/list.h>
#include <errno.h>
@ -23,6 +22,7 @@
#include <stdlib.h> /* malloc, free */
#include "thread.h"
#include "task.h"
#include "timed_semaphore.h"
using namespace Genode;
@ -31,6 +31,24 @@ void * operator new(__SIZE_TYPE__ size) { return malloc(size); }
void operator delete (void * p) { return free(p); }
static Env *_env_ptr; /* solely needed to spawn the timeout thread for the
timed semaphore */
namespace Libc { void init_pthread_support(Env &env) { _env_ptr = &env; } }
static Libc::Timeout_entrypoint &_global_timeout_ep()
{
class Missing_call_of_init_pthread_support { };
if (!_env_ptr)
throw Missing_call_of_init_pthread_support();
static Libc::Timeout_entrypoint timeout_ep { *_env_ptr };
return timeout_ep;
}
/*
* We initialize the main-thread pointer in a constructor depending on the
* assumption that libpthread is loaded on application startup by ldso. During
@ -72,7 +90,7 @@ void pthread::join(void **retval)
{
retry = !_thread._exiting;
return retry;
}
}
} check(*this);
do {
@ -579,7 +597,7 @@ extern "C" {
int num_waiters;
int num_signallers;
Lock counter_lock;
Timed_semaphore signal_sem;
Libc::Timed_semaphore signal_sem { _global_timeout_ep() };
Semaphore handshake_sem;
pthread_cond() : num_waiters(0), num_signallers(0) { }
@ -712,9 +730,9 @@ extern "C" {
try {
c->signal_sem.down(timeout);
} catch (Timeout_exception) {
} catch (Libc::Timeout_exception) {
result = ETIMEDOUT;
} catch (Genode::Nonblocking_exception) {
} catch (Libc::Nonblocking_exception) {
errno = ETIMEDOUT;
result = ETIMEDOUT;
}

View File

@ -224,4 +224,7 @@ struct pthread : Genode::Noncopyable, Genode::Thread::Tls::Base
size_t stack_size() const { return _stack_size; }
};
namespace Libc { void init_pthread_support(Env &env); }
#endif /* _INCLUDE__SRC_LIB_PTHREAD_THREAD_H_ */

View File

@ -16,86 +16,87 @@
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__BASE__TIMED_SEMAPHORE_H_
#define _INCLUDE__BASE__TIMED_SEMAPHORE_H_
#ifndef _TIMED_SEMAPHORE_H_
#define _TIMED_SEMAPHORE_H_
#include <base/thread.h>
#include <base/semaphore.h>
#include <base/alarm.h>
#include <timer_session/connection.h>
namespace Genode {
namespace Libc {
class Timeout_thread;
using namespace Genode;
class Timeout_entrypoint;
class Timed_semaphore;
/**
* Exception types
*/
class Timeout_exception;
class Nonblocking_exception;
class Timeout_exception : public Exception { };
class Nonblocking_exception : public Exception { };
}
/**
* Alarm thread, which counts jiffies and triggers timeout events.
*/
class Genode::Timeout_thread : public Thread_deprecated<2048*sizeof(long)>,
private Alarm_scheduler
class Libc::Timeout_entrypoint : private Entrypoint
{
private:
enum { JIFFIES_STEP_MS = 10 };
static Genode::Env *_env;
Alarm_scheduler _alarm_scheduler { };
Timer::Connection _timer { *_env };
Signal_context _context { };
Signal_receiver _receiver { };
Timer::Connection _timer;
void entry(void);
Signal_handler<Timeout_entrypoint> _timer_handler;
void _handle_timer() { _alarm_scheduler.handle(_timer.elapsed_ms()); }
static size_t constexpr STACK_SIZE = 2048*sizeof(long);
public:
using Alarm_scheduler::schedule_absolute;
using Alarm_scheduler::discard;
Timeout_thread() : Thread_deprecated("alarm-timer")
Timeout_entrypoint(Genode::Env &env)
:
Entrypoint(env, STACK_SIZE, "alarm-timer", Affinity::Location()),
_timer(env),
_timer_handler(*this, *this, &Timeout_entrypoint::_handle_timer)
{
_timer.sigh(_receiver.manage(&_context));
_timer.sigh(_timer_handler);
_timer.trigger_periodic(JIFFIES_STEP_MS*1000);
start();
}
Genode::Alarm::Time time(void) { return _timer.elapsed_ms(); }
Alarm::Time time(void) { return _timer.elapsed_ms(); }
/*
* Returns the singleton timeout-thread used for all timeouts.
*/
static Timeout_thread *alarm_timer();
void schedule_absolute(Alarm &alarm, Alarm::Time timeout)
{
_alarm_scheduler.schedule_absolute(&alarm, timeout);
}
static void env(Genode::Env &env) { _env = &env; }
void discard(Alarm &alarm) { _alarm_scheduler.discard(&alarm); }
};
class Genode::Timeout_exception : public Exception { };
class Genode::Nonblocking_exception : public Exception { };
/**
* Semaphore with timeout on down operation.
*/
class Genode::Timed_semaphore : public Semaphore
class Libc::Timed_semaphore : public Semaphore
{
private:
typedef Semaphore::Element Element;
Timeout_entrypoint &_timeout_ep;
/**
* Aborts blocking on the semaphore, raised when a timeout occured.
*
* \param element the waiting-queue element associated with a timeout.
* \return true if a thread was aborted/woken up
* \return true if a thread was aborted/woken up
*/
bool _abort(Element &element)
{
@ -151,22 +152,17 @@ class Genode::Timed_semaphore : public Semaphore
{
private:
Timed_semaphore &_sem; /* semaphore we block on */
Element &_element; /* queue element timeout belongs to */
bool _triggered; /* timeout expired */
Time _start { };
Timed_semaphore &_sem; /* semaphore we block on */
Element &_element; /* queue element timeout belongs to */
bool _triggered { false };
Time const _start;
public:
Timeout(Time duration, Timed_semaphore *s, Element *e)
: _sem(*s), _element(*e), _triggered(false)
{
Timeout_thread *tt = Timeout_thread::alarm_timer();
_start = tt->time();
tt->schedule_absolute(this, _start + duration);
}
Timeout(Time start, Timed_semaphore &s, Element &e)
: _sem(s), _element(e), _triggered(false), _start(start)
{ }
void discard(void) { Timeout_thread::alarm_timer()->discard(this); }
bool triggered(void) { return _triggered; }
Time start() { return _start; }
@ -186,7 +182,8 @@ class Genode::Timed_semaphore : public Semaphore
*
* \param n initial counter value of the semphore
*/
Timed_semaphore(int n = 0) : Semaphore(n) { }
Timed_semaphore(Timeout_entrypoint &timeout_ep, int n = 0)
: Semaphore(n), _timeout_ep(timeout_ep) { }
/**
* Decrements semaphore and blocks when it's already zero.
@ -206,7 +203,7 @@ class Genode::Timed_semaphore : public Semaphore
if (t == 0) {
++_cnt;
Semaphore::_meta_lock.unlock();
throw Genode::Nonblocking_exception();
throw Nonblocking_exception();
}
/*
@ -218,7 +215,9 @@ class Genode::Timed_semaphore : public Semaphore
Semaphore::_meta_lock.unlock();
/* Create the timeout */
Timeout to(t, this, &queue_element);
Alarm::Time const curr_time = _timeout_ep.time();
Timeout timeout(curr_time, *this, queue_element);
_timeout_ep.schedule_absolute(timeout, curr_time + t);
/*
* The thread is going to block on a local lock now,
@ -228,17 +227,18 @@ class Genode::Timed_semaphore : public Semaphore
queue_element.block();
/* Deactivate timeout */
to.discard();
_timeout_ep.discard(timeout);
/*
* When we were only woken up, because of a timeout,
* throw an exception.
*/
if (to.triggered())
throw Genode::Timeout_exception();
if (timeout.triggered())
throw Timeout_exception();
/* return blocking time */
return Timeout_thread::alarm_timer()->time() - to.start();
return _timeout_ep.time() - timeout.start();
} else {
Semaphore::_meta_lock.unlock();
}
@ -254,4 +254,4 @@ class Genode::Timed_semaphore : public Semaphore
void up() { Semaphore::up(); }
};
#endif /* _INCLUDE__BASE__TIMED_SEMAPHORE_H_ */
#endif /* _TIMED_SEMAPHORE_H_ */

View File

@ -1,5 +1,5 @@
/*
* \brief Test for the timed-semaphore
* \brief Test for the libc-internal timed semaphore
* \author Stefan Kalkowski
* \author Martin Stein
* \date 2010-03-05
@ -14,24 +14,28 @@
/* Genode includes */
#include <timer_session/connection.h>
#include <base/timed_semaphore.h>
#include <base/thread.h>
#include <base/component.h>
/* libc-internal include */
#include <timed_semaphore.h>
using namespace Genode;
using namespace Libc;
struct Test : Thread
{
struct Failed : Exception { };
unsigned id;
Timer::Connection wakeup_timer;
unsigned const wakeup_period;
Timed_semaphore sem { };
bool stop_wakeup { false };
Lock wakeup_stopped { Lock::LOCKED };
bool got_timeouts { false };
Timeout_entrypoint timeout_ep;
unsigned id;
Timer::Connection wakeup_timer;
unsigned const wakeup_period;
Timed_semaphore sem { timeout_ep };
bool stop_wakeup { false };
Lock wakeup_stopped { Lock::LOCKED };
bool got_timeouts { false };
void entry()
{
@ -43,8 +47,10 @@ struct Test : Thread
}
Test(Env &env, bool timeouts, unsigned id, char const *brief)
: Thread(env, "wakeup", 1024 * sizeof(addr_t)), id(id), wakeup_timer(env),
wakeup_period(timeouts ? 1000 : 100)
:
Thread(env, "wakeup", 1024 * sizeof(addr_t)),
timeout_ep(env), id(id), wakeup_timer(env),
wakeup_period(timeouts ? 1000 : 100)
{
log("\nTEST ", id, ": ", brief, "\n");
Thread::start();

View File

@ -0,0 +1,4 @@
TARGET = test-timed_semaphore
SRC_CC = main.cc
LIBS = base
INC_DIR += $(REP_DIR)/src/lib/libc

View File

@ -1 +0,0 @@
2019-01-13 08224b7c04f8a7cbad27286820d81df43c4402ea