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"
24 struct carla_shm_t
{ HANDLE map
; bool isServer
; const char* filename
; };
25 # define carla_shm_t_INIT { INVALID_HANDLE_VALUE, true, nullptr }
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 }
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.
48 bool carla_is_shm_valid(const carla_shm_t
& shm
) noexcept
51 return (shm
.filename
!= nullptr);
58 * Initialize a shared memory object to an invalid state.
61 void carla_shm_init(carla_shm_t
& shm
) noexcept
67 * Create and open a new shared memory object.
68 * Returns an invalid object if the operation failed or the filename already exists.
71 carla_shm_t
carla_shm_create(const char* const filename
) noexcept
73 CARLA_SAFE_ASSERT_RETURN(filename
!= nullptr && filename
[0] != '\0', gNullCarlaShm
);
78 ret
.map
= INVALID_HANDLE_VALUE
;
80 ret
.filename
= carla_strdup_safe(filename
);
83 ret
.fd
= ::shm_open(filename
, O_CREAT
|O_EXCL
|O_RDWR
, 0600);
84 ret
.filename
= (ret
.fd
>= 0) ? carla_strdup_safe(filename
) : nullptr;
87 if (ret
.fd
>= 0 && ret
.filename
== nullptr)
90 ::shm_unlink(filename
);
93 } CARLA_SAFE_EXCEPTION_RETURN("carla_shm_create", gNullCarlaShm
);
100 * Attach to an existing shared memory object.
103 carla_shm_t
carla_shm_attach(const char* const filename
) noexcept
105 CARLA_SAFE_ASSERT_RETURN(filename
!= nullptr && filename
[0] != '\0', gNullCarlaShm
);
110 ret
.map
= INVALID_HANDLE_VALUE
;
111 ret
.isServer
= false;
112 ret
.filename
= carla_strdup_safe(filename
);
115 ret
.fd
= ::shm_open(filename
, O_RDWR
, 0);
116 ret
.filename
= nullptr;
118 } CARLA_SAFE_EXCEPTION_RETURN("carla_shm_attach", gNullCarlaShm
);
125 * Close a shared memory object and invalidate it.
128 void carla_shm_close(carla_shm_t
& shm
) noexcept
130 CARLA_SAFE_ASSERT_RETURN(carla_is_shm_valid(shm
),);
133 CARLA_SAFE_ASSERT(shm
.map
== INVALID_HANDLE_VALUE
);
138 if (shm
.filename
!= nullptr)
139 delete[] shm
.filename
;
144 if (shm
.filename
!= nullptr)
146 ::shm_unlink(shm
.filename
);
147 delete[] shm
.filename
;
149 } CARLA_SAFE_EXCEPTION("carla_shm_close");
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.
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);
165 CARLA_SAFE_ASSERT_RETURN(shm
.map
== INVALID_HANDLE_VALUE
, nullptr);
167 CARLA_SAFE_ASSERT_RETURN(shm
.size
== 0, nullptr);
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
);
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
);
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
);
211 ::VirtualLock(ptr
, size
);
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);
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
)
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
));
243 } CARLA_SAFE_EXCEPTION("carla_shm_map mlock");
250 } CARLA_SAFE_EXCEPTION_RETURN("carla_shm_map", nullptr);
254 * Unmap a shared memory object address.
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,);
262 CARLA_SAFE_ASSERT_RETURN(shm
.map
!= INVALID_HANDLE_VALUE
,);
264 CARLA_SAFE_ASSERT_RETURN(shm
.size
> 0,);
269 const HANDLE map
= shm
.map
;
270 shm
.map
= INVALID_HANDLE_VALUE
;
272 ::UnmapViewOfFile(ptr
);
275 const std::size_t size(shm
.size
);
278 const int ret(::munmap(ptr
, size
));
279 CARLA_SAFE_ASSERT(ret
== 0);
281 } CARLA_SAFE_EXCEPTION("carla_shm_unmap");
285 // -----------------------------------------------------------------------
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.
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"
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
312 // fill the XXXXXX characters randomly
313 for (std::size_t c
= fileBaseLen
- 6; c
< fileBaseLen
; ++c
)
314 fileBase
[c
] = charSet
[std::rand() % charSetLen
];
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();
330 if (error
== ERROR_ALREADY_EXISTS
)
332 carla_stderr("carla_shm_create_temp(%s) - file exists, retrying", fileBase
);
337 // (try to) create new shm for this filename
338 const carla_shm_t shm
= carla_shm_create(fileBase
);
341 if (carla_is_shm_valid(shm
))
345 // Non-Windows: if file already exists, keep trying
348 carla_stderr("carla_shm_create_temp(%s) - file exists, retrying", fileBase
);
351 const int localerrno
= errno
;
352 carla_stderr("carla_shm_create_temp(%s) - failed, error code %i", fileBase
, localerrno
);
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.
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.
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);
386 // -----------------------------------------------------------------------
388 #endif // CARLA_SHM_UTILS_HPP_INCLUDED