FreeBSD: Use driver.h relay include.
[jack2.git] / linux / JackLinuxFutex.cpp
blob29b13901cc86a0608d8e5f95363555093d0badeb
1 /*
2 Copyright (C) 2004-2008 Grame
3 Copyright (C) 2016 Filipe Coelho
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
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 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 #include "JackLinuxFutex.h"
22 #include "JackTools.h"
23 #include "JackConstants.h"
24 #include "JackError.h"
25 #include "promiscuous.h"
26 #include <climits>
27 #include <fcntl.h>
28 #include <stdio.h>
29 #include <sys/mman.h>
30 #include <syscall.h>
31 #include <linux/futex.h>
33 #if !defined(SYS_futex) && defined(SYS_futex_time64)
34 #define SYS_futex SYS_futex_time64
35 #endif
37 namespace Jack
40 JackLinuxFutex::JackLinuxFutex() : JackSynchro(), fSharedMem(-1), fFutex(NULL), fPrivate(false)
42 const char* promiscuous = getenv("JACK_PROMISCUOUS_SERVER");
43 fPromiscuous = (promiscuous != NULL);
44 fPromiscuousGid = jack_group2gid(promiscuous);
47 void JackLinuxFutex::BuildName(const char* client_name, const char* server_name, char* res, int size)
49 char ext_client_name[SYNC_MAX_NAME_SIZE + 1];
50 JackTools::RewriteName(client_name, ext_client_name);
51 if (fPromiscuous) {
52 snprintf(res, size, "jack_sem.%s_%s", server_name, ext_client_name);
53 } else {
54 snprintf(res, size, "jack_sem.%d_%s_%s", JackTools::GetUID(), server_name, ext_client_name);
58 bool JackLinuxFutex::Signal()
60 if (!fFutex) {
61 jack_error("JackLinuxFutex::Signal name = %s already deallocated!!", fName);
62 return false;
65 if (fFlush) {
66 return true;
69 if (! __sync_bool_compare_and_swap(&fFutex->futex, 0, 1))
71 // already unlocked, do not wake futex
72 if (! fFutex->internal) return true;
75 ::syscall(SYS_futex, fFutex, fFutex->internal ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1, NULL, NULL, 0);
76 return true;
79 bool JackLinuxFutex::SignalAll()
81 return Signal();
84 bool JackLinuxFutex::Wait()
86 if (!fFutex) {
87 jack_error("JackLinuxFutex::Wait name = %s already deallocated!!", fName);
88 return false;
91 if (fFutex->needsChange)
93 fFutex->needsChange = false;
94 fFutex->internal = !fFutex->internal;
97 const int wait_mode = fFutex->internal ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT;
99 for (;;)
101 if (__sync_bool_compare_and_swap(&fFutex->futex, 1, 0))
102 return true;
104 if (::syscall(SYS_futex, fFutex, wait_mode, 0, NULL, NULL, 0) != 0)
105 if (errno != EAGAIN && errno != EINTR)
106 return false;
110 bool JackLinuxFutex::TimedWait(long usec)
112 if (usec == LONG_MAX)
113 return Wait();
115 if (!fFutex) {
116 jack_error("JackLinuxFutex::TimedWait name = %s already deallocated!!", fName);
117 return false;
120 if (fFutex->needsChange)
122 fFutex->needsChange = false;
123 fFutex->internal = !fFutex->internal;
126 const uint secs = usec / 1000000;
127 const int nsecs = (usec % 1000000) * 1000;
129 const timespec timeout = { static_cast<time_t>(secs), nsecs };
130 const int wait_mode = fFutex->internal ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT;
132 for (;;)
134 if (__sync_bool_compare_and_swap(&fFutex->futex, 1, 0))
135 return true;
137 if (::syscall(SYS_futex, fFutex, wait_mode, 0, &timeout, NULL, 0) != 0)
138 if (errno != EAGAIN && errno != EINTR)
139 return false;
143 // Server side : publish the futex in the global namespace
144 bool JackLinuxFutex::Allocate(const char* name, const char* server_name, int value, bool internal)
146 BuildName(name, server_name, fName, sizeof(fName));
147 jack_log("JackLinuxFutex::Allocate name = %s val = %ld", fName, value);
149 if ((fSharedMem = shm_open(fName, O_CREAT | O_RDWR, 0777)) < 0) {
150 jack_error("Allocate: can't check in named futex name = %s err = %s", fName, strerror(errno));
151 return false;
154 if (ftruncate(fSharedMem, sizeof(FutexData)) != 0) {
155 jack_error("Allocate: can't set shared memory size in named futex name = %s err = %s", fName, strerror(errno));
156 return false;
159 if (fPromiscuous && (jack_promiscuous_perms(fSharedMem, fName, fPromiscuousGid) < 0)) {
160 close(fSharedMem);
161 fSharedMem = -1;
162 shm_unlink(fName);
163 return false;
166 FutexData* futex = (FutexData*)mmap(NULL, sizeof(FutexData), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, fSharedMem, 0);
168 if (futex == NULL || futex == MAP_FAILED) {
169 jack_error("Allocate: can't check in named futex name = %s err = %s", fName, strerror(errno));
170 close(fSharedMem);
171 fSharedMem = -1;
172 shm_unlink(fName);
173 return false;
176 fPrivate = internal;
178 futex->futex = value;
179 futex->internal = internal;
180 futex->wasInternal = internal;
181 futex->needsChange = false;
182 futex->externalCount = 0;
183 fFutex = futex;
184 return true;
187 // Client side : get the published futex from server
188 bool JackLinuxFutex::Connect(const char* name, const char* server_name)
190 BuildName(name, server_name, fName, sizeof(fName));
191 jack_log("JackLinuxFutex::Connect name = %s", fName);
193 // Temporary...
194 if (fFutex) {
195 jack_log("Already connected name = %s", name);
196 return true;
199 if ((fSharedMem = shm_open(fName, O_RDWR, 0)) < 0) {
200 jack_error("Connect: can't connect named futex name = %s err = %s", fName, strerror(errno));
201 return false;
204 FutexData* futex = (FutexData*)mmap(NULL, sizeof(FutexData), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, fSharedMem, 0);
206 if (futex == NULL || futex == MAP_FAILED) {
207 jack_error("Connect: can't connect named futex name = %s err = %s", fName, strerror(errno));
208 close(fSharedMem);
209 fSharedMem = -1;
210 return false;
213 if (! fPrivate && futex->wasInternal)
215 const char* externalSync = getenv("JACK_INTERNAL_CLIENT_SYNC");
217 if (externalSync != NULL && strstr(fName, externalSync) != NULL && ++futex->externalCount == 1)
219 jack_error("Note: client %s running as external client temporarily", fName);
220 futex->needsChange = true;
224 fFutex = futex;
225 return true;
228 bool JackLinuxFutex::ConnectInput(const char* name, const char* server_name)
230 return Connect(name, server_name);
233 bool JackLinuxFutex::ConnectOutput(const char* name, const char* server_name)
235 return Connect(name, server_name);
238 bool JackLinuxFutex::Disconnect()
240 if (!fFutex) {
241 return true;
244 if (! fPrivate && fFutex->wasInternal)
246 const char* externalSync = getenv("JACK_INTERNAL_CLIENT_SYNC");
248 if (externalSync != NULL && strstr(fName, externalSync) != NULL && --fFutex->externalCount == 0)
250 jack_error("Note: client %s now running as internal client again", fName);
251 fFutex->needsChange = true;
255 munmap(fFutex, sizeof(FutexData));
256 fFutex = NULL;
258 close(fSharedMem);
259 fSharedMem = -1;
260 return true;
263 // Server side : destroy the futex
264 void JackLinuxFutex::Destroy()
266 if (!fFutex) {
267 return;
270 munmap(fFutex, sizeof(FutexData));
271 fFutex = NULL;
273 close(fSharedMem);
274 fSharedMem = -1;
276 shm_unlink(fName);
279 } // end of namespace