Merge branch 'master' into windows+cc
[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(PLATFORM_WINDOWS)
26 #    include <windows.h>
27 #ifndef INFINITE
28 #define INFINITE 0xffffffffL
29 #endif
30 #else
31 #    include <semaphore.h>
32 #    include <errno.h>
33 #endif
34
35 #include "pbd/failed_constructor.h"
36
37 namespace PBD {
38
39 /**
40    Unnamed (process local) counting semaphore.
41
42    The civilized person's synchronisation primitive.  A counting semaphore is
43    an integer which is always non-negative, so, an attempted decrement (or
44    "wait") will block if the value is 0, until another thread does an increment
45    (or "post").
46
47    At least on Lignux, the main advantage of this is that it is fast and the
48    only safe way to reliably signal from a real-time audio thread.  The
49    counting semantics also complement ringbuffers of events nicely.
50 */
51 class Semaphore
52 {
53 public:
54         /**
55            Create a new semaphore.
56
57            Chances are you want 1 wait() per 1 post(), an initial value of 0.
58         */
59         inline Semaphore(unsigned initial);
60
61         inline ~Semaphore();
62
63         /** Post/Increment/Signal */
64         inline void post();
65
66         /** Wait/Decrement.  Returns false on error. */
67         inline bool wait();
68
69         /** Attempt Wait/Decrement.  Returns true iff a decrement occurred. */
70         inline bool try_wait();
71
72 private:
73 #if defined(__APPLE__)
74         semaphore_t _sem;  // sem_t is a worthless broken mess on OSX
75 #elif defined(PLATFORM_WINDOWS)
76         HANDLE _sem;  // types are overrated anyway
77 #else
78         sem_t _sem;
79 #endif
80 };
81
82 #ifdef __APPLE__
83
84 inline
85 Semaphore::Semaphore(unsigned initial)
86 {
87         if (semaphore_create(mach_task_self(), &_sem, SYNC_POLICY_FIFO, initial)) {
88                 throw failed_constructor();
89         }
90 }
91
92 inline
93 Semaphore::~Semaphore()
94 {
95         semaphore_destroy(mach_task_self(), _sem);
96 }
97
98 inline void
99 Semaphore::post()
100 {
101         semaphore_signal(_sem);
102 }
103
104 inline bool
105 Semaphore::wait()
106 {
107         if (semaphore_wait(_sem) != KERN_SUCCESS) {
108                 return false;
109         }
110         return true;
111 }
112
113 inline bool
114 Semaphore::try_wait()
115 {
116         const mach_timespec_t zero = { 0, 0 };
117         return semaphore_timedwait(_sem, zero) == KERN_SUCCESS;
118 }
119
120 #elif defined(PLATFORM_WINDOWS)
121
122 inline
123 Semaphore::Semaphore(unsigned initial)
124 {
125         if (!(_sem = CreateSemaphore(NULL, initial, LONG_MAX, NULL))) {
126                 throw failed_constructor();
127         }
128 }
129
130 inline
131 Semaphore::~Semaphore()
132 {
133         CloseHandle(_sem);
134 }
135
136 inline void
137 Semaphore::post()
138 {
139         ReleaseSemaphore(_sem, 1, NULL);
140 }
141
142 inline bool
143 Semaphore::wait()
144 {
145         if (WaitForSingleObject(_sem, INFINITE) != WAIT_OBJECT_0) {
146                 return false;
147         }
148         return true;
149 }
150
151 inline bool
152 Semaphore::try_wait()
153 {
154         return WaitForSingleObject(_sem, 0) == WAIT_OBJECT_0;
155 }
156
157 #else  /* !defined(__APPLE__) && !defined(PLATFORM_WINDOWS) */
158
159 Semaphore::Semaphore(unsigned initial)
160 {
161         if (sem_init(&_sem, 0, initial)) {
162                 throw failed_constructor();
163         }
164 }
165
166 inline
167 Semaphore::~Semaphore()
168 {
169         sem_destroy(&_sem);
170 }
171
172 inline void
173 Semaphore::post()
174 {
175         sem_post(&_sem);
176 }
177
178 inline bool
179 Semaphore::wait()
180 {
181         while (sem_wait(&_sem)) {
182                 if (errno != EINTR) {
183                         return false;  // We are all doomed
184                 }
185                 /* Otherwise, interrupted (rare/weird), so try again. */
186         }
187
188         return true;
189 }
190
191 inline bool
192 Semaphore::try_wait()
193 {
194         return (sem_trywait(&_sem) == 0);
195 }
196
197 #endif
198
199 }  // namespace PBD
200
201 #endif  /* __pbd_semaphore_h__ */