merge exportvis branch into cairocanvas, to reduce the number of "floating" branches.
[ardour.git] / libs / pbd / pbd / semaphore.h
1 /*
2   Copyright (C) 2012 Paul Davis
3   Author: David Robillard
4
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2 of the License, or
8   (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #ifndef __pbd_semaphore_h__
21 #define __pbd_semaphore_h__
22
23 #ifdef __APPLE__
24 #    include <mach/mach.h>
25 #elif defined(_WIN32)
26 #    include <windows.h>
27 #else
28 #    include <semaphore.h>
29 #    include <errno.h>
30 #endif
31
32 #include "pbd/libpbd_visibility.h"
33 #include "pbd/failed_constructor.h"
34
35 namespace PBD {
36
37 /**
38    Unnamed (process local) counting semaphore.
39
40    The civilized person's synchronisation primitive.  A counting semaphore is
41    an integer which is always non-negative, so, an attempted decrement (or
42    "wait") will block if the value is 0, until another thread does an increment
43    (or "post").
44
45    At least on Lignux, the main advantage of this is that it is fast and the
46    only safe way to reliably signal from a real-time audio thread.  The
47    counting semantics also complement ringbuffers of events nicely.
48 */
49 class LIBPBD_API Semaphore
50 {
51 public:
52         /**
53            Create a new semaphore.
54
55            Chances are you want 1 wait() per 1 post(), an initial value of 0.
56         */
57         inline Semaphore(unsigned initial);
58
59         inline ~Semaphore();
60
61         /** Post/Increment/Signal */
62         inline void post();
63
64         /** Wait/Decrement.  Returns false on error. */
65         inline bool wait();
66
67         /** Attempt Wait/Decrement.  Returns true iff a decrement occurred. */
68         inline bool try_wait();
69
70 private:
71 #if defined(__APPLE__)
72         semaphore_t _sem;  // sem_t is a worthless broken mess on OSX
73 #elif defined(_WIN32)
74         HANDLE _sem;  // types are overrated anyway
75 #else
76         sem_t _sem;
77 #endif
78 };
79
80 #ifdef __APPLE__
81
82 inline
83 Semaphore::Semaphore(unsigned initial)
84 {
85         if (semaphore_create(mach_task_self(), &_sem, SYNC_POLICY_FIFO, initial)) {
86                 throw failed_constructor();
87         }
88 }
89
90 inline
91 Semaphore::~Semaphore()
92 {
93         semaphore_destroy(mach_task_self(), _sem);
94 }
95
96 inline void
97 Semaphore::post()
98 {
99         semaphore_signal(_sem);
100 }
101
102 inline bool
103 Semaphore::wait()
104 {
105         if (semaphore_wait(_sem) != KERN_SUCCESS) {
106                 return false;
107         }
108         return true;
109 }
110
111 inline bool
112 Semaphore::try_wait()
113 {
114         const mach_timespec_t zero = { 0, 0 };
115         return semaphore_timedwait(_sem, zero) == KERN_SUCCESS;
116 }
117
118 #elif defined(_WIN32)
119
120 inline
121 Semaphore::Semaphore(unsigned initial)
122 {
123         if (!(_sem = CreateSemaphore(NULL, initial, LONG_MAX, NULL))) {
124                 throw failed_constructor();
125         }
126 }
127
128 inline
129 Semaphore::~Semaphore()
130 {
131         CloseHandle(_sem);
132 }
133
134 inline void
135 Semaphore::post()
136 {
137         ReleaseSemaphore(_sem, 1, NULL);
138 }
139
140 inline bool
141 Semaphore::wait()
142 {
143         if (WaitForSingleObject(_sem, INFINITE) != WAIT_OBJECT_0) {
144                 return false;
145         }
146         return true;
147 }
148
149 inline bool
150 Semaphore::try_wait()
151 {
152         return WaitForSingleObject(_sem, 0) == WAIT_OBJECT_0;
153 }
154
155 #else  /* !defined(__APPLE__) && !defined(_WIN32) */
156
157 Semaphore::Semaphore(unsigned initial)
158 {
159         if (sem_init(&_sem, 0, initial)) {
160                 throw failed_constructor();
161         }
162 }
163
164 inline
165 Semaphore::~Semaphore()
166 {
167         sem_destroy(&_sem);
168 }
169
170 inline void
171 Semaphore::post()
172 {
173         sem_post(&_sem);
174 }
175
176 inline bool
177 Semaphore::wait()
178 {
179         while (sem_wait(&_sem)) {
180                 if (errno != EINTR) {
181                         return false;  // We are all doomed
182                 }
183                 /* Otherwise, interrupted (rare/weird), so try again. */
184         }
185
186         return true;
187 }
188
189 inline bool
190 Semaphore::try_wait()
191 {
192         return (sem_trywait(&_sem) == 0);
193 }
194
195 #endif
196
197 }  // namespace PBD
198
199 #endif  /* __pbd_semaphore_h__ */