1 /*
2 * \brief Semaphore
3 * \author Norman Feske
4 * \author Christian Prochaska
5 * \date 2006-09-22
6 */
7
8 /*
9 * Copyright (C) 2006-2013 Genode Labs GmbH
10 *
11 * This file is part of the Genode OS framework, which is distributed
12 * under the terms of the GNU General Public License version 2.
13 */
14
15 #ifndef _INCLUDE__BASE__SEMAPHORE_H_
16 #define _INCLUDE__BASE__SEMAPHORE_H_
17
18 #include <base/lock.h>
19 #include <util/fifo.h>
20
21 namespace Genode { class Semaphore; }
22
23
24 class Genode::Semaphore
25 {
26 protected:
27
28 int _cnt;
29 Lock _meta_lock;
30
31 struct Element : Fifo<Element>::Element
32 {
33 Lock lock { Lock::LOCKED };
34
35 void block() { lock.lock(); }
36 void wake_up() { lock.unlock(); }
37 };
38
39 Fifo<Element> _queue;
40
41 public:
42
43 /**
44 * Constructor
45 *
46 * \param n initial counter value of the semphore
47 */
48 Semaphore(int n = 0) : _cnt(n) { }
49
50 ~Semaphore()
51 {
52 /* synchronize destruction with unfinished `up()` */
53 try { _meta_lock.lock(); } catch (...) { }
54 }
55
56 /**
57 * Increment semphore counter
58 *
59 * This method may wake up another thread that currently blocks on
60 * a `down` call at the same semaphore.
61 */
62 void up()
63 {
64 Element * element = nullptr;
65
66 {
67 Lock::Guard lock_guard(_meta_lock);
68
69 if (++_cnt > 0)
70 return;
71
72 /*
73 * Remove element from queue and wake up the corresponding
74 * blocking thread
75 */
76 element = _queue.dequeue();
77 }
78
79 /* do not hold the lock while unblocking a waiting thread */
80 if (element) element->wake_up();
81 }
82
83 /**
84 * Decrement semaphore counter, block if the counter reaches zero
85 */
86 void down()
87 {
88 _meta_lock.lock();
89
90 if (--_cnt < 0) {
91
92 /*
93 * Create semaphore queue element representing the thread
94 * in the wait queue.
95 */
96 Element queue_element;
97 _queue.enqueue(&queue_element);
98 _meta_lock.unlock();
99
100 /*
101 * The thread is going to block on a local lock now,
102 * waiting for getting waked from another thread
103 * calling `up()`
104 * */
105 queue_element.block();
106
107 } else {
108 _meta_lock.unlock();
109 }
110 }
111
112 /**
113 * Return current semaphore counter
114 */
115 int cnt() { return _cnt; }
116 };
117
118 #endif /* _INCLUDE__BASE__SEMAPHORE_H_ */