genode/repos/base/include/base/semaphore.h
Stefan Kalkowski 23f9761297 base: minimize critical section in Semaphore::up
When unblocking a thread in Semaphore::up() while holding the fifo meta-data
lock, it might happen that the lock holder gets destroyed by the one it was
unblocking. This happened for instance in the pthread test in the past, where
thread destruction was synchronized via a semaphore. There is no need to hold
the lock during the unblock operation, so we should do it outside the critical
section.

Fix #1333
2015-12-10 13:16:27 +01:00

119 lines
2.1 KiB
C++

/*
* \brief Semaphore
* \author Norman Feske
* \author Christian Prochaska
* \date 2006-09-22
*/
/*
* Copyright (C) 2006-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#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
*/
element = _queue.dequeue();
}
/* 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_ */