| 
							- /*
 -  * Carla shared memory utils
 -  * Copyright (C) 2013-2023 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 carla_shm_t { HANDLE map; bool isServer; const char* filename; };
 - # define carla_shm_t_INIT { INVALID_HANDLE_VALUE, true, nullptr }
 - #else
 - # ifndef __WINE__
 - #  include <cerrno>
 - # endif
 - # include <fcntl.h>
 - # include <sys/mman.h>
 - 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
 -     if (shm.isServer) {
 -         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)
 -         {
 -             SECURITY_ATTRIBUTES sa;
 -             carla_zeroStruct(sa);
 -             sa.nLength = sizeof(sa);
 -             sa.bInheritHandle = TRUE;
 - 
 -             map = ::CreateFileMappingA(INVALID_HANDLE_VALUE, &sa,
 -                                        PAGE_READWRITE|SEC_COMMIT, 0, static_cast<DWORD>(size), shm.filename);
 - 
 -             if (map == nullptr || map == INVALID_HANDLE_VALUE)
 -             {
 -                 const DWORD errorCode = ::GetLastError();
 -                 carla_stderr2("CreateFileMapping failed for '%s', size:%lu, isServer:%i, errorCode:%x",
 -                               shm.filename, size, shm.isServer, errorCode);
 -                 return nullptr;
 -             }
 -         }
 -         else
 -         {
 -             map = ::OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, shm.filename);
 - 
 -             CARLA_SAFE_ASSERT_RETURN(map != nullptr, nullptr);
 -             CARLA_SAFE_ASSERT_RETURN(map != INVALID_HANDLE_VALUE, nullptr);
 -         }
 - 
 -         void* const ptr = ::MapViewOfFile(map, FILE_MAP_ALL_ACCESS, 0, 0, size);
 - 
 -         if (ptr == nullptr)
 -         {
 -             const DWORD errorCode = ::GetLastError();
 -             carla_stderr2("MapViewOfFile failed for '%s', size:%lu, isServer:%i, errorCode:%u",
 -                           shm.filename, size, shm.isServer, errorCode);
 -             ::CloseHandle(map);
 -             return nullptr;
 -         }
 - 
 -         ::VirtualLock(ptr, size);
 - 
 -         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* ptr;
 - 
 -        #ifdef MAP_LOCKED
 -         ptr = ::mmap(nullptr, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, shm.fd, 0);
 -         CARLA_SAFE_ASSERT_RETURN(ptr != nullptr, nullptr);
 - 
 -         if (ptr == MAP_FAILED)
 -        #endif
 -         {
 -             ptr = ::mmap(nullptr, size, PROT_READ|PROT_WRITE, MAP_SHARED, shm.fd, 0);
 -             CARLA_SAFE_ASSERT_RETURN(ptr != nullptr, nullptr);
 - 
 -             if (ptr == MAP_FAILED)
 -             {
 -                 carla_stderr2("carla_shm_map() - mmap failed: %s", std::strerror(errno));
 -                 return nullptr;
 -             }
 - 
 -            #ifndef MAP_LOCKED
 -             try {
 -                 ::mlock(ptr, size);
 -             } CARLA_SAFE_EXCEPTION("carla_shm_map mlock");
 -            #endif
 -         }
 - 
 -         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<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];
 - 
 -        #ifdef CARLA_OS_WIN
 -         // Windows: check if file already exists
 -         const HANDLE h = ::CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr,
 -                                               PAGE_READWRITE|SEC_COMMIT, 0, 8, fileBase);
 - 
 -         if (h == INVALID_HANDLE_VALUE)
 -         {
 -             carla_stderr("carla_shm_create_temp(%s) - file mapping test error", fileBase);
 -             return gNullCarlaShm;
 -         }
 - 
 -         const DWORD error = ::GetLastError();
 -         ::CloseHandle(h);
 - 
 -         if (error == ERROR_ALREADY_EXISTS)
 -         {
 -             carla_stderr("carla_shm_create_temp(%s) - file exists, retrying", fileBase);
 -             continue;
 -         }
 -        #endif
 - 
 -         // (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;
 - 
 -        #ifndef CARLA_OS_WIN
 -         // Non-Windows: if file already exists, keep trying
 -         if (errno == EEXIST)
 -         {
 -             carla_stderr("carla_shm_create_temp(%s) - file exists, retrying", fileBase);
 -             continue;
 -         }
 -         const int localerrno = errno;
 -         carla_stderr("carla_shm_create_temp(%s) - failed, error code %i", fileBase, localerrno);
 -        #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(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<typename T>
 - 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
 
 
  |