23f9761297
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
119 lines
2.1 KiB
C++
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_ */
|