/* * Carla shared memory utils * Copyright (C) 2013-2015 Filipe Coelho * * 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 carla_shm_t { HANDLE map; bool isServer; const char* filename; }; # define carla_shm_t_INIT { INVALID_HANDLE_VALUE, true, nullptr } #else # ifndef __WINE__ # include # endif # include # include struct carla_shm_t { int fd; const char* filename; std::size_t size; }; # define carla_shm_t_INIT { -1, nullptr, 0 } #endif // ----------------------------------------------------------------------- // shared memory calls /* * Null object returned when a shared memory operation fails. */ static const carla_shm_t gNullCarlaShm = carla_shm_t_INIT; /* * Check if a shared memory object is valid. */ static inline bool carla_is_shm_valid(const carla_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(carla_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 carla_shm_t carla_shm_create(const char* const filename) noexcept { CARLA_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0', gNullCarlaShm); carla_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 carla_shm_t carla_shm_attach(const char* const filename) noexcept { CARLA_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0', gNullCarlaShm); carla_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(carla_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(carla_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(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(carla_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"); } #ifndef __WINE__ // ----------------------------------------------------------------------- // 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 carla_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(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 carla_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; carla_stderr("carla_shm_create_temp(%s) - failed, error code %i", fileBase, errno); #endif // some unknown error occurred, return null return gNullCarlaShm; } } // ----------------------------------------------------------------------- // shared memory, templated calls /* * Map a shared memory object, handling object type and size. */ template static inline T* carla_shm_map(carla_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 static inline bool carla_shm_map(carla_shm_t& shm, T*& value) noexcept { value = (T*)carla_shm_map(shm, sizeof(T)); return (value != nullptr); } // ----------------------------------------------------------------------- #endif // __WINE__ #endif // CARLA_SHM_UTILS_HPP_INCLUDED