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"
29 #include <linux/futex.h>
34 void JackLinuxFutex::BuildName(const char* client_name
, const char* server_name
, char* res
, int size
)
36 char ext_client_name
[SYNC_MAX_NAME_SIZE
+ 1];
37 JackTools::RewriteName(client_name
, ext_client_name
);
38 if (getenv("JACK_PROMISCUOUS_SERVER")) {
39 snprintf(res
, size
, "jack_sem.%s_%s", server_name
, ext_client_name
);
41 snprintf(res
, size
, "jack_sem.%d_%s_%s", JackTools::GetUID(), server_name
, ext_client_name
);
45 bool JackLinuxFutex::Signal()
48 jack_error("JackLinuxFutex::Signal name = %s already deallocated!!", fName
);
56 if (! __sync_bool_compare_and_swap(&fFutex
->futex
, 0, 1))
58 // already unlocked, do not wake futex
59 if (! fFutex
->internal
) return true;
62 ::syscall(__NR_futex
, fFutex
, fFutex
->internal
? FUTEX_WAKE_PRIVATE
: FUTEX_WAKE
, 1, NULL
, NULL
, 0);
66 bool JackLinuxFutex::SignalAll()
71 bool JackLinuxFutex::Wait()
74 jack_error("JackLinuxFutex::Wait name = %s already deallocated!!", fName
);
78 if (fFutex
->needsChange
)
80 fFutex
->needsChange
= false;
81 fFutex
->internal
= !fFutex
->internal
;
86 if (__sync_bool_compare_and_swap(&fFutex
->futex
, 1, 0))
89 if (::syscall(__NR_futex
, fFutex
, fFutex
->internal
? FUTEX_WAIT_PRIVATE
: FUTEX_WAIT
, 0, NULL
, NULL
, 0) != 0 && errno
!= EWOULDBLOCK
)
94 bool JackLinuxFutex::TimedWait(long usec
)
97 jack_error("JackLinuxFutex::TimedWait name = %s already deallocated!!", fName
);
101 if (fFutex
->needsChange
)
103 fFutex
->needsChange
= false;
104 fFutex
->internal
= !fFutex
->internal
;
107 const uint secs
= usec
/ 1000000;
108 const int nsecs
= (usec
% 1000000) * 1000;
110 const timespec timeout
= { static_cast<time_t>(secs
), nsecs
};
114 if (__sync_bool_compare_and_swap(&fFutex
->futex
, 1, 0))
117 if (::syscall(__NR_futex
, fFutex
, fFutex
->internal
? FUTEX_WAIT_PRIVATE
: FUTEX_WAIT
, 0, &timeout
, NULL
, 0) != 0 && errno
!= EWOULDBLOCK
)
122 // Server side : publish the futex in the global namespace
123 bool JackLinuxFutex::Allocate(const char* name
, const char* server_name
, int value
, bool internal
)
125 BuildName(name
, server_name
, fName
, sizeof(fName
));
126 jack_log("JackLinuxFutex::Allocate name = %s val = %ld", fName
, value
);
128 if ((fSharedMem
= shm_open(fName
, O_CREAT
| O_RDWR
, 0777)) < 0) {
129 jack_error("Allocate: can't check in named futex name = %s err = %s", fName
, strerror(errno
));
133 ftruncate(fSharedMem
, sizeof(FutexData
));
135 if ((fFutex
= (FutexData
*)mmap(NULL
, sizeof(FutexData
), PROT_READ
|PROT_WRITE
, MAP_SHARED
|MAP_LOCKED
, fSharedMem
, 0)) == NULL
) {
136 jack_error("Allocate: can't check in named futex name = %s err = %s", fName
, strerror(errno
));
145 fFutex
->futex
= value
;
146 fFutex
->internal
= internal
;
147 fFutex
->wasInternal
= internal
;
148 fFutex
->needsChange
= false;
149 fFutex
->externalCount
= 0;
153 // Client side : get the published futex from server
154 bool JackLinuxFutex::Connect(const char* name
, const char* server_name
)
156 BuildName(name
, server_name
, fName
, sizeof(fName
));
157 jack_log("JackLinuxFutex::Connect name = %s", fName
);
161 jack_log("Already connected name = %s", name
);
165 if ((fSharedMem
= shm_open(fName
, O_RDWR
, 0)) < 0) {
166 jack_error("Connect: can't connect named futex name = %s err = %s", fName
, strerror(errno
));
170 if ((fFutex
= (FutexData
*)mmap(NULL
, sizeof(FutexData
), PROT_READ
|PROT_WRITE
, MAP_SHARED
|MAP_LOCKED
, fSharedMem
, 0)) == NULL
) {
171 jack_error("Connect: can't connect named futex name = %s err = %s", fName
, strerror(errno
));
177 if (! fPrivate
&& fFutex
->wasInternal
)
179 const char* externalSync
= getenv("JACK_INTERNAL_CLIENT_SYNC");
181 if (externalSync
!= NULL
&& strstr(fName
, externalSync
) != NULL
&& ++fFutex
->externalCount
== 1)
183 jack_error("Note: client %s running as external client temporarily", fName
);
184 fFutex
->needsChange
= true;
191 bool JackLinuxFutex::ConnectInput(const char* name
, const char* server_name
)
193 return Connect(name
, server_name
);
196 bool JackLinuxFutex::ConnectOutput(const char* name
, const char* server_name
)
198 return Connect(name
, server_name
);
201 bool JackLinuxFutex::Disconnect()
207 if (! fPrivate
&& fFutex
->wasInternal
)
209 const char* externalSync
= getenv("JACK_INTERNAL_CLIENT_SYNC");
211 if (externalSync
!= NULL
&& strstr(fName
, externalSync
) != NULL
&& --fFutex
->externalCount
== 0)
213 jack_error("Note: client %s now running as internal client again", fName
);
214 fFutex
->needsChange
= true;
218 munmap(fFutex
, sizeof(FutexData
));
226 // Server side : destroy the futex
227 void JackLinuxFutex::Destroy()
233 munmap(fFutex
, sizeof(FutexData
));
242 } // end of namespace