Jack2 1.9.12

JackLinuxFutex.cpp
1 /*
2 Copyright (C) 2004-2008 Grame
3 Copyright (C) 2016 Filipe Coelho
4
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.
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 Lesser General Public License for more details.
14
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.
18
19 */
20
21 #include "JackLinuxFutex.h"
22 #include "JackTools.h"
23 #include "JackConstants.h"
24 #include "JackError.h"
25 #include "promiscuous.h"
26 #include <fcntl.h>
27 #include <stdio.h>
28 #include <sys/mman.h>
29 #include <syscall.h>
30 #include <linux/futex.h>
31
32 namespace Jack
33 {
34
35 JackLinuxFutex::JackLinuxFutex() : JackSynchro(), fSharedMem(-1), fFutex(NULL), fPrivate(false)
36 {
37 const char* promiscuous = getenv("JACK_PROMISCUOUS_SERVER");
38 fPromiscuous = (promiscuous != NULL);
39 fPromiscuousGid = jack_group2gid(promiscuous);
40 }
41
42 void JackLinuxFutex::BuildName(const char* client_name, const char* server_name, char* res, int size)
43 {
44 char ext_client_name[SYNC_MAX_NAME_SIZE + 1];
45 JackTools::RewriteName(client_name, ext_client_name);
46 if (fPromiscuous) {
47 snprintf(res, size, "jack_sem.%s_%s", server_name, ext_client_name);
48 } else {
49 snprintf(res, size, "jack_sem.%d_%s_%s", JackTools::GetUID(), server_name, ext_client_name);
50 }
51 }
52
53 bool JackLinuxFutex::Signal()
54 {
55 if (!fFutex) {
56 jack_error("JackLinuxFutex::Signal name = %s already deallocated!!", fName);
57 return false;
58 }
59
60 if (fFlush) {
61 return true;
62 }
63
64 if (! __sync_bool_compare_and_swap(&fFutex->futex, 0, 1))
65 {
66 // already unlocked, do not wake futex
67 if (! fFutex->internal) return true;
68 }
69
70 ::syscall(__NR_futex, fFutex, fFutex->internal ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1, NULL, NULL, 0);
71 return true;
72 }
73
74 bool JackLinuxFutex::SignalAll()
75 {
76 return Signal();
77 }
78
79 bool JackLinuxFutex::Wait()
80 {
81 if (!fFutex) {
82 jack_error("JackLinuxFutex::Wait name = %s already deallocated!!", fName);
83 return false;
84 }
85
86 if (fFutex->needsChange)
87 {
88 fFutex->needsChange = false;
89 fFutex->internal = !fFutex->internal;
90 }
91
92 for (;;)
93 {
94 if (__sync_bool_compare_and_swap(&fFutex->futex, 1, 0))
95 return true;
96
97 if (::syscall(__NR_futex, fFutex, fFutex->internal ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, 0, NULL, NULL, 0) != 0 && errno != EWOULDBLOCK)
98 return false;
99 }
100 }
101
102 bool JackLinuxFutex::TimedWait(long usec)
103 {
104 if (!fFutex) {
105 jack_error("JackLinuxFutex::TimedWait name = %s already deallocated!!", fName);
106 return false;
107 }
108
109 if (fFutex->needsChange)
110 {
111 fFutex->needsChange = false;
112 fFutex->internal = !fFutex->internal;
113 }
114
115 const uint secs = usec / 1000000;
116 const int nsecs = (usec % 1000000) * 1000;
117
118 const timespec timeout = { static_cast<time_t>(secs), nsecs };
119
120 for (;;)
121 {
122 if (__sync_bool_compare_and_swap(&fFutex->futex, 1, 0))
123 return true;
124
125 if (::syscall(__NR_futex, fFutex, fFutex->internal ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, 0, &timeout, NULL, 0) != 0 && errno != EWOULDBLOCK)
126 return false;
127 }
128 }
129
130 // Server side : publish the futex in the global namespace
131 bool JackLinuxFutex::Allocate(const char* name, const char* server_name, int value, bool internal)
132 {
133 BuildName(name, server_name, fName, sizeof(fName));
134 jack_log("JackLinuxFutex::Allocate name = %s val = %ld", fName, value);
135
136 if ((fSharedMem = shm_open(fName, O_CREAT | O_RDWR, 0777)) < 0) {
137 jack_error("Allocate: can't check in named futex name = %s err = %s", fName, strerror(errno));
138 return false;
139 }
140
141 ftruncate(fSharedMem, sizeof(FutexData));
142
143 if (fPromiscuous && (jack_promiscuous_perms(fSharedMem, fName, fPromiscuousGid) < 0)) {
144 close(fSharedMem);
145 fSharedMem = -1;
146 shm_unlink(fName);
147 return false;
148 }
149
150 if ((fFutex = (FutexData*)mmap(NULL, sizeof(FutexData), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, fSharedMem, 0)) == NULL) {
151 jack_error("Allocate: can't check in named futex name = %s err = %s", fName, strerror(errno));
152 close(fSharedMem);
153 fSharedMem = -1;
154 shm_unlink(fName);
155 return false;
156 }
157
158 fPrivate = internal;
159
160 fFutex->futex = value;
161 fFutex->internal = internal;
162 fFutex->wasInternal = internal;
163 fFutex->needsChange = false;
164 fFutex->externalCount = 0;
165 return true;
166 }
167
168 // Client side : get the published futex from server
169 bool JackLinuxFutex::Connect(const char* name, const char* server_name)
170 {
171 BuildName(name, server_name, fName, sizeof(fName));
172 jack_log("JackLinuxFutex::Connect name = %s", fName);
173
174 // Temporary...
175 if (fFutex) {
176 jack_log("Already connected name = %s", name);
177 return true;
178 }
179
180 if ((fSharedMem = shm_open(fName, O_RDWR, 0)) < 0) {
181 jack_error("Connect: can't connect named futex name = %s err = %s", fName, strerror(errno));
182 return false;
183 }
184
185 if ((fFutex = (FutexData*)mmap(NULL, sizeof(FutexData), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, fSharedMem, 0)) == NULL) {
186 jack_error("Connect: can't connect named futex name = %s err = %s", fName, strerror(errno));
187 close(fSharedMem);
188 fSharedMem = -1;
189 return false;
190 }
191
192 if (! fPrivate && fFutex->wasInternal)
193 {
194 const char* externalSync = getenv("JACK_INTERNAL_CLIENT_SYNC");
195
196 if (externalSync != NULL && strstr(fName, externalSync) != NULL && ++fFutex->externalCount == 1)
197 {
198 jack_error("Note: client %s running as external client temporarily", fName);
199 fFutex->needsChange = true;
200 }
201 }
202
203 return true;
204 }
205
206 bool JackLinuxFutex::ConnectInput(const char* name, const char* server_name)
207 {
208 return Connect(name, server_name);
209 }
210
211 bool JackLinuxFutex::ConnectOutput(const char* name, const char* server_name)
212 {
213 return Connect(name, server_name);
214 }
215
216 bool JackLinuxFutex::Disconnect()
217 {
218 if (!fFutex) {
219 return true;
220 }
221
222 if (! fPrivate && fFutex->wasInternal)
223 {
224 const char* externalSync = getenv("JACK_INTERNAL_CLIENT_SYNC");
225
226 if (externalSync != NULL && strstr(fName, externalSync) != NULL && --fFutex->externalCount == 0)
227 {
228 jack_error("Note: client %s now running as internal client again", fName);
229 fFutex->needsChange = true;
230 }
231 }
232
233 munmap(fFutex, sizeof(FutexData));
234 fFutex = NULL;
235
236 close(fSharedMem);
237 fSharedMem = -1;
238 return true;
239 }
240
241 // Server side : destroy the futex
242 void JackLinuxFutex::Destroy()
243 {
244 if (!fFutex) {
245 return;
246 }
247
248 munmap(fFutex, sizeof(FutexData));
249 fFutex = NULL;
250
251 close(fSharedMem);
252 fSharedMem = -1;
253
254 shm_unlink(fName);
255 }
256
257 } // end of namespace
258
jack_error
SERVER_EXPORT void jack_error(const char *fmt,...)
Definition: JackError.cpp:92
jack_log
SERVER_EXPORT void jack_log(const char *fmt,...)
Definition: JackError.cpp:108