genode/repos/base/include/base/semaphore.h

120 lines
2.2 KiB
C++

/*
* \brief Semaphore
* \author Norman Feske
* \author Christian Prochaska
* \date 2006-09-22
*/
/*
* Copyright (C) 2006-2019 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__SEMAPHORE_H_
#define _INCLUDE__BASE__SEMAPHORE_H_
#include <base/lock.h>
#include <util/fifo.h>
namespace Genode { class Semaphore; }
class Genode::Semaphore
{
protected:
int _cnt;
Lock _meta_lock { };
struct Element : Fifo<Element>::Element
{
Lock lock { Lock::LOCKED };
void block() { lock.lock(); }
void wake_up() { lock.unlock(); }
};
Fifo<Element> _queue { };
public:
/**
* Constructor
*
* \param n initial counter value of the semphore
*/
Semaphore(int n = 0) : _cnt(n) { }
~Semaphore()
{
/* synchronize destruction with unfinished 'up()' */
try { _meta_lock.lock(); } catch (...) { }
}
/**
* Increment semphore counter
*
* This method may wake up another thread that currently blocks on
* a 'down' call at the same semaphore.
*/
void up()
{
Element * element = nullptr;
{
Lock::Guard lock_guard(_meta_lock);
if (++_cnt > 0)
return;
/*
* Remove element from queue and wake up the corresponding
* blocking thread
*/
_queue.dequeue([&element] (Element &head) {
element = &head; });
}
/* do not hold the lock while unblocking a waiting thread */
if (element) element->wake_up();
}
/**
* Decrement semaphore counter, block if the counter reaches zero
*/
void down()
{
_meta_lock.lock();
if (--_cnt < 0) {
/*
* Create semaphore queue element representing the thread
* in the wait queue.
*/
Element queue_element;
_queue.enqueue(queue_element);
_meta_lock.unlock();
/*
* The thread is going to block on a local lock now,
* waiting for getting waked from another thread
* calling 'up()'
* */
queue_element.block();
} else {
_meta_lock.unlock();
}
}
/**
* Return current semaphore counter
*/
int cnt() { return _cnt; }
};
#endif /* _INCLUDE__BASE__SEMAPHORE_H_ */