diff --git a/source/tests/CarlaUtils.cpp b/source/tests/CarlaUtils.cpp index cc3f0c9b9..660348ed3 100644 --- a/source/tests/CarlaUtils.cpp +++ b/source/tests/CarlaUtils.cpp @@ -36,6 +36,8 @@ #include "CarlaLv2Utils.hpp" #include "CarlaVstUtils.hpp" +#include "CarlaShmUtils.hpp" + // used in dssi utils #include "juce_core.h" #include @@ -46,17 +48,7 @@ // #include "CarlaJuceUtils.hpp" // #include "CarlaLibUtils.hpp" // #include "CarlaOscUtils.hpp" -// #include "CarlaShmUtils.hpp" // #include "CarlaStateUtils.hpp" -// #include "CarlaVstUtils.hpp" - -// #include "CarlaLibCounter.hpp" -// #include "CarlaLogThread.hpp" -// #include "CarlaRingBuffer.hpp" -// #include "CarlaThread.hpp" -// #include "Lv2AtomQueue.hpp" - -// #include "JucePluginWindow.hpp" #if 0 // ----------------------------------------------------------------------- @@ -594,7 +586,6 @@ static void test_CarlaLv2Utils() noexcept is_lv2_feature_supported("test1"); is_lv2_ui_feature_supported("test2"); } -#endif // ----------------------------------------------------------------------- @@ -613,6 +604,111 @@ static void test_CarlaVstUtils() noexcept carla_stdout(vstEffectOpcode2str(effOpen)); carla_stdout(vstMasterOpcode2str(audioMasterAutomate)); } +#endif + +// ----------------------------------------------------------------------- + +struct ShmStruct { + char stringStart[255]; + bool boolean; + int integer; + float floating; + char stringEnd[255]; +}; + +static void test_CarlaShmUtils() noexcept +{ + shm_t shm, shma; + ShmStruct* shmStruct1; + ShmStruct* shmStruct2; + + // base tests first + carla_shm_init(shm); + assert(! carla_is_shm_valid(shm)); + + shm = carla_shm_create("/carla-shm-test1"); + carla_stdout("test %i", shm); + assert(carla_is_shm_valid(shm)); + + carla_shm_close(shm); + assert(! carla_is_shm_valid(shm)); + + shm = carla_shm_create("/carla-shm-test1"); + assert(carla_is_shm_valid(shm)); + + shma = carla_shm_attach("/carla-shm-test1"); + assert(carla_is_shm_valid(shma)); + + carla_shm_close(shm); + carla_shm_close(shma); + assert(! carla_is_shm_valid(shm)); + assert(! carla_is_shm_valid(shma)); + + // test attach invalid + shma = carla_shm_attach("/carla-shm-test-NOT"); + assert(! carla_is_shm_valid(shma)); + + // test memory, start + shm = carla_shm_create("/carla-shm-test1"); + assert(carla_is_shm_valid(shm)); + + shma = carla_shm_attach("/carla-shm-test1"); + assert(carla_is_shm_valid(shma)); + + // test memory, check valid + shmStruct1 = carla_shm_map(shm); + assert(shmStruct1 != nullptr); + + shmStruct2 = carla_shm_map(shma); + assert(shmStruct2 != nullptr); + + carla_shm_unmap(shma, shmStruct2); + assert(shmStruct2 == nullptr); + + carla_shm_unmap(shm, shmStruct1); + assert(shmStruct1 == nullptr); + + // test memory, check if write data matches + shmStruct1 = carla_shm_map(shm); + assert(shmStruct1 != nullptr); + + shmStruct2 = carla_shm_map(shma); + assert(shmStruct2 != nullptr); + + carla_zeroStruct(*shmStruct1); + assert(shmStruct1->stringStart[0] == '\0'); + assert(shmStruct2->stringStart[0] == '\0'); + assert(shmStruct1->stringEnd[0] == '\0'); + assert(shmStruct2->stringEnd[0] == '\0'); + assert(! shmStruct1->boolean); + assert(! shmStruct2->boolean); + + shmStruct1->boolean = true; + shmStruct1->integer = 232312; + assert(shmStruct1->boolean == shmStruct2->boolean); + assert(shmStruct1->integer == shmStruct2->integer); + + shmStruct2->floating = 2342.231f; + std::strcpy(shmStruct2->stringStart, "test1start"); + std::strcpy(shmStruct2->stringEnd, "test2end"); + assert(shmStruct1->floating == shmStruct2->floating); + assert(std::strcmp(shmStruct1->stringStart, "test1start") == 0); + assert(std::strcmp(shmStruct1->stringStart, shmStruct2->stringStart) == 0); + assert(std::strcmp(shmStruct1->stringEnd, "test2end") == 0); + assert(std::strcmp(shmStruct1->stringEnd, shmStruct2->stringEnd) == 0); + + carla_shm_unmap(shma, shmStruct2); + assert(shmStruct2 == nullptr); + + carla_shm_unmap(shm, shmStruct1); + assert(shmStruct1 == nullptr); + + // test memory, done + carla_shm_close(shm); + carla_shm_close(shma); + assert(! carla_is_shm_valid(shm)); + assert(! carla_is_shm_valid(shma)); +} // ----------------------------------------------------------------------- // main @@ -620,16 +716,20 @@ static void test_CarlaVstUtils() noexcept int main() { // already tested, skip for now -// test_CarlaUtils(); -// test_CarlaMathUtils(); -// -// test_CarlaBackendUtils(); -// test_CarlaEngineUtils(); -// -// test_CarlaLadspaUtils(); -// test_CarlaDssiUtils(); -// test_CarlaLv2Utils(); +#if 0 + test_CarlaUtils(); + test_CarlaMathUtils(); + + test_CarlaBackendUtils(); + test_CarlaEngineUtils(); + + test_CarlaLadspaUtils(); + test_CarlaDssiUtils(); + test_CarlaLv2Utils(); test_CarlaVstUtils(); +#endif + + test_CarlaShmUtils(); return 0; } diff --git a/source/utils/CarlaShmUtils.hpp b/source/utils/CarlaShmUtils.hpp index 55fdd39a2..0c719b38c 100644 --- a/source/utils/CarlaShmUtils.hpp +++ b/source/utils/CarlaShmUtils.hpp @@ -25,169 +25,230 @@ struct shm_t { HANDLE shm; HANDLE map; }; #else # include # include -typedef int shm_t; +struct shm_t { int fd; const char* filename; }; #endif +// ----------------------------------------------------------------------- +// shared memory calls + +/* + * Null object returned when a shared memory operation fails. + */ #ifdef CARLA_OS_WIN static const shm_t gNullCarlaShm = { nullptr, nullptr }; #else -static const shm_t gNullCarlaShm = -1; +static const shm_t gNullCarlaShm = { -1, nullptr }; #endif -// ----------------------------------------------------------------------- -// shared memory calls - +/* + * Initialize a shared memory object to an invalid state. + */ static inline -bool carla_is_shm_valid(const shm_t& shm) noexcept +void carla_shm_init(shm_t& shm) noexcept { #ifdef CARLA_OS_WIN - return (shm.shm != nullptr && shm.shm != INVALID_HANDLE_VALUE); + shm.shm = nullptr; + shm.map = nullptr; #else - return (shm >= 0); + shm.fd = -1; + shm.filename = nullptr; #endif } +/* + * Check if a shared memory object is valid. + */ static inline -void carla_shm_init(shm_t& shm) noexcept +bool carla_is_shm_valid(const shm_t& shm) noexcept { #ifdef CARLA_OS_WIN - shm.shm = nullptr; - shm.map = nullptr; + return (shm.shm != nullptr && shm.shm != INVALID_HANDLE_VALUE); #else - shm = -1; + return (shm.fd >= 0); #endif } -#ifdef CARLA_OS_WIN +/* + * 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 name) +shm_t carla_shm_create(const char* const filename) noexcept { - CARLA_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', gNullCarlaShm); + CARLA_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0', gNullCarlaShm); shm_t ret; - ret.shm = nullptr; // TODO - ret.map = nullptr; + + try { +#ifdef CARLA_OS_WIN + ret.shm = nullptr; // TODO + ret.map = nullptr; +#else + ret.fd = ::shm_open(filename, O_CREAT|O_EXCL|O_RDWR, 0600); + ret.filename = (ret.fd >= 0) ? carla_strdup(filename) : nullptr; +#endif + } + catch(...) { + carla_safe_exception("carla_shm_create", __FILE__, __LINE__); + ret = gNullCarlaShm; + } return ret; } +/* + * Attach to an existing shared memory object. + */ static inline -shm_t carla_shm_attach(const char* const name) +shm_t carla_shm_attach(const char* const filename) noexcept { - CARLA_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', gNullCarlaShm); + CARLA_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0', gNullCarlaShm); shm_t ret; - ret.shm = ::CreateFileA(name, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); - ret.map = nullptr; - return ret; -} + try { +#ifdef CARLA_OS_WIN + ret.shm = ::CreateFileA(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + ret.map = nullptr; #else -static inline -shm_t carla_shm_create(const char* const name) -{ - CARLA_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', gNullCarlaShm); - - return ::shm_open(name, O_RDWR|O_CREAT|O_EXCL, 0600); -} - -static inline -shm_t carla_shm_attach(const char* const name) -{ - CARLA_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', gNullCarlaShm); + ret.fd = ::shm_open(filename, O_RDWR, 0); + ret.filename = nullptr; +#endif + } + catch(...) { + ret = gNullCarlaShm; + } - return ::shm_open(name, O_RDWR, 0); + return ret; } -#endif +/* + * Close a shared memory object and invalidate it. + */ static inline -void carla_shm_close(shm_t& shm) +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 == nullptr); +#endif - ::CloseHandle(shm.shm); - shm.shm = nullptr; + try { +#ifdef CARLA_OS_WIN + ::CloseHandle(shm.shm); #else - ::close(shm); - shm = -1; + ::close(shm.fd); + + if (shm.filename != nullptr) + { + ::shm_unlink(shm.filename); + delete[] shm.filename; + } #endif + } CARLA_SAFE_EXCEPTION("carla_shm_close"); + + shm = gNullCarlaShm; } +/* + * Map a shared memory object to @a size bytes and returns its address. + * @note One shared memory object can only have one mapping at a time. + */ static inline -void* carla_shm_map(shm_t& shm, const size_t size) +void* carla_shm_map(shm_t& shm, const 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 == nullptr, nullptr); +#endif - HANDLE map = ::CreateFileMapping(shm.shm, NULL, PAGE_READWRITE, size, size, NULL); - - CARLA_SAFE_ASSERT_RETURN(map != nullptr, nullptr); - - HANDLE ptr = ::MapViewOfFile(map, FILE_MAP_COPY, 0, 0, size); + try { +#ifdef CARLA_OS_WIN + const HANDLE map = ::CreateFileMapping(shm.shm, NULL, PAGE_READWRITE, size, size, NULL); + CARLA_SAFE_ASSERT_RETURN(map != nullptr, nullptr); - if (ptr == nullptr) - { - ::CloseHandle(map); - return nullptr; - } + HANDLE ptr = ::MapViewOfFile(map, FILE_MAP_COPY, 0, 0, size); - shm.map = map; + if (ptr == nullptr) + { + carla_safe_assert("ptr != nullptr", __FILE__, __LINE__); + ::CloseHandle(map); + return nullptr; + } - return ptr; + shm.map = map; + return ptr; #else - if (::ftruncate(shm, static_cast(size)) != 0) - return nullptr; + const int ret = ::ftruncate(shm.fd, static_cast(size)); + CARLA_SAFE_ASSERT_RETURN(ret == 0, nullptr); - return ::mmap(nullptr, size, PROT_READ|PROT_WRITE, MAP_SHARED, shm, 0); + return ::mmap(nullptr, size, PROT_READ|PROT_WRITE, MAP_SHARED, shm.fd, 0); #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, const size_t size) +void carla_shm_unmap(shm_t& shm, void* const ptr, const size_t size) noexcept { CARLA_SAFE_ASSERT_RETURN(carla_is_shm_valid(shm),); CARLA_SAFE_ASSERT_RETURN(ptr != nullptr,); CARLA_SAFE_ASSERT_RETURN(size > 0,); - #ifdef CARLA_OS_WIN CARLA_SAFE_ASSERT_RETURN(shm.map != nullptr,); +#endif - ::UnmapViewOfFile(ptr); - ::CloseHandle(shm.map); - shm.map = nullptr; - return; + try { +#ifdef CARLA_OS_WIN + const Handle map = shm.map; + shm.map = nullptr; - // unused - (void)size; + ::UnmapViewOfFile(ptr); + ::CloseHandle(map); #else - ::munmap(ptr, size); - return; + const int ret = ::munmap(ptr, size); + CARLA_SAFE_ASSERT(ret == 0); +#endif + } CARLA_SAFE_EXCEPTION("carla_shm_unmap"); - // unused + return; // unused depending on platform (void)shm; -#endif + (void)size; } // ----------------------------------------------------------------------- // shared memory, templated calls +/* + * Map a shared memory object, handling object type and size. + */ +template +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 static inline -bool carla_shm_map(shm_t& shm, T*& value) +bool carla_shm_map(shm_t& shm, T*& value) noexcept { value = (T*)carla_shm_map(shm, sizeof(T)); return (value != nullptr); } +/* + * Unmap a shared memory object address and set it as null. + */ template static inline -void carla_shm_unmap(shm_t& shm, T*& value) +void carla_shm_unmap(shm_t& shm, T*& value) noexcept { carla_shm_unmap(shm, value, sizeof(T)); value = nullptr;