Cleanup
[carla.git] / source / utils / CarlaSemUtils.hpp
blob1677c994931bd0f112a6d3c7d40c43aa6348a436
1 /*
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"
23 #include <ctime>
25 #if defined(CARLA_OS_LINUX) && ! defined(STOAT_TEST_BUILD)
26 # define CARLA_USE_FUTEXES 1
27 #endif
29 #if defined(CARLA_OS_WIN)
30 # ifdef __WINE__
31 # error Wine is not supposed to use this!
32 # endif
33 struct carla_sem_t { HANDLE handle; };
34 #elif defined(CARLA_OS_MAC)
35 # include <cerrno>
36 #define UL_COMPARE_AND_WAIT 1
37 #define UL_COMPARE_AND_WAIT_SHARED 3
38 #define ULF_NO_ERRNO 0x01000000
39 extern "C" {
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)
45 # include <cerrno>
46 # include <syscall.h>
47 # include <sys/time.h>
48 # include <linux/futex.h>
49 struct carla_sem_t { int count; bool external; };
50 #else
51 # include <cerrno>
52 # include <semaphore.h>
53 # include <sys/time.h>
54 # include <sys/types.h>
55 struct carla_sem_t { sem_t sem; };
56 #endif
59 * Create a new semaphore, pre-allocated version.
61 static inline
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;
67 carla_zeroStruct(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;
76 return true;
77 #else
78 return (::sem_init(&sem.sem, externalIPC, 0) == 0);
79 #endif
83 * Create a new semaphore.
85 static inline
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))
91 return sem;
93 std::free(sem);
96 return nullptr;
100 * Destroy a semaphore, pre-allocated version.
102 static inline
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)
108 // nothing to do
109 #else
110 ::sem_destroy(&sem.sem);
111 #endif
112 carla_zeroStruct(sem);
116 * Destroy a semaphore.
118 static inline
119 void carla_sem_destroy(carla_sem_t* const sem) noexcept
121 CARLA_SAFE_ASSERT_RETURN(sem != nullptr,);
123 carla_sem_destroy2(*sem);
124 std::free(sem);
128 * Post semaphore (unlock).
130 static inline
131 void carla_sem_post(carla_sem_t& sem) noexcept
133 #ifdef CARLA_OS_WIN
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);
143 #else
144 ::sem_post(&sem.sem);
145 #endif
148 #ifndef CARLA_OS_WASM
150 * Wait for a semaphore (lock).
152 static inline
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;
162 for (;;)
164 if (__sync_bool_compare_and_swap(&sem.count, 1, 0))
165 return true;
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)
169 return false;
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) };
176 for (;;)
178 if (__sync_bool_compare_and_swap(&sem.count, 1, 0))
179 return true;
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)
183 return false;
185 #else
186 if (::sem_trywait(&sem.sem) == 0)
187 return true;
189 timespec now;
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) {
197 ++end.tv_sec;
198 end.tv_nsec -= 1000000000L;
201 for (int ret;;)
203 try {
204 ret = sem_timedwait(&sem.sem, &end);
205 } CARLA_SAFE_EXCEPTION_RETURN("carla_sem_timedwait", false);
207 if (ret == 0)
208 return true;
209 if (errno != EAGAIN && errno != EINTR)
210 return false;
212 #endif
214 #endif
216 // -----------------------------------------------------------------------
218 #endif // CARLA_SEM_UTILS_HPP_INCLUDED