diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a7bcf1..15bb36a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,16 +40,10 @@ include(CTest) enable_testing() endif() -Include(FetchContent) -FetchContent_Declare( - spdlog - GIT_REPOSITORY https://github.com/gabime/spdlog.git - GIT_TAG v1.x -) -FetchContent_MakeAvailable(spdlog) add_subdirectory(src) +add_subdirectory(examples) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..5a4dff5 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,10 @@ +Include(FetchContent) +FetchContent_Declare( + spdlog + GIT_REPOSITORY https://github.com/gabime/spdlog.git + GIT_TAG v1.x +) + +FetchContent_MakeAvailable(spdlog) + +add_subdirectory(shared_memory) \ No newline at end of file diff --git a/examples/shared_memory/CMakeLists.txt b/examples/shared_memory/CMakeLists.txt new file mode 100644 index 0000000..7dee61c --- /dev/null +++ b/examples/shared_memory/CMakeLists.txt @@ -0,0 +1,6 @@ +project(example_shared_memory VERSION 1.0.0 LANGUAGES CXX) + + +add_executable(shared_memory src/main.cpp) + +target_link_libraries(shared_memory PRIVATE CPPUTILS2::cpputils2 spdlog::spdlog) \ No newline at end of file diff --git a/examples/shared_memory/src/main.cpp b/examples/shared_memory/src/main.cpp new file mode 100644 index 0000000..67ce186 --- /dev/null +++ b/examples/shared_memory/src/main.cpp @@ -0,0 +1,61 @@ + +#ifdef _WIN32 +#include +#else +#include +#endif + +#include +#include + +const int32_t expected = 42; +const std::string file_name("test_shm"); + +int main(int argc, char** argv) { + SPDLOG_INFO("Test shared memory"); + + CppUtils2::Shm shm(file_name); + auto ret = shm.allocate(sizeof(int32_t)); + if (ret == CppUtils2::Result::RET_ERROR) { + SPDLOG_ERROR("Error"); + return 1; + } + + void* ptr = shm.get_raw_ptr(); + if (ptr == nullptr) { + SPDLOG_ERROR("Error"); + return 1; + } + int32_t* ptr_int = reinterpret_cast(ptr); + SPDLOG_INFO("ptr_int: {}", *ptr_int); + *ptr_int = expected; + + int32_t val = *ptr_int; + + + + CppUtils2::Shm shm2("test_shm"); + ret = shm2.open_existing(sizeof(int32_t)); + if (ret == CppUtils2::Result::RET_ERROR) + { + SPDLOG_ERROR("Error"); + return 1; + } + ptr = shm2.get_raw_ptr(); + ptr_int = reinterpret_cast(ptr); + + SPDLOG_INFO("ptr_int: {}", *ptr_int); + + val = *ptr_int; + + SPDLOG_INFO("val: {} expected: {}", val, expected); + if (val != expected) { + SPDLOG_ERROR("Error"); + return 1; + } + shm2.close(); + shm.close(); + shm.unlink(); + + return 0; +} \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0e582be..9a7e9a3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,7 +24,7 @@ set(SOURCES_AND_HEADERS ) if (WIN32) - +list(APPEND SOURCES_AND_HEADERS include/cpputils2/win/shm/shm.hpp) else() list(APPEND SOURCES_AND_HEADERS include/cpputils2/linux/net/socket/tcpsocketclient.hpp diff --git a/src/include/cpputils2/win/shm/shm.hpp b/src/include/cpputils2/win/shm/shm.hpp new file mode 100644 index 0000000..f0cb8aa --- /dev/null +++ b/src/include/cpputils2/win/shm/shm.hpp @@ -0,0 +1,182 @@ +/** + * @file shm.hpp + * @brief Shared memory abstraction for POSIX systems. + * + * + */ + +#pragma once + +#include "cpputils2/common/types.hpp" + +#include +#include +#include +#include + +namespace CppUtils2 +{ + + + class Shm + { + public: + Shm() + : hMapFile(nullptr), pBuf{nullptr} + { + } + + Shm(const std::string& file_mem_path) + : mem_path(file_mem_path), hMapFile(nullptr), pBuf{ nullptr } + { + } + + /// @brief Open an existing shared memory + /// @param file_mem_path Path to the shared memory + /// @param mem_size Size of the shared memory + /// @return 0 on success, -1 on error + Result open_existing(const std::string& file_mem_path, std::size_t mem_size) + { + mem_path = file_mem_path; + return open_existing(mem_size); + } + + /// @brief Open an existing shared memory + /// @param mem_size Size of the shared memory + /// @return 0 on success, -1 on error + Result open_existing(std::size_t mem_size) + { + if (hMapFile != nullptr) + { + return Result::RET_ERROR; + } + int flags = 0; + return shared_open(flags, mem_size); + } + + /// @brief Allocate a new shared memory + /// @param mem_size Size of the shared memory + /// @return 0 on success, -1 on error + Result allocate(std::size_t mem_size) + { + if (hMapFile != nullptr) + { + return Result::RET_ERROR; + } + + int flags = 1; + + return shared_open(flags, mem_size); + } + + /// @brief Removes the shared memory file + /// @return 0 on success, -1 on error + Result unlink() + { + if (hMapFile != nullptr) + { + UnmapViewOfFile(pBuf); + CloseHandle(hMapFile); + } + return Result::RET_ERROR; + } + + /// @brief Closes the shared memory + void close() + { + if (hMapFile != nullptr) + { + UnmapViewOfFile(pBuf); + CloseHandle(hMapFile); + } + hMapFile = nullptr; + } + + /// @brief Get the pointer to the shared memory + /// @return Pointer to the shared memory + void* get_raw_ptr() + { + return pBuf; + } + + virtual ~Shm() + { + close(); + } + + private: + std::string mem_path; + HANDLE hMapFile; + LPVOID pBuf; + + Result shared_open(int flags, std::size_t mem_size) + { + if (flags == 1) { + + //TCHAR szName[] = TEXT(mem_path.c_str()); + + hMapFile = CreateFileMappingA( + INVALID_HANDLE_VALUE, // use paging file + nullptr, // default security + PAGE_READWRITE, // read/write access + 0, // maximum object size (high-order DWORD) + mem_size, // maximum object size (low-order DWORD) + mem_path.c_str()); // name of mapping object + + if (hMapFile == NULL) + { + + return Result::RET_ERROR; + } + + pBuf = MapViewOfFile(hMapFile, // handle to map object + FILE_MAP_ALL_ACCESS, // read/write permission + 0, + 0, + mem_size); + + if (pBuf == nullptr) + { + //_tprintf(TEXT("Could not map view of file (%d).\n"), GetLastError()); + + CloseHandle(hMapFile); + + return Result::RET_ERROR; + } + + } + else { + + hMapFile = OpenFileMappingA( + FILE_MAP_ALL_ACCESS, // read/write access + FALSE, // do not inherit the name + mem_path.c_str()); // name of mapping object + + if (hMapFile == NULL) + { + //_tprintf(TEXT("Could not open file mapping object (%d).\n"), + // GetLastError()); + return Result::RET_ERROR; + } + + pBuf = MapViewOfFile(hMapFile, // handle to map object + FILE_MAP_ALL_ACCESS, // read/write permission + 0, + 0, + mem_size); + + if (pBuf == nullptr) + { + //_tprintf(TEXT("Could not map view of file (%d).\n"), + // GetLastError()); + + CloseHandle(hMapFile); + + return Result::RET_ERROR; + } + + } + return Result::RET_OK; + } + }; +} \ No newline at end of file diff --git a/src/test/cpputils2_tests.cpp b/src/test/cpputils2_tests.cpp index e6c5a09..e9ba701 100644 --- a/src/test/cpputils2_tests.cpp +++ b/src/test/cpputils2_tests.cpp @@ -17,6 +17,10 @@ #include "cpputils2/linux/shm/shm.hpp" #endif +#ifdef _WIN32 +#include "cpputils2/win/shm/shm.hpp" +#endif + #include #include #include @@ -139,6 +143,54 @@ namespace #endif +#ifdef _WIN32 + TEST(ExampleSHM, TestSHM) { + CppUtils2::Shm shm("test_shm"); + auto ret = shm.allocate(sizeof(int32_t)); + EXPECT_NE(ret, CppUtils2::Result::RET_ERROR); + void* ptr = shm.get_raw_ptr(); + EXPECT_NE(ptr, nullptr); + int32_t* ptr_int = reinterpret_cast(ptr); + std::cout << "ptr_int: " << *ptr_int << std::endl; + *ptr_int = 42; + int32_t val = *ptr_int; + EXPECT_EQ(val, 42); + shm.close(); + shm.unlink(); + EXPECT_TRUE(true); + } + TEST(ExampleShm, TestExisting) + { + CppUtils2::Shm shm("test_shm"); + auto ret = shm.allocate(sizeof(int32_t)); + EXPECT_NE(ret, CppUtils2::Result::RET_ERROR); + + void* ptr = shm.get_raw_ptr(); + EXPECT_NE(ptr, nullptr); + int32_t* ptr_int = reinterpret_cast(ptr); + std::cout << "ptr_int: " << *ptr_int << std::endl; + *ptr_int = 42; + + int32_t val = *ptr_int; + EXPECT_EQ(val, 42); + + + + CppUtils2::Shm shm2("test_shm"); + ret = shm2.open_existing(sizeof(int32_t)); + EXPECT_NE(ret, CppUtils2::Result::RET_ERROR); + ptr = shm2.get_raw_ptr(); + ptr_int = reinterpret_cast(ptr); + std::cout << "ptr_int: " << *ptr_int << std::endl; + + val = *ptr_int; + EXPECT_EQ(val, 42); + shm2.close(); + shm.close(); + shm.unlink(); + } +#endif + TEST(ExampleTrigger, TestTrigger) { CppUtils2::TriggerWaitable trigger;