LCOV - code coverage report
Current view: top level - src/base - sys_sharemem.cc (source / functions) Hit Total Coverage
Test: vlink Lines: 69 71 97.2 %
Date: 2026-06-27 19:56:04 Functions: 14 15 93.3 %
Branches: 61 96 63.5 %

           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_sharemem.h"
      25                 :            : 
      26                 :            : #include <string>
      27                 :            : 
      28                 :            : #include "./base/logger.h"
      29                 :            : 
      30                 :            : #if __has_include(<unistd.h>)
      31                 :            : #include <unistd.h>
      32                 :            : #endif
      33                 :            : 
      34                 :            : #if defined(_WIN32) || defined(__CYGWIN__)
      35                 :            : #include <Windows.h>
      36                 :            : #else
      37                 :            : #include <fcntl.h>
      38                 :            : #include <sys/mman.h>
      39                 :            : #include <sys/stat.h>
      40                 :            : #include <sys/types.h>
      41                 :            : #endif
      42                 :            : 
      43                 :            : namespace vlink {
      44                 :            : 
      45                 :            : // SysSharemem::Impl
      46                 :            : struct SysSharemem::Impl final {
      47                 :            : #if defined(_WIN32) || defined(__CYGWIN__)
      48                 :            :   HANDLE handle{nullptr};
      49                 :            : #else
      50                 :            :   int handle{-1};
      51                 :            : #endif
      52                 :            : 
      53                 :            :   void* data{nullptr};
      54                 :            :   size_t size{0};
      55                 :            :   std::string name;
      56                 :            :   SysSharemem::Mode mode{SysSharemem::kReadWrite};
      57                 :            : };
      58                 :            : 
      59                 :            : // SysSharemem
      60                 :         25 : SysSharemem::SysSharemem() : impl_(std::make_unique<Impl>()) {}
      61                 :            : 
      62                 :         25 : SysSharemem::~SysSharemem() {
      63         [ +  + ]:         25 :   if VLIKELY (is_attached()) {
      64                 :          1 :     detach(false);
      65                 :            : #if defined(_WIN32) || defined(__CYGWIN__)
      66                 :            :   } else if VUNLIKELY (impl_->handle != nullptr) {
      67                 :            :     ::CloseHandle(impl_->handle);
      68                 :            :     impl_->handle = nullptr;
      69                 :            : #endif
      70                 :            :   }
      71                 :         25 : }
      72                 :            : 
      73                 :         17 : bool SysSharemem::create(const std::string& name, size_t size, Mode mode) {
      74         [ +  + ]:         17 :   if VUNLIKELY (is_attached()) {
      75   [ +  -  +  - ]:          2 :     VLOG_E("SysSharemem: Already attached.");
      76                 :          1 :     return false;
      77                 :            :   }
      78                 :            : 
      79                 :            : #if defined(_WIN32) || defined(__CYGWIN__)
      80                 :            :   DWORD high = 0;
      81                 :            :   DWORD low = 0;
      82                 :            : 
      83                 :            :   if constexpr (sizeof(size_t) == 8) {
      84                 :            :     high = static_cast<DWORD>(static_cast<uint64_t>(size) >> 32);
      85                 :            :   }
      86                 :            : 
      87                 :            :   low = static_cast<DWORD>(static_cast<size_t>(size) & 0xFFFFFFFF);  // NOLINT(readability-redundant-casting)
      88                 :            :   impl_->handle = ::CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, high, low, name.c_str());
      89                 :            : 
      90                 :            :   if VUNLIKELY (!impl_->handle) {
      91                 :            :     VLOG_E("SysSharemem: CreateFileMapping failed.");
      92                 :            :     return false;
      93                 :            :   }
      94                 :            : 
      95                 :            :   if VUNLIKELY (::GetLastError() == ERROR_ALREADY_EXISTS) {
      96                 :            :     ::CloseHandle(impl_->handle);
      97                 :            :     impl_->handle = nullptr;
      98                 :            :     VLOG_E("SysSharemem: Shared memory already exists.");
      99                 :            :     return false;
     100                 :            :   }
     101                 :            : 
     102                 :            :   return attach(name, mode);
     103                 :            : #elif defined(__ANDROID__)
     104                 :            :   (void)name;
     105                 :            :   (void)size;
     106                 :            :   (void)mode;
     107                 :            : 
     108                 :            :   VLOG_E("SysSharemem: shm_open is not supported on this platform.");
     109                 :            : 
     110                 :            :   return false;
     111                 :            : #else
     112                 :            : 
     113                 :         16 :   int fd = ::shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
     114                 :            : 
     115         [ +  + ]:         16 :   if VUNLIKELY (fd == -1) {
     116                 :          1 :     return false;
     117                 :            :   }
     118                 :            : 
     119                 :         15 :   int ret = ::ftruncate(fd, size);
     120                 :            : 
     121         [ -  + ]:         15 :   if VUNLIKELY (ret == -1) {
     122                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
     123                 :            :     ::close(fd);
     124                 :            :     ::shm_unlink(name.c_str());
     125                 :            :     VLOG_E("SysSharemem: ftruncate failed.");
     126                 :            :     return false;
     127                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     128                 :            :   }
     129                 :            : 
     130                 :         15 :   ::close(fd);
     131                 :            : 
     132         [ -  + ]:         15 :   if VUNLIKELY (!attach(name, mode)) {
     133                 :            :     ::shm_unlink(name.c_str());  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     134                 :            :     return false;                // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     135                 :            :   }
     136                 :            : 
     137                 :         15 :   return true;
     138                 :            : #endif
     139                 :            : }
     140                 :            : 
     141                 :         22 : bool SysSharemem::attach(const std::string& name, Mode mode) {
     142   [ +  -  -  + ]:         22 :   if VUNLIKELY (is_attached()) {
     143   [ #  #  #  # ]:          0 :     VLOG_E("SysSharemem: Already attached.");
     144                 :          0 :     return false;
     145                 :            :   }
     146                 :            : 
     147                 :            : #if defined(_WIN32) || defined(__CYGWIN__)
     148                 :            :   const DWORD access = (mode == kReadOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS);
     149                 :            : 
     150                 :            :   if (!impl_->handle) {
     151                 :            :     // NOLINTNEXTLINE(readability-implicit-bool-conversion)
     152                 :            :     impl_->handle = ::OpenFileMapping(access, false, name.c_str());
     153                 :            : 
     154                 :            :     if (!impl_->handle) {
     155                 :            :       return false;
     156                 :            :     }
     157                 :            :   }
     158                 :            : 
     159                 :            :   impl_->data = ::MapViewOfFile(impl_->handle, access, 0, 0, 0);
     160                 :            : 
     161                 :            :   if VUNLIKELY (!impl_->data) {
     162                 :            :     VLOG_E("SysSharemem: MapViewOfFile failed.");
     163                 :            :     ::CloseHandle(impl_->handle);
     164                 :            :     impl_->handle = nullptr;
     165                 :            : 
     166                 :            :     return false;
     167                 :            :   }
     168                 :            : 
     169                 :            :   MEMORY_BASIC_INFORMATION info;
     170                 :            : 
     171                 :            :   if VUNLIKELY (!::VirtualQuery(impl_->data, &info, sizeof(info))) {
     172                 :            :     VLOG_E("SysSharemem: VirtualQuery failed.");
     173                 :            :     ::UnmapViewOfFile(impl_->data);
     174                 :            :     impl_->data = nullptr;
     175                 :            :     ::CloseHandle(impl_->handle);
     176                 :            :     impl_->handle = nullptr;
     177                 :            : 
     178                 :            :     return false;
     179                 :            :   }
     180                 :            : 
     181                 :            :   impl_->size = static_cast<size_t>(info.RegionSize);
     182                 :            :   impl_->name = name;
     183                 :            :   impl_->mode = mode;
     184                 :            : 
     185                 :            :   return true;
     186                 :            : #elif defined(__ANDROID__)
     187                 :            :   (void)name;
     188                 :            :   (void)mode;
     189                 :            :   VLOG_E("SysSharemem: shm_open is not supported on this platform.");
     190                 :            : 
     191                 :            :   return false;
     192                 :            : #else
     193                 :            :   // ::shm_unlink(impl_->name.c_str());  // clear
     194         [ +  + ]:         22 :   const int oflag = (mode == kReadOnly ? O_RDONLY : O_RDWR);
     195         [ +  + ]:         22 :   const mode_t omode = (mode == kReadOnly ? 0400 : 0600);
     196                 :            : 
     197         [ +  - ]:         22 :   impl_->handle = ::shm_open(name.c_str(), oflag | O_CLOEXEC, omode);
     198                 :            : 
     199         [ +  + ]:         22 :   if VUNLIKELY (impl_->handle == -1) {
     200                 :          1 :     return false;
     201                 :            :   }
     202                 :            : 
     203                 :            :   struct stat st;
     204                 :            : 
     205         [ -  + ]:         21 :   if VUNLIKELY (::fstat(impl_->handle, &st) == -1) {
     206                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
     207                 :            :     VLOG_E("SysSharemem: fstat failed.");
     208                 :            :     ::close(impl_->handle);
     209                 :            :     impl_->handle = -1;
     210                 :            :     impl_->size = 0;
     211                 :            :     return false;
     212                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     213                 :            :   }
     214                 :            : 
     215                 :         21 :   impl_->size = static_cast<size_t>(st.st_size);
     216                 :            : 
     217         [ +  + ]:         21 :   const int mprot = (mode == kReadOnly ? PROT_READ : PROT_READ | PROT_WRITE);
     218                 :         21 :   impl_->data = ::mmap(nullptr, impl_->size, mprot, MAP_SHARED, impl_->handle, 0);
     219                 :            : 
     220   [ +  -  -  +  :         21 :   if VUNLIKELY (impl_->data == MAP_FAILED || !impl_->data) {
                   -  + ]
     221                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
     222                 :            :     VLOG_E("SysSharemem: mmap failed.");
     223                 :            : 
     224                 :            :     impl_->data = nullptr;
     225                 :            :     impl_->size = 0;
     226                 :            :     ::close(impl_->handle);
     227                 :            :     impl_->handle = -1;
     228                 :            : 
     229                 :            :     return false;
     230                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     231                 :            :   }
     232                 :            : 
     233                 :            : #ifdef F_ADD_SEALS
     234         [ +  - ]:         21 :   ::fcntl(impl_->handle, F_ADD_SEALS, F_SEAL_SHRINK);
     235                 :            : #endif
     236                 :            : 
     237         [ +  - ]:         21 :   impl_->name = name;
     238                 :         21 :   impl_->mode = mode;
     239                 :         21 :   return true;
     240                 :            : #endif
     241                 :            : }
     242                 :            : 
     243                 :         23 : bool SysSharemem::detach(bool force) {
     244         [ +  + ]:         23 :   if VUNLIKELY (!is_attached()) {
     245   [ +  -  +  - ]:          4 :     VLOG_E("SysSharemem: Not attached.");
     246                 :          2 :     return false;
     247                 :            :   }
     248                 :            : 
     249                 :            : #if defined(_WIN32) || defined(__CYGWIN__)
     250                 :            :   (void)force;
     251                 :            :   bool ok = true;
     252                 :            : 
     253                 :            :   if VUNLIKELY (!::UnmapViewOfFile(impl_->data)) {
     254                 :            :     VLOG_E("SysSharemem: UnmapViewOfFile failed.");
     255                 :            :     ok = false;
     256                 :            :   }
     257                 :            : 
     258                 :            :   impl_->data = nullptr;
     259                 :            :   impl_->size = 0;
     260                 :            :   impl_->name.clear();
     261                 :            :   impl_->mode = kReadWrite;
     262                 :            : 
     263                 :            :   // NOLINTNEXTLINE(readability-implicit-bool-conversion)
     264                 :            : 
     265                 :            :   if VUNLIKELY (!::CloseHandle(impl_->handle)) {
     266                 :            :     VLOG_E("SysSharemem: CloseHandle failed.");
     267                 :            :     ok = false;
     268                 :            :   }
     269                 :            : 
     270                 :            :   impl_->handle = nullptr;
     271                 :            : 
     272                 :            :   return ok;
     273                 :            : #elif defined(__ANDROID__)
     274                 :            :   (void)force;
     275                 :            :   VLOG_E("SysSharemem: shm_unlink is not supported on this platform.");
     276                 :            : 
     277                 :            :   return false;
     278                 :            : #else
     279                 :         21 :   bool ok = true;
     280                 :            : 
     281         [ -  + ]:         21 :   if VUNLIKELY (::munmap(impl_->data, size_t(impl_->size)) == -1) {
     282                 :            :     VLOG_E("SysSharemem: munmap failed.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     283                 :            :     ok = false;                             // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     284                 :            :   }
     285                 :            : 
     286         [ +  + ]:         21 :   int shm_nattch = force ? 0 : -1;
     287                 :            : 
     288                 :            : #ifdef __QNX__
     289                 :            :   struct stat st;
     290                 :            : 
     291                 :            :   if (::fstat(impl_->handle, &st) == 0) {
     292                 :            :     shm_nattch = st.st_nlink - 2;
     293                 :            :   }
     294                 :            : #endif
     295                 :            : 
     296         [ +  - ]:         21 :   if (impl_->handle != -1) {
     297                 :         21 :     ::close(impl_->handle);
     298                 :         21 :     impl_->handle = -1;
     299                 :            :   }
     300                 :            : 
     301   [ +  +  +  -  :         21 :   if (shm_nattch == 0 && !impl_->name.empty()) {
                   +  + ]
     302   [ -  +  -  -  :         15 :     if VUNLIKELY (::shm_unlink(impl_->name.c_str()) == -1 && errno != ENOENT) {
                   -  + ]
     303                 :            :       VLOG_E("SysSharemem: shm_unlink failed.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     304                 :            :       ok = false;                                 // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     305                 :            :     }
     306                 :            :   }
     307                 :            : 
     308                 :         21 :   impl_->data = nullptr;
     309                 :         21 :   impl_->size = 0;
     310                 :         21 :   impl_->name.clear();
     311                 :         21 :   impl_->mode = kReadWrite;
     312                 :            : 
     313                 :         21 :   return ok;
     314                 :            : #endif
     315                 :            : }
     316                 :            : 
     317                 :        134 : bool SysSharemem::is_attached() const {
     318                 :            : #if defined(_WIN32) || defined(__CYGWIN__)
     319                 :            :   return impl_->handle != nullptr && impl_->data != nullptr && impl_->size != 0 && !impl_->name.empty();
     320                 :            : #else
     321   [ +  +  +  -  :        134 :   return impl_->handle != -1 && impl_->data != nullptr && impl_->size != 0 && !impl_->name.empty();
             +  -  +  - ]
     322                 :            : #endif
     323                 :            : }
     324                 :            : 
     325                 :         15 : void* SysSharemem::data() {
     326         [ +  + ]:         15 :   if VUNLIKELY (!is_attached()) {
     327   [ +  -  +  - ]:          6 :     VLOG_E("SysSharemem: Not attached.");
     328                 :          3 :     return nullptr;
     329                 :            :   }
     330                 :            : 
     331         [ +  + ]:         12 :   if VUNLIKELY (impl_->mode == kReadOnly) {
     332                 :          1 :     return nullptr;
     333                 :            :   }
     334                 :            : 
     335                 :         11 :   return impl_->data;
     336                 :            : }
     337                 :            : 
     338                 :          7 : const void* SysSharemem::data() const {
     339         [ +  + ]:          7 :   if VUNLIKELY (!is_attached()) {
     340   [ +  -  +  - ]:          2 :     VLOG_E("SysSharemem: Not attached.");
     341                 :          1 :     return nullptr;
     342                 :            :   }
     343                 :            : 
     344                 :          6 :   return impl_->data;
     345                 :            : }
     346                 :            : 
     347                 :          9 : size_t SysSharemem::size() const {
     348         [ +  + ]:          9 :   if VUNLIKELY (!is_attached()) {
     349   [ +  -  +  - ]:          6 :     VLOG_E("SysSharemem: Not attached.");
     350                 :          3 :     return 0;
     351                 :            :   }
     352                 :            : 
     353                 :          6 :   return impl_->size;
     354                 :            : }
     355                 :            : 
     356                 :            : }  // namespace vlink

Generated by: LCOV version 1.14