Branch data Line data Source code
1 : : /* 2 : : * Copyright (C) 2026 by Thun Lu. All rights reserved. 3 : : * Author: Thun Lu <thun.lu@zohomail.cn> 4 : : * Repo: https://github.com/thun-res/vlink 5 : : * _ __ __ _ __ 6 : : * | | / / / / (_) ____ / /__ 7 : : * | | / / / / / / / __ \ / //_/ 8 : : * | |/ / / /___ / / / / / / / ,< 9 : : * |___/ /_____/ /_/ /_/ /_/ /_/|_| 10 : : * 11 : : * Licensed under the Apache License, Version 2.0 (the "License"); 12 : : * you may not use this file except in compliance with the License. 13 : : * You may obtain a copy of the License at 14 : : * 15 : : * http://www.apache.org/licenses/LICENSE-2.0 16 : : * 17 : : * Unless required by applicable law or agreed to in writing, software 18 : : * distributed under the License is distributed on an "AS IS" BASIS, 19 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 : : * See the License for the specific language governing permissions and 21 : : * limitations under the License. 22 : : */ 23 : : 24 : : #include "./base/sys_semaphore.h" 25 : : 26 : : #include <chrono> 27 : : #include <string> 28 : : 29 : : #include "./base/logger.h" 30 : : 31 : : #if defined(_WIN32) || defined(__CYGWIN__) 32 : : #include <Windows.h> 33 : : #elif defined(__APPLE__) 34 : : #include <dispatch/dispatch.h> 35 : : #else 36 : : #include <fcntl.h> 37 : : #include <semaphore.h> 38 : : #endif 39 : : 40 : : namespace vlink { 41 : : 42 : : // SysSemaphore::Impl 43 : : struct SysSemaphore::Impl final { 44 : : #if defined(_WIN32) || defined(__CYGWIN__) 45 : : HANDLE handle{nullptr}; 46 : : #elif defined(__APPLE__) 47 : : dispatch_semaphore_t handle{nullptr}; 48 : : #else 49 : : sem_t* handle{SEM_FAILED}; 50 : : std::string name; 51 : : bool is_create{false}; 52 : : #endif 53 : : size_t count{0}; 54 : : }; 55 : : 56 : : // SysSemaphore 57 : 22 : SysSemaphore::SysSemaphore(size_t count) : impl_(std::make_unique<Impl>()) { impl_->count = count; } 58 : : 59 : 22 : SysSemaphore::~SysSemaphore() { 60 [ + + ]: 22 : if VLIKELY (is_attached()) { 61 : 3 : detach(false); 62 : : } 63 : 22 : } 64 : : 65 : 19 : bool SysSemaphore::attach(const std::string& name) { 66 [ + + ]: 19 : if VUNLIKELY (is_attached()) { 67 [ + - + - ]: 2 : VLOG_E("SysSemaphore: Already attached."); 68 : 1 : return false; 69 : : } 70 : : 71 : : #if defined(_WIN32) || defined(__CYGWIN__) 72 : : impl_->handle = ::CreateSemaphore(nullptr, impl_->count, MAXLONG, name.c_str()); 73 : : 74 : : if VUNLIKELY (!impl_->handle) { 75 : : VLOG_E("SysSemaphore: CreateSemaphore failed."); 76 : : return false; 77 : : } 78 : : 79 : : return true; 80 : : 81 : : #elif defined(__APPLE__) 82 : : (void)name; 83 : : impl_->handle = dispatch_semaphore_create(static_cast<int64_t>(impl_->count)); 84 : : 85 : : if VUNLIKELY (!impl_->handle) { 86 : : VLOG_E("SysSemaphore: dispatch_semaphore_create failed."); 87 : : return false; 88 : : } 89 : : 90 : : return true; 91 : : 92 : : #else 93 : : // ::sem_unlink(impl_->name.c_str()); // clear 94 : 18 : int oflag = O_CREAT | O_EXCL; 95 [ + - ]: 21 : for (int i = 0, max_try_times = 1; i < max_try_times; ++i) { 96 : : do { 97 : 21 : impl_->handle = ::sem_open(name.c_str(), oflag, 0600, impl_->count); 98 [ + + - + : 21 : } while (impl_->handle == SEM_FAILED && errno == EINTR); - + ] 99 : : 100 [ + + + - : 21 : if (impl_->handle == SEM_FAILED && errno == EEXIST) { + + ] 101 : 3 : oflag &= ~O_EXCL; 102 : 3 : max_try_times = 2; 103 : : } else { 104 : 18 : break; 105 : : } 106 : : } 107 : : 108 [ - + ]: 18 : if VUNLIKELY (impl_->handle == SEM_FAILED) { 109 : : VLOG_E("SysSemaphore: sem_open failed."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 110 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 111 : : } 112 : : 113 : 18 : impl_->name = name; 114 : 18 : impl_->is_create = (oflag & O_EXCL) != 0; 115 : : 116 : 18 : return true; 117 : : #endif 118 : : } 119 : : 120 : 20 : bool SysSemaphore::detach(bool force) { 121 [ + + ]: 20 : if VUNLIKELY (!is_attached()) { 122 [ + - + - ]: 4 : VLOG_E("SysSemaphore: Not attached."); 123 : 2 : return false; 124 : : } 125 : : 126 : : #if defined(_WIN32) || defined(__CYGWIN__) 127 : : (void)force; 128 : : 129 : : if VUNLIKELY (!::CloseHandle(impl_->handle)) { 130 : : VLOG_E("SysSemaphore: CloseHandle failed."); 131 : : return false; 132 : : } 133 : : 134 : : impl_->handle = nullptr; 135 : : 136 : : return true; 137 : : 138 : : #elif defined(__APPLE__) 139 : : (void)force; 140 : : impl_->handle = nullptr; 141 : : return true; 142 : : 143 : : #else 144 : : 145 [ - + ]: 18 : if VUNLIKELY (impl_->handle == SEM_FAILED) { 146 : : VLOG_E("SysSemaphore: Handle is empty."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 147 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 148 : : } 149 : : 150 : 18 : bool ok = true; 151 : : 152 [ - + ]: 18 : if VUNLIKELY (::sem_close(impl_->handle) == -1) { 153 : : VLOG_E("SysSemaphore: sem_close failed."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 154 : : ok = false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 155 : : } 156 : : 157 : 18 : impl_->handle = SEM_FAILED; 158 : : 159 [ + + + - : 18 : if (force && !impl_->name.empty()) { + + ] 160 [ - + - - : 13 : if VUNLIKELY (::sem_unlink(impl_->name.c_str()) == -1 && errno != ENOENT) { - + ] 161 : : VLOG_E("SysSemaphore: sem_unlink failed."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 162 : : ok = false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 163 : : } 164 : : } 165 : : 166 : 18 : impl_->is_create = false; 167 : 18 : impl_->name.clear(); 168 : 18 : return ok; 169 : : #endif 170 : : } 171 : : 172 : 13 : bool SysSemaphore::acquire(size_t n, int timeout_ms) { 173 [ + + ]: 13 : if VUNLIKELY (!is_attached()) { 174 [ + - + - ]: 2 : VLOG_E("SysSemaphore: Not attached."); 175 : 1 : return false; 176 : : } 177 : : 178 : : #if defined(_WIN32) || defined(__CYGWIN__) 179 : : size_t acquired = 0; 180 : : 181 : : if (timeout_ms < 0) { 182 : : for (; n > 0; --n) { 183 : : if VUNLIKELY (::WaitForSingleObjectEx(impl_->handle, INFINITE, FALSE) != WAIT_OBJECT_0) { 184 : : VLOG_E("SysSemaphore: WaitForSingleObjectEx failed."); 185 : : if (acquired > 0) { 186 : : release(acquired); 187 : : } 188 : : return false; 189 : : } 190 : : 191 : : ++acquired; 192 : : } 193 : : } else { 194 : : const auto start_time = std::chrono::steady_clock::now(); 195 : : 196 : : for (; n > 0; --n) { 197 : : const auto elapsed = 198 : : std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_time).count(); 199 : : const auto remaining_timeout = static_cast<DWORD>(elapsed >= timeout_ms ? 0 : timeout_ms - elapsed); 200 : : const auto wait_result = ::WaitForSingleObjectEx(impl_->handle, remaining_timeout, FALSE); 201 : : 202 : : if VLIKELY (wait_result == WAIT_OBJECT_0) { 203 : : ++acquired; 204 : : continue; 205 : : } 206 : : 207 : : if VUNLIKELY (wait_result != WAIT_TIMEOUT) { 208 : : VLOG_E("SysSemaphore: WaitForSingleObjectEx failed."); 209 : : } 210 : : 211 : : if (acquired > 0) { 212 : : release(acquired); 213 : : } 214 : : 215 : : return false; 216 : : } 217 : : } 218 : : 219 : : return true; 220 : : #elif defined(__APPLE__) 221 : : for (; n > 0; --n) { 222 : : if (timeout_ms < 0) { 223 : : dispatch_semaphore_wait(impl_->handle, DISPATCH_TIME_FOREVER); 224 : : } else { 225 : : int64_t timeout_ns = static_cast<int64_t>(timeout_ms) * 1000000; 226 : : if VUNLIKELY (dispatch_semaphore_wait(impl_->handle, dispatch_time(DISPATCH_TIME_NOW, timeout_ns)) != 0) { 227 : : VLOG_E("SysSemaphore: dispatch_semaphore_wait timeout or failed."); 228 : : return false; 229 : : } 230 : : } 231 : : } 232 : : 233 : : return true; 234 : : 235 : : #else 236 : 12 : size_t acquired = 0; 237 : : 238 [ + + ]: 12 : if (timeout_ms < 0) { 239 [ + + ]: 2 : for (; n > 0; --n) { 240 : 1 : int rc = -1; 241 : : do { 242 : 1 : rc = ::sem_wait(impl_->handle); 243 [ - + - - ]: 1 : } while (rc == -1 && errno == EINTR); 244 : : 245 [ - + ]: 1 : if VUNLIKELY (rc == -1) { 246 : : // if (is_attached()) { 247 : : // VLOG_E("Sys semaphore sem_wait failed."); 248 : : // } 249 : : 250 : : // LCOV_EXCL_START GCOVR_EXCL_START 251 : : if (acquired > 0) { 252 : : release(acquired); 253 : : } 254 : : 255 : : return false; 256 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP 257 : : } 258 : 1 : ++acquired; 259 : : } 260 : : } else { 261 : : struct timespec ts; 262 : : 263 [ - + ]: 11 : if VUNLIKELY (::clock_gettime(CLOCK_REALTIME, &ts) == -1) { 264 : : VLOG_E("SysSemaphore: clock_gettime failed."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 265 : 4 : return false; 266 : : } 267 : : 268 : 11 : ts.tv_sec += timeout_ms / 1000; 269 : 11 : ts.tv_nsec += (timeout_ms % 1000) * 1000'000; 270 : : 271 [ + + ]: 11 : if (ts.tv_nsec >= 1000'000'000) { 272 : 1 : ts.tv_sec++; 273 : 1 : ts.tv_nsec -= 1000'000'000; 274 : : } 275 : : 276 [ + + ]: 20 : for (; n > 0; --n) { 277 : 13 : int rc = -1; 278 : : do { 279 [ + - ]: 13 : rc = ::sem_timedwait(impl_->handle, &ts); 280 [ + + - + ]: 13 : } while (rc == -1 && errno == EINTR); 281 : : 282 [ + + ]: 13 : if VUNLIKELY (rc == -1) { 283 : : // if (is_attached()) { 284 : : // if (errno == ETIMEDOUT) { 285 : : // VLOG_E("Sys semaphore sem_timedwait timed out."); 286 : : // } else { 287 : : // VLOG_E("Sys semaphore sem_timedwait failed."); 288 : : // } 289 : : // } 290 : : 291 [ + + ]: 4 : if (acquired > 0) { 292 [ + - ]: 1 : release(acquired); 293 : : } 294 : : 295 : 4 : return false; 296 : : } 297 : 9 : ++acquired; 298 : : } 299 : : } 300 : : 301 : 8 : return true; 302 : : #endif 303 : : } 304 : : 305 : 10 : void SysSemaphore::release(size_t n) { 306 [ + + ]: 10 : if VUNLIKELY (!is_attached()) { 307 [ + - + - ]: 2 : VLOG_E("SysSemaphore: Not attached."); 308 : 1 : return; 309 : : } 310 : : 311 [ + + ]: 9 : if VUNLIKELY (n == 0) { 312 : 1 : return; 313 : : } 314 : : 315 : : #if defined(_WIN32) || defined(__CYGWIN__) 316 : : 317 : : if VUNLIKELY (!::ReleaseSemaphore(impl_->handle, n, nullptr)) { 318 : : VLOG_E("SysSemaphore: ReleaseSemaphore failed."); 319 : : return; 320 : : } 321 : : 322 : : #elif defined(__APPLE__) 323 : : for (; n > 0; --n) { 324 : : dispatch_semaphore_signal(impl_->handle); 325 : : } 326 : : 327 : : #else 328 [ + + ]: 20 : for (; n > 0; --n) { 329 [ - + ]: 12 : if VUNLIKELY (::sem_post(impl_->handle) == -1) { 330 : : VLOG_E("SysSemaphore: sem_post failed."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 331 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 332 : : } 333 : : } 334 : : #endif 335 : : } 336 : : 337 : 109 : bool SysSemaphore::is_attached() const { 338 : : #if defined(_WIN32) || defined(__CYGWIN__) 339 : : return impl_->handle != nullptr; 340 : : #elif defined(__APPLE__) 341 : : return impl_->handle != nullptr; 342 : : #else 343 [ + + + - ]: 109 : return impl_->handle != SEM_FAILED && !impl_->name.empty(); 344 : : #endif 345 : : } 346 : : 347 : 11 : size_t SysSemaphore::get_count() const { 348 [ + - + + ]: 11 : if VUNLIKELY (!is_attached()) { 349 [ + - + - ]: 4 : VLOG_E("SysSemaphore: Not attached."); 350 : 2 : return 0; 351 : : } 352 : : 353 : : #if defined(_WIN32) || defined(__CYGWIN__) 354 : : VLOG_E("SysSemaphore: Querying current count is not supported."); 355 : : return 0; 356 : : 357 : : #elif defined(__APPLE__) 358 : : VLOG_E("SysSemaphore: Querying current count is not supported."); 359 : : 360 : : return 0; 361 : : 362 : : #else 363 : 9 : int count = 0; 364 : : 365 [ - + ]: 9 : if VUNLIKELY (::sem_getvalue(impl_->handle, &count) != 0) { 366 : : VLOG_E("SysSemaphore: sem_getvalue failed."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 367 : : return 0; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 368 : : } 369 : : 370 : 9 : return (count >= 0) ? static_cast<size_t>(count) : 0; 371 : : #endif 372 : : } 373 : : 374 : : } // namespace vlink