2011-12-22 15:19:25 +00:00
|
|
|
/*
|
|
|
|
* \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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2017-02-20 12:23:52 +00:00
|
|
|
* Copyright (C) 2010-2017 Genode Labs GmbH
|
2011-12-22 15:19:25 +00:00
|
|
|
*
|
|
|
|
* This file is part of the Genode OS framework, which is distributed
|
2017-02-20 12:23:52 +00:00
|
|
|
* under the terms of the GNU Affero General Public License version 3.
|
2011-12-22 15:19:25 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef _INCLUDE__OS__TIMED_SEMAPHORE_H_
|
|
|
|
#define _INCLUDE__OS__TIMED_SEMAPHORE_H_
|
|
|
|
|
|
|
|
#include <base/thread.h>
|
|
|
|
#include <base/semaphore.h>
|
|
|
|
#include <timer_session/connection.h>
|
|
|
|
#include <os/alarm.h>
|
|
|
|
|
|
|
|
namespace Genode {
|
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
class Timeout_thread;
|
|
|
|
class Timed_semaphore;
|
|
|
|
|
2011-12-22 15:19:25 +00:00
|
|
|
/**
|
2015-03-04 20:12:14 +00:00
|
|
|
* Exception types
|
2011-12-22 15:19:25 +00:00
|
|
|
*/
|
2015-03-04 20:12:14 +00:00
|
|
|
class Timeout_exception;
|
|
|
|
class Nonblocking_exception;
|
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2013-07-18 08:43:28 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Alarm thread, which counts jiffies and triggers timeout events.
|
|
|
|
*/
|
2016-07-19 12:45:22 +00:00
|
|
|
class Genode::Timeout_thread : public Thread_deprecated<2048*sizeof(long)>,
|
2015-03-04 20:12:14 +00:00
|
|
|
public Alarm_scheduler
|
|
|
|
{
|
|
|
|
private:
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
enum { JIFFIES_STEP_MS = 10 };
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2017-05-23 13:10:20 +00:00
|
|
|
static Genode::Env *_env;
|
|
|
|
|
|
|
|
Timer::Connection _timer { *_env }; /* timer session */
|
2015-03-04 20:12:14 +00:00
|
|
|
Signal_context _context;
|
|
|
|
Signal_receiver _receiver;
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
void entry(void);
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
public:
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2016-07-19 12:45:22 +00:00
|
|
|
Timeout_thread() : Thread_deprecated("alarm-timer")
|
2015-03-04 20:12:14 +00:00
|
|
|
{
|
|
|
|
_timer.sigh(_receiver.manage(&_context));
|
|
|
|
_timer.trigger_periodic(JIFFIES_STEP_MS*1000);
|
|
|
|
start();
|
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
Genode::Alarm::Time time(void) { return _timer.elapsed_ms(); }
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/*
|
|
|
|
* Returns the singleton timeout-thread used for all timeouts.
|
|
|
|
*/
|
|
|
|
static Timeout_thread *alarm_timer();
|
2017-05-23 13:10:20 +00:00
|
|
|
|
|
|
|
static void env(Genode::Env &env) { _env = &env; }
|
2015-03-04 20:12:14 +00:00
|
|
|
};
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
class Genode::Timeout_exception : public Exception { };
|
|
|
|
class Genode::Nonblocking_exception : public Exception { };
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/**
|
|
|
|
* Semaphore with timeout on down operation.
|
|
|
|
*/
|
|
|
|
class Genode::Timed_semaphore : public Semaphore
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
2015-03-16 15:44:11 +00:00
|
|
|
typedef Semaphore::Element Element;
|
2015-03-04 20:12:14 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
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) {
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
/*
|
2015-03-04 20:12:14 +00:00
|
|
|
* Wakeup the thread.
|
2011-12-22 15:19:25 +00:00
|
|
|
*/
|
2015-03-04 20:12:14 +00:00
|
|
|
if (element == e) {
|
|
|
|
e->wake_up();
|
|
|
|
return true;
|
2011-12-22 15:19:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2015-03-04 20:12:14 +00:00
|
|
|
* Noninvolved threads are enqueued again.
|
2011-12-22 15:19:25 +00:00
|
|
|
*/
|
2015-03-04 20:12:14 +00:00
|
|
|
Semaphore::_queue.enqueue(e);
|
|
|
|
e = Semaphore::_queue.dequeue();
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
/*
|
2015-03-04 20:12:14 +00:00
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/* 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; /* Timeout expired */
|
|
|
|
Time _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);
|
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
void discard(void) { Timeout_thread::alarm_timer()->discard(this); }
|
|
|
|
bool triggered(void) { return _triggered; }
|
|
|
|
Time start() { return _start; }
|
2013-07-18 08:43:28 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
protected:
|
|
|
|
|
|
|
|
bool on_alarm(unsigned) override
|
|
|
|
{
|
|
|
|
/* Abort blocking operation */
|
|
|
|
_triggered = _sem->_abort(_element);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*
|
|
|
|
* \param n initial counter value of the semphore
|
|
|
|
*/
|
|
|
|
Timed_semaphore(int n = 0) : Semaphore(n) { }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
2011-12-22 15:19:25 +00:00
|
|
|
Semaphore::_meta_lock.unlock();
|
2015-03-04 20:12:14 +00:00
|
|
|
throw Genode::Nonblocking_exception();
|
2011-12-22 15:19:25 +00:00
|
|
|
}
|
2015-03-04 20:12:14 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 */
|
|
|
|
Timeout to(t, this, &queue_element);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 */
|
|
|
|
to.discard();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When we were only woken up, because of a timeout,
|
|
|
|
* throw an exception.
|
|
|
|
*/
|
|
|
|
if (to.triggered())
|
|
|
|
throw Genode::Timeout_exception();
|
|
|
|
|
|
|
|
/* return blocking time */
|
|
|
|
return Timeout_thread::alarm_timer()->time() - to.start();
|
|
|
|
} else {
|
|
|
|
Semaphore::_meta_lock.unlock();
|
2011-12-22 15:19:25 +00:00
|
|
|
}
|
2015-03-04 20:12:14 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
/********************************
|
|
|
|
** Base class implementations **
|
|
|
|
********************************/
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2015-03-04 20:12:14 +00:00
|
|
|
void down() { Semaphore::down(); }
|
|
|
|
void up() { Semaphore::up(); }
|
|
|
|
};
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
#endif /* _INCLUDE__OS__TIMED_SEMAPHORE_H_ */
|