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"
30 #include <linux/futex.h>
35 JackLinuxFutex::JackLinuxFutex() : JackSynchro(), fSharedMem(-1), fFutex(NULL
), fPrivate(false)
37 const char* promiscuous
= getenv("JACK_PROMISCUOUS_SERVER");
38 fPromiscuous
= (promiscuous
!= NULL
);
39 fPromiscuousGid
= jack_group2gid(promiscuous
);
42 void JackLinuxFutex::BuildName(const char* client_name
, const char* server_name
, char* res
, int size
)
44 char ext_client_name
[SYNC_MAX_NAME_SIZE
+ 1];
45 JackTools::RewriteName(client_name
, ext_client_name
);
47 snprintf(res
, size
, "jack_sem.%s_%s", server_name
, ext_client_name
);
49 snprintf(res
, size
, "jack_sem.%d_%s_%s", JackTools::GetUID(), server_name
, ext_client_name
);
53 bool JackLinuxFutex::Signal()
56 jack_error("JackLinuxFutex::Signal name = %s already deallocated!!", fName
);
64 if (! __sync_bool_compare_and_swap(&fFutex
->futex
, 0, 1))
66 // already unlocked, do not wake futex
67 if (! fFutex
->internal
) return true;
70 ::syscall(__NR_futex
, fFutex
, fFutex
->internal
? FUTEX_WAKE_PRIVATE
: FUTEX_WAKE
, 1, NULL
, NULL
, 0);
74 bool JackLinuxFutex::SignalAll()
79 bool JackLinuxFutex::Wait()
82 jack_error("JackLinuxFutex::Wait name = %s already deallocated!!", fName
);
86 if (fFutex
->needsChange
)
88 fFutex
->needsChange
= false;
89 fFutex
->internal
= !fFutex
->internal
;
94 if (__sync_bool_compare_and_swap(&fFutex
->futex
, 1, 0))
97 if (::syscall(__NR_futex
, fFutex
, fFutex
->internal
? FUTEX_WAIT_PRIVATE
: FUTEX_WAIT
, 0, NULL
, NULL
, 0) != 0 && errno
!= EWOULDBLOCK
)
102 bool JackLinuxFutex::TimedWait(long usec
)
105 jack_error("JackLinuxFutex::TimedWait name = %s already deallocated!!", fName
);
109 if (fFutex
->needsChange
)
111 fFutex
->needsChange
= false;
112 fFutex
->internal
= !fFutex
->internal
;
115 const uint secs
= usec
/ 1000000;
116 const int nsecs
= (usec
% 1000000) * 1000;
118 const timespec timeout
= { static_cast<time_t>(secs
), nsecs
};
122 if (__sync_bool_compare_and_swap(&fFutex
->futex
, 1, 0))
125 if (::syscall(__NR_futex
, fFutex
, fFutex
->internal
? FUTEX_WAIT_PRIVATE
: FUTEX_WAIT
, 0, &timeout
, NULL
, 0) != 0 && errno
!= EWOULDBLOCK
)
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
)
133 BuildName(name
, server_name
, fName
, sizeof(fName
));
134 jack_log("JackLinuxFutex::Allocate name = %s val = %ld", fName
, value
);
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
));
141 if (ftruncate(fSharedMem
, sizeof(FutexData
)) != 0) {
142 jack_error("Allocate: can't set shared memory size in named futex name = %s err = %s", fName
, strerror(errno
));
146 if (fPromiscuous
&& (jack_promiscuous_perms(fSharedMem
, fName
, fPromiscuousGid
) < 0)) {
153 FutexData
* futex
= (FutexData
*)mmap(NULL
, sizeof(FutexData
), PROT_READ
|PROT_WRITE
, MAP_SHARED
|MAP_LOCKED
, fSharedMem
, 0);
155 if (futex
== NULL
|| futex
== MAP_FAILED
) {
156 jack_error("Allocate: can't check in named futex name = %s err = %s", fName
, strerror(errno
));
165 futex
->futex
= value
;
166 futex
->internal
= internal
;
167 futex
->wasInternal
= internal
;
168 futex
->needsChange
= false;
169 futex
->externalCount
= 0;
174 // Client side : get the published futex from server
175 bool JackLinuxFutex::Connect(const char* name
, const char* server_name
)
177 BuildName(name
, server_name
, fName
, sizeof(fName
));
178 jack_log("JackLinuxFutex::Connect name = %s", fName
);
182 jack_log("Already connected name = %s", name
);
186 if ((fSharedMem
= shm_open(fName
, O_RDWR
, 0)) < 0) {
187 jack_error("Connect: can't connect named futex name = %s err = %s", fName
, strerror(errno
));
191 FutexData
* futex
= (FutexData
*)mmap(NULL
, sizeof(FutexData
), PROT_READ
|PROT_WRITE
, MAP_SHARED
|MAP_LOCKED
, fSharedMem
, 0);
193 if (futex
== NULL
|| futex
== MAP_FAILED
) {
194 jack_error("Connect: can't connect named futex name = %s err = %s", fName
, strerror(errno
));
200 if (! fPrivate
&& futex
->wasInternal
)
202 const char* externalSync
= getenv("JACK_INTERNAL_CLIENT_SYNC");
204 if (externalSync
!= NULL
&& strstr(fName
, externalSync
) != NULL
&& ++futex
->externalCount
== 1)
206 jack_error("Note: client %s running as external client temporarily", fName
);
207 futex
->needsChange
= true;
215 bool JackLinuxFutex::ConnectInput(const char* name
, const char* server_name
)
217 return Connect(name
, server_name
);
220 bool JackLinuxFutex::ConnectOutput(const char* name
, const char* server_name
)
222 return Connect(name
, server_name
);
225 bool JackLinuxFutex::Disconnect()
231 if (! fPrivate
&& fFutex
->wasInternal
)
233 const char* externalSync
= getenv("JACK_INTERNAL_CLIENT_SYNC");
235 if (externalSync
!= NULL
&& strstr(fName
, externalSync
) != NULL
&& --fFutex
->externalCount
== 0)
237 jack_error("Note: client %s now running as internal client again", fName
);
238 fFutex
->needsChange
= true;
242 munmap(fFutex
, sizeof(FutexData
));
250 // Server side : destroy the futex
251 void JackLinuxFutex::Destroy()
257 munmap(fFutex
, sizeof(FutexData
));
266 } // end of namespace