Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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.

Expand Down
25 changes: 20 additions & 5 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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 )
Expand All @@ -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()
Expand All @@ -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)
Expand Down
8 changes: 4 additions & 4 deletions src/abstractfactory/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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)

Expand Down
2 changes: 1 addition & 1 deletion src/example/tcpepollserver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
2 changes: 1 addition & 1 deletion src/example/tcpsecuresocket/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
2 changes: 1 addition & 1 deletion src/example/tcpsocket/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
2 changes: 1 addition & 1 deletion src/example/thread/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
2 changes: 1 addition & 1 deletion src/factory/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 3 additions & 3 deletions src/factory/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)

Expand Down
8 changes: 4 additions & 4 deletions src/graphs/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)



Expand All @@ -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)

Expand Down
3 changes: 3 additions & 0 deletions src/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# set(CMAKE_CXX_COMPILER "/usr/bin/x86_64-linux-gnu-g++-12")
add_subdirectory(futex)
add_subdirectory(shm)
49 changes: 49 additions & 0 deletions src/linux/futex/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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()
7 changes: 7 additions & 0 deletions src/linux/futex/FutexConfig.cmake
Original file line number Diff line number Diff line change
@@ -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")
136 changes: 136 additions & 0 deletions src/linux/futex/inc/futex/futex.hpp
Original file line number Diff line number Diff line change
@@ -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 <atomic>
#include <cstdint>
#include <iostream>
#include <sstream>
#include <thread>

#include <linux/futex.h>
#include <pthread.h>
#include <sys/resource.h>
#include <sys/shm.h>
#include <sys/syscall.h>
#include <unistd.h>

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<int>* 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<int>;
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<int>* 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<int*>(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<int*>(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<int*>(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<int*>(ptr_atom), FUTEX_WAIT, wait_value, 0, 0, 0);
}

/// @brief Destructor
virtual ~Futex()
{
if (free_resources) {
delete ptr_atom;
}
}

private:
std::atomic<int>* ptr_atom;
bool free_resources;
};
} // namespace CppUtils
Loading