|
- /*
- * Carla shared memory utils
- * Copyright (C) 2013-2014 Filipe Coelho <falktx@falktx.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * For a full copy of the GNU General Public License see the doc/GPL.txt file.
- */
-
- #ifndef CARLA_SHM_UTILS_HPP_INCLUDED
- #define CARLA_SHM_UTILS_HPP_INCLUDED
-
- #include "CarlaUtils.hpp"
-
- #ifdef CARLA_OS_WIN
- struct shm_t { HANDLE map; bool isServer; const char* filename; };
- # define shm_t_INIT { INVALID_HANDLE_VALUE, true, nullptr }
- #else
- # include <cerrno>
- # include <fcntl.h>
- # include <sys/mman.h>
- struct shm_t { int fd; const char* filename; std::size_t size; };
- # define shm_t_INIT { -1, nullptr, 0 }
- #endif
-
- // -----------------------------------------------------------------------
- // shared memory calls
-
- /*
- * Null object returned when a shared memory operation fails.
- */
- static const shm_t gNullCarlaShm = shm_t_INIT;
-
- /*
- * Check if a shared memory object is valid.
- */
- static inline
- bool carla_is_shm_valid(const shm_t& shm) noexcept
- {
- #ifdef CARLA_OS_WIN
- return (shm.filename != nullptr);
- #else
- return (shm.fd >= 0);
- #endif
- }
-
- /*
- * Initialize a shared memory object to an invalid state.
- */
- static inline
- void carla_shm_init(shm_t& shm) noexcept
- {
- shm = gNullCarlaShm;
- }
-
- /*
- * Create and open a new shared memory object.
- * Returns an invalid object if the operation failed or the filename already exists.
- */
- static inline
- shm_t carla_shm_create(const char* const filename) noexcept
- {
- CARLA_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0', gNullCarlaShm);
-
- shm_t ret;
-
- #ifdef CARLA_OS_WIN
- ret.map = INVALID_HANDLE_VALUE;
- ret.isServer = true;
- ret.filename = carla_strdup_safe(filename);
- #else
- try {
- ret.fd = ::shm_open(filename, O_CREAT|O_EXCL|O_RDWR, 0600);
- ret.filename = (ret.fd >= 0) ? carla_strdup_safe(filename) : nullptr;
- ret.size = 0;
-
- if (ret.fd >= 0 && ret.filename == nullptr)
- {
- ::close(ret.fd);
- ::shm_unlink(filename);
- ret.fd = -1;
- }
- } CARLA_SAFE_EXCEPTION_RETURN("carla_shm_create", gNullCarlaShm);
- #endif
-
- return ret;
- }
-
- /*
- * Attach to an existing shared memory object.
- */
- static inline
- shm_t carla_shm_attach(const char* const filename) noexcept
- {
- CARLA_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0', gNullCarlaShm);
-
- shm_t ret;
-
- #ifdef CARLA_OS_WIN
- ret.map = INVALID_HANDLE_VALUE;
- ret.isServer = false;
- ret.filename = carla_strdup_safe(filename);
- #else
- try {
- ret.fd = ::shm_open(filename, O_RDWR, 0);
- ret.filename = nullptr;
- ret.size = 0;
- } CARLA_SAFE_EXCEPTION_RETURN("carla_shm_attach", gNullCarlaShm);
- #endif
-
- return ret;
- }
-
- /*
- * Close a shared memory object and invalidate it.
- */
- static inline
- void carla_shm_close(shm_t& shm) noexcept
- {
- CARLA_SAFE_ASSERT_RETURN(carla_is_shm_valid(shm),);
- #ifdef CARLA_OS_WIN
- CARLA_SAFE_ASSERT(shm.map == INVALID_HANDLE_VALUE);
- #endif
-
- #ifdef CARLA_OS_WIN
- if (shm.filename != nullptr)
- delete[] shm.filename;
- #else
- try {
- ::close(shm.fd);
-
- if (shm.filename != nullptr)
- {
- ::shm_unlink(shm.filename);
- delete[] shm.filename;
- }
- } CARLA_SAFE_EXCEPTION("carla_shm_close");
- #endif
-
- shm = gNullCarlaShm;
- }
-
- /*
- * Map a shared memory object to @a size bytes and return the memory address.
- * @note One shared memory object can only have one mapping at a time.
- */
- static inline
- void* carla_shm_map(shm_t& shm, const std::size_t size) noexcept
- {
- CARLA_SAFE_ASSERT_RETURN(carla_is_shm_valid(shm), nullptr);
- CARLA_SAFE_ASSERT_RETURN(size > 0, nullptr);
- #ifdef CARLA_OS_WIN
- CARLA_SAFE_ASSERT_RETURN(shm.map == INVALID_HANDLE_VALUE, nullptr);
- #else
- CARLA_SAFE_ASSERT_RETURN(shm.size == 0, nullptr);
- #endif
-
- try {
- #ifdef CARLA_OS_WIN
- HANDLE map;
-
- if (shm.isServer)
- map = ::CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE|SEC_COMMIT, 0, size, shm.filename);
- else
- map = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, shm.filename);
-
- CARLA_SAFE_ASSERT_RETURN(map != INVALID_HANDLE_VALUE, nullptr);
-
- void* const ptr(::MapViewOfFile(map, FILE_MAP_ALL_ACCESS, 0, 0, size));
-
- if (ptr == nullptr)
- {
- carla_safe_assert("ptr != nullptr", __FILE__, __LINE__);
- ::CloseHandle(map);
- return nullptr;
- }
-
- shm.map = map;
- return ptr;
- #else
- if (shm.filename != nullptr)
- {
- const int ret(::ftruncate(shm.fd, static_cast<off_t>(size)));
- CARLA_SAFE_ASSERT_RETURN(ret == 0, nullptr);
- }
-
- void* const ptr(::mmap(nullptr, size, PROT_READ|PROT_WRITE, MAP_SHARED, shm.fd, 0));
-
- if (ptr == nullptr)
- {
- carla_safe_assert("ptr != nullptr", __FILE__, __LINE__);
- return nullptr;
- }
-
- shm.size = size;
- return ptr;
- #endif
- } CARLA_SAFE_EXCEPTION_RETURN("carla_shm_map", nullptr);
- }
-
- /*
- * Unmap a shared memory object address.
- */
- static inline
- void carla_shm_unmap(shm_t& shm, void* const ptr) noexcept
- {
- CARLA_SAFE_ASSERT_RETURN(carla_is_shm_valid(shm),);
- CARLA_SAFE_ASSERT_RETURN(ptr != nullptr,);
- #ifdef CARLA_OS_WIN
- CARLA_SAFE_ASSERT_RETURN(shm.map != INVALID_HANDLE_VALUE,);
- #else
- CARLA_SAFE_ASSERT_RETURN(shm.size > 0,);
- #endif
-
- try {
- #ifdef CARLA_OS_WIN
- const HANDLE map(shm.map);
- shm.map = INVALID_HANDLE_VALUE;
-
- ::UnmapViewOfFile(ptr);
- ::CloseHandle(map);
- #else
- const std::size_t size(shm.size);
- shm.size = 0;
-
- const int ret(::munmap(ptr, size));
- CARLA_SAFE_ASSERT(ret == 0);
- #endif
- } CARLA_SAFE_EXCEPTION("carla_shm_unmap");
- }
-
- // -----------------------------------------------------------------------
- // advanced calls
-
- /*
- * Create and open a new shared memory object for a XXXXXX temp filename.
- * Will keep trying until a free random filename is obtained.
- */
- static inline
- shm_t carla_shm_create_temp(char* const fileBase) noexcept
- {
- // check if the fileBase name is valid
- CARLA_SAFE_ASSERT_RETURN(fileBase != nullptr, gNullCarlaShm);
-
- const std::size_t fileBaseLen(std::strlen(fileBase));
-
- CARLA_SAFE_ASSERT_RETURN(fileBaseLen > 6, gNullCarlaShm);
- CARLA_SAFE_ASSERT_RETURN(std::strcmp(fileBase + (fileBaseLen - 6), "XXXXXX") == 0, gNullCarlaShm);
-
- // character set to use randomly
- static const char charSet[] = "abcdefghijklmnopqrstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "0123456789";
- static const int charSetLen = static_cast<int>(std::strlen(charSet) - 1); // -1 to avoid trailing '\0'
-
- // try until getting a valid shm is obtained or an error occurs
- for (;;)
- {
- // fill the XXXXXX characters randomly
- for (std::size_t c = fileBaseLen - 6; c < fileBaseLen; ++c)
- fileBase[c] = charSet[std::rand() % charSetLen];
-
- // (try to) create new shm for this filename
- const shm_t shm = carla_shm_create(fileBase);
-
- // all ok!
- if (carla_is_shm_valid(shm))
- return shm;
-
- // file already exists, keep trying
- #ifdef CARLA_OS_WIN
- if (::GetLastError() == ERROR_ALREADY_EXISTS)
- continue;
- #else
- if (errno == EEXIST)
- continue;
- #endif
-
- // some unknown error occurred, return null
- return gNullCarlaShm;
- }
- }
-
- // -----------------------------------------------------------------------
- // shared memory, templated calls
-
- /*
- * Map a shared memory object, handling object type and size.
- */
- template<typename T>
- static inline
- T* carla_shm_map(shm_t& shm) noexcept
- {
- return (T*)carla_shm_map(shm, sizeof(T));
- }
-
- /*
- * Map a shared memory object and return if it's non-null.
- */
- template<typename T>
- static inline
- bool carla_shm_map(shm_t& shm, T*& value) noexcept
- {
- value = (T*)carla_shm_map(shm, sizeof(T));
- return (value != nullptr);
- }
-
- // -----------------------------------------------------------------------
-
- #endif // CARLA_SHM_UTILS_HPP_INCLUDED
|