mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-29 15:44:02 +00:00
parent
af80ecb651
commit
95aba3feef
@ -4,7 +4,7 @@ SHARED_LIB = yes
|
||||
LIBS += rump_include
|
||||
|
||||
CC_OPT += -DLIBRUMPUSER
|
||||
SRC_CC = dummies.cc hypercall.cc bootstrap.cc io.cc sync.cc env.cc alarm.cc
|
||||
SRC_CC = dummies.cc hypercall.cc bootstrap.cc io.cc sync.cc env.cc
|
||||
|
||||
CC_C_OPT += -DHAVE_PROP_DICTIONARY_T
|
||||
SRC_C = __main.c \
|
||||
|
@ -1,189 +0,0 @@
|
||||
/*
|
||||
* \brief Timed event scheduler interface
|
||||
* \date 2005-10-24
|
||||
* \author Norman Feske
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2005-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__BASE__ALARM_H_
|
||||
#define _INCLUDE__BASE__ALARM_H_
|
||||
|
||||
#include <base/mutex.h>
|
||||
|
||||
namespace Genode {
|
||||
class Alarm_scheduler;
|
||||
class Alarm;
|
||||
}
|
||||
|
||||
|
||||
class Genode::Alarm
|
||||
{
|
||||
public:
|
||||
|
||||
typedef uint64_t Time;
|
||||
|
||||
private:
|
||||
|
||||
friend class Alarm_scheduler;
|
||||
|
||||
struct Raw
|
||||
{
|
||||
Time deadline; /* next deadline */
|
||||
bool deadline_period;
|
||||
Time period; /* duration between alarms */
|
||||
|
||||
bool is_pending_at(uint64_t time, bool time_period) const;
|
||||
};
|
||||
|
||||
Mutex _dispatch_mutex { }; /* taken during handle method */
|
||||
Raw _raw { };
|
||||
int _active { 0 }; /* set to one when active */
|
||||
Alarm *_next { nullptr }; /* next alarm in alarm list */
|
||||
Alarm_scheduler *_scheduler { nullptr }; /* currently assigned scheduler */
|
||||
|
||||
void _assign(Time period,
|
||||
Time deadline,
|
||||
bool deadline_period,
|
||||
Alarm_scheduler *scheduler)
|
||||
{
|
||||
_raw.period = period;
|
||||
_raw.deadline_period = deadline_period;
|
||||
_raw.deadline = deadline;
|
||||
_scheduler = scheduler;
|
||||
}
|
||||
|
||||
void _reset() {
|
||||
_assign(0, 0, false, 0), _active = 0, _next = 0; }
|
||||
|
||||
/*
|
||||
* Noncopyable
|
||||
*/
|
||||
Alarm(Alarm const &);
|
||||
Alarm &operator = (Alarm const &);
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Method to be called on when deadline is reached
|
||||
*
|
||||
* This method must be implemented by a derived class. If the
|
||||
* return value is 'true' and the alarm is periodically scheduled,
|
||||
* the alarm is scheduled again.
|
||||
*/
|
||||
virtual bool on_alarm(uint64_t) { return false; }
|
||||
|
||||
public:
|
||||
|
||||
Alarm() { _reset(); }
|
||||
|
||||
virtual ~Alarm();
|
||||
};
|
||||
|
||||
|
||||
class Genode::Alarm_scheduler
|
||||
{
|
||||
private:
|
||||
|
||||
Mutex _mutex { }; /* protect alarm list */
|
||||
Alarm *_head { nullptr }; /* head of alarm list */
|
||||
Alarm::Time _now { 0UL }; /* recent time (updated by handle method) */
|
||||
bool _now_period { false };
|
||||
Alarm::Raw _min_handle_period { };
|
||||
|
||||
/**
|
||||
* Enqueue alarm into alarm queue
|
||||
*
|
||||
* This is a helper for 'schedule' and 'handle'.
|
||||
*/
|
||||
void _unsynchronized_enqueue(Alarm *alarm);
|
||||
|
||||
/**
|
||||
* Dequeue alarm from alarm queue
|
||||
*/
|
||||
void _unsynchronized_dequeue(Alarm *alarm);
|
||||
|
||||
/**
|
||||
* Dequeue next pending alarm from alarm list
|
||||
*
|
||||
* \return dequeued pending alarm
|
||||
* \retval 0 no alarm pending
|
||||
*/
|
||||
Alarm *_get_pending_alarm();
|
||||
|
||||
/**
|
||||
* Assign timeout values to alarm object and add it to the schedule
|
||||
*/
|
||||
void _setup_alarm(Alarm &alarm, Alarm::Time period, Alarm::Time deadline);
|
||||
|
||||
/*
|
||||
* Noncopyable
|
||||
*/
|
||||
Alarm_scheduler(Alarm_scheduler const &);
|
||||
Alarm_scheduler &operator = (Alarm_scheduler const &);
|
||||
|
||||
public:
|
||||
|
||||
Alarm_scheduler(Alarm::Time min_handle_period = 1)
|
||||
{
|
||||
Alarm::Time const deadline = _now + min_handle_period;
|
||||
_min_handle_period.period = min_handle_period;
|
||||
_min_handle_period.deadline = deadline;
|
||||
_min_handle_period.deadline_period = _now > deadline ?
|
||||
!_now_period : _now_period;
|
||||
}
|
||||
|
||||
~Alarm_scheduler();
|
||||
|
||||
/**
|
||||
* Schedule absolute timeout
|
||||
*
|
||||
* \param timeout absolute point in time for execution
|
||||
*/
|
||||
void schedule_absolute(Alarm *alarm, Alarm::Time timeout);
|
||||
|
||||
/**
|
||||
* Schedule alarm (periodic timeout)
|
||||
*
|
||||
* \param period alarm period
|
||||
*
|
||||
* The first deadline is overdue after this call, i.e. on_alarm() is
|
||||
* called immediately.
|
||||
*/
|
||||
void schedule(Alarm *alarm, Alarm::Time period);
|
||||
|
||||
/**
|
||||
* Remove alarm from schedule
|
||||
*/
|
||||
void discard(Alarm *alarm);
|
||||
|
||||
/**
|
||||
* Handle alarms
|
||||
*
|
||||
* \param now current time
|
||||
*/
|
||||
void handle(Alarm::Time now);
|
||||
|
||||
/**
|
||||
* Determine next deadline (absolute)
|
||||
*
|
||||
* \param deadline out parameter for storing the next deadline
|
||||
* \return true if an alarm is scheduled
|
||||
*/
|
||||
bool next_deadline(Alarm::Time *deadline);
|
||||
|
||||
/**
|
||||
* Determine if given alarm object is current head element
|
||||
*
|
||||
* \param alarm alarm object
|
||||
* \return true if alarm is head element of timeout queue
|
||||
*/
|
||||
bool head_timeout(const Alarm * alarm) { return _head == alarm; }
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__BASE__ALARM_H_ */
|
@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2017 Genode Labs GmbH
|
||||
* Copyright (C) 2016-2022 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.
|
||||
@ -19,6 +19,7 @@
|
||||
#include <base/heap.h>
|
||||
#include <util/reconstructible.h>
|
||||
#include <rump/timed_semaphore.h>
|
||||
#include <timer_session/connection.h>
|
||||
|
||||
namespace Rump {
|
||||
class Env;
|
||||
@ -32,19 +33,21 @@ 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" };
|
||||
Genode::Env &_env;
|
||||
Genode::Heap _heap { _env.ram(), _env.rm() };
|
||||
Genode::Attached_rom_dataspace _config { _env, "config" };
|
||||
Genode::Thread const *_ep_thread { Genode::Thread::myself() };
|
||||
Timer::Connection _timer { _env };
|
||||
|
||||
public:
|
||||
|
||||
Env(Genode::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; }
|
||||
Genode::Thread const *ep_thread() { return _ep_thread; }
|
||||
Timer::Connection &timer() { return _timer; }
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1,16 +1,15 @@
|
||||
/*
|
||||
* \brief Semaphore implementation with timeout facility
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2010-03-05
|
||||
* \author Christian Prochaska
|
||||
* \date 2022-04-06
|
||||
*
|
||||
* This semaphore implementation allows to block on a semaphore for a
|
||||
* given time instead of blocking indefinetely.
|
||||
* given time instead of blocking indefinitely.
|
||||
*
|
||||
* For the timeout functionality the alarm framework is used.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2010-2017 Genode Labs GmbH
|
||||
* Copyright (C) 2022 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.
|
||||
@ -19,242 +18,329 @@
|
||||
#ifndef _INCLUDE__RUMP__TIMED_SEMAPHORE_H_
|
||||
#define _INCLUDE__RUMP__TIMED_SEMAPHORE_H_
|
||||
|
||||
#include <base/thread.h>
|
||||
#include <base/semaphore.h>
|
||||
#include <rump/alarm.h>
|
||||
#include <base/entrypoint.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
|
||||
class Ep_blockade
|
||||
{
|
||||
private:
|
||||
|
||||
enum { JIFFIES_STEP_MS = 10 };
|
||||
Genode::Entrypoint &_ep;
|
||||
|
||||
Alarm_scheduler _alarm_scheduler { };
|
||||
Genode::Io_signal_handler<Ep_blockade> _wakeup_handler
|
||||
{ _ep, *this, &Ep_blockade::_handle_wakeup };
|
||||
|
||||
Timer::Connection _timer;
|
||||
bool _signal_handler_called { false };
|
||||
|
||||
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);
|
||||
void _handle_wakeup()
|
||||
{
|
||||
_signal_handler_called = true;
|
||||
}
|
||||
|
||||
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)
|
||||
Ep_blockade(Genode::Entrypoint &ep) : _ep(ep) { }
|
||||
|
||||
void block()
|
||||
{
|
||||
_timer.sigh(_timer_handler);
|
||||
_timer.trigger_periodic(JIFFIES_STEP_MS*1000);
|
||||
while (!_signal_handler_called)
|
||||
_ep.wait_and_dispatch_one_io_signal();
|
||||
|
||||
_signal_handler_called = false;
|
||||
}
|
||||
|
||||
Alarm::Time time(void) { return _timer.elapsed_ms(); }
|
||||
|
||||
void schedule_absolute(Alarm &alarm, Alarm::Time timeout)
|
||||
void wakeup()
|
||||
{
|
||||
_alarm_scheduler.schedule_absolute(&alarm, timeout);
|
||||
_wakeup_handler.local_submit();
|
||||
}
|
||||
|
||||
void discard(Alarm &alarm) { _alarm_scheduler.discard(&alarm); }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Semaphore with timeout on down operation.
|
||||
*/
|
||||
class Timed_semaphore : public Semaphore
|
||||
struct Timed_semaphore_blockade
|
||||
{
|
||||
virtual void block() = 0;
|
||||
virtual void wakeup() = 0;
|
||||
};
|
||||
|
||||
|
||||
class Timed_semaphore_ep_blockade : public Timed_semaphore_blockade
|
||||
{
|
||||
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::Mutex::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 = nullptr;
|
||||
Semaphore::_queue.dequeue([&first] (Element &e) {
|
||||
first = &e; });
|
||||
Element *e = first;
|
||||
|
||||
while (e) {
|
||||
|
||||
/*
|
||||
* Wakeup the thread.
|
||||
*/
|
||||
if (&element == e) {
|
||||
e->blockade.wakeup();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Noninvolved threads are enqueued again.
|
||||
*/
|
||||
Semaphore::_queue.enqueue(*e);
|
||||
e = nullptr;
|
||||
Semaphore::_queue.dequeue([&e] (Element &next) {
|
||||
e = &next; });
|
||||
|
||||
/*
|
||||
* 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(Genode::uint64_t) override
|
||||
{
|
||||
_triggered = _sem._abort(_element);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
Ep_blockade _blockade;
|
||||
|
||||
public:
|
||||
|
||||
Timed_semaphore_ep_blockade(Genode::Entrypoint &ep)
|
||||
: _blockade(ep) { }
|
||||
|
||||
void block() override
|
||||
{
|
||||
_blockade.block();
|
||||
}
|
||||
|
||||
void wakeup() override
|
||||
{
|
||||
_blockade.wakeup();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Timed_semaphore_thread_blockade : public Timed_semaphore_blockade
|
||||
{
|
||||
private:
|
||||
|
||||
Genode::Blockade _blockade;
|
||||
|
||||
public:
|
||||
|
||||
Timed_semaphore_thread_blockade() { }
|
||||
|
||||
void block() override
|
||||
{
|
||||
_blockade.block();
|
||||
}
|
||||
|
||||
void wakeup() override
|
||||
{
|
||||
_blockade.wakeup();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Timed_semaphore
|
||||
{
|
||||
public:
|
||||
|
||||
struct Down_ok { };
|
||||
struct Down_timed_out { };
|
||||
using Down_result = Genode::Attempt<Down_ok, Down_timed_out>;
|
||||
|
||||
private:
|
||||
|
||||
Genode::Env &_env;
|
||||
Genode::Thread const *_ep_thread_ptr;
|
||||
Timer::Connection &_timer;
|
||||
|
||||
int _cnt;
|
||||
Genode::Mutex _meta_mutex { };
|
||||
|
||||
struct Element : Genode::Fifo<Element>::Element
|
||||
{
|
||||
Timed_semaphore_blockade &blockade;
|
||||
int &cnt;
|
||||
Genode::Mutex &meta_mutex;
|
||||
Genode::Fifo<Element> &queue;
|
||||
|
||||
Genode::Mutex destruct_mutex { };
|
||||
Timer::One_shot_timeout<Element> timeout;
|
||||
bool wakeup_called { false };
|
||||
|
||||
void handle_timeout(Genode::Duration)
|
||||
{
|
||||
{
|
||||
Genode::Mutex::Guard guard(meta_mutex);
|
||||
|
||||
/*
|
||||
* If 'wakeup()' was called, 'Timed_semaphore::up()'
|
||||
* has already taken care of this.
|
||||
*/
|
||||
|
||||
if (!wakeup_called) {
|
||||
|
||||
cnt++;
|
||||
|
||||
/*
|
||||
* Remove element from queue so that a future 'up()'
|
||||
* does not select it for wakeup.
|
||||
*/
|
||||
queue.remove(*this);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Protect the 'blockade' member from destruction
|
||||
* until 'blockade.wakeup()' has returned.
|
||||
*/
|
||||
Genode::Mutex::Guard guard(destruct_mutex);
|
||||
|
||||
blockade.wakeup();
|
||||
}
|
||||
|
||||
Element(Timed_semaphore_blockade &blockade,
|
||||
int &cnt,
|
||||
Genode::Mutex &meta_mutex,
|
||||
Genode::Fifo<Element> &queue,
|
||||
Timer::Connection &timer,
|
||||
bool use_timeout = false,
|
||||
Genode::Microseconds timeout_us = Genode::Microseconds(0))
|
||||
: blockade(blockade),
|
||||
cnt(cnt),
|
||||
meta_mutex(meta_mutex),
|
||||
queue(queue),
|
||||
timeout(timer, *this, &Element::handle_timeout)
|
||||
{
|
||||
if (use_timeout)
|
||||
timeout.schedule(timeout_us);
|
||||
}
|
||||
|
||||
~Element()
|
||||
{
|
||||
/*
|
||||
* Synchronize destruction with unfinished
|
||||
* 'handle_timeout()' or 'wakeup()'
|
||||
*/
|
||||
Genode::Mutex::Guard guard(destruct_mutex);
|
||||
}
|
||||
|
||||
Down_result block()
|
||||
{
|
||||
blockade.block();
|
||||
|
||||
if (wakeup_called)
|
||||
return Down_ok();
|
||||
else
|
||||
return Down_timed_out();
|
||||
}
|
||||
|
||||
/* meta_mutex must be acquired when calling and is released */
|
||||
void wakeup()
|
||||
{
|
||||
/*
|
||||
* It is possible that 'handle_timeout()' is already being
|
||||
* called and waiting for the meta_mutex, so in addition to
|
||||
* discarding the timeout, the 'wakeup_called' variable is
|
||||
* set for 'handle_timeout()' (and for 'block()').
|
||||
*/
|
||||
|
||||
wakeup_called = true;
|
||||
|
||||
meta_mutex.release();
|
||||
|
||||
/*
|
||||
* 'timeout.discard()' waits until an ongoing signal
|
||||
* handler execution is finished, so meta_mutex must
|
||||
* be released at this point.
|
||||
*/
|
||||
timeout.discard();
|
||||
|
||||
/*
|
||||
* Protect the 'blockade' member from destruction
|
||||
* until 'blockade.wakeup()' has returned.
|
||||
*/
|
||||
Genode::Mutex::Guard guard(destruct_mutex);
|
||||
|
||||
blockade.wakeup();
|
||||
}
|
||||
};
|
||||
|
||||
Genode::Fifo<Element> _queue { };
|
||||
|
||||
/* _meta_mutex must be acquired when calling and is released */
|
||||
Down_result _down_internal(Timed_semaphore_blockade &blockade,
|
||||
bool use_timeout,
|
||||
Genode::Microseconds timeout_us)
|
||||
{
|
||||
/*
|
||||
* Create semaphore queue element representing the thread
|
||||
* in the wait queue and release _meta_mutex.
|
||||
*/
|
||||
Element queue_element { blockade, _cnt, _meta_mutex, _queue,
|
||||
_timer, use_timeout, timeout_us };
|
||||
_queue.enqueue(queue_element);
|
||||
_meta_mutex.release();
|
||||
|
||||
/*
|
||||
* The thread is going to block now,
|
||||
* waiting for getting woken up from another thread
|
||||
* calling 'up()' or by the timeout handler.
|
||||
*/
|
||||
return queue_element.block();
|
||||
}
|
||||
|
||||
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 env Genode environment
|
||||
* \param timer timer connection
|
||||
* \param n initial counter value of the semphore
|
||||
*
|
||||
* \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
|
||||
* Note: currently it is assumed that the constructor is called
|
||||
* by the ep thread.
|
||||
*/
|
||||
Alarm::Time down(Alarm::Time t)
|
||||
Timed_semaphore(Genode::Env &env, Genode::Thread const *ep_thread_ptr,
|
||||
Timer::Connection &timer, int n = 0)
|
||||
: _env(env), _ep_thread_ptr(ep_thread_ptr),
|
||||
_timer(timer), _cnt(n) { }
|
||||
|
||||
~Timed_semaphore()
|
||||
{
|
||||
Semaphore::_meta_lock.acquire();
|
||||
|
||||
if (--Semaphore::_cnt < 0) {
|
||||
|
||||
/* If t==0 we shall not block */
|
||||
if (t == 0) {
|
||||
++_cnt;
|
||||
Semaphore::_meta_lock.release();
|
||||
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.release();
|
||||
|
||||
/* 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.blockade.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.release();
|
||||
}
|
||||
return 0;
|
||||
/* synchronize destruction with unfinished 'up()' */
|
||||
try { _meta_mutex.acquire(); } catch (...) { }
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment semaphore counter
|
||||
*
|
||||
* This method may wake up another thread that currently blocks on
|
||||
* a 'down' call at the same semaphore.
|
||||
*/
|
||||
void up()
|
||||
{
|
||||
Element * element = nullptr;
|
||||
|
||||
/********************************
|
||||
** Base class implementations **
|
||||
********************************/
|
||||
_meta_mutex.acquire();
|
||||
|
||||
void down() { Semaphore::down(); }
|
||||
void up() { Semaphore::up(); }
|
||||
if (++_cnt > 0) {
|
||||
_meta_mutex.release();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove element from queue and wake up the corresponding
|
||||
* blocking thread
|
||||
*/
|
||||
_queue.dequeue([&element] (Element &head) {
|
||||
element = &head; });
|
||||
|
||||
if (element) {
|
||||
/* 'element->wakeup()' releases the _meta_mutex */
|
||||
element->wakeup();
|
||||
} else
|
||||
_meta_mutex.release();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement semaphore counter, block if the counter reaches zero
|
||||
*/
|
||||
Down_result down(bool use_timeout = false,
|
||||
Genode::Microseconds timeout_us = Genode::Microseconds(0))
|
||||
{
|
||||
if (use_timeout && (timeout_us.value == 0))
|
||||
return Down_timed_out();
|
||||
|
||||
_meta_mutex.acquire();
|
||||
|
||||
if (--_cnt < 0) {
|
||||
|
||||
/* _down_internal() releases _meta_mutex */
|
||||
|
||||
if (Genode::Thread::myself() == _ep_thread_ptr) {
|
||||
Timed_semaphore_ep_blockade blockade { _env.ep() };
|
||||
return _down_internal(blockade, use_timeout, timeout_us);
|
||||
} else {
|
||||
Timed_semaphore_thread_blockade blockade;
|
||||
return _down_internal(blockade, use_timeout, timeout_us);
|
||||
}
|
||||
|
||||
} else {
|
||||
_meta_mutex.release();
|
||||
return Down_ok();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__RUMP__TIMED_SEMAPHORE_H_ */
|
||||
|
@ -1,293 +0,0 @@
|
||||
/*
|
||||
* \brief Timed event scheduler
|
||||
* \date 2005-10-24
|
||||
* \author Norman Feske
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2005-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/log.h>
|
||||
#include <rump/alarm.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
void Alarm_scheduler::_unsynchronized_enqueue(Alarm *alarm)
|
||||
{
|
||||
if (alarm->_active) {
|
||||
error("trying to insert the same alarm twice!");
|
||||
return;
|
||||
}
|
||||
|
||||
alarm->_active++;
|
||||
|
||||
/* if alarmlist is empty add first element */
|
||||
if (!_head) {
|
||||
alarm->_next = 0;
|
||||
_head = alarm;
|
||||
return;
|
||||
}
|
||||
|
||||
/* if deadline is smaller than any other deadline, put it on the head */
|
||||
if (alarm->_raw.is_pending_at(_head->_raw.deadline, _head->_raw.deadline_period)) {
|
||||
alarm->_next = _head;
|
||||
_head = alarm;
|
||||
return;
|
||||
}
|
||||
|
||||
/* find list element with a higher deadline */
|
||||
Alarm *curr = _head;
|
||||
while (curr->_next &&
|
||||
curr->_next->_raw.is_pending_at(alarm->_raw.deadline, alarm->_raw.deadline_period))
|
||||
{
|
||||
curr = curr->_next;
|
||||
}
|
||||
|
||||
/* if end of list is reached, append new element */
|
||||
if (curr->_next == 0) {
|
||||
curr->_next = alarm;
|
||||
return;
|
||||
}
|
||||
|
||||
/* insert element in middle of list */
|
||||
alarm->_next = curr->_next;
|
||||
curr->_next = alarm;
|
||||
}
|
||||
|
||||
|
||||
void Alarm_scheduler::_unsynchronized_dequeue(Alarm *alarm)
|
||||
{
|
||||
if (!_head) return;
|
||||
|
||||
if (_head == alarm) {
|
||||
_head = alarm->_next;
|
||||
alarm->_reset();
|
||||
return;
|
||||
}
|
||||
|
||||
/* find predecessor in alarm queue */
|
||||
Alarm *curr;
|
||||
for (curr = _head; curr && (curr->_next != alarm); curr = curr->_next);
|
||||
|
||||
/* alarm is not enqueued */
|
||||
if (!curr) return;
|
||||
|
||||
/* remove alarm from alarm queue */
|
||||
curr->_next = alarm->_next;
|
||||
alarm->_reset();
|
||||
}
|
||||
|
||||
|
||||
bool Alarm::Raw::is_pending_at(uint64_t time, bool time_period) const
|
||||
{
|
||||
return (time_period == deadline_period &&
|
||||
time >= deadline) ||
|
||||
(time_period != deadline_period &&
|
||||
time < deadline);
|
||||
}
|
||||
|
||||
|
||||
Alarm *Alarm_scheduler::_get_pending_alarm()
|
||||
{
|
||||
Mutex::Guard guard(_mutex);
|
||||
|
||||
if (!_head || !_head->_raw.is_pending_at(_now, _now_period)) {
|
||||
return nullptr; }
|
||||
|
||||
/* remove alarm from head of the list */
|
||||
Alarm *pending_alarm = _head;
|
||||
_head = _head->_next;
|
||||
|
||||
/*
|
||||
* Acquire dispatch mutex to defer destruction until the call of 'on_alarm'
|
||||
* is finished
|
||||
*/
|
||||
pending_alarm->_dispatch_mutex.acquire();
|
||||
|
||||
/* reset alarm object */
|
||||
pending_alarm->_next = nullptr;
|
||||
pending_alarm->_active--;
|
||||
|
||||
return pending_alarm;
|
||||
}
|
||||
|
||||
|
||||
void Alarm_scheduler::handle(Alarm::Time curr_time)
|
||||
{
|
||||
/*
|
||||
* Raise the time counter and if it wraps, update also in which
|
||||
* period of the time counter we are.
|
||||
*/
|
||||
if (_now > curr_time) {
|
||||
_now_period = !_now_period;
|
||||
}
|
||||
_now = curr_time;
|
||||
|
||||
if (!_min_handle_period.is_pending_at(_now, _now_period)) {
|
||||
return;
|
||||
}
|
||||
Alarm::Time const deadline = _now + _min_handle_period.period;
|
||||
_min_handle_period.deadline = deadline;
|
||||
_min_handle_period.deadline_period = _now > deadline ?
|
||||
!_now_period : _now_period;
|
||||
|
||||
Alarm *curr;
|
||||
while ((curr = _get_pending_alarm())) {
|
||||
|
||||
uint64_t triggered = 1;
|
||||
|
||||
if (curr->_raw.period) {
|
||||
Alarm::Time deadline = curr->_raw.deadline;
|
||||
|
||||
/* schedule next event */
|
||||
if (deadline == 0)
|
||||
deadline = curr_time;
|
||||
|
||||
triggered += (curr_time - deadline) / curr->_raw.period;
|
||||
}
|
||||
|
||||
/* do not reschedule if alarm function returns 0 */
|
||||
bool reschedule = curr->on_alarm(triggered);
|
||||
|
||||
if (reschedule) {
|
||||
|
||||
/*
|
||||
* At this point, the alarm deadline normally is somewhere near
|
||||
* the current time but If the alarm had no deadline by now,
|
||||
* initialize it with the current time.
|
||||
*/
|
||||
if (curr->_raw.deadline == 0) {
|
||||
curr->_raw.deadline = _now;
|
||||
curr->_raw.deadline_period = _now_period;
|
||||
}
|
||||
/*
|
||||
* Raise the deadline value by one period of the alarm and
|
||||
* if the deadline value wraps thereby, update also in which
|
||||
* period it is located.
|
||||
*/
|
||||
Alarm::Time const deadline = curr->_raw.deadline +
|
||||
triggered * curr->_raw.period;
|
||||
if (curr->_raw.deadline > deadline) {
|
||||
curr->_raw.deadline_period = !curr->_raw.deadline_period;
|
||||
}
|
||||
curr->_raw.deadline = deadline;
|
||||
|
||||
/* synchronize enqueue operation */
|
||||
Mutex::Guard guard(_mutex);
|
||||
_unsynchronized_enqueue(curr);
|
||||
}
|
||||
|
||||
/* release alarm, resume concurrent destructor operation */
|
||||
curr->_dispatch_mutex.release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Alarm_scheduler::_setup_alarm(Alarm &alarm, Alarm::Time period, Alarm::Time deadline)
|
||||
{
|
||||
/*
|
||||
* If the alarm is already present in the queue, re-consider its queue
|
||||
* position because its deadline might have changed. I.e., if an alarm is
|
||||
* rescheduled with a new timeout before the original timeout triggered.
|
||||
*/
|
||||
if (alarm._active)
|
||||
_unsynchronized_dequeue(&alarm);
|
||||
|
||||
alarm._assign(period, deadline, _now > deadline ? !_now_period : _now_period, this);
|
||||
|
||||
_unsynchronized_enqueue(&alarm);
|
||||
}
|
||||
|
||||
|
||||
void Alarm_scheduler::schedule_absolute(Alarm *alarm, Alarm::Time timeout)
|
||||
{
|
||||
Mutex::Guard alarm_list_guard(_mutex);
|
||||
|
||||
_setup_alarm(*alarm, 0, timeout);
|
||||
}
|
||||
|
||||
|
||||
void Alarm_scheduler::schedule(Alarm *alarm, Alarm::Time period)
|
||||
{
|
||||
Mutex::Guard alarm_list_guard(_mutex);
|
||||
|
||||
/*
|
||||
* Refuse to schedule a periodic timeout of 0 because it would trigger
|
||||
* infinitely in the 'handle' function. To account for the case where the
|
||||
* alarm object was already scheduled, we make sure to remove it from the
|
||||
* queue.
|
||||
*/
|
||||
if (period == 0) {
|
||||
_unsynchronized_dequeue(alarm);
|
||||
return;
|
||||
}
|
||||
|
||||
/* first deadline is overdue */
|
||||
_setup_alarm(*alarm, period, _now);
|
||||
}
|
||||
|
||||
|
||||
void Alarm_scheduler::discard(Alarm *alarm)
|
||||
{
|
||||
/*
|
||||
* Make sure that nobody is inside the '_get_pending_alarm' when
|
||||
* grabbing the '_dispatch_mutex'. This is important when this function
|
||||
* is called from the 'Alarm' destructor. Without the '_dispatch_mutex',
|
||||
* we could take the mutex and proceed with destruction just before
|
||||
* '_get_pending_alarm' tries to grab the mutex. When the destructor is
|
||||
* finished, '_get_pending_alarm' would proceed with operating on a
|
||||
* dangling pointer.
|
||||
*/
|
||||
Mutex::Guard alarm_list_guard(_mutex);
|
||||
|
||||
if (alarm) {
|
||||
Mutex::Guard alarm_guard(alarm->_dispatch_mutex);
|
||||
_unsynchronized_dequeue(alarm);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Alarm_scheduler::next_deadline(Alarm::Time *deadline)
|
||||
{
|
||||
Mutex::Guard alarm_list_guard(_mutex);
|
||||
|
||||
if (!_head) return false;
|
||||
|
||||
if (deadline)
|
||||
*deadline = _head->_raw.deadline;
|
||||
|
||||
if (deadline && *deadline < _min_handle_period.deadline) {
|
||||
*deadline = _min_handle_period.deadline;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Alarm_scheduler::~Alarm_scheduler()
|
||||
{
|
||||
Mutex::Guard guard(_mutex);
|
||||
|
||||
while (_head) {
|
||||
|
||||
Alarm *next = _head->_next;
|
||||
|
||||
/* reset alarm object */
|
||||
_head->_reset();
|
||||
|
||||
/* remove from list */
|
||||
_head = next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Alarm::~Alarm()
|
||||
{
|
||||
if (_scheduler)
|
||||
_scheduler->discard(this);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Genode Labs GmbH
|
||||
* Copyright (C) 2014-2022 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.
|
||||
@ -18,6 +18,7 @@ extern "C" {
|
||||
#include <base/mutex.h>
|
||||
#include <util/fifo.h>
|
||||
#include <rump/env.h>
|
||||
#include <rump/timed_semaphore.h>
|
||||
|
||||
#include "sched.h"
|
||||
|
||||
@ -208,13 +209,14 @@ static uint64_t timeout_ms(struct timespec currtime,
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct Cond
|
||||
{
|
||||
int num_waiters;
|
||||
int num_signallers;
|
||||
Genode::Mutex counter_mutex;
|
||||
Timed_semaphore signal_sem { Rump::env().timeout_ep() };
|
||||
Timed_semaphore signal_sem { Rump::env().env(),
|
||||
Rump::env().ep_thread(),
|
||||
Rump::env().timer() };
|
||||
Genode::Semaphore handshake_sem;
|
||||
|
||||
Cond() : num_waiters(0), num_signallers(0) { }
|
||||
@ -237,15 +239,13 @@ struct Cond
|
||||
struct timespec currtime;
|
||||
rumpuser_clock_gettime(0, &currtime.tv_sec, &currtime.tv_nsec);
|
||||
|
||||
Alarm::Time timeout = timeout_ms(currtime, *abstime);
|
||||
try {
|
||||
signal_sem.down(timeout);
|
||||
} catch (Timeout_exception) {
|
||||
result = -2;
|
||||
}
|
||||
catch (Nonblocking_exception) {
|
||||
result = 0;
|
||||
}
|
||||
Genode::Microseconds timeout_us =
|
||||
Genode::Microseconds(timeout_ms(currtime, *abstime) * 1000);
|
||||
|
||||
signal_sem.down(true, timeout_us).with_result(
|
||||
[&] (Timed_semaphore::Down_ok) { },
|
||||
[&] (Timed_semaphore::Down_timed_out) { result = -2; }
|
||||
);
|
||||
}
|
||||
|
||||
counter_mutex.acquire();
|
||||
|
Loading…
x
Reference in New Issue
Block a user