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
|