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(0); 84 : : 85 : : if VUNLIKELY (!impl_->handle) { 86 : : VLOG_E("SysSemaphore: dispatch_semaphore_create failed."); 87 : : return false; 88 : : } 89 : : 90 : : for (size_t i = 0; i < impl_->count; ++i) { 91 : : dispatch_semaphore_signal(impl_->handle); 92 : : } 93 : : 94 : : return true; 95 : : 96 : : #else 97 : : // ::sem_unlink(impl_->name.c_str()); // clear 98 : 18 : int oflag = O_CREAT | O_EXCL; 99 [ + - ]: 21 : for (int i = 0, max_try_times = 1; i < max_try_times; ++i) { 100 : : do { 101 : 21 : impl_->handle = ::sem_open(name.c_str(), oflag, 0600, impl_->count); 102 [ + + - + : 21 : } while (impl_->handle == SEM_FAILED && errno == EINTR); - + ] 103 : : 104 [ + + + - : 21 : if (impl_->handle == SEM_FAILED && errno == EEXIST) { + + ] 105 : 3 : oflag &= ~O_EXCL; 106 : 3 : max_try_times = 2; 107 : : } else { 108 : 18 : break; 109 : : } 110 : : } 111 : : 112 [ - + ]: 18 : if VUNLIKELY (impl_->handle == SEM_FAILED) { 113 : : VLOG_E("SysSemaphore: sem_open failed."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 114 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 115 : : } 116 : : 117 : 18 : impl_->name = name; 118 : 18 : impl_->is_create = (oflag & O_EXCL) != 0; 119 : : 120 : 18 : return true; 121 : : #endif 122 : : } 123 : : 124 : 20 : bool SysSemaphore::detach(bool force) { 125 [ + + ]: 20 : if VUNLIKELY (!is_attached()) { 126 [ + - + - : 4 : VLOG_E("SysSemaphore: Not attached."); + - ] 127 : 2 : return false; 128 : : } 129 : : 130 : : #if defined(_WIN32) || defined(__CYGWIN__) 131 : : (void)force; 132 : : 133 : : if VUNLIKELY (!::CloseHandle(impl_->handle)) { 134 : : VLOG_E("SysSemaphore: CloseHandle failed."); 135 : : return false; 136 : : } 137 : : 138 : : impl_->handle = nullptr; 139 : : 140 : : return true; 141 : : 142 : : #elif defined(__APPLE__) 143 : : (void)force; 144 : : dispatch_release(impl_->handle); 145 : : impl_->handle = nullptr; 146 : : return true; 147 : : 148 : : #else 149 : : 150 [ - + ]: 18 : if VUNLIKELY (impl_->handle == SEM_FAILED) { 151 : : VLOG_E("SysSemaphore: Handle is empty."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 152 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 153 : : } 154 : : 155 : 18 : bool ok = true; 156 : : 157 [ - + ]: 18 : if VUNLIKELY (::sem_close(impl_->handle) == -1) { 158 : : VLOG_E("SysSemaphore: sem_close failed."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 159 : : ok = false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 160 : : } 161 : : 162 : 18 : impl_->handle = SEM_FAILED; 163 : : 164 [ + + + - : 18 : if (force && !impl_->name.empty()) { + + ] 165 [ - + - - : 13 : if VUNLIKELY (::sem_unlink(impl_->name.c_str()) == -1 && errno != ENOENT) { - + ] 166 : : VLOG_E("SysSemaphore: sem_unlink failed."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 167 : : ok = false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 168 : : } 169 : : } 170 : : 171 : 18 : impl_->is_create = false; 172 : 18 : impl_->name.clear(); 173 : 18 : return ok; 174 : : #endif 175 : : } 176 : : 177 : 13 : bool SysSemaphore::acquire(size_t n, int timeout_ms) { 178 [ + + ]: 13 : if VUNLIKELY (!is_attached()) { 179 [ + - + - : 2 : VLOG_E("SysSemaphore: Not attached."); + - ] 180 : 1 : return false; 181 : : } 182 : : 183 : : #if defined(_WIN32) || defined(__CYGWIN__) 184 : : size_t acquired = 0; 185 : : 186 : : if (timeout_ms < 0) { 187 : : for (; n > 0; --n) { 188 : : if VUNLIKELY (::WaitForSingleObjectEx(impl_->handle, INFINITE, FALSE) != WAIT_OBJECT_0) { 189 : : VLOG_E("SysSemaphore: WaitForSingleObjectEx failed."); 190 : : if (acquired > 0) { 191 : : release(acquired); 192 : : } 193 : : return false; 194 : : } 195 : : 196 : : ++acquired; 197 : : } 198 : : } else { 199 : : const auto start_time = std::chrono::steady_clock::now(); 200 : : 201 : : for (; n > 0; --n) { 202 : : const auto elapsed = 203 : : std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_time).count(); 204 : : const auto remaining_timeout = static_cast<DWORD>(elapsed >= timeout_ms ? 0 : timeout_ms - elapsed); 205 : : const auto wait_result = ::WaitForSingleObjectEx(impl_->handle, remaining_timeout, FALSE); 206 : : 207 : : if VLIKELY (wait_result == WAIT_OBJECT_0) { 208 : : ++acquired; 209 : : continue; 210 : : } 211 : : 212 : : if VUNLIKELY (wait_result != WAIT_TIMEOUT) { 213 : : VLOG_E("SysSemaphore: WaitForSingleObjectEx failed."); 214 : : } 215 : : 216 : : if (acquired > 0) { 217 : : release(acquired); 218 : : } 219 : : 220 : : return false; 221 : : } 222 : : } 223 : : 224 : : return true; 225 : : #elif defined(__APPLE__) 226 : : if (timeout_ms < 0) { 227 : : for (; n > 0; --n) { 228 : : dispatch_semaphore_wait(impl_->handle, DISPATCH_TIME_FOREVER); 229 : : } 230 : : 231 : : return true; 232 : : } 233 : : 234 : : size_t acquired = 0; 235 : : const int64_t timeout_ns = static_cast<int64_t>(timeout_ms) * 1000000; 236 : : const dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, timeout_ns); 237 : : 238 : : for (; n > 0; --n) { 239 : : if VUNLIKELY (dispatch_semaphore_wait(impl_->handle, timeout) != 0) { 240 : : VLOG_E("SysSemaphore: dispatch_semaphore_wait timeout or failed."); 241 : : 242 : : if (acquired > 0) { 243 : : release(acquired); 244 : : } 245 : : 246 : : return false; 247 : : } 248 : : 249 : : ++acquired; 250 : : } 251 : : 252 : : return true; 253 : : 254 : : #else 255 : 12 : size_t acquired = 0; 256 : : 257 [ + + ]: 12 : if (timeout_ms < 0) { 258 [ + + ]: 2 : for (; n > 0; --n) { 259 : 1 : int rc = -1; 260 : : do { 261 : 1 : rc = ::sem_wait(impl_->handle); 262 [ - + - - ]: 1 : } while (rc == -1 && errno == EINTR); 263 : : 264 [ - + ]: 1 : if VUNLIKELY (rc == -1) { 265 : : // if (is_attached()) { 266 : : // VLOG_E("Sys semaphore sem_wait failed."); 267 : : // } 268 : : 269 : : // LCOV_EXCL_START GCOVR_EXCL_START 270 : : if (acquired > 0) { 271 : : release(acquired); 272 : : } 273 : : 274 : : return false; 275 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP 276 : : } 277 : 1 : ++acquired; 278 : : } 279 : : } else { 280 : : struct timespec ts; 281 : : 282 [ - + ]: 11 : if VUNLIKELY (::clock_gettime(CLOCK_REALTIME, &ts) == -1) { 283 : : VLOG_E("SysSemaphore: clock_gettime failed."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 284 : 4 : return false; 285 : : } 286 : : 287 : 11 : ts.tv_sec += timeout_ms / 1000; 288 : 11 : ts.tv_nsec += (timeout_ms % 1000) * 1000'000; 289 : : 290 [ + + ]: 11 : if (ts.tv_nsec >= 1000'000'000) { 291 : 1 : ts.tv_sec++; 292 : 1 : ts.tv_nsec -= 1000'000'000; 293 : : } 294 : : 295 [ + + ]: 20 : for (; n > 0; --n) { 296 : 13 : int rc = -1; 297 : : do { 298 [ + - ]: 13 : rc = ::sem_timedwait(impl_->handle, &ts); 299 [ + + - + ]: 13 : } while (rc == -1 && errno == EINTR); 300 : : 301 [ + + ]: 13 : if VUNLIKELY (rc == -1) { 302 : : // if (is_attached()) { 303 : : // if (errno == ETIMEDOUT) { 304 : : // VLOG_E("Sys semaphore sem_timedwait timed out."); 305 : : // } else { 306 : : // VLOG_E("Sys semaphore sem_timedwait failed."); 307 : : // } 308 : : // } 309 : : 310 [ + + ]: 4 : if (acquired > 0) { 311 [ + - ]: 1 : release(acquired); 312 : : } 313 : : 314 : 4 : return false; 315 : : } 316 : 9 : ++acquired; 317 : : } 318 : : } 319 : : 320 : 8 : return true; 321 : : #endif 322 : : } 323 : : 324 : 10 : void SysSemaphore::release(size_t n) { 325 [ + + ]: 10 : if VUNLIKELY (!is_attached()) { 326 [ + - + - : 2 : VLOG_E("SysSemaphore: Not attached."); + - ] 327 : 1 : return; 328 : : } 329 : : 330 [ + + ]: 9 : if VUNLIKELY (n == 0) { 331 : 1 : return; 332 : : } 333 : : 334 : : #if defined(_WIN32) || defined(__CYGWIN__) 335 : : 336 : : if VUNLIKELY (!::ReleaseSemaphore(impl_->handle, n, nullptr)) { 337 : : VLOG_E("SysSemaphore: ReleaseSemaphore failed."); 338 : : return; 339 : : } 340 : : 341 : : #elif defined(__APPLE__) 342 : : for (; n > 0; --n) { 343 : : dispatch_semaphore_signal(impl_->handle); 344 : : } 345 : : 346 : : #else 347 [ + + ]: 20 : for (; n > 0; --n) { 348 [ - + ]: 12 : if VUNLIKELY (::sem_post(impl_->handle) == -1) { 349 : : VLOG_E("SysSemaphore: sem_post failed."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 350 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 351 : : } 352 : : } 353 : : #endif 354 : : } 355 : : 356 : 109 : bool SysSemaphore::is_attached() const { 357 : : #if defined(_WIN32) || defined(__CYGWIN__) 358 : : return impl_->handle != nullptr; 359 : : #elif defined(__APPLE__) 360 : : return impl_->handle != nullptr; 361 : : #else 362 [ + + + - ]: 109 : return impl_->handle != SEM_FAILED && !impl_->name.empty(); 363 : : #endif 364 : : } 365 : : 366 : 11 : size_t SysSemaphore::get_count() const { 367 [ + - + + ]: 11 : if VUNLIKELY (!is_attached()) { 368 [ + - + - : 4 : VLOG_E("SysSemaphore: Not attached."); + - ] 369 : 2 : return 0; 370 : : } 371 : : 372 : : #if defined(_WIN32) || defined(__CYGWIN__) 373 : : VLOG_E("SysSemaphore: Querying current count is not supported."); 374 : : return 0; 375 : : 376 : : #elif defined(__APPLE__) 377 : : VLOG_E("SysSemaphore: Querying current count is not supported."); 378 : : 379 : : return 0; 380 : : 381 : : #else 382 : 9 : int count = 0; 383 : : 384 [ - + ]: 9 : if VUNLIKELY (::sem_getvalue(impl_->handle, &count) != 0) { 385 : : VLOG_E("SysSemaphore: sem_getvalue failed."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 386 : : return 0; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 387 : : } 388 : : 389 : 9 : return (count >= 0) ? static_cast<size_t>(count) : 0; 390 : : #endif 391 : : } 392 : : 393 : : } // namespace vlink