dde_rump: dispatch I/O signals in Timed_semaphore

Issue #4459
This commit is contained in:
Christian Prochaska 2022-04-06 22:09:05 +02:00 committed by Christian Helmuth
parent af80ecb651
commit 95aba3feef
6 changed files with 314 additions and 707 deletions

View File

@ -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 \

View File

@ -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_ */

View File

@ -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; }
};
/**

View File

@ -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_ */

View File

@ -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);
}

View File

@ -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();