2 * Carla semaphore utils
3 * Copyright (C) 2013-2023 Filipe Coelho <falktx@falktx.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or 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 General Public License for more details.
15 * For a full copy of the GNU General Public License see the doc/GPL.txt file.
18 #ifndef CARLA_SEM_UTILS_HPP_INCLUDED
19 #define CARLA_SEM_UTILS_HPP_INCLUDED
21 #include "CarlaUtils.hpp"
25 #if defined(CARLA_OS_LINUX) && ! defined(STOAT_TEST_BUILD)
26 # define CARLA_USE_FUTEXES 1
29 #if defined(CARLA_OS_WIN)
31 # error Wine is not supposed to use this!
33 struct carla_sem_t
{ HANDLE handle
; };
34 #elif defined(CARLA_OS_MAC)
36 #define UL_COMPARE_AND_WAIT 1
37 #define UL_COMPARE_AND_WAIT_SHARED 3
38 #define ULF_NO_ERRNO 0x01000000
40 int __ulock_wait(uint32_t operation
, void* addr
, uint64_t value
, uint32_t timeout_us
);
41 int __ulock_wake(uint32_t operation
, void* addr
, uint64_t value
);
43 struct carla_sem_t
{ int count
; bool external
; };
44 #elif defined(CARLA_USE_FUTEXES)
47 # include <sys/time.h>
48 # include <linux/futex.h>
49 struct carla_sem_t
{ int count
; bool external
; };
52 # include <semaphore.h>
53 # include <sys/time.h>
54 # include <sys/types.h>
55 struct carla_sem_t
{ sem_t sem
; };
59 * Create a new semaphore, pre-allocated version.
62 bool carla_sem_create2(carla_sem_t
& sem
, const bool externalIPC
) noexcept
64 carla_zeroStruct(sem
);
65 #if defined(CARLA_OS_WIN)
66 SECURITY_ATTRIBUTES sa
;
68 sa
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
69 sa
.bInheritHandle
= TRUE
;
71 sem
.handle
= ::CreateSemaphoreA(externalIPC
? &sa
: nullptr, 0, 1, nullptr);
73 return (sem
.handle
!= INVALID_HANDLE_VALUE
);
74 #elif defined(CARLA_OS_MAC) || defined(CARLA_USE_FUTEXES)
75 sem
.external
= externalIPC
;
78 return (::sem_init(&sem
.sem
, externalIPC
, 0) == 0);
83 * Create a new semaphore.
86 carla_sem_t
* carla_sem_create(const bool externalIPC
) noexcept
88 if (carla_sem_t
* const sem
= (carla_sem_t
*)std::malloc(sizeof(carla_sem_t
)))
90 if (carla_sem_create2(*sem
, externalIPC
))
100 * Destroy a semaphore, pre-allocated version.
103 void carla_sem_destroy2(carla_sem_t
& sem
) noexcept
105 #if defined(CARLA_OS_WIN)
106 ::CloseHandle(sem
.handle
);
107 #elif defined(CARLA_OS_MAC) || defined(CARLA_USE_FUTEXES)
110 ::sem_destroy(&sem
.sem
);
112 carla_zeroStruct(sem
);
116 * Destroy a semaphore.
119 void carla_sem_destroy(carla_sem_t
* const sem
) noexcept
121 CARLA_SAFE_ASSERT_RETURN(sem
!= nullptr,);
123 carla_sem_destroy2(*sem
);
128 * Post semaphore (unlock).
131 void carla_sem_post(carla_sem_t
& sem
) noexcept
134 ::ReleaseSemaphore(sem
.handle
, 1, nullptr);
135 #elif defined(CARLA_OS_MAC)
136 const bool unlocked
= __sync_bool_compare_and_swap(&sem
.count
, 0, 1);
137 CARLA_SAFE_ASSERT_RETURN(unlocked
,);
138 __ulock_wake(ULF_NO_ERRNO
| (sem
.external
? UL_COMPARE_AND_WAIT_SHARED
: UL_COMPARE_AND_WAIT
), &sem
.count
, 0);
139 #elif defined(CARLA_USE_FUTEXES)
140 const bool unlocked
= __sync_bool_compare_and_swap(&sem
.count
, 0, 1);
141 CARLA_SAFE_ASSERT_RETURN(unlocked
,);
142 ::syscall(__NR_futex
, &sem
.count
, sem
.external
? FUTEX_WAKE
: FUTEX_WAKE_PRIVATE
, 1, nullptr, nullptr, 0);
144 ::sem_post(&sem
.sem
);
148 #ifndef CARLA_OS_WASM
150 * Wait for a semaphore (lock).
153 bool carla_sem_timedwait(carla_sem_t
& sem
, const uint msecs
) noexcept
155 CARLA_SAFE_ASSERT_RETURN(msecs
> 0, false);
157 #if defined(CARLA_OS_WIN)
158 return (::WaitForSingleObject(sem
.handle
, msecs
) == WAIT_OBJECT_0
);
159 #elif defined(CARLA_OS_MAC)
160 const uint32_t timeout
= msecs
* 1000;
164 if (__sync_bool_compare_and_swap(&sem
.count
, 1, 0))
167 if (__ulock_wait(sem
.external
? UL_COMPARE_AND_WAIT_SHARED
: UL_COMPARE_AND_WAIT
, &sem
.count
, 0, timeout
) != 0)
168 if (errno
!= EAGAIN
&& errno
!= EINTR
)
171 #elif defined(CARLA_USE_FUTEXES)
172 const uint secs
= msecs
/ 1000;
173 const uint nsecs
= (msecs
% 1000) * 1000000;
174 const timespec timeout
= { static_cast<time_t>(secs
), static_cast<long>(nsecs
) };
178 if (__sync_bool_compare_and_swap(&sem
.count
, 1, 0))
181 if (::syscall(__NR_futex
, &sem
.count
, sem
.external
? FUTEX_WAIT
: FUTEX_WAIT_PRIVATE
, 0, &timeout
, nullptr, 0) != 0)
182 if (errno
!= EAGAIN
&& errno
!= EINTR
)
186 if (::sem_trywait(&sem
.sem
) == 0)
190 ::clock_gettime(CLOCK_REALTIME
, &now
);
192 const uint secs
= msecs
/ 1000;
193 const uint nsecs
= (msecs
% 1000) * 1000000;
194 const timespec delta
= { static_cast<time_t>(secs
), static_cast<long>(nsecs
) };
195 /* */ timespec end
= { now
.tv_sec
+ delta
.tv_sec
, now
.tv_nsec
+ delta
.tv_nsec
};
196 if (end
.tv_nsec
>= 1000000000L) {
198 end
.tv_nsec
-= 1000000000L;
204 ret
= sem_timedwait(&sem
.sem
, &end
);
205 } CARLA_SAFE_EXCEPTION_RETURN("carla_sem_timedwait", false);
209 if (errno
!= EAGAIN
&& errno
!= EINTR
)
216 // -----------------------------------------------------------------------
218 #endif // CARLA_SEM_UTILS_HPP_INCLUDED