diff --git a/README.md b/README.md index 12a37da..3871611 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ # cpputils - -[![Build Status](https://travis-ci.org/yangosoft/cpputils.svg?branch=master)](https://travis-ci.org/yangosoft/cpputils) +![cpputils workflow](https://github.com/yangosoft/cpputils/actions/workflows/cpp.yml/badge.svg) C++14 utilites @@ -15,6 +14,8 @@ C++14 utilites * TCP socket server * BTree * Colors: RGB and LAB color converters and distance calculation +* Futex and shared memory futex implementation based on [Eli Bendersky Mutex https://eli.thegreenplace.net/2018/basics-of-futexes/](https://eli.thegreenplace.net/2018/basics-of-futexes/) implementation of the [Ulrich Drepper's Futexes are Tricky paper](https://www.akkadia.org/drepper/futex.pdf) +* Shared memory wrapper for POSIX Check examples folder and build them after install CppUtils. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b3f5221..71ad1e7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,18 +1,20 @@ -cmake_minimum_required(VERSION 3.4) +cmake_minimum_required(VERSION 3.17) set (CppUtils_VERSION_MAJOR 0) set (CppUtils_VERSION_MINOR 1) project(CppUtilsLibs VERSION ${CppUtils_VERSION_MAJOR}.${CppUtils_VERSION_MINOR} LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 14) +# set(CMAKE_C_COMPILER /usr/bin/gcc-12) +# set(CMAKE_CXX_COMPILER "/usr/bin/g++-12") +set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wsign-promo -Wnon-virtual-dtor -Wctor-dtor-privacy -Woverloaded-virtual -Wold-style-cast -Wpointer-arith -Wshadow -Wunused -Wuninitialized -Winit-self -Wdeprecated -Wfloat-equal") -set(CMAKE_CXX_EXTENSIONS OFF) -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(ENABLE_LINUX ON) -set(ENABLE_TEST OFF CACHE BOOL "Enable Google Test") +set(ENABLE_TEST ON CACHE BOOL "Enable Google Test") set(ENABLE_GNUTLS OFF CACHE BOOL "Enable GNUTLS link") if( NOT CMAKE_BUILD_TYPE ) @@ -21,6 +23,15 @@ MinSizeRel." FORCE ) endif() if( ENABLE_TEST ) + include(CTest) + include(FetchContent) + FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip + ) + # For Windows: Prevent overriding the parent project's compiler/linker settings + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(googletest) message("Unit test enabled") enable_testing() endif() @@ -37,6 +48,10 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/graphs) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/tree) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/misc/colors) +if (ENABLE_LINUX) +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/linux) +endif() + #add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/namedpipes) #add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/example/tcpsocket) diff --git a/src/abstractfactory/test/CMakeLists.txt b/src/abstractfactory/test/CMakeLists.txt index fc3f1a8..88399bc 100644 --- a/src/abstractfactory/test/CMakeLists.txt +++ b/src/abstractfactory/test/CMakeLists.txt @@ -1,12 +1,12 @@ project(AbstractFactoryTest) -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.14) + -set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pthread") enable_testing() -find_package(GTest REQUIRED) +include(GoogleTest) if(NOT TARGET CppUtils::AbstractFactory) @@ -21,7 +21,7 @@ file(GLOB SRCS ${CMAKE_CURRENT_SOURCE_DIR}/test/src/*.cpp) add_executable(Test_abstractfactory ${SRCS}) -target_link_libraries(Test_abstractfactory PRIVATE ${GTEST_BOTH_LIBRARIES}) +target_link_libraries(Test_abstractfactory PRIVATE GTest::gtest_main) add_test(FactoryTests2 Test_abstractfactory --gtest_output=xml) diff --git a/src/example/tcpepollserver/CMakeLists.txt b/src/example/tcpepollserver/CMakeLists.txt index 0462bde..d0863bc 100644 --- a/src/example/tcpepollserver/CMakeLists.txt +++ b/src/example/tcpepollserver/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.8) project(TcpEpollServerExample) -set(CMAKE_CXX_STANDARD 14) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pthread") set(CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/src/example/tcpsecuresocket/CMakeLists.txt b/src/example/tcpsecuresocket/CMakeLists.txt index 7ccf6a9..157643a 100644 --- a/src/example/tcpsecuresocket/CMakeLists.txt +++ b/src/example/tcpsecuresocket/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.8) project(TcpSecureExample) -set(CMAKE_CXX_STANDARD 14) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pthread") set(CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/src/example/tcpsocket/CMakeLists.txt b/src/example/tcpsocket/CMakeLists.txt index 3ca4f51..f095208 100644 --- a/src/example/tcpsocket/CMakeLists.txt +++ b/src/example/tcpsocket/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.8) project(TcpExample) -set(CMAKE_CXX_STANDARD 14) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pthread") set(CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/src/example/thread/CMakeLists.txt b/src/example/thread/CMakeLists.txt index c291d9e..0f7a153 100644 --- a/src/example/thread/CMakeLists.txt +++ b/src/example/thread/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.8) project(ThreadExample) -set(CMAKE_CXX_STANDARD 14) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pthread") set(CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/src/factory/CMakeLists.txt b/src/factory/CMakeLists.txt index e74913f..870a689 100644 --- a/src/factory/CMakeLists.txt +++ b/src/factory/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.8) project(Factory VERSION ${CppUtils_VERSION_MAJOR}.${CppUtils_VERSION_MINOR} LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 14) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") include (GNUInstallDirs) diff --git a/src/factory/test/CMakeLists.txt b/src/factory/test/CMakeLists.txt index bdbe4e6..9519279 100644 --- a/src/factory/test/CMakeLists.txt +++ b/src/factory/test/CMakeLists.txt @@ -2,12 +2,12 @@ cmake_minimum_required(VERSION 3.4) project(FactoryTest) -set(CMAKE_CXX_STANDARD 14) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pthread") enable_testing() -find_package(GTest REQUIRED) +include(GoogleTest) if(NOT TARGET CppUtils::Factory) find_package(Factory CONFIG REQUIRED) @@ -25,7 +25,7 @@ message("FILES ${SRCS}") add_executable(Test_factory ${SRCS}) target_include_directories(Test_factory PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/inc) -target_link_libraries(Test_factory ${GTEST_BOTH_LIBRARIES}) +target_link_libraries(Test_factory GTest::gtest_main) add_test(FactoryTests Test_factory --gtest_output=xml) diff --git a/src/graphs/test/CMakeLists.txt b/src/graphs/test/CMakeLists.txt index 5334d1e..adbd1dc 100644 --- a/src/graphs/test/CMakeLists.txt +++ b/src/graphs/test/CMakeLists.txt @@ -2,12 +2,12 @@ cmake_minimum_required(VERSION 3.4) project(GraphsTest) -set(CMAKE_CXX_STANDARD 14) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pthread") enable_testing() -find_package(GTest REQUIRED) +include(GoogleTest) @@ -27,11 +27,11 @@ include_directories(inc) include_directories(${graphs_INCLUDE_DIRS}) file(GLOB SRCS ${CMAKE_CURRENT_SOURCE_DIR}/test/src/*.cpp) -message("LIBS ${GTEST_BOTH_LIBRARIES}") +message("LIBS GTest::gtest_main") add_executable(Test_graphs ${SRCS}) target_include_directories(Test_graphs PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/inc) -target_link_libraries(Test_graphs ${GTEST_BOTH_LIBRARIES}) +target_link_libraries(Test_graphs GTest::gtest_main) add_test(GraphsTests Test_graphs --gtest_output=xml) diff --git a/src/linux/CMakeLists.txt b/src/linux/CMakeLists.txt new file mode 100644 index 0000000..fbb86c9 --- /dev/null +++ b/src/linux/CMakeLists.txt @@ -0,0 +1,3 @@ +# set(CMAKE_CXX_COMPILER "/usr/bin/x86_64-linux-gnu-g++-12") +add_subdirectory(futex) +add_subdirectory(shm) diff --git a/src/linux/futex/CMakeLists.txt b/src/linux/futex/CMakeLists.txt new file mode 100644 index 0000000..abc6ba6 --- /dev/null +++ b/src/linux/futex/CMakeLists.txt @@ -0,0 +1,49 @@ +cmake_minimum_required(VERSION 3.8) + +project(Futex VERSION ${CppUtils_VERSION_MAJOR}.${CppUtils_VERSION_MINOR} LANGUAGES CXX) + + +include (GNUInstallDirs) + + +include_directories(inc) + +file(GLOB SRC_FILES + "src/*.cpp" +) + +file(GLOB HEADER_FILES + "inc/*.h" +) + +add_library(Futex INTERFACE ) +target_link_libraries(Futex INTERFACE CppUtils::Shm) + +set( futex_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/inc" CACHE STRING "Include-directories for futex" FORCE ) + + +add_library(CppUtils::Futex ALIAS Futex) + + +install (TARGETS Futex EXPORT FutexTargets + LIBRARY DESTINATION lib/cpputils/futex + INCLUDES DESTINATION include/cpputils/futex) + +install (EXPORT FutexTargets + DESTINATION lib/cpputils/cmake/Futex + FILE FutexTargets.cmake + NAMESPACE CppUtils::) + +install (DIRECTORY inc/futex + DESTINATION include/cpputils) + +include (CMakePackageConfigHelpers) +write_basic_package_version_file(FutexConfigVersion.cmake COMPATIBILITY SameMajorVersion) +install (FILES FutexConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/FutexConfigVersion.cmake + DESTINATION lib/cmake/cpputils/Futex) + + +if( ENABLE_TEST ) + message("Unit test enabled") + include(${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt) +endif() diff --git a/src/linux/futex/FutexConfig.cmake b/src/linux/futex/FutexConfig.cmake new file mode 100644 index 0000000..80d6cce --- /dev/null +++ b/src/linux/futex/FutexConfig.cmake @@ -0,0 +1,7 @@ +include (CMakeFindDependencyMacro) + +# set(SOCKETS_LIBRARIES Sockets) +# FIND_LIBRARY(SOCKETS_LIBRARIES NAMES "Sockets" HINTS "/usr/local/lib/cpputils/sockets/") + + +include ("${CMAKE_CURRENT_LIST_DIR}/FutexTargets.cmake") diff --git a/src/linux/futex/inc/futex/futex.hpp b/src/linux/futex/inc/futex/futex.hpp new file mode 100644 index 0000000..8df5ad5 --- /dev/null +++ b/src/linux/futex/inc/futex/futex.hpp @@ -0,0 +1,136 @@ +/** + * @file futex.hpp + * @brief Implementation of a futex-based mutex. + * + * This file contains the implementation of a mutex using futexes, based on Ulrich Drepper's + * "Futexes Are Tricky" paper. + * + * @see https://www.akkadia.org/drepper/futex.pdf + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace CppUtils +{ + +// 0 means unlocked +const int UNLOCKED(0); +// 1 means locked, no waiters +const int LOCKED_NO_WAITERS(1); +// 2 means locked, there are waiters in lock() +const int LOCKED(2); + +// An atomic_compare_exchange as expected in the paper +static int cmpxchg(std::atomic* atom, int expected, int desired) +{ + int* ep = &expected; + std::atomic_compare_exchange_strong(atom, ep, desired); + return *ep; +} + +/// @brief Futex-based mutex +/// +/// This class implements a mutex using futexes. It's based on Ulrich Drepper's "Futexes Are +/// Tricky" paper. +/// +/// @see https://www.akkadia.org/drepper/futex.pdf +class Futex +{ +public: + /// @brief Default constructor + Futex() + : free_resources(true) + { + ptr_atom = new std::atomic; + ptr_atom->store(UNLOCKED); + } + + /// @brief Constructor that allows to pass a pointer to the atomic to use + /// @param val_atomic Pointer to atomic value + Futex(std::atomic* val_atomic) + : ptr_atom(val_atomic) + , free_resources(false) + { + ptr_atom->store(UNLOCKED); + } + + /// @brief Locks the mutex + void lock() + { + int c = cmpxchg(ptr_atom, UNLOCKED, LOCKED_NO_WAITERS); + // If the lock was previously unlocked, there's nothing else for us to do. + // Otherwise, we'll probably have to wait. + if (c != UNLOCKED) { + do { + // If the mutex is locked, we signal that we're waiting by setting the + // atom to 2. A shortcut checks is it's 2 already and avoids the atomic + // operation in this case. + if (c == LOCKED || cmpxchg(ptr_atom, LOCKED_NO_WAITERS, LOCKED) != UNLOCKED) { + // Here we have to actually sleep, because the mutex is actually + // locked. Note that it's not necessary to loop around this syscall; + // a spurious wakeup will do no harm since we only exit the do...while + // loop when ptr_atom is indeed 0. + syscall(SYS_futex, reinterpret_cast(ptr_atom), FUTEX_WAIT, LOCKED, 0, 0, 0); + } + // We're here when either: + // (a) the mutex was in fact unlocked (by an intervening thread). + // (b) we slept waiting for the atom and were awoken. + // + // So we try to lock the atom again. We set teh state to 2 because we + // can't be certain there's no other thread at this exact point. So we + // prefer to err on the safe side. + } while ((c = cmpxchg(ptr_atom, UNLOCKED, LOCKED)) != UNLOCKED); + } + } + + /// @brief Unlock the mutex + void unlock() + { + if (ptr_atom->fetch_sub(1) != LOCKED_NO_WAITERS) { + ptr_atom->store(UNLOCKED); + syscall(SYS_futex, reinterpret_cast(ptr_atom), FUTEX_WAKE, LOCKED_NO_WAITERS, 0, 0, 0); + } + } + + /// @brief Notifies the number of waiters + /// @param number_of_waiters + /// @return -1 on ERROR, 0 on success + int32_t post(uint32_t number_of_waiters) + { + return syscall(SYS_futex, reinterpret_cast(ptr_atom), FUTEX_WAKE, number_of_waiters, 0, 0, 0); + } + + /// @brief Wait for the value to change + /// @param wait_value Value to wait for + /// @return -1 on ERROR, 0 on success + int32_t wait(uint32_t wait_value) + { + return syscall(SYS_futex, reinterpret_cast(ptr_atom), FUTEX_WAIT, wait_value, 0, 0, 0); + } + + /// @brief Destructor + virtual ~Futex() + { + if (free_resources) { + delete ptr_atom; + } + } + +private: + std::atomic* ptr_atom; + bool free_resources; +}; +} // namespace CppUtils \ No newline at end of file diff --git a/src/linux/futex/inc/futex/shared_futex.hpp b/src/linux/futex/inc/futex/shared_futex.hpp new file mode 100644 index 0000000..f04b525 --- /dev/null +++ b/src/linux/futex/inc/futex/shared_futex.hpp @@ -0,0 +1,89 @@ +/** + * @file futex.hpp + * @brief Implementation of a futex-based mutex. + * + * This file contains the implementation of a mutex using futexes, based on Ulrich Drepper's + * "Futexes Are Tricky" paper. + * + * @see https://www.akkadia.org/drepper/futex.pdf + */ + +#pragma once + +#include "futex.hpp" +#include "shm/shm.hpp" + +#include +#include +#include + +#include +#include + +namespace CppUtils +{ + +static bool file_exists(const std::string& file_path) +{ + struct stat buffer; + return (stat(file_path.c_str(), &buffer) == 0); +} + +class SharedFutex +{ +public: + SharedFutex(const std::string& mem_file_path) + : file_path(mem_file_path) + , ptr_futex(nullptr) + { + init(file_path); + } + + SharedFutex() + : ptr_futex(nullptr) + { + } + + int32_t init(const std::string& mem_path) + { + if (ptr_futex != nullptr) { + return -1; + } + + file_path = mem_path; + + if (file_exists("/dev/shm/" + mem_path)) { + shm_atomic.open_existing(file_path, sizeof(int)); + } else { + + auto ret = shm_atomic.open_existing(file_path, sizeof(int)); + + ret = shm_atomic.allocate(sizeof(int)); + + if (ret != 0) { + return ret; + } + } + + auto* ptr_int = static_cast(shm_atomic.get_raw_ptr()); + + ptr_futex = std::make_unique(ptr_int); + return 0; + } + + void lock() + { + ptr_futex->lock(); + } + + void unlock() + { + ptr_futex->unlock(); + } + +private: + std::string file_path; + Shm shm_atomic; + std::unique_ptr ptr_futex; +}; +} // namespace CppUtils \ No newline at end of file diff --git a/src/linux/futex/test/CMakeLists.txt b/src/linux/futex/test/CMakeLists.txt new file mode 100644 index 0000000..7156920 --- /dev/null +++ b/src/linux/futex/test/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.14) +project(FutexTest) + + + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pthread") + + +enable_testing() +include(GoogleTest) + + + +FIND_LIBRARY(GRAPHS_LIBRARIES NAMES "Futex" HINTS "/usr/local/lib/cpputils/futex/") + + +if(NOT TARGET CppUtils::Futex) + message("Not target") + find_package(Futex REQUIRED) +endif() + + + +include_directories(${GTEST_INCLUDE_DIRS} ${futex_INCLUDE_DIRS} ) + +include_directories(inc) +include_directories(${futex_INCLUDE_DIRS}) + +file(GLOB SRCS ${CMAKE_CURRENT_SOURCE_DIR}/test/src/*.cpp) +message("LIBS GTest::gtest_main") + +add_executable(Test_futex ${SRCS}) +target_include_directories(Test_futex PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/inc ${CMAKE_CURRENT_SOURCE_DIR}/../shm/inc) +target_link_libraries(Test_futex GTest::gtest_main Shm rt) + +add_test(FutexTests Test_futex --gtest_output=xml) + +# install(TARGETS testsignal DESTINATION bin) + diff --git a/src/linux/futex/test/src/futex_test.cpp b/src/linux/futex/test/src/futex_test.cpp new file mode 100644 index 0000000..3effc3c --- /dev/null +++ b/src/linux/futex/test/src/futex_test.cpp @@ -0,0 +1,58 @@ +#include + +#include +#include +#include + +#include + +namespace +{ + +TEST(ExampleFutex, TestInstance) +{ + CppUtils::Futex futex; + futex.lock(); + futex.unlock(); + + EXPECT_TRUE(true); +} + +TEST(ExampleFutex, TestPost) +{ + std::atomic_int32_t atom(0); + CppUtils::Futex futex(&atom); + + std::thread t([&futex]() { + auto ret = futex.wait(0); + EXPECT_NE(ret, -1); + }); + + std::thread t2([&futex]() { + auto ret = futex.wait(0); + EXPECT_NE(ret, -1); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + atom.store(2); + auto ret = futex.post(2); + EXPECT_NE(ret, -1); + + atom.store(0); + ret = futex.post(2); + EXPECT_NE(ret, -1); + + t.join(); + t2.join(); +} + +TEST(ExampleFutex, TestShared) +{ + CppUtils::SharedFutex futex("/test_futex"); + futex.lock(); + futex.unlock(); + + EXPECT_TRUE(true); +} + +}; // namespace diff --git a/src/linux/futex/test/src/main.cpp b/src/linux/futex/test/src/main.cpp new file mode 100644 index 0000000..774645a --- /dev/null +++ b/src/linux/futex/test/src/main.cpp @@ -0,0 +1,9 @@ + +#include + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + return ret; +} diff --git a/src/linux/shm/CMakeLists.txt b/src/linux/shm/CMakeLists.txt new file mode 100644 index 0000000..e058b19 --- /dev/null +++ b/src/linux/shm/CMakeLists.txt @@ -0,0 +1,48 @@ +cmake_minimum_required(VERSION 3.14) + +project(Shm VERSION ${CppUtils_VERSION_MAJOR}.${CppUtils_VERSION_MINOR} LANGUAGES CXX) + + +include (GNUInstallDirs) + + +include_directories(inc) + +file(GLOB SRC_FILES + "src/*.cpp" +) + +file(GLOB HEADER_FILES + "inc/*.h" +) + +add_library(Shm INTERFACE ) + +set( shm_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/inc" CACHE STRING "Include-directories for shm" FORCE ) + + +add_library(CppUtils::Shm ALIAS Shm) + + +install (TARGETS Shm EXPORT ShmTargets + LIBRARY DESTINATION lib/cpputils/shm + INCLUDES DESTINATION include/cpputils/shm) + +install (EXPORT ShmTargets + DESTINATION lib/cpputils/cmake/Shm + FILE ShmTargets.cmake + NAMESPACE CppUtils::) + +install (DIRECTORY inc/shm + DESTINATION include/cpputils) + +include (CMakePackageConfigHelpers) +write_basic_package_version_file(ShmConfigVersion.cmake COMPATIBILITY SameMajorVersion) +install (FILES ShmConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/ShmConfigVersion.cmake + DESTINATION lib/cmake/cpputils/Shm) + + +if( ENABLE_TEST ) + message("Unit test enabled") + include(${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt) +endif() diff --git a/src/linux/shm/ShmConfig.cmake b/src/linux/shm/ShmConfig.cmake new file mode 100644 index 0000000..c7b6782 --- /dev/null +++ b/src/linux/shm/ShmConfig.cmake @@ -0,0 +1,4 @@ +include (CMakeFindDependencyMacro) + + +include ("${CMAKE_CURRENT_LIST_DIR}/ShmTargets.cmake") diff --git a/src/linux/shm/inc/shm/shm.hpp b/src/linux/shm/inc/shm/shm.hpp new file mode 100644 index 0000000..77d2931 --- /dev/null +++ b/src/linux/shm/inc/shm/shm.hpp @@ -0,0 +1,134 @@ +/** + * @file shm.hpp + * @brief Shared memory abstraction for POSIX systems. + * + * + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include +// #include +#include + +namespace CppUtils +{ + +const int32_t INVALID_FD = -1; +class Shm +{ +public: + Shm() + : fd(INVALID_FD) + , ptr_shared_mem{nullptr} + { + } + + Shm(const std::string& file_mem_path) + : mem_path(file_mem_path) + , fd(INVALID_FD) + , ptr_shared_mem{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 + int32_t 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 + int32_t open_existing(std::size_t mem_size) + { + if (fd != INVALID_FD) { + return -1; + } + int flags = O_RDWR; + 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 + int32_t allocate(std::size_t mem_size) + { + if (fd != INVALID_FD) { + return -1; + } + + int flags = O_CREAT | O_EXCL | O_RDWR; + + return shared_open(flags, mem_size); + } + + /// @brief Removes the shared memory file + /// @return 0 on success, -1 on error + int32_t unlink() + { + auto ret = shm_unlink(mem_path.c_str()); + if (ret != -1) { + return 0; + } + return -1; + } + + /// @brief Closes the shared memory + void close() + { + if (fd != INVALID_FD) { + ::close(fd); + } + fd = INVALID_FD; + } + + /// @brief Get the pointer to the shared memory + /// @return Pointer to the shared memory + void* get_raw_ptr() + { + return ptr_shared_mem; + } + + virtual ~Shm() + { + close(); + } + +private: + std::string mem_path; + int fd; + void* ptr_shared_mem; + + int32_t shared_open(int flags, std::size_t mem_size) + { + + fd = shm_open(mem_path.c_str(), flags, S_IRUSR | S_IWUSR); + + if (fd == INVALID_FD) { + return -1; + } + + if (ftruncate(fd, mem_size) == INVALID_FD) { + return -1; + } + + ptr_shared_mem = mmap(nullptr, mem_size, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0); + if (ptr_shared_mem == MAP_FAILED) { + return -1; + } + + return 0; + } +}; +} // namespace CppUtils \ No newline at end of file diff --git a/src/linux/shm/test/CMakeLists.txt b/src/linux/shm/test/CMakeLists.txt new file mode 100644 index 0000000..d3f4c18 --- /dev/null +++ b/src/linux/shm/test/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.14) +project(ShmTest) + + + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pthread") + + +enable_testing() +include(GoogleTest) + + + +FIND_LIBRARY(GRAPHS_LIBRARIES NAMES "Shm" HINTS "/usr/local/lib/cpputils/shm/") + + +if(NOT TARGET CppUtils::Shm) + message("Not target") + find_package(Shm REQUIRED) +endif() + + + +include_directories(${GTEST_INCLUDE_DIRS} ${shm_INCLUDE_DIRS} ) + +include_directories(inc) +include_directories(${shm_INCLUDE_DIRS}) + +file(GLOB SRCS ${CMAKE_CURRENT_SOURCE_DIR}/test/src/*.cpp) +message("LIBS GTest::gtest_main") + +add_executable(Test_shm ${SRCS}) +target_include_directories(Test_shm PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/inc) +target_link_libraries(Test_shm GTest::gtest_main rt) + +add_test(ShmTests Test_shm --gtest_output=xml) + +# install(TARGETS testsignal DESTINATION bin) + diff --git a/src/linux/shm/test/src/main.cpp b/src/linux/shm/test/src/main.cpp new file mode 100644 index 0000000..774645a --- /dev/null +++ b/src/linux/shm/test/src/main.cpp @@ -0,0 +1,9 @@ + +#include + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + return ret; +} diff --git a/src/linux/shm/test/src/shm_test.cpp b/src/linux/shm/test/src/shm_test.cpp new file mode 100644 index 0000000..18e48f6 --- /dev/null +++ b/src/linux/shm/test/src/shm_test.cpp @@ -0,0 +1,61 @@ +#include + +#include + +#include + +#include + +namespace +{ + +TEST(ExampleShm, TestInstance) +{ + CppUtils::Shm shm("/test_shm"); + auto ret = shm.allocate(sizeof(int32_t)); + EXPECT_NE(ret, -1); + + void* ptr = shm.get_raw_ptr(); + 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) +{ + CppUtils::Shm shm("/test_shm"); + auto ret = shm.allocate(sizeof(int32_t)); + EXPECT_NE(ret, -1); + + void* ptr = shm.get_raw_ptr(); + 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(); + + CppUtils::Shm shm2("/test_shm"); + ret = shm.open_existing(sizeof(int32_t)); + EXPECT_NE(ret, -1); + ptr = shm.get_raw_ptr(); + ptr_int = reinterpret_cast(ptr); + std::cout << "ptr_int: " << *ptr_int << std::endl; + + val = *ptr_int; + EXPECT_EQ(val, 42); + shm.close(); + shm.unlink(); +} + +}; // namespace diff --git a/src/measuretime/CMakeLists.txt b/src/measuretime/CMakeLists.txt index d22245d..0d5c3ca 100644 --- a/src/measuretime/CMakeLists.txt +++ b/src/measuretime/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.8) project(MeasureTime VERSION ${CppUtils_VERSION_MAJOR}.${CppUtils_VERSION_MINOR} LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 14) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") include (GNUInstallDirs) diff --git a/src/measuretime/test/CMakeLists.txt b/src/measuretime/test/CMakeLists.txt index ea5942e..975ac40 100644 --- a/src/measuretime/test/CMakeLists.txt +++ b/src/measuretime/test/CMakeLists.txt @@ -1,12 +1,12 @@ -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.14) + -set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pthread") enable_testing() -find_package(GTest REQUIRED) +include(GoogleTest) include_directories(${GTEST_INCLUDE_DIRS}) include_directories(inc) @@ -15,7 +15,7 @@ file(GLOB SRCS src/*.cpp) add_executable(Test_measuretime ${SRCS}) -target_link_libraries(Test_measuretime ${GTEST_BOTH_LIBRARIES}) +target_link_libraries(Test_measuretime GTest::gtest_main) add_test(MeasureTimeTests Test_measuretime --gtest_output=xml) diff --git a/src/memallocatortracer/CMakeLists.txt b/src/memallocatortracer/CMakeLists.txt index 178b095..0e44298 100644 --- a/src/memallocatortracer/CMakeLists.txt +++ b/src/memallocatortracer/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.4) project(MemAllocatorTracer VERSION ${CppUtils_VERSION_MAJOR}.${CppUtils_VERSION_MINOR} LANGUAGES CXX) - +set(CMAKE_CXX_STANDARD 14) include (GNUInstallDirs) diff --git a/src/memallocatortracer/inc/memallocatortracer/memallocatortracer.h b/src/memallocatortracer/inc/memallocatortracer/memallocatortracer.h index 29e9e42..6822e29 100644 --- a/src/memallocatortracer/inc/memallocatortracer/memallocatortracer.h +++ b/src/memallocatortracer/inc/memallocatortracer/memallocatortracer.h @@ -1,83 +1,95 @@ #ifndef __CPPUTILS_MEMALLOCATORTRACER_H #define __CPPUTILS_MEMALLOCATORTRACER_H -//namespace CppUtils { +// namespace CppUtils { #include #include +#include -template -struct track_alloc : std::allocator { +template +struct track_alloc : std::allocator +{ typedef typename std::allocator::pointer pointer; typedef typename std::allocator::size_type size_type; - template - struct rebind { + template + struct rebind + { typedef track_alloc other; }; - track_alloc() {} + track_alloc() + { + } - template + template track_alloc(track_alloc const& u) - :std::allocator(u) {} + : std::allocator(u) + { + } - pointer allocate(size_type size, - std::allocator::const_pointer = 0) { - void * p = std::malloc(size * sizeof(T)); - if(p == 0) { + pointer allocate(size_type size, std::allocator::const_pointer = 0) + { + void* p = std::malloc(size * sizeof(T)); + if (p == 0) { throw std::bad_alloc(); } return static_cast(p); } - void deallocate(pointer p, size_type) { + void deallocate(pointer p, size_type) + { std::free(p); } }; -typedef std::map< void*, std::size_t, std::less, - track_alloc< std::pair > > track_type; +typedef std::map, track_alloc > > track_type; -struct track_printer { - track_type * track; - track_printer(track_type * track):track(track) {} - ~track_printer() { +struct track_printer +{ + track_type* track; + track_printer(track_type* track) + : track(track) + { + } + ~track_printer() + { track_type::const_iterator it = track->begin(); - while(it != track->end()) { - std::cerr << "TRACK: leaked at " << it->first << ", " - << it->second << " bytes\n"; + while (it != track->end()) { + std::cerr << "TRACK: leaked at " << it->first << ", " << it->second << " bytes\n"; ++it; } } }; -track_type * get_map() { +track_type* get_map() +{ // don't use normal new to avoid infinite recursion. - static track_type * track = new (std::malloc(sizeof *track)) - track_type; + static track_type* track = new (std::malloc(sizeof *track)) track_type; static track_printer printer(track); return track; } -void * operator new(std::size_t size) throw(std::bad_alloc) { +void* operator new(std::size_t size) throw(std::bad_alloc) +{ // we are required to return non-null - void * mem = std::malloc(size == 0 ? 1 : size); - if(mem == 0) { + void* mem = std::malloc(size == 0 ? 1 : size); + if (mem == 0) { throw std::bad_alloc(); } (*get_map())[mem] = size; return mem; } -void operator delete(void * mem) throw() { - if(get_map()->erase(mem) == 0) { +void operator delete(void* mem) throw() +{ + if (get_map()->erase(mem) == 0) { // this indicates a serious bug - std::cerr << "bug: memory at " - << mem << " wasn't allocated by us\n"; + std::cerr << "bug: memory at " << mem << " wasn't allocated by us\n"; } std::free(mem); } //} //namespace -#endif //__CPPUTILS_MEMALLOCATORTRACER_H +#endif //__CPPUTILS_MEMALLOCATORTRACER_H diff --git a/src/memallocatortracer/test/CMakeLists.txt b/src/memallocatortracer/test/CMakeLists.txt index 4b7d813..65389a3 100644 --- a/src/memallocatortracer/test/CMakeLists.txt +++ b/src/memallocatortracer/test/CMakeLists.txt @@ -1,13 +1,13 @@ -cmake_minimum_required(VERSION 3.4) +cmake_minimum_required(VERSION 3.14) + -set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pthread") enable_testing() -find_package(GTest REQUIRED) +include(GoogleTest) include_directories( ${memallocatortrace_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS} "${CMAKE_CURRENT_SOURCE_DIR}/inc" ) @@ -21,7 +21,7 @@ message("FILES ${SRCS}") add_executable(Test_memallocatortrace ${SRCS}) -target_link_libraries(Test_memallocatortrace ${GTEST_BOTH_LIBRARIES}) +target_link_libraries(Test_memallocatortrace GTest::gtest_main) add_test(MemallocatortraceTests Test_memallocatortrace --gtest_output=xml) diff --git a/src/misc/colors/test/CMakeLists.txt b/src/misc/colors/test/CMakeLists.txt index a251cbe..54386a4 100644 --- a/src/misc/colors/test/CMakeLists.txt +++ b/src/misc/colors/test/CMakeLists.txt @@ -1,12 +1,12 @@ cmake_minimum_required(VERSION 3.4) -set(CMAKE_CXX_STANDARD 14) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pthread") enable_testing() -find_package(GTest REQUIRED) +include(GoogleTest) include_directories( ${colors_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS} "${CMAKE_CURRENT_SOURCE_DIR}/inc" ) @@ -19,7 +19,7 @@ file(GLOB SRCS ${CMAKE_CURRENT_SOURCE_DIR}/test/src/*.cpp) message("FILES*** ${SRCS}") add_executable(Test_colors ${SRCS}) target_include_directories(Test_colors PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/inc) -target_link_libraries(Test_colors ${GTEST_BOTH_LIBRARIES}) +target_link_libraries(Test_colors GTest::gtest_main) add_test(ColorTests Test_colors --gtest_output=xml) diff --git a/src/signal/test/CMakeLists.txt b/src/signal/test/CMakeLists.txt index 6b1ebf9..0f62536 100644 --- a/src/signal/test/CMakeLists.txt +++ b/src/signal/test/CMakeLists.txt @@ -1,12 +1,14 @@ cmake_minimum_required(VERSION 3.4) -set(CMAKE_CXX_STANDARD 14) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pthread") enable_testing() -find_package(GTest REQUIRED) +include(GoogleTest) + + include_directories( ${signal_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS} "${CMAKE_CURRENT_SOURCE_DIR}/inc" ) @@ -21,9 +23,9 @@ add_executable(Test_signal ${SRCS}) target_include_directories(Test_signal PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/inc) -target_link_libraries(Test_signal ${GTEST_BOTH_LIBRARIES}) +target_link_libraries(Test_signal GTest::gtest_main) add_test(SignalTests Test_signal --gtest_output=xml) - +gtest_discover_tests(Test_signal) diff --git a/src/sockets/test/CMakeLists.txt b/src/sockets/test/CMakeLists.txt index 7083a1f..505e2b2 100644 --- a/src/sockets/test/CMakeLists.txt +++ b/src/sockets/test/CMakeLists.txt @@ -2,12 +2,12 @@ cmake_minimum_required(VERSION 3.4) project(SocketsTest) -set(CMAKE_CXX_STANDARD 14) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pthread") enable_testing() -find_package(GTest REQUIRED) +include(GoogleTest) @@ -29,11 +29,11 @@ include_directories(inc) include_directories(${sockets_INCLUDE_DIRS}) file(GLOB SRCS ${CMAKE_CURRENT_SOURCE_DIR}/test/src/*.cpp) -message("LIBS ${SOCKETS_LIBRARIES} ${GTEST_BOTH_LIBRARIES}") +message("LIBS ${SOCKETS_LIBRARIES} GTest::gtest_main") add_executable(Test_sockets ${SRCS}) target_include_directories(Test_sockets PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/inc) -target_link_libraries(Test_sockets ${GTEST_BOTH_LIBRARIES} ${SOCKETS_LIBRARIES} ${GNUTLS_LIBRARIES}) +target_link_libraries(Test_sockets GTest::gtest_main ${SOCKETS_LIBRARIES} ${GNUTLS_LIBRARIES}) add_test(SocketsTests Test_sockets --gtest_output=xml) diff --git a/src/threadpool/test/CMakeLists.txt b/src/threadpool/test/CMakeLists.txt index 80ab9a8..8bfab30 100644 --- a/src/threadpool/test/CMakeLists.txt +++ b/src/threadpool/test/CMakeLists.txt @@ -2,12 +2,12 @@ cmake_minimum_required(VERSION 3.4) project(ThreadPoolTest) -set(CMAKE_CXX_STANDARD 14) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pthread") enable_testing() -find_package(GTest REQUIRED) +include(GoogleTest) @@ -29,11 +29,11 @@ include_directories(inc) include_directories(${threadpool_INCLUDE_DIRS}) file(GLOB SRCS ${CMAKE_CURRENT_SOURCE_DIR}/test/src/*.cpp) -message("LIBS ${THREADPOOL_LIBRARIES} ${GTEST_BOTH_LIBRARIES}") +message("LIBS ${THREADPOOL_LIBRARIES} GTest::gtest_main") add_executable(Test_threadpool ${SRCS}) target_include_directories(Test_threadpool PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/inc) -target_link_libraries(Test_threadpool ${GTEST_BOTH_LIBRARIES} ${THREADPOOL_LIBRARIES} ${GNUTLS_LIBRARIES}) +target_link_libraries(Test_threadpool GTest::gtest_main ${THREADPOOL_LIBRARIES} ${GNUTLS_LIBRARIES}) add_test(ThreadPoolTests Test_threadpool --gtest_output=xml) diff --git a/src/tree/test/CMakeLists.txt b/src/tree/test/CMakeLists.txt index 4c0f2e5..67bbcbf 100644 --- a/src/tree/test/CMakeLists.txt +++ b/src/tree/test/CMakeLists.txt @@ -1,12 +1,12 @@ cmake_minimum_required(VERSION 3.4) -set(CMAKE_CXX_STANDARD 14) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pthread") enable_testing() -find_package(GTest REQUIRED) +include(GoogleTest) include_directories( ${tree_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS} "${CMAKE_CURRENT_SOURCE_DIR}/inc" ) @@ -19,7 +19,7 @@ file(GLOB SRCS ${CMAKE_CURRENT_SOURCE_DIR}/test/src/*.cpp) message("FILES*** ${SRCS}") add_executable(Test_tree ${SRCS}) target_include_directories(Test_tree PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/inc) -target_link_libraries(Test_tree ${GTEST_BOTH_LIBRARIES}) +target_link_libraries(Test_tree GTest::gtest_main) add_test(TreeTests Test_tree --gtest_output=xml)