From 92e5a327b20896e92042f5ab9875d6663ab4dcad Mon Sep 17 00:00:00 2001 From: Michael Fabian 'Xaymar' Dirks Date: Sun, 6 Jan 2019 11:11:21 +0100 Subject: [PATCH] Initial Code --- .clang-format | 99 +++++++++ CMakeLists.txt | 325 ++++++++++++++++++++++++++++ cmake/modules/cppcheck.cmake | 237 ++++++++++++++++++++ cmake/modules/util.cmake | 19 ++ cmake/version.hpp.in | 35 +++ include/bitmask.hpp | 45 ++++ include/datapath.hpp | 31 +++ include/error.hpp | 45 ++++ include/event.hpp | 64 ++++++ include/iserver.hpp | 30 +++ include/isocket.hpp | 38 ++++ include/itask.hpp | 42 ++++ include/permissions.hpp | 32 +++ include/waitable.hpp | 63 ++++++ source/datapath.cpp | 42 ++++ source/windows/overlapped-queue.cpp | 158 ++++++++++++++ source/windows/overlapped-queue.hpp | 49 +++++ source/windows/overlapped.cpp | 98 +++++++++ source/windows/overlapped.hpp | 58 +++++ source/windows/server.cpp | 228 +++++++++++++++++++ source/windows/server.hpp | 73 +++++++ source/windows/socket.cpp | 233 ++++++++++++++++++++ source/windows/socket.hpp | 71 ++++++ source/windows/task.cpp | 74 +++++++ source/windows/task.hpp | 59 +++++ source/windows/utility.hpp | 55 +++++ source/windows/waitable.cpp | 165 ++++++++++++++ 27 files changed, 2468 insertions(+) create mode 100644 .clang-format create mode 100644 CMakeLists.txt create mode 100644 cmake/modules/cppcheck.cmake create mode 100644 cmake/modules/util.cmake create mode 100644 cmake/version.hpp.in create mode 100644 include/bitmask.hpp create mode 100644 include/datapath.hpp create mode 100644 include/error.hpp create mode 100644 include/event.hpp create mode 100644 include/iserver.hpp create mode 100644 include/isocket.hpp create mode 100644 include/itask.hpp create mode 100644 include/permissions.hpp create mode 100644 include/waitable.hpp create mode 100644 source/datapath.cpp create mode 100644 source/windows/overlapped-queue.cpp create mode 100644 source/windows/overlapped-queue.hpp create mode 100644 source/windows/overlapped.cpp create mode 100644 source/windows/overlapped.hpp create mode 100644 source/windows/server.cpp create mode 100644 source/windows/server.hpp create mode 100644 source/windows/socket.cpp create mode 100644 source/windows/socket.hpp create mode 100644 source/windows/task.cpp create mode 100644 source/windows/task.hpp create mode 100644 source/windows/utility.hpp create mode 100644 source/windows/waitable.cpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..37b9bdf --- /dev/null +++ b/.clang-format @@ -0,0 +1,99 @@ +# Basic Formatting +TabWidth: 8 +UseTab: ForIndentation +ColumnLimit: 120 + +# Language +Language: Cpp +Standard: Cpp11 + +# Indentation +AccessModifierOffset: 0 +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +IndentCaseLabels: false +#IndentPPDirectives: true +IndentWidth: 8 +IndentWrappedFunctionNames: true +NamespaceIndentation: All + +# Includes +#IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^<' + Priority: 1 + - Regex: '^"' + Priority: 2 +SortIncludes: true + +# Alignment +AlignAfterOpenBracket: true +AlignConsecutiveAssignments: true +AlignConsecutiveDeclarations: true +AlignEscapedNewlines: DontAlign +AlignOperands: true +AlignTrailingComments: true +DerivePointerAlignment: false +PointerAlignment: Left + +# Wrapping and Breaking +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false +# AfterExternBlock: false + AfterFunction: true + AfterNamespace: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +BinPackArguments: true +BinPackParameters: true +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Custom +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +#BreakInheritanceList: BeforeColon +BreakStringLiterals: true +ConstructorInitializerAllOnOneLineOrOnePerLine: false +Cpp11BracedListStyle: true + +# Spaces +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +#SpaceBeforeCpp11BracedList: false +#SpaceBeforeCtorInitializerColon: true +#SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +#SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false + +# Other +CommentPragmas: '^(!FIXME!|!TODO!|ToDo:)' +CompactNamespaces: false +DisableFormat: false +FixNamespaceComments: true +#ForEachMacros: '' +KeepEmptyLinesAtTheStartOfBlocks: false +ReflowComments: false +SortUsingDeclarations: true diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..4e11814 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,325 @@ +# Low Latency IPC Library for high-speed traffic +# Copyright (C) 2019 Michael Fabian Dirks +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +################################################################################ +# CMake Bootstrap +################################################################################ +# CMake Setup +cmake_minimum_required(VERSION 3.1.0) + +################################################################################ +# Project Bootstrap +################################################################################ +# Automatic Versioning +set(VERSION_MAJOR 0) +set(VERSION_MINOR 1) +set(VERSION_PATCH 0) +set(VERSION_TWEAK 0) +set(PROJECT_COMMIT "N/A") +if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/.git") + set(GIT_RESULT "") + set(GIT_OUTPUT "") + execute_process( + COMMAND git rev-list --count --topo-order ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}..HEAD + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + RESULT_VARIABLE GIT_RESULT + OUTPUT_VARIABLE GIT_OUTPUT + OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE ERROR_QUIET + ) + if(GIT_RESULT EQUAL 0) + set(VERSION_TWEAK ${GIT_OUTPUT}) + endif() + execute_process( + COMMAND git rev-parse HEAD + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + RESULT_VARIABLE GIT_RESULT + OUTPUT_VARIABLE GIT_OUTPUT + OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE ERROR_QUIET + ) + if(GIT_RESULT EQUAL 0) + set(PROJECT_COMMIT ${GIT_OUTPUT}) + endif() +endif() + +# Define Project +project( + datapath + VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}.${VERSION_TWEAK} +) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/modules/") + +# Check if we are compiling standalone +set(IsStandalone false) +if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + set(IsStandalone true) +endif() + +# Modules +include("util") +if(IsStandalone) + include("cppcheck") +endif() + +# Detect Build Type +if(IsStandalone) + set(PropertyPrefix "") +else() + set(PropertyPrefix "${PROJECT_NAME}_") +endif() + +# Detect Architecture +math(EXPR BITS "8*${CMAKE_SIZEOF_VOID_P}") +if("${BITS}" STREQUAL "32") + set(ARCH "x86") +else() + set(ARCH "x64") +endif() + +# Configure Version Header +configure_file( + "${PROJECT_SOURCE_DIR}/cmake/version.hpp.in" + "${PROJECT_BINARY_DIR}/include/version.hpp" +) + +################################################################################ +# Options +################################################################################ +# Static or Dynamic? +option(${OPTIONPREFIX}MAKE_STATIC "Make Static Library" ON) +option(${OPTIONPREFIX}MAKE_DYNAMIC "Make Dynamic Library" OFF) +option(${OPTIONPREFIX}MAKE_MODULE "Make Module Library" OFF) + +# Tests & Samples +option(${PropertyPrefix}BUILD_TESTS "Build Tests" ON) +option(${PropertyPrefix}BUILD_SAMPLES "Build Samples" ON) + +if(!IsStandalone) + CacheSet("${PropertyPrefix}BUILD_TESTS" OFF) + CacheSet("${PropertyPrefix}BUILD_SAMPLES" OFF) +endif() + +################################################################################ +# Source +################################################################################ +# Public (exported with module) +set(PROJECT_PUBLIC + "include/bitmask.hpp" + "include/datapath.hpp" + "include/error.hpp" + "include/event.hpp" + "include/isocket.hpp" + "include/iserver.hpp" + "include/itask.hpp" + "include/waitable.hpp" + "include/permissions.hpp" +) + +set(PROJECT_PUBLIC_GENERATED + "${PROJECT_BINARY_DIR}/include/version.hpp" +) + +set(PROJECT_DATA + "README.md" + "LICENSE" +) + +# Private (only compiled/used locally) +set(PROJECT_PRIVATE + "source/datapath.cpp" +) + +# Libraries +set(PROJECT_LIBRARIES +) + +# Defines +set(PROJECT_DEFINES +) + +# Platforms +if(WIN32) + # Windows + list(APPEND PROJECT_LIBRARIES + advapi32 + ) + + list(APPEND PROJECT_DEFINES + _CRT_SECURE_NO_WARNINGS + WIN32_LEAN_AND_MEAN + NOGPICAPMASKS + NOVIRTUALKEYCODES + NOWINMESSAGES + NOWINSTYLES + NOSYSMETRICS + NOMENUS + NOICONS + NOKEYSTATES + NOSYSCOMMANDS + NORASTEROPS + NOSHOWWINDOW + NOATOM + NOCLIPBOARD + NOCOLOR + NOCTLMGR + NODRAWTEXT + NOGDI + NOKERNEL + #NOUSER + #NONLS + NOMB + NOMEMMGR + NOMETAFILE + NOMINMAX + NOMSG + NOOPENFILE + NOSCROLL + NOSERVICE + NOSOUND + NOTEXTMETRIC + NOWH + NOWINOFFSETS + NOCOMM + NOKANJI + NOHELP + NOPROFILER + NODEFERWINDOWPOS + NOMCX + NOIME + NOMDI + NOINOUT + ) + + list(APPEND PROJECT_PRIVATE + "source/windows/overlapped.hpp" + "source/windows/overlapped.cpp" + "source/windows/socket.hpp" + "source/windows/socket.cpp" + "source/windows/server.hpp" + "source/windows/server.cpp" + "source/windows/task.hpp" + "source/windows/task.cpp" + "source/windows/utility.hpp" + "source/windows/waitable.cpp" + ) +elseif(APPLE) + # MacOSX + + list(APPEND PROJECT_PRIVATE + ) +elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Linux") + # Linux + + list(APPEND PROJECT_PRIVATE + ) +elseif("${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD") + # FreeBSD + + list(APPEND PROJECT_PRIVATE + ) +endif() + +# Grouping +source_group("Data Files" FILES $PROJECT_DATA) +source_group(TREE "${PROJECT_SOURCE_DIR}/source" PREFIX "Source" FILES ${PROJECT_PRIVATE}) +source_group(TREE "${PROJECT_SOURCE_DIR}/include" PREFIX "Include" FILES ${PROJECT_PUBLIC}) +source_group(TREE "${PROJECT_BINARY_DIR}" PREFIX "Generated" FILES ${PROJECT_PUBLIC_GENERATED}) + +################################################################################ +# Building +################################################################################ +# Library definition +if(${OPTIONPREFIX}MAKE_STATIC) + add_library(${PROJECT_NAME} STATIC + ${PROJECT_PRIVATE} + ${PROJECT_PUBLIC} + ${PROJECT_PUBLIC_GENERATED} + ${PROJECT_DATA} + ) +elseif(${PropertyPrefix}MAKE_DYNAMIC) + add_library(${PROJECT_NAME} SHARED + ${PROJECT_PRIVATE} + ${PROJECT_PUBLIC} + ${PROJECT_PUBLIC_GENERATED} + ${PROJECT_DATA} + ) +elseif(${OPTIONPREFIX}MAKE_MODULE) + add_library(${PROJECT_NAME} MODULE + ${PROJECT_PRIVATE} + ${PROJECT_PUBLIC} + ${PROJECT_PUBLIC_GENERATED} + ${PROJECT_DATA} + ) +else() + message(CRITICAL "Building nothing completed, aborting. Check MAKE_STATIC, MAKE_DYNAMIC and MAKE_DYNAMIC.") + return() +endif() + +# Includes +target_include_directories(${PROJECT_NAME} + PRIVATE source + PUBLIC include +) + +# Defines +target_compile_definitions(${PROJECT_NAME} + PRIVATE ${PROJECT_DEFINES} +) + +# Linking Directories +link_directories( +) + +# Linking +target_link_libraries(${PROJECT_NAME} + ${PROJECT_LIBRARIES} +) + +# File Version +if(WIN32) + set_target_properties( + ${PROJECT_NAME} + PROPERTIES + VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}.${PROJECT_VERSION_TWEAK} + SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}.${PROJECT_VERSION_TWEAK} + ) +else() + set_target_properties( + ${PROJECT_NAME} + PROPERTIES + VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}.${PROJECT_VERSION_TWEAK} + SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}.${PROJECT_VERSION_TWEAK} + ) +endif() + +# CPPCheck +if(IsStandalone) + cppcheck() + cppcheck_add_project(${PROJECT_NAME}) +endif() + +################################################################################ +# Samples +################################################################################ +if(${PropertyPrefix}BUILD_SAMPLES) + add_subdirectory(${PROJECT_SOURCE_DIR}/samples) +endif() + +################################################################################ +# Tests +################################################################################ +if(${PropertyPrefix}BUILD_TESTS) + add_subdirectory(${PROJECT_SOURCE_DIR}/tests) +endif() diff --git a/cmake/modules/cppcheck.cmake b/cmake/modules/cppcheck.cmake new file mode 100644 index 0000000..652f91c --- /dev/null +++ b/cmake/modules/cppcheck.cmake @@ -0,0 +1,237 @@ +set(CPPCHECK_PROJECTS "") +set(CPPCHECK_ARGUMENTS "") +set(CPPCHECK_PLATFORM "") +set(CPPCHECK_LIBRARIES "") + +include(CMakeParseArguments) + +function(cppcheck) + set(CPPCHECK_PATH "" CACHE PATH "Path to cppcheck binary") + set(CPPCHECK_BIN "cppcheck.exe" CACHE STRING "CPPCheck Binary File") + set(CPPCHECK_ENABLE_INCONCLUSIVE OFF CACHE BOOL "Enable inconclusive checks?") + set(CPPCHECK_ENABLE_MISSING_INCLUDE ON CACHE BOOL "Check for missing includes?") + set(CPPCHECK_ENABLE_UNUSED_FUNCTION OFF CACHE BOOL "Check for unused functions?") + set(CPPCHECK_ENABLE_INFORMATION ON CACHE BOOL "Enable information messages?") + set(CPPCHECK_ENABLE_PORTABILITY ON CACHE BOOL "Enable portability messages?") + set(CPPCHECK_ENABLE_PERFORMANCE ON CACHE BOOL "Enable performance messages?") + set(CPPCHECK_ENABLE_WARNING ON CACHE BOOL "Enable warning messages?") + set(CPPCHECK_STD_POSIX OFF CACHE BOOL "POSIX Standard Compatibility Checks") + set(CPPCHECK_STD_C89 OFF CACHE BOOL "C89 Standard Compatibility Checks") + set(CPPCHECK_STD_C99 OFF CACHE BOOL "C99 Standard Compatibility Checks") + set(CPPCHECK_STD_C11 ON CACHE BOOL "C11 Standard Compatibility Checks") + set(CPPCHECK_STD_CPP03 OFF CACHE BOOL "C++03 Standard Compatibility Checks") + set(CPPCHECK_STD_CPP11 OFF CACHE BOOL "C++11 Standard Compatibility Checks") + set(CPPCHECK_STD_CPP14 ON CACHE BOOL "C++14 Standard Compatibility Checks") + set(CPPCHECK_FORCE_C OFF CACHE BOOL "Force checking with C language") + set(CPPCHECK_FORCE_CPP OFF CACHE BOOL "Force checking with C++ language (overrides CPPCHECK_FORCE_C)") + set(CPPCHECK_VERBOSE ON CACHE BOOL "Show more detailed error reports") + set(CPPCHECK_QUIET ON CACHE BOOL "Hide progress reports") + set(CPPCHECK_ENABLE_INLINE_SUPPRESSION ON CACHE BOOL "Enable inline suppression of warnings via '// cppcheck-suppress id' comments") + set(CPPCHECK_LIBRARIES "" CACHE STRING "List of Libraries to load separated by semicolon") + set(CPPCHECK_EXCLUDE_DIRECTORIES "" CACHE STRING "List of directories to exclude separated by semicolon") + set(CPPCHECK_PARALLEL_TASKS "4" CACHE STRING "Number of threads to use for cppcheck") + set(CPPCHECK_TEMPLATE "{file}({line}:{column}): {severity} {id}: {message}" CACHE STRING "Template for reported messages") + if(WIN32) + set(CPPCHECK_WIN32_UNICODE ON CACHE BOOL "Use Unicode character encoding for Win32") + endif() + + mark_as_advanced(CPPCHECK_BIN CPPCHECK_QUIET CPPCHECK_VERBOSE CPPCHECK_LIBRARIES CPPCHECK_ENABLE_INCONCLUSIVE CPPCHECK_PARALLEL_TASKS CPPCHECK_TEMPLATE) + + # Parse arguments + set(cppcheck_options ) + set(cppcheck_oneval ) + set(cppcheck_mulval EXCLUDE) + cmake_parse_arguments( + CPPCHECKP + "${cppcheck_options}" + "${cppcheck_oneval}" + "${cppcheck_mulval}" + ${ARGN} + ) + + # Detect Architecture (Bitness) + math(EXPR BITS "8*${CMAKE_SIZEOF_VOID_P}") + + # Detect Platform + if(WIN32) + if(BITS EQUAL "32") + if(CPPCHECK_WIN32_UNICODE) + set(CPPCHECK_PLATFORM "win32W") + else() + set(CPPCHECK_PLATFORM "win32A") + endif() + else() + set(CPPCHECK_PLATFORM "win64") + endif() + elseif(("${CMAKE_SYSTEM_NAME}" MATCHES "Linux") OR ("${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD") OR APPLE) + if(BITS EQUAL "32") + set(CPPCHECK_PLATFORM "unix32") + else() + set(CPPCHECK_PLATFORM "unix64") + endif() + else() + set(CPPCHECK_PLATFORM "unspecified") + endif() + if(WIN32) + list(APPEND CPPCHECK_LIBRARIES "windows") + elseif("${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD") + list(APPEND CPPCHECK_LIBRARIES "bsd") + elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Linux") + list(APPEND CPPCHECK_LIBRARIES "gnu") + endif() + + # Arguments + set(CPPCHECK_ARGUMENTS "") + + # Compiler + list(APPEND CPPCHECK_ARGUMENTS --template=${CPPCHECK_TEMPLATE}) + + # Flags + if(CPPCHECK_ENABLE_INCONCLUSIVE) + list(APPEND CPPCHECK_ARGUMENTS --inconclusive) + endif() + if(CPPCHECK_VERBOSE) + list(APPEND CPPCHECK_ARGUMENTS -v) + endif() + if(CPPCHECK_QUIET) + list(APPEND CPPCHECK_ARGUMENTS -q) + endif() + if(CPPCHECK_PLATFORM) + list(APPEND CPPCHECK_ARGUMENTS --platform=${CPPCHECK_PLATFORM}) + endif() + if(CPPCHECK_PARALLEL_TASKS) + list(APPEND CPPCHECK_ARGUMENTS -j ${CPPCHECK_PARALLEL_TASKS}) + endif() + if(CPPCHECK_ENABLE_INLINE_SUPPRESSION) + list(APPEND CPPCHECK_ARGUMENTS --inline-suppr) + endif() + + # Libraries + foreach(_library ${CPPCHECK_LIBRARIES}) + list(APPEND CPPCHECK_ARGUMENTS --library=${_library}) + endforeach() + + # Exclusion + foreach(_path ${CPPCHECK_EXCLUDE_DIRECTORIES}) + file(TO_NATIVE_PATH "${_path}" _npath) + if(MSVC) + list(APPEND CPPCHECK_ARGUMENTS --suppress=*:${_npath}\\*) + else() + list(APPEND CPPCHECK_ARGUMENTS -i${_npath}) + endif() + endforeach() + if(CPPCHECKP_EXCLUDE) + foreach(_path ${CPPCHECKP_EXCLUDE}) + file(TO_NATIVE_PATH "${_path}" _npath) + if(MSVC) + list(APPEND CPPCHECK_ARGUMENTS --suppress=*:${_npath}\\*) + else() + list(APPEND CPPCHECK_ARGUMENTS -i${_npath}) + endif() + endforeach() + endif() + + # Checks + if(CPPCHECK_ENABLE_MISSING_INCLUDE) + list(APPEND CPPCHECK_ARGUMENTS --enable=missingInclude) + endif() + if(CPPCHECK_ENABLE_UNUSED_FUNCTION) + list(APPEND CPPCHECK_ARGUMENTS --enable=unusedFunction) + endif() + if(CPPCHECK_ENABLE_INFORMATION) + list(APPEND CPPCHECK_ARGUMENTS --enable=information) + endif() + if(CPPCHECK_ENABLE_PORTABILITY) + list(APPEND CPPCHECK_ARGUMENTS --enable=portability) + endif() + if(CPPCHECK_ENABLE_PERFORMANCE) + list(APPEND CPPCHECK_ARGUMENTS --enable=performance) + endif() + if(CPPCHECK_ENABLE_WARNING) + list(APPEND CPPCHECK_ARGUMENTS --enable=warning) + endif() + + # Std + if(CPPCHECK_STD_POSIX) + list(APPEND CPPCHECK_ARGUMENTS --std=posix) + endif() + if(CPPCHECK_STD_C89) + list(APPEND CPPCHECK_ARGUMENTS --std=c89) + endif() + if(CPPCHECK_STD_C99) + list(APPEND CPPCHECK_ARGUMENTS --std=c99) + endif() + if(CPPCHECK_STD_C11) + list(APPEND CPPCHECK_ARGUMENTS --std=c11) + endif() + if(CPPCHECK_STD_CPP03) + list(APPEND CPPCHECK_ARGUMENTS --std=c++03) + endif() + if(CPPCHECK_STD_CPP11) + list(APPEND CPPCHECK_ARGUMENTS --std=c++11) + endif() + if(CPPCHECK_STD_CPP14) + list(APPEND CPPCHECK_ARGUMENTS --std=c++14) + endif() + + # Force Language + if(CPPCHECK_FORCE_CPP) + list(APPEND CPPCHECK_ARGUMENTS --language=c++) + elseif(CPPCHECK_FORCE_C) + list(APPEND CPPCHECK_ARGUMENTS --language=c) + endif() + + add_custom_target( + CPPCHECK + ) + + # Propagate to parent scope + set(CPPCHECK_PROJECTS "${CPPCHECK_PROJECTS}" PARENT_SCOPE) + set(CPPCHECK_ARGUMENTS "${CPPCHECK_ARGUMENTS}" PARENT_SCOPE) + set(CPPCHECK_PLATFORM "${CPPCHECK_PLATFORM}" PARENT_SCOPE) + set(CPPCHECK_LIBRARIES "${CPPCHECK_LIBRARIES}" PARENT_SCOPE) +endfunction() + +function(cppcheck_add_project u_project) + list(APPEND CPPCHECK_PROJECTS ${u_project}) + + # Include Directories + get_target_property(_INCLUDE_DIRECTORIES ${u_project} INCLUDE_DIRECTORIES) + foreach(_path ${_INCLUDE_DIRECTORIES}) + file(TO_NATIVE_PATH "${_path}" _npath) + list(APPEND CPPCHECK_ARGUMENTS -I${_npath}) + endforeach() + + if(MSVC) + add_custom_target( + CPPCHECK_${u_project} + COMMAND "${CPPCHECK_PATH}/${CPPCHECK_BIN}" ${CPPCHECK_ARGUMENTS} --project=${${u_project}_BINARY_DIR}/${u_project}.sln + COMMAND_EXPAND_LISTS + VERBATIM + ) + else() + # Non-MSVC and Unix (Linux, FreeBSD, APPLE) need to have -I, -i, -D and -U specified manually. + # Each file can be added to --file-list= as a comma separated list. + + # Defines + get_target_property(_COMPILE_DEFINITIONS ${u_project} COMPILE_DEFINITIONS) + foreach(_def ${_COMPILE_DEFINITIONS}) + list(APPEND CPPCHECK_ARGUMENTS -D${_def}) + endforeach() + + # Source Files + get_target_property(_SOURCES ${u_project} SOURCES) + foreach(_path ${_SOURCES}) + file(TO_NATIVE_PATH "${_path}" _npath) + list(APPEND CPPCHECK_ARGUMENTS ${_npath}) + endforeach() + + add_custom_target( + CPPCHECK_${u_project} + COMMAND "${CPPCHECK_PATH}/${CPPCHECK_BIN}" ${CPPCHECK_ARGUMENTS} + COMMAND_EXPAND_LISTS + VERBATIM + ) + endif() + add_dependencies(CPPCHECK CPPCHECK_${u_project}) +endfunction() diff --git a/cmake/modules/util.cmake b/cmake/modules/util.cmake new file mode 100644 index 0000000..d54a0cf --- /dev/null +++ b/cmake/modules/util.cmake @@ -0,0 +1,19 @@ +Function(CacheSet Name Value) + GET_PROPERTY(V_ADVANCED CACHE "${Name}" PROPERTY ADVANCED) + GET_PROPERTY(V_TYPE CACHE "${Name}" PROPERTY TYPE) + GET_PROPERTY(V_HELPSTRING CACHE "${Name}" PROPERTY HELPSTRING) + Set(${Name} ${Value} CACHE ${V_TYPE} ${V_HELPSTRING} FORCE) + If(${V_ADVANCED}) + Mark_As_Advanced(FORCE ${Name}) + EndIf() +EndFunction() + +Function(CacheClear Name) + GET_PROPERTY(V_ADVANCED CACHE "${Name}" PROPERTY ADVANCED) + GET_PROPERTY(V_TYPE CACHE "${Name}" PROPERTY TYPE) + GET_PROPERTY(V_HELPSTRING CACHE "${Name}" PROPERTY HELPSTRING) + Set(${Name} 0 CACHE ${V_TYPE} ${V_HELPSTRING} FORCE) + If(${V_ADVANCED}) + Mark_As_Advanced(FORCE ${Name}) + EndIf() +EndFunction() \ No newline at end of file diff --git a/cmake/version.hpp.in b/cmake/version.hpp.in new file mode 100644 index 0000000..bc50f69 --- /dev/null +++ b/cmake/version.hpp.in @@ -0,0 +1,35 @@ +/* + * Ultra fast IPC library written in C++ + * Copyright (C) 2017 Michael Fabian Dirks + * + * 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 + * (at your option) 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#pragma once +#include + +#define MAKE_VERSION(major,minor,patch,tweak) ((uint64_t(major) & 0xFFFFull) << 48ull) | ((uint64_t(minor) & 0xFFFFull) << 32ull) | ((uint64_t(patch) & 0xFFFFull) << 16ull) | ((uint64_t(patch) & 0xFFFFull)) + +const uint16_t PROJECT_VERSION_MAJOR = @PROJECT_VERSION_MAJOR@; +const uint16_t PROJECT_VERSION_MINOR = @PROJECT_VERSION_MINOR@; +const uint16_t PROJECT_VERSION_PATCH = @PROJECT_VERSION_PATCH@; +const uint16_t PROJECT_VERSION_TWEAK = @PROJECT_VERSION_TWEAK@; +const uint64_t PROJECT_VERSION = MAKE_VERSION(PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR, PROJECT_VERSION_PATCH, PROJECT_VERSION_TWEAK); + +const uint16_t PLUGIN_VERSION_MAJOR = @PROJECT_VERSION_MAJOR@; +const uint16_t PLUGIN_VERSION_MINOR = @PROJECT_VERSION_MINOR@; +const uint16_t PLUGIN_VERSION_PATCH = @PROJECT_VERSION_PATCH@; +const uint16_t PLUGIN_VERSION_TWEAK = @PROJECT_VERSION_TWEAK@; +const uint64_t PLUGIN_VERSION_FULL = (((uint64_t)(PLUGIN_VERSION_MAJOR & 0xFFFF) << 48ull) | ((uint64_t)(PLUGIN_VERSION_MINOR & 0xFFFF) << 32ull) | ((uint64_t)(PLUGIN_VERSION_PATCH) & 0xFFFFFFFF)); diff --git a/include/bitmask.hpp b/include/bitmask.hpp new file mode 100644 index 0000000..42af229 --- /dev/null +++ b/include/bitmask.hpp @@ -0,0 +1,45 @@ +/* +Low Latency IPC Library for high-speed traffic +Copyright (C) 2019 Michael Fabian Dirks + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +#pragma once +#include + +template +struct enable_bitmask_operators { + static const bool enable = false; +}; + +template +typename std::enable_if::enable, Enum>::type operator|(Enum lhs, Enum rhs) +{ + using underlying = typename std::underlying_type::type; + return static_cast(static_cast(lhs) | static_cast(rhs)); +} + +template +typename std::enable_if::enable, Enum>::type operator&(Enum lhs, Enum rhs) +{ + using underlying = typename std::underlying_type::type; + return static_cast(static_cast(lhs) & static_cast(rhs)); +} + +#define ENABLE_BITMASK_OPERATORS(x) \ + template<> \ + struct enable_bitmask_operators { \ + static const bool enable = true; \ + }; diff --git a/include/datapath.hpp b/include/datapath.hpp new file mode 100644 index 0000000..3c32b1a --- /dev/null +++ b/include/datapath.hpp @@ -0,0 +1,31 @@ +/* +Low Latency IPC Library for high-speed traffic +Copyright (C) 2019 Michael Fabian Dirks + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +#pragma once +#include "error.hpp" +#include "iserver.hpp" +#include "isocket.hpp" +#include "itask.hpp" +#include "permissions.hpp" +#include + +namespace datapath { + datapath::error connect(std::shared_ptr& socket, std::string path); + + datapath::error host(std::shared_ptr& server, std::string path, datapath::permissions permissions, size_t max_clients); +} diff --git a/include/error.hpp b/include/error.hpp new file mode 100644 index 0000000..2f29766 --- /dev/null +++ b/include/error.hpp @@ -0,0 +1,45 @@ +/* +Low Latency IPC Library for high-speed traffic +Copyright (C) 2019 Michael Fabian Dirks + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +#pragma once +#include + +namespace datapath { + enum class error : int32_t { + // Unknown + Unknown = -1, + + // Success + Success, + + // Failure (Generic) + Failure, + + // Failure (Critical Generic) + CriticalFailure, + + // Socket Closed + Closed, + + // Timed Out + TimedOut, + + // Invalid Path + InvalidPath, + }; +} diff --git a/include/event.hpp b/include/event.hpp new file mode 100644 index 0000000..8a4f921 --- /dev/null +++ b/include/event.hpp @@ -0,0 +1,64 @@ +/* +Low Latency IPC Library for high-speed traffic +Copyright (C) 2019 Michael Fabian Dirks + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +#pragma once +#include +#include + +namespace datapath { + template + class event { + std::list> listeners; + + public: + void add(std::function listener) + { + listeners.push_back(listener); + } + + void remove(std::function listener) + { + listeners.remove(listener); + } + + // Not valid without the extra template. + template + void operator()(_largs... args) + { + for (auto& l : listeners) { + l(args...); + } + } + + operator bool() + { + return !listeners.empty(); + } + + public: + bool empty() + { + return listeners.empty(); + } + + void clear() + { + listeners.clear(); + } + }; +}; // namespace datapath diff --git a/include/iserver.hpp b/include/iserver.hpp new file mode 100644 index 0000000..1759a27 --- /dev/null +++ b/include/iserver.hpp @@ -0,0 +1,30 @@ +/* +Low Latency IPC Library for high-speed traffic +Copyright (C) 2019 Michael Fabian Dirks + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +#pragma once +#include +#include "error.hpp" +#include "isocket.hpp" + +namespace datapath { + class iserver { + virtual datapath::error accept(std::shared_ptr& socket) = 0; + + virtual datapath::error close() = 0; + }; +} // namespace datapath diff --git a/include/isocket.hpp b/include/isocket.hpp new file mode 100644 index 0000000..dd8fcb3 --- /dev/null +++ b/include/isocket.hpp @@ -0,0 +1,38 @@ +/* +Low Latency IPC Library for high-speed traffic +Copyright (C) 2019 Michael Fabian Dirks + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +#pragma once +#include "error.hpp" +#include "event.hpp" +#include "itask.hpp" + +namespace datapath { + class isocket { + public /*events*/: + datapath::event&> on_message; + + datapath::event on_close; + + public: + virtual bool good() = 0; + + virtual datapath::error close() = 0; + + virtual datapath::error write(std::shared_ptr& task, const std::vector& data) = 0; + }; +} // namespace datapath diff --git a/include/itask.hpp b/include/itask.hpp new file mode 100644 index 0000000..11588b2 --- /dev/null +++ b/include/itask.hpp @@ -0,0 +1,42 @@ +/* +Low Latency IPC Library for high-speed traffic +Copyright (C) 2019 Michael Fabian Dirks + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +#pragma once +#include +#include +#include "error.hpp" +#include "event.hpp" +#include "waitable.hpp" + +namespace datapath { + class itask : public waitable { + public /*event*/: + datapath::event on_failure; + + datapath::event&> on_success; + + public: + virtual datapath::error cancel() = 0; + + virtual bool is_completed() = 0; + + virtual size_t length() = 0; + + virtual const std::vector& data() = 0; + }; +} // namespace datapath diff --git a/include/permissions.hpp b/include/permissions.hpp new file mode 100644 index 0000000..3abef72 --- /dev/null +++ b/include/permissions.hpp @@ -0,0 +1,32 @@ +/* +Low Latency IPC Library for high-speed traffic +Copyright (C) 2019 Michael Fabian Dirks + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +#pragma once +#include +#include "bitmask.hpp" + +namespace datapath { + enum class permissions : int8_t { + None, + User, + Group, + World, + Reserved + }; + ENABLE_BITMASK_OPERATORS(datapath::permissions); +} // namespace datapath diff --git a/include/waitable.hpp b/include/waitable.hpp new file mode 100644 index 0000000..3c9d308 --- /dev/null +++ b/include/waitable.hpp @@ -0,0 +1,63 @@ +/* +Low Latency IPC Library for high-speed traffic +Copyright (C) 2019 Michael Fabian Dirks + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +#pragma once +#include +#include +#include "error.hpp" +#include "event.hpp" + +namespace datapath { + class waitable { + public /*events*/: + datapath::event on_wait_error; + + datapath::event on_wait_success; + + public: + virtual void* get_waitable() = 0; + + inline datapath::error wait(std::chrono::nanoseconds duration = std::chrono::nanoseconds(0)) + { + return datapath::waitable::wait(this, duration); + } + + public /*static*/: + + static datapath::error wait(datapath::waitable* obj, + std::chrono::nanoseconds duration = std::chrono::nanoseconds(0)); + + static datapath::error wait(datapath::waitable** objs, size_t count, + std::chrono::nanoseconds duration = std::chrono::nanoseconds(0)); + + static inline datapath::error wait(std::vector objs, + std::chrono::nanoseconds duration = std::chrono::nanoseconds(0)) + { + return datapath::waitable::wait(objs.data(), objs.size(), duration); + } + + static datapath::error wait_any(datapath::waitable** objs, size_t count, size_t& index, + std::chrono::nanoseconds duration = std::chrono::nanoseconds(0)); + + static inline datapath::error wait_any(std::vector objs, size_t& index, + std::chrono::nanoseconds duration = std::chrono::nanoseconds(0)) + { + return datapath::waitable::wait_any(objs.data(), objs.size(), index, duration); + } + }; +} // namespace datapath diff --git a/source/datapath.cpp b/source/datapath.cpp new file mode 100644 index 0000000..1af6e08 --- /dev/null +++ b/source/datapath.cpp @@ -0,0 +1,42 @@ +/* +Low Latency IPC Library for high-speed traffic +Copyright (C) 2019 Michael Fabian Dirks + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +#include "datapath.hpp" +#if defined(_WIN32) +#include "windows/server.hpp" +#include "windows/socket.hpp" +#endif + +datapath::error datapath::connect(std::shared_ptr& socket, std::string path) +{ +#if defined(_WIN32) + return datapath::windows::socket::connect(socket, path); +#else + return datapath::error::Unknown; +#endif +} + +datapath::error datapath::host(std::shared_ptr& server, std::string path, + datapath::permissions permissions, size_t max_clients) +{ +#if defined(_WIN32) + return datapath::windows::server::host(server, path, permissions, max_clients); +#else + return datapath::error::Unknown; +#endif +} diff --git a/source/windows/overlapped-queue.cpp b/source/windows/overlapped-queue.cpp new file mode 100644 index 0000000..9723b26 --- /dev/null +++ b/source/windows/overlapped-queue.cpp @@ -0,0 +1,158 @@ +/* +Low Latency IPC Library for high-speed traffic +Copyright (C) 2019 Michael Fabian Dirks + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +#include "overlapped-queue.hpp" + +datapath::windows::overlapped_queue::overlapped_queue(size_t backlog) +{ + std::unique_lock ul(this->objs_lock); + for (size_t idx = 0; idx < backlog; idx++) { + free_objs.push(std::make_shared()); + } +} + +datapath::windows::overlapped_queue::~overlapped_queue() +{ + { + std::unique_lock ul(this->objs_lock); + while (free_objs.size() > 0) { + free_objs.pop(); + } + used_objs.clear(); + } +} + +std::shared_ptr datapath::windows::overlapped_queue::alloc() +{ + std::shared_ptr obj; + std::unique_lock ul(this->objs_lock); + + if (free_objs.size() > 0) { + obj = free_objs.front(); + free_objs.pop(); + } else { + obj = std::make_shared(); + } + + used_objs.push_back(obj); + return obj; +} + +void datapath::windows::overlapped_queue::free(std::shared_ptr overlapped) +{ + std::unique_lock ul(this->objs_lock); + for (auto itr = used_objs.begin(); itr != used_objs.end(); itr++) { + if (*itr == overlapped) { + used_objs.erase(itr); + break; + } + } + free_objs.push(overlapped); +} + +/* + // Security Descriptor Stuff + SECURITY_ATTRIBUTES security_attributes; + PSECURITY_DESCRIPTOR security_descriptor_ptr = NULL; + PSID sid_everyone_ptr = NULL; + PSID sid_admin_ptr = NULL; + PACL acl_ptr = NULL; + EXPLICIT_ACCESS explicit_access[2]; + SID_IDENTIFIER_AUTHORITY sid_auth_world = SECURITY_WORLD_SID_AUTHORITY; + SID_IDENTIFIER_AUTHORITY sid_auth_nt = SECURITY_NT_AUTHORITY; + +bool datapath::windows::overlapped_queue::create_security_attributes() +{ + DWORD dwRes; + + // Create a well-known SID for the Everyone group. + if (!AllocateAndInitializeSid(&sid_auth_world, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &sid_everyone_ptr)) { + return false; + } + + // Initialize an EXPLICIT_ACCESS structure for an ACE. + // The ACE will allow Everyone read access to the key. + ZeroMemory(&explicit_access, 2 * sizeof(EXPLICIT_ACCESS)); + explicit_access[0].grfAccessPermissions = KEY_READ; + explicit_access[0].grfAccessMode = SET_ACCESS; + explicit_access[0].grfInheritance = NO_INHERITANCE; + explicit_access[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; + explicit_access[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; + explicit_access[0].Trustee.ptstrName = (LPTSTR)sid_everyone_ptr; + + // Create a SID for the BUILTIN\Administrators group. + if (!AllocateAndInitializeSid(&sid_auth_nt, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, + 0, 0, &sid_admin_ptr)) { + return false; + } + + // Initialize an EXPLICIT_ACCESS structure for an ACE. + // The ACE will allow the Administrators group full access to + // the key. + explicit_access[1].grfAccessPermissions = KEY_ALL_ACCESS; + explicit_access[1].grfAccessMode = SET_ACCESS; + explicit_access[1].grfInheritance = NO_INHERITANCE; + explicit_access[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; + explicit_access[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP; + explicit_access[1].Trustee.ptstrName = (LPTSTR)sid_admin_ptr; + + // Create a new ACL that contains the new ACEs. + dwRes = SetEntriesInAcl(2, explicit_access, NULL, &acl_ptr); + if (ERROR_SUCCESS != dwRes) { + return false; + } + + // Initialize a security descriptor. + security_descriptor_ptr = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); + if (NULL == security_descriptor_ptr) { + return false; + } + + if (!InitializeSecurityDescriptor(security_descriptor_ptr, SECURITY_DESCRIPTOR_REVISION)) { + return false; + } + + // Add the ACL to the security descriptor. + if (!SetSecurityDescriptorDacl(security_descriptor_ptr, + TRUE, // bDaclPresent flag + acl_ptr, + FALSE)) // not a default DACL + { + return false; + } + + // Initialize a security attributes structure. + security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); + security_attributes.lpSecurityDescriptor = security_descriptor_ptr; + security_attributes.bInheritHandle = FALSE; + return true; +} + +void datapath::windows::overlapped_queue::destroy_security_attributes() +{ + if (sid_everyone_ptr) + FreeSid(sid_everyone_ptr); + if (sid_admin_ptr) + FreeSid(sid_admin_ptr); + if (acl_ptr) + LocalFree(acl_ptr); + if (security_descriptor_ptr) + LocalFree(security_descriptor_ptr); +} + +*/ \ No newline at end of file diff --git a/source/windows/overlapped-queue.hpp b/source/windows/overlapped-queue.hpp new file mode 100644 index 0000000..7c7b508 --- /dev/null +++ b/source/windows/overlapped-queue.hpp @@ -0,0 +1,49 @@ +/* +Low Latency IPC Library for high-speed traffic +Copyright (C) 2019 Michael Fabian Dirks + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include +#include "overlapped.hpp" + +extern "C" { +#include +#include +#include +} + +namespace datapath { + namespace windows { + class overlapped_queue { + std::queue> free_objs; + std::list> used_objs; + std::mutex objs_lock; + + public: + overlapped_queue(size_t backlog = 8); + + virtual ~overlapped_queue(); + + std::shared_ptr alloc(); + + void free(std::shared_ptr overlapped); + }; + } // namespace windows +} // namespace datapath diff --git a/source/windows/overlapped.cpp b/source/windows/overlapped.cpp new file mode 100644 index 0000000..45019ef --- /dev/null +++ b/source/windows/overlapped.cpp @@ -0,0 +1,98 @@ +/* +Low Latency IPC Library for high-speed traffic +Copyright (C) 2019 Michael Fabian Dirks + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +#include "overlapped.hpp" + +datapath::windows::overlapped::overlapped() : overlapped_ptr(nullptr), data(nullptr) +{ + size_t memory_size = sizeof(OVERLAPPED) + sizeof(void*); + buffer.resize(memory_size); + + reinterpret_cast(buffer[sizeof(OVERLAPPED)]) = this; + handle = INVALID_HANDLE_VALUE; + + // Initialize OVERLAPPED + overlapped_ptr = &reinterpret_cast(buffer[0]); + memset(overlapped_ptr, 0, sizeof(OVERLAPPED)); + overlapped_ptr->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); +} + +datapath::windows::overlapped::~overlapped() +{ + cancel(); + buffer.clear(); +} + +OVERLAPPED* datapath::windows::overlapped::get_overlapped() +{ + return this->overlapped_ptr; +} + +HANDLE datapath::windows::overlapped::get_handle() +{ + return this->handle; +} + +void datapath::windows::overlapped::set_handle(HANDLE handle) +{ + this->handle = handle; +} + +void* datapath::windows::overlapped::get_data() +{ + return this->data; +} + +void datapath::windows::overlapped::set_data(void* data) +{ + this->data = data; +} + +void datapath::windows::overlapped::cancel() +{ + if (overlapped_ptr) { + CancelIoEx(handle, overlapped_ptr); + ResetEvent(overlapped_ptr->hEvent); + CloseHandle(overlapped_ptr->hEvent); + } +} + +bool datapath::windows::overlapped::is_completed() +{ + if (overlapped_ptr) { + return HasOverlappedIoCompleted(overlapped_ptr); + } + return false; +} + +void datapath::windows::overlapped::reset() +{ + cancel(); + if (overlapped_ptr) { + overlapped_ptr->Internal = 0; + overlapped_ptr->InternalHigh = 0; + overlapped_ptr->Offset = 0; + overlapped_ptr->OffsetHigh = 0; + overlapped_ptr->Pointer = 0; + } +} + +void* datapath::windows::overlapped::get_waitable() +{ + return reinterpret_cast(overlapped_ptr->hEvent); +} diff --git a/source/windows/overlapped.hpp b/source/windows/overlapped.hpp new file mode 100644 index 0000000..3829a0d --- /dev/null +++ b/source/windows/overlapped.hpp @@ -0,0 +1,58 @@ +/* +Low Latency IPC Library for high-speed traffic +Copyright (C) 2019 Michael Fabian Dirks + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +#pragma once +#include +#include +#include "waitable.hpp" + +extern "C" { +#include +} + +namespace datapath { + namespace windows { + class overlapped : public datapath::waitable { + std::vector buffer; + OVERLAPPED* overlapped_ptr; + HANDLE handle; + void* data; + + public: + overlapped(); + ~overlapped(); + + OVERLAPPED* get_overlapped(); + + HANDLE get_handle(); + void set_handle(HANDLE handle); + + void* get_data(); + void set_data(void* data); + + void cancel(); + + bool is_completed(); + + void reset(); + + public /*virtual override*/: + virtual void* get_waitable() override; + }; + } // namespace windows +} // namespace datapath diff --git a/source/windows/server.cpp b/source/windows/server.cpp new file mode 100644 index 0000000..c7f74da --- /dev/null +++ b/source/windows/server.cpp @@ -0,0 +1,228 @@ +/* +Low Latency IPC Library for high-speed traffic +Copyright (C) 2019 Michael Fabian Dirks + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +#include "server.hpp" +#include "socket.hpp" +#include "utility.hpp" + +// Buffer Size that Windows just ignores, for the most part. +#define WIN_BUFFER_SIZE 64 * 1024 * 1024 +#define WIN_WAIT_TIME 100 +#define WIN_BACKLOG_NUM 8 + +datapath::error datapath::windows::server::create(std::string path, datapath::permissions permissions, + size_t max_clients) +{ + // If old sockets are available, close them. + this->close(); + + // Apply options + this->max_clients = max_clients; + this->path = path; + + // Generate Security Attributes. + std::memset(&this->security_attributes, 0, sizeof(SECURITY_ATTRIBUTES)); + this->security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); + this->security_attributes.lpSecurityDescriptor = nullptr; + this->security_attributes.bInheritHandle = true; + // TODO: Respect permissions. + + // Spawn x backlog connections + // TODO: Add parameter for this. + for (size_t n = 0; n < WIN_BACKLOG_NUM; n++) { + HANDLE handle = _create_socket(path, n == 0); + if (handle == INVALID_HANDLE_VALUE) { + // Clean up again. + this->close(); + return datapath::error::CriticalFailure; + } + + { + std::unique_lock ul(this->lock); + sockets.push_back(handle); + waiting_sockets.push_back(handle); + } + } + + // Watcher Thread + { + std::unique_lock ul(this->watcher.lock); + this->watcher.shutdown = false; + this->watcher.task = std::thread(std::bind(&datapath::windows::server::_watcher, this)); + } + + is_created = true; + return datapath::error::Success; +} + +HANDLE datapath::windows::server::_create_socket(std::string path, bool initial) +{ + if (!datapath::windows::utility::make_pipe_path(path)) { + return INVALID_HANDLE_VALUE; + } + std::wstring wpath = datapath::windows::utility::make_wide_string(path); + + DWORD file_flags = PIPE_ACCESS_DUPLEX | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_OVERLAPPED; + if (initial) { + file_flags |= FILE_FLAG_FIRST_PIPE_INSTANCE; + } + + DWORD pipe_flags = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT; + + HANDLE handle = CreateNamedPipeW(wpath.c_str(), file_flags, pipe_flags, PIPE_UNLIMITED_INSTANCES, + WIN_BUFFER_SIZE, WIN_BUFFER_SIZE, WIN_WAIT_TIME, &this->security_attributes); + return handle; +} + +void datapath::windows::server::_watcher() +{ + std::map> ovmap; + + while (!this->watcher.shutdown) { + // Verify existing connections. + { + std::unique_lock ul(this->lock); + for (auto itr = this->active_sockets.begin(); itr != this->active_sockets.end(); itr++) { + if (itr->second.expired()) { + this->active_sockets.erase(itr); + this->waiting_sockets.push_back(itr->first); + continue; + } + auto obj = itr->second.lock(); + if (!obj->good()) { + this->active_sockets.erase(itr); + this->waiting_sockets.push_back(itr->first); + continue; + } + } + } + + // Update list of overlappeds to track. + { + std::unique_lock ul(this->lock); + for (auto itr = this->waiting_sockets.begin(); itr != this->waiting_sockets.end(); itr++) { + if (ovmap.count(*itr) == 0) { + auto ov = std::make_shared(); + ov->set_handle(*itr); + ov->set_data(this); + ov->on_wait_success.add([this, &ovmap, &itr](datapath::error ec) { + std::unique_lock ul(this->lock); + this->waiting_sockets.remove(*itr); + this->pending_sockets.push_back(*itr); + ovmap.erase(*itr); + }); + BOOL suc = ConnectNamedPipe(*itr, ov->get_overlapped()); + if (suc) { + ovmap.insert({*itr, ov}); + } else { + continue; + } + } + } + } + + // Wait for any overlapped objects. + if (ovmap.size() > 0) { + // No lock as we aren't touching any list or map yet. + std::vector waits; + waits.reserve(ovmap.size()); + + for (auto kv : ovmap) { + waits.push_back(&(*kv.second)); + } + + size_t index = 0; + datapath::error ec = datapath::waitable::wait_any(waits, index, std::chrono::milliseconds(1)); + } else { + // Just sleep 1ms to not use too much CPU. + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + } +} + +datapath::error datapath::windows::server::accept(std::shared_ptr& socket) +{ + std::unique_lock ul(this->lock); + if (this->pending_sockets.size() == 0) { + return datapath::error::Failure; + } + + HANDLE handle = this->pending_sockets.front(); + this->pending_sockets.pop_front(); + + if (!socket) { + socket = std::dynamic_pointer_cast(std::make_shared()); + } + std::shared_ptr obj = std::dynamic_pointer_cast(socket); + + obj->_connect(handle); + + // Stock up on backlog and total sockets. + if ((this->waiting_sockets.size() + this->pending_sockets.size()) < WIN_BACKLOG_NUM) { + if ((this->sockets.size() <= this->max_clients) && (this->max_clients > 0)) { + HANDLE handle = _create_socket(this->path, false); + if (handle != INVALID_HANDLE_VALUE) { + this->sockets.push_back(handle); + this->waiting_sockets.push_back(handle); + } + } + } + + this->active_sockets.insert({handle, socket}); + return datapath::error::Success; +} + +datapath::error datapath::windows::server::close() +{ + // Watcher Thread + { + std::unique_lock ul(this->watcher.lock); + this->watcher.shutdown = true; + if (this->watcher.task.joinable()) { + this->watcher.task.join(); + } + } + + // Kill all sockets. + std::unique_lock ul(this->lock); + for (HANDLE socket : sockets) { + DisconnectNamedPipe(socket); + CloseHandle(socket); + } + + // Notify Sockets of being dead. + + // Clear all lists. + sockets.clear(); + waiting_sockets.clear(); + pending_sockets.clear(); + active_sockets.clear(); + + return datapath::error::Success; +} + +datapath::error datapath::windows::server::host(std::shared_ptr& server, std::string path, + datapath::permissions permissions, size_t max_clients = -1) +{ + if (!server) { + server = std::dynamic_pointer_cast(std::make_shared()); + } + std::shared_ptr obj = std::dynamic_pointer_cast(server); + + return obj->create(path, permissions, max_clients); +} diff --git a/source/windows/server.hpp b/source/windows/server.hpp new file mode 100644 index 0000000..65d448a --- /dev/null +++ b/source/windows/server.hpp @@ -0,0 +1,73 @@ +/* +Low Latency IPC Library for high-speed traffic +Copyright (C) 2019 Michael Fabian Dirks + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +#pragma once +#include +#include +#include +#include +#include +#include "iserver.hpp" +#include "permissions.hpp" + +extern "C" { +#include +} + +namespace datapath { + namespace windows { + class server : public iserver { + bool is_created = false; + size_t max_clients = -1; + std::string path; + + private /*critical data*/: + // Lock for critical data. + std::mutex lock; + + SECURITY_ATTRIBUTES security_attributes; + + std::list sockets; + std::list waiting_sockets; + std::list pending_sockets; + std::map> active_sockets; + + struct { + std::thread task; + std::mutex lock; + bool shutdown = false; + } watcher; + + protected: + datapath::error create(std::string path, datapath::permissions permissions, size_t max_clients); + + HANDLE _create_socket(std::string path, bool initial = false); + + void _watcher(); + + public /*virtual override*/: + virtual datapath::error accept(std::shared_ptr& socket) override; + + virtual datapath::error close() override; + + public: + static datapath::error host(std::shared_ptr& server, std::string path, + datapath::permissions permissions, size_t max_clients); + }; + } // namespace windows +} // namespace datapath \ No newline at end of file diff --git a/source/windows/socket.cpp b/source/windows/socket.cpp new file mode 100644 index 0000000..ac279a1 --- /dev/null +++ b/source/windows/socket.cpp @@ -0,0 +1,233 @@ +/* +Low Latency IPC Library for high-speed traffic +Copyright (C) 2019 Michael Fabian Dirks + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +#include "socket.hpp" +#include +#include "task.hpp" +#include "utility.hpp" + +#define SIZE_ELEMENT uint32_t + +void datapath::windows::socket::_connect(HANDLE handle) +{ + this->socket_handle = handle; + if (handle != INVALID_HANDLE_VALUE) { + this->is_connected = true; + + { + std::unique_lock ul(this->watcher.lock); + this->watcher.shutdown = false; + this->watcher.task = std::thread(std::bind(&datapath::windows::socket::_watcher, this)); + } + } +} + +void datapath::windows::socket::_disconnect() +{ + if (this->on_close) { + this->on_close(datapath::error::Closed); + } + + { + { + std::unique_lock ul(this->watcher.lock); + this->watcher.shutdown = true; + } + if (this->watcher.task.joinable()) { + this->watcher.task.join(); + } + } + + this->is_connected = false; +} + +void datapath::windows::socket::_watcher() +{ + enum class readstate { Unknown, Header, Content } state = readstate::Unknown; + + std::vector read_buffer; + + std::shared_ptr read_header_ov = + std::make_shared(); + std::shared_ptr read_content_ov = + std::make_shared(); + std::shared_ptr waitable; + + read_header_ov->on_wait_error.add([&state, &waitable](datapath::error ec) { + // There was an error waiting on the header. + state = readstate::Unknown; + waitable.reset(); + }); + read_header_ov->on_wait_success.add( + [this, &read_buffer, &read_content_ov, &state, &waitable](datapath::error ec) { + read_content_ov->set_handle(this->socket_handle); + read_content_ov->set_data(this); + + // ToDo: Add optional message size limit, messages above this size kill the connection for attempting DoS. + size_t msg_size = reinterpret_cast(read_buffer[0]); + read_buffer.resize(msg_size); + + // Read content. + if (ReadFileEx(this->socket_handle, read_buffer.data(), DWORD(read_buffer.size()), + read_content_ov->get_overlapped(), NULL)) { + state = readstate::Content; + waitable = read_content_ov; + } else { + state = readstate::Unknown; + waitable.reset(); + } + }); + read_content_ov->on_wait_error.add([&state, &waitable](datapath::error ec) { + // There was an error waiting on the content. + state = readstate::Unknown; + waitable.reset(); + }); + read_content_ov->on_wait_success.add([this, &read_buffer, &state, &waitable](datapath::error ec) { + // We have content! + if (this->on_message) { + this->on_message(read_buffer); + state = readstate::Unknown; + } else { + // We're buffering the message in read_buffer until there is a hook to on_message. + } + waitable.reset(); + }); + + while (!this->watcher.shutdown) { + if (this->socket_handle == INVALID_HANDLE_VALUE) { + break; + } + if (!this->is_connected) { + break; + } + + if (state == readstate::Unknown) { + // Read the header of the next message. + // The header simply contains the length of the message. + // ToDo: Figure out if Message transfer/read mode and WaitCommEvent work together. + + read_header_ov->set_handle(this->socket_handle); + read_header_ov->set_data(this); + read_buffer.resize(sizeof(SIZE_ELEMENT)); + + // Read content. + if (ReadFileEx(this->socket_handle, read_buffer.data(), DWORD(read_buffer.size()), + read_header_ov->get_overlapped(), NULL)) { + state = readstate::Header; + waitable = read_header_ov; + + } else { + state = readstate::Unknown; + waitable.reset(); + } + } else if (state == readstate::Header) { + // This logic is in the on_wait_success handler. + } else if (state == readstate::Content) { + // This logic is in the on_wait_success handler, and continued here. + if (!waitable) { + // We currently have a message buffered, but there was no handler last time we checked. + if (this->on_message) { + this->on_message(read_buffer); + state = readstate::Unknown; + } + } + } + + if (!waitable) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } else { + datapath::error err = waitable->wait(std::chrono::milliseconds(1)); + if (err == datapath::error::Closed) { + _disconnect(); + continue; + } + } + } +} + +datapath::windows::socket::socket() : is_connected(false), socket_handle(INVALID_HANDLE_VALUE) {} + +datapath::windows::socket::~socket() +{ + close(); +} + +bool datapath::windows::socket::good() +{ + return this->is_connected; +} + +datapath::error datapath::windows::socket::close() +{ + if (this->is_connected) { + DisconnectNamedPipe(this->socket_handle); + _disconnect(); + return datapath::error::Success; + } + return datapath::error::Closed; +} + +datapath::error datapath::windows::socket::write(std::shared_ptr& task, const std::vector& data) +{ + if (!task) { + task = std::dynamic_pointer_cast(std::make_shared()); + } + std::shared_ptr obj = std::dynamic_pointer_cast(task); + std::shared_ptr ov = std::make_shared(); + + obj->_assign(data, ov); + + BOOL suc = + WriteFileEx(socket_handle, obj->data().data(), DWORD(obj->data().size()), ov->get_overlapped(), NULL); + if (suc) { + return datapath::error::Success; + } else { + return datapath::error::Failure; + } +} + +datapath::error datapath::windows::socket::connect(std::shared_ptr& socket, std::string path) +{ + if (!datapath::windows::utility::make_pipe_path(path)) { + return datapath::error::InvalidPath; + } + std::wstring wpath = datapath::windows::utility::make_wide_string(path); + + SetLastError(ERROR_SUCCESS); + HANDLE handle = CreateFileW(wpath.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, NULL); + if ((handle == INVALID_HANDLE_VALUE) || (GetLastError() != ERROR_SUCCESS)) { + return datapath::error::Failure; + } + + DWORD pipe_read_mode = PIPE_WAIT | PIPE_READMODE_BYTE; + + SetLastError(ERROR_SUCCESS); + if (!SetNamedPipeHandleState(handle, &pipe_read_mode, NULL, NULL)) { + // ToDo. This doesn't actually affect us as the default mode is the one we're setting + } + + if (!socket) { + socket = std::dynamic_pointer_cast(std::make_shared()); + } + std::shared_ptr obj = std::dynamic_pointer_cast(socket); + + obj->_connect(handle); + + return datapath::error::Success; +} diff --git a/source/windows/socket.hpp b/source/windows/socket.hpp new file mode 100644 index 0000000..79c0c01 --- /dev/null +++ b/source/windows/socket.hpp @@ -0,0 +1,71 @@ +/* +Low Latency IPC Library for high-speed traffic +Copyright (C) 2019 Michael Fabian Dirks + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +#pragma once +#include +#include +#include +#include "event.hpp" +#include "isocket.hpp" +#include "overlapped-queue.hpp" +#include "overlapped.hpp" +#include "server.hpp" + +extern "C" { +#include +} + +namespace datapath { + namespace windows { + class socket : public isocket { + bool is_connected; + HANDLE socket_handle; + + struct { + std::thread task; + std::mutex lock; + bool shutdown = false; + } watcher; + + protected: + void _connect(HANDLE handle); + + void _disconnect(); + + void _watcher(); + + public: + socket(); + + virtual ~socket(); + + public /*virtual override*/: + virtual bool good() override; + + virtual datapath::error close() override; + + virtual datapath::error write(std::shared_ptr& task, + const std::vector& data) override; + + public: + static datapath::error connect(std::shared_ptr& socket, std::string path); + + friend class datapath::windows::server; + }; + } // namespace windows +} // namespace datapath diff --git a/source/windows/task.cpp b/source/windows/task.cpp new file mode 100644 index 0000000..5dbd0ea --- /dev/null +++ b/source/windows/task.cpp @@ -0,0 +1,74 @@ +/* +Low Latency IPC Library for high-speed traffic +Copyright (C) 2019 Michael Fabian Dirks + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +#include "task.hpp" + +void datapath::windows::task::_assign(const std::vector& data, std::shared_ptr ov){ + this->buffer.resize(data.size()); + std::memcpy(buffer.data(), data.data(), data.size()); + this->overlapped = ov; +} + +datapath::windows::task::task() +{ + this->on_wait_error.add([this](datapath::error ec) { this->on_failure(ec); }); + this->on_wait_success.add([this](datapath::error ec) { this->on_success(ec, this->data()); }); +} + +datapath::windows::task::~task() +{ + cancel(); +} + +datapath::error datapath::windows::task::cancel() +{ + if (!overlapped) { + return datapath::error::Unknown; + } + if (!overlapped->is_completed()) { + overlapped->cancel(); + return datapath::error::Success; + } + return datapath::error::Failure; +} + +bool datapath::windows::task::is_completed() +{ + if (!overlapped) { + return false; + } + return overlapped->is_completed(); +} + +size_t datapath::windows::task::length() +{ + return buffer.size(); +} + +const std::vector& datapath::windows::task::data() +{ + return buffer; +} + +void* datapath::windows::task::get_waitable() +{ + if (!overlapped) { + return nullptr; + } + return overlapped->get_waitable(); +} diff --git a/source/windows/task.hpp b/source/windows/task.hpp new file mode 100644 index 0000000..9e8d488 --- /dev/null +++ b/source/windows/task.hpp @@ -0,0 +1,59 @@ +/* +Low Latency IPC Library for high-speed traffic +Copyright (C) 2019 Michael Fabian Dirks + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +#pragma once +#include +#include "itask.hpp" +#include "overlapped.hpp" +#include "socket.hpp" +#include "server.hpp" + +extern "C" { +#include +} + +namespace datapath { + namespace windows { + class task : public itask { + std::shared_ptr overlapped; + std::vector buffer; + + protected: + void _assign(const std::vector& data, std::shared_ptr ov); + + public: + task(); + ~task(); + + public /*virtual override*/ /*itask*/: + virtual datapath::error cancel() override; + + virtual bool is_completed() override; + + virtual size_t length() override; + + virtual const std::vector& data() override; + + public /*virtual override*/ /*waitable*/: + virtual void* get_waitable() override; + + friend class datapath::windows::socket; + friend class datapath::windows::server; + }; + } // namespace windows +} // namespace datapath diff --git a/source/windows/utility.hpp b/source/windows/utility.hpp new file mode 100644 index 0000000..4ae3e1a --- /dev/null +++ b/source/windows/utility.hpp @@ -0,0 +1,55 @@ +/* +Low Latency IPC Library for high-speed traffic +Copyright (C) 2019 Michael Fabian Dirks + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +#pragma once +#include +#include +#include + +extern "C" { +#include +} + +namespace datapath { + namespace windows { + namespace utility { + static inline bool make_pipe_path(std::string& string) + { + // Convert path to WinAPI compatible string. + if (string.length() >= (MAX_PATH - 10ull)) { + // \\.\pipe\ is 9 characters, but we count 10 here. + return false; + } + for (char& v : string) { + if (v == '\\') { // Backslash is not allowed. + v = '/'; + } + } + string = {"\\\\.\\pipe\\" + string}; + return true; + } + + static inline std::wstring make_wide_string(std::string string) + { + std::wstring_convert> converter; + return converter.from_bytes(string); + } + + } // namespace utility + } // namespace windows +} // namespace datapath diff --git a/source/windows/waitable.cpp b/source/windows/waitable.cpp new file mode 100644 index 0000000..5ea30a4 --- /dev/null +++ b/source/windows/waitable.cpp @@ -0,0 +1,165 @@ +/* +Low Latency IPC Library for high-speed traffic +Copyright (C) 2019 Michael Fabian Dirks + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +#include "waitable.hpp" +#include + +extern "C" { +#include +} + +datapath::error datapath::waitable::wait(datapath::waitable* obj, std::chrono::nanoseconds duration) +{ + assert(obj == nullptr); + + HANDLE handle = (HANDLE)obj->get_waitable(); + int64_t timeout = std::chrono::duration_cast(duration).count(); + + if (timeout < 0) { + timeout = 0; + } else if (timeout > std::numeric_limits::max()) { + timeout = std::numeric_limits::max(); + } + + do { + auto start = std::chrono::high_resolution_clock::now(); + + DWORD result = WaitForSingleObjectEx(handle, DWORD(timeout), TRUE); + switch (result) { + case WAIT_OBJECT_0: + obj->on_wait_success(datapath::error::Success); + return datapath::error::Success; + case WAIT_TIMEOUT: + return datapath::error::TimedOut; + case WAIT_ABANDONED: + obj->on_wait_error(datapath::error::Closed); + return datapath::error::Closed; + case WAIT_IO_COMPLETION: + duration = (std::chrono::high_resolution_clock::now() - start); + timeout = std::chrono::duration_cast(duration).count(); + continue; + } + } while (timeout > 0); + + return datapath::error::Failure; +} + +datapath::error datapath::waitable::wait(datapath::waitable** objs, size_t count, std::chrono::nanoseconds duration) +{ + assert(objs == nullptr); + assert(count == 0); + assert(count > MAXIMUM_WAIT_OBJECTS); + + int64_t timeout = std::chrono::duration_cast(duration).count(); + + if (timeout < 0) { + timeout = 0; + } else if (timeout > std::numeric_limits::max()) { + timeout = std::numeric_limits::max(); + } + + // Rebuild a valid obj+index translation list. + std::vector handles(count); + std::vector indexes(count); + size_t valid_handles = 0; + for (size_t idx = 0; idx < count; idx++) { + datapath::waitable* obj = objs[idx]; + if (obj) { + handles[valid_handles] = reinterpret_cast(obj->get_waitable()); + indexes[valid_handles] = idx; + valid_handles++; + } + } + + do { + auto start = std::chrono::high_resolution_clock::now(); + + DWORD result = WaitForMultipleObjectsEx(handles.size(), handles.data(), TRUE, DWORD(timeout), TRUE); + if ((result >= WAIT_OBJECT_0) && (result < (WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS))) { + for (auto idx : indexes) { + objs[idx]->on_wait_success(datapath::error::Success); + } + return datapath::error::Success; + } else if ((result >= WAIT_ABANDONED_0) && (result < (WAIT_ABANDONED_0 + MAXIMUM_WAIT_OBJECTS))) { + for (auto idx : indexes) { + objs[idx]->on_wait_error(datapath::error::Closed); + } + return datapath::error::Closed; + } else if (result == WAIT_TIMEOUT) { + return datapath::error::TimedOut; + } else if (result == WAIT_IO_COMPLETION) { + duration = (std::chrono::high_resolution_clock::now() - start); + timeout = std::chrono::duration_cast(duration).count(); + continue; + } + } while (timeout > 0); + + return datapath::error::Failure; +} + +datapath::error datapath::waitable::wait_any(datapath::waitable** objs, size_t count, size_t& index, + std::chrono::nanoseconds duration) +{ + assert(objs == nullptr); + assert(count == 0); + assert(count > MAXIMUM_WAIT_OBJECTS); + + int64_t timeout = std::chrono::duration_cast(duration).count(); + + if (timeout < 0) { + timeout = 0; + } else if (timeout > std::numeric_limits::max()) { + timeout = std::numeric_limits::max(); + } + + // Rebuild a valid obj+index translation list. + std::vector handles(count); + std::vector indexes(count); + size_t valid_handles = 0; + for (size_t idx = 0; idx < count; idx++) { + datapath::waitable* obj = objs[idx]; + if (obj) { + handles[valid_handles] = reinterpret_cast(obj->get_waitable()); + indexes[valid_handles] = idx; + valid_handles++; + } + } + + do { + auto start = std::chrono::high_resolution_clock::now(); + + DWORD result = WaitForMultipleObjectsEx(handles.size(), handles.data(), FALSE, DWORD(timeout), TRUE); + if ((result >= WAIT_OBJECT_0) && (result < (WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS))) { + index = indexes[result - WAIT_OBJECT_0]; + objs[index]->on_wait_success(datapath::error::Success); + return datapath::error::Success; + } else if ((result >= WAIT_ABANDONED_0) && (result < (WAIT_ABANDONED_0 + MAXIMUM_WAIT_OBJECTS))) { + index = indexes[result - WAIT_OBJECT_0]; + objs[index]->on_wait_error(datapath::error::Closed); + return datapath::error::Closed; + } else if (result == WAIT_TIMEOUT) { + return datapath::error::TimedOut; + } else if (result == WAIT_IO_COMPLETION) { + duration = (std::chrono::high_resolution_clock::now() - start); + timeout = std::chrono::duration_cast(duration).count(); + continue; + } + } while (timeout > 0); + + return datapath::error::Failure; +}