LCOV - code coverage report
Current view: top level - src/base - sys_semaphore.cc (source / functions) Hit Total Coverage
Test: vlink Lines: 80 80 100.0 %
Date: 2026-06-27 19:56:04 Functions: 13 13 100.0 %
Branches: 72 106 67.9 %

           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

Generated by: LCOV version 1.14