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"
31 #include <linux/futex.h>
33 #if !defined(SYS_futex) && defined(SYS_futex_time64)
34 #define SYS_futex SYS_futex_time64
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
);
52 snprintf(res
, size
, "jack_sem.%s_%s", server_name
, ext_client_name
);
54 snprintf(res
, size
, "jack_sem.%d_%s_%s", JackTools::GetUID(), server_name
, ext_client_name
);
58 bool JackLinuxFutex::Signal()
61 jack_error("JackLinuxFutex::Signal name = %s already deallocated!!", fName
);
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);
79 bool JackLinuxFutex::SignalAll()
84 bool JackLinuxFutex::Wait()
87 jack_error("JackLinuxFutex::Wait name = %s already deallocated!!", fName
);
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
;
101 if (__sync_bool_compare_and_swap(&fFutex
->futex
, 1, 0))
104 if (::syscall(SYS_futex
, fFutex
, wait_mode
, 0, NULL
, NULL
, 0) != 0)
105 if (errno
!= EAGAIN
&& errno
!= EINTR
)
110 bool JackLinuxFutex::TimedWait(long usec
)
112 if (usec
== LONG_MAX
)
116 jack_error("JackLinuxFutex::TimedWait name = %s already deallocated!!", fName
);
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
;
134 if (__sync_bool_compare_and_swap(&fFutex
->futex
, 1, 0))
137 if (::syscall(SYS_futex
, fFutex
, wait_mode
, 0, &timeout
, NULL
, 0) != 0)
138 if (errno
!= EAGAIN
&& errno
!= EINTR
)
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
));
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
));
159 if (fPromiscuous
&& (jack_promiscuous_perms(fSharedMem
, fName
, fPromiscuousGid
) < 0)) {
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
));
178 futex
->futex
= value
;
179 futex
->internal
= internal
;
180 futex
->wasInternal
= internal
;
181 futex
->needsChange
= false;
182 futex
->externalCount
= 0;
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
);
195 jack_log("Already connected name = %s", name
);
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
));
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
));
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;
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()
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
));
263 // Server side : destroy the futex
264 void JackLinuxFutex::Destroy()
270 munmap(fFutex
, sizeof(FutexData
));
279 } // end of namespace