Rename a pipe method, add docs
[carla.git] / source / utils / CarlaShmUtils.hpp
blob8f11b260a371485f7af2d419fadb371e86f53f18
1 /*
2 * Carla shared memory utils
3 * Copyright (C) 2013-2023 Filipe Coelho <falktx@falktx.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * For a full copy of the GNU General Public License see the doc/GPL.txt file.
18 #ifndef CARLA_SHM_UTILS_HPP_INCLUDED
19 #define CARLA_SHM_UTILS_HPP_INCLUDED
21 #include "CarlaUtils.hpp"
23 #ifdef CARLA_OS_WIN
24 struct carla_shm_t { HANDLE map; bool isServer; const char* filename; };
25 # define carla_shm_t_INIT { INVALID_HANDLE_VALUE, true, nullptr }
26 #else
27 # ifndef __WINE__
28 # include <cerrno>
29 # endif
30 # include <fcntl.h>
31 # include <sys/mman.h>
32 struct carla_shm_t { int fd; const char* filename; std::size_t size; };
33 # define carla_shm_t_INIT { -1, nullptr, 0 }
34 #endif
36 // -----------------------------------------------------------------------
37 // shared memory calls
40 * Null object returned when a shared memory operation fails.
42 static const carla_shm_t gNullCarlaShm = carla_shm_t_INIT;
45 * Check if a shared memory object is valid.
47 static inline
48 bool carla_is_shm_valid(const carla_shm_t& shm) noexcept
50 #ifdef CARLA_OS_WIN
51 return (shm.filename != nullptr);
52 #else
53 return (shm.fd >= 0);
54 #endif
58 * Initialize a shared memory object to an invalid state.
60 static inline
61 void carla_shm_init(carla_shm_t& shm) noexcept
63 shm = gNullCarlaShm;
67 * Create and open a new shared memory object.
68 * Returns an invalid object if the operation failed or the filename already exists.
70 static inline
71 carla_shm_t carla_shm_create(const char* const filename) noexcept
73 CARLA_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0', gNullCarlaShm);
75 carla_shm_t ret;
77 #ifdef CARLA_OS_WIN
78 ret.map = INVALID_HANDLE_VALUE;
79 ret.isServer = true;
80 ret.filename = carla_strdup_safe(filename);
81 #else
82 try {
83 ret.fd = ::shm_open(filename, O_CREAT|O_EXCL|O_RDWR, 0600);
84 ret.filename = (ret.fd >= 0) ? carla_strdup_safe(filename) : nullptr;
85 ret.size = 0;
87 if (ret.fd >= 0 && ret.filename == nullptr)
89 ::close(ret.fd);
90 ::shm_unlink(filename);
91 ret.fd = -1;
93 } CARLA_SAFE_EXCEPTION_RETURN("carla_shm_create", gNullCarlaShm);
94 #endif
96 return ret;
100 * Attach to an existing shared memory object.
102 static inline
103 carla_shm_t carla_shm_attach(const char* const filename) noexcept
105 CARLA_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0', gNullCarlaShm);
107 carla_shm_t ret;
109 #ifdef CARLA_OS_WIN
110 ret.map = INVALID_HANDLE_VALUE;
111 ret.isServer = false;
112 ret.filename = carla_strdup_safe(filename);
113 #else
114 try {
115 ret.fd = ::shm_open(filename, O_RDWR, 0);
116 ret.filename = nullptr;
117 ret.size = 0;
118 } CARLA_SAFE_EXCEPTION_RETURN("carla_shm_attach", gNullCarlaShm);
119 #endif
121 return ret;
125 * Close a shared memory object and invalidate it.
127 static inline
128 void carla_shm_close(carla_shm_t& shm) noexcept
130 CARLA_SAFE_ASSERT_RETURN(carla_is_shm_valid(shm),);
131 #ifdef CARLA_OS_WIN
132 if (shm.isServer) {
133 CARLA_SAFE_ASSERT(shm.map == INVALID_HANDLE_VALUE);
135 #endif
137 #ifdef CARLA_OS_WIN
138 if (shm.filename != nullptr)
139 delete[] shm.filename;
140 #else
141 try {
142 ::close(shm.fd);
144 if (shm.filename != nullptr)
146 ::shm_unlink(shm.filename);
147 delete[] shm.filename;
149 } CARLA_SAFE_EXCEPTION("carla_shm_close");
150 #endif
152 shm = gNullCarlaShm;
156 * Map a shared memory object to @a size bytes and return the memory address.
157 * @note One shared memory object can only have one mapping at a time.
159 static inline
160 void* carla_shm_map(carla_shm_t& shm, const std::size_t size) noexcept
162 CARLA_SAFE_ASSERT_RETURN(carla_is_shm_valid(shm), nullptr);
163 CARLA_SAFE_ASSERT_RETURN(size > 0, nullptr);
164 #ifdef CARLA_OS_WIN
165 CARLA_SAFE_ASSERT_RETURN(shm.map == INVALID_HANDLE_VALUE, nullptr);
166 #else
167 CARLA_SAFE_ASSERT_RETURN(shm.size == 0, nullptr);
168 #endif
170 try {
171 #ifdef CARLA_OS_WIN
172 HANDLE map;
174 if (shm.isServer)
176 SECURITY_ATTRIBUTES sa;
177 carla_zeroStruct(sa);
178 sa.nLength = sizeof(sa);
179 sa.bInheritHandle = TRUE;
181 map = ::CreateFileMappingA(INVALID_HANDLE_VALUE, &sa,
182 PAGE_READWRITE|SEC_COMMIT, 0, static_cast<DWORD>(size), shm.filename);
184 if (map == nullptr || map == INVALID_HANDLE_VALUE)
186 const DWORD errorCode = ::GetLastError();
187 carla_stderr2("CreateFileMapping failed for '%s', size:%lu, isServer:%i, errorCode:%x",
188 shm.filename, size, shm.isServer, errorCode);
189 return nullptr;
192 else
194 map = ::OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, shm.filename);
196 CARLA_SAFE_ASSERT_RETURN(map != nullptr, nullptr);
197 CARLA_SAFE_ASSERT_RETURN(map != INVALID_HANDLE_VALUE, nullptr);
200 void* const ptr = ::MapViewOfFile(map, FILE_MAP_ALL_ACCESS, 0, 0, size);
202 if (ptr == nullptr)
204 const DWORD errorCode = ::GetLastError();
205 carla_stderr2("MapViewOfFile failed for '%s', size:%lu, isServer:%i, errorCode:%u",
206 shm.filename, size, shm.isServer, errorCode);
207 ::CloseHandle(map);
208 return nullptr;
211 ::VirtualLock(ptr, size);
213 shm.map = map;
214 return ptr;
215 #else
216 if (shm.filename != nullptr)
218 const int ret(::ftruncate(shm.fd, static_cast<off_t>(size)));
219 CARLA_SAFE_ASSERT_RETURN(ret == 0, nullptr);
222 void* ptr;
224 #ifdef MAP_LOCKED
225 ptr = ::mmap(nullptr, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, shm.fd, 0);
226 CARLA_SAFE_ASSERT_RETURN(ptr != nullptr, nullptr);
228 if (ptr == MAP_FAILED)
229 #endif
231 ptr = ::mmap(nullptr, size, PROT_READ|PROT_WRITE, MAP_SHARED, shm.fd, 0);
232 CARLA_SAFE_ASSERT_RETURN(ptr != nullptr, nullptr);
234 if (ptr == MAP_FAILED)
236 carla_stderr2("carla_shm_map() - mmap failed: %s", std::strerror(errno));
237 return nullptr;
240 #ifndef MAP_LOCKED
241 try {
242 ::mlock(ptr, size);
243 } CARLA_SAFE_EXCEPTION("carla_shm_map mlock");
244 #endif
247 shm.size = size;
248 return ptr;
249 #endif
250 } CARLA_SAFE_EXCEPTION_RETURN("carla_shm_map", nullptr);
254 * Unmap a shared memory object address.
256 static inline
257 void carla_shm_unmap(carla_shm_t& shm, void* const ptr) noexcept
259 CARLA_SAFE_ASSERT_RETURN(carla_is_shm_valid(shm),);
260 CARLA_SAFE_ASSERT_RETURN(ptr != nullptr,);
261 #ifdef CARLA_OS_WIN
262 CARLA_SAFE_ASSERT_RETURN(shm.map != INVALID_HANDLE_VALUE,);
263 #else
264 CARLA_SAFE_ASSERT_RETURN(shm.size > 0,);
265 #endif
267 try {
268 #ifdef CARLA_OS_WIN
269 const HANDLE map = shm.map;
270 shm.map = INVALID_HANDLE_VALUE;
272 ::UnmapViewOfFile(ptr);
273 ::CloseHandle(map);
274 #else
275 const std::size_t size(shm.size);
276 shm.size = 0;
278 const int ret(::munmap(ptr, size));
279 CARLA_SAFE_ASSERT(ret == 0);
280 #endif
281 } CARLA_SAFE_EXCEPTION("carla_shm_unmap");
284 #ifndef __WINE__
285 // -----------------------------------------------------------------------
286 // advanced calls
289 * Create and open a new shared memory object for a XXXXXX temp filename.
290 * Will keep trying until a free random filename is obtained.
292 static inline
293 carla_shm_t carla_shm_create_temp(char* const fileBase) noexcept
295 // check if the fileBase name is valid
296 CARLA_SAFE_ASSERT_RETURN(fileBase != nullptr, gNullCarlaShm);
298 const std::size_t fileBaseLen(std::strlen(fileBase));
300 CARLA_SAFE_ASSERT_RETURN(fileBaseLen > 6, gNullCarlaShm);
301 CARLA_SAFE_ASSERT_RETURN(std::strcmp(fileBase + (fileBaseLen - 6), "XXXXXX") == 0, gNullCarlaShm);
303 // character set to use randomly
304 static const char charSet[] = "abcdefghijklmnopqrstuvwxyz"
305 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
306 "0123456789";
307 static const int charSetLen = static_cast<int>(std::strlen(charSet) - 1); // -1 to avoid trailing '\0'
309 // try until getting a valid shm is obtained or an error occurs
310 for (;;)
312 // fill the XXXXXX characters randomly
313 for (std::size_t c = fileBaseLen - 6; c < fileBaseLen; ++c)
314 fileBase[c] = charSet[std::rand() % charSetLen];
316 #ifdef CARLA_OS_WIN
317 // Windows: check if file already exists
318 const HANDLE h = ::CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr,
319 PAGE_READWRITE|SEC_COMMIT, 0, 8, fileBase);
321 if (h == INVALID_HANDLE_VALUE)
323 carla_stderr("carla_shm_create_temp(%s) - file mapping test error", fileBase);
324 return gNullCarlaShm;
327 const DWORD error = ::GetLastError();
328 ::CloseHandle(h);
330 if (error == ERROR_ALREADY_EXISTS)
332 carla_stderr("carla_shm_create_temp(%s) - file exists, retrying", fileBase);
333 continue;
335 #endif
337 // (try to) create new shm for this filename
338 const carla_shm_t shm = carla_shm_create(fileBase);
340 // all ok!
341 if (carla_is_shm_valid(shm))
342 return shm;
344 #ifndef CARLA_OS_WIN
345 // Non-Windows: if file already exists, keep trying
346 if (errno == EEXIST)
348 carla_stderr("carla_shm_create_temp(%s) - file exists, retrying", fileBase);
349 continue;
351 const int localerrno = errno;
352 carla_stderr("carla_shm_create_temp(%s) - failed, error code %i", fileBase, localerrno);
353 #endif
355 // some unknown error occurred, return null
356 return gNullCarlaShm;
360 // -----------------------------------------------------------------------
361 // shared memory, templated calls
364 * Map a shared memory object, handling object type and size.
366 template<typename T>
367 static inline
368 T* carla_shm_map(carla_shm_t& shm) noexcept
370 return (T*)carla_shm_map(shm, sizeof(T));
374 * Map a shared memory object and return if it's non-null.
376 template<typename T>
377 static inline
378 bool carla_shm_map(carla_shm_t& shm, T*& value) noexcept
380 value = (T*)carla_shm_map(shm, sizeof(T));
381 return (value != nullptr);
384 #endif // __WINE__
386 // -----------------------------------------------------------------------
388 #endif // CARLA_SHM_UTILS_HPP_INCLUDED