diff --git a/.clang-format b/.clang-format index 4e1f056f..f8743e16 100644 --- a/.clang-format +++ b/.clang-format @@ -1,7 +1,60 @@ -BasedOnStyle: LLVM -IndentWidth: 4 -Standard: Cpp11 -UseTab: Never +BasedOnStyle: Chromium +AccessModifierOffset: -2 +AlignAfterOpenBracket: true +AlignEscapedNewlinesLeft: true +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: false +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false ColumnLimit: 80 -AccessModifierOffset: -4 +CommentPragmas: '^ IWYU pragma:' ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +IndentCaseLabels: false +IndentWidth: 2 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: false +Language: Cpp +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: false +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Right +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: true +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +TabWidth: 2 +UseTab: Never diff --git a/.gitignore b/.gitignore index 169dfff2..7d84a11d 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ report utils/release doc/all.xml reference.md + +cmake-build-*/ \ No newline at end of file diff --git a/.ycm_extra_conf.py b/.ycm_extra_conf.py index 579b2fbd..0668fe26 100644 --- a/.ycm_extra_conf.py +++ b/.ycm_extra_conf.py @@ -1,63 +1,57 @@ import os import ycm_core + def current_path(): - return os.path.dirname(os.path.abspath(__file__)) + return os.path.dirname(os.path.abspath(__file__)) flags = [ - '-Wall', - '-DRPCLIB_ASIO=clmdep_asio', - '-DRPCLIB_FMT=clmdep_fmt', - '-Wextra', - '-Wno-long-long', - '-Wno-variadic-macros', - '-fexceptions', - '-DNDEBUG', - '-std=c++14', - '-x', - 'c++', - '-I', os.path.join(current_path(), 'include'), - '-I', os.path.join(current_path(), 'dependencies', 'include') + '-Wall', '-DRPCLIB_ASIO=clmdep_asio', '-DRPCLIB_FMT=clmdep_fmt', '-Wextra', + '-Wno-long-long', '-Wno-variadic-macros', '-fexceptions', '-DNDEBUG', + '-std=c++14', '-x', 'c++', '-I', + os.path.join(current_path(), 'include'), '-I', + os.path.join(current_path(), 'dependencies', 'include') ] -compilation_database_folder = current_path() +compilation_database_folder = '' if os.path.exists(compilation_database_folder): - database = ycm_core.CompilationDatabase(compilation_database_folder) + database = ycm_core.CompilationDatabase(compilation_database_folder) else: - database = None + database = None SOURCE_EXTENSIONS = ['.cc', '.inl'] + def MakeRelativePathsInFlagsAbsolute(flags, working_directory): - if not working_directory: - return list(flags) - new_flags = [] - make_next_absolute = False - path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ] - for flag in flags: - new_flag = flag - if make_next_absolute: - make_next_absolute = False - if not flag.startswith('/'): - new_flag = os.path.join(working_directory, flag) - for path_flag in path_flags: - if flag == path_flag: - make_next_absolute = True - break - if flag.startswith(path_flag): - path = flag[ len(path_flag): ] - new_flag = path_flag + os.path.join(working_directory, path) - break - if new_flag: - new_flags.append(new_flag) - return new_flags + if not working_directory: + return list(flags) + new_flags = [] + make_next_absolute = False + path_flags = ['-isystem', '-I', '-iquote', '--sysroot='] + for flag in flags: + new_flag = flag + if make_next_absolute: + make_next_absolute = False + if not flag.startswith('/'): + new_flag = os.path.join(working_directory, flag) + for path_flag in path_flags: + if flag == path_flag: + make_next_absolute = True + break + if flag.startswith(path_flag): + path = flag[len(path_flag):] + new_flag = path_flag + os.path.join(working_directory, path) + break + if new_flag: + new_flags.append(new_flag) + return new_flags def IsHeaderFile(filename): - extension = os.path.splitext(filename)[ 1 ] - return extension in [ '.h', '.hxx', '.hpp', '.hh', '.inl' ] + extension = os.path.splitext(filename)[1] + return extension in ['.h', '.hxx', '.hpp', '.hh', '.inl'] # The compilation_commands.json file generated by CMake does not have entries @@ -65,31 +59,30 @@ def IsHeaderFile(filename): # corresponding source file, if any. If one exists, the flags for that file # should be good enough. def GetCompilationInfoForFile(filename): - if IsHeaderFile(filename): - basename = os.path.splitext(filename)[0].replace('include', 'lib') - replacement_file = basename + '.cc' - if os.path.exists(replacement_file): - compilation_info = database.GetCompilationInfoForFile( - replacement_file) - if compilation_info.compiler_flags_: - return compilation_info - return None - return database.GetCompilationInfoForFile(filename) + if IsHeaderFile(filename): + basename = os.path.splitext(filename)[0].replace('include', 'lib') + replacement_file = basename + '.cc' + if os.path.exists(replacement_file): + compilation_info = database.GetCompilationInfoForFile(replacement_file) + if compilation_info.compiler_flags_: + return compilation_info + return None + return database.GetCompilationInfoForFile(filename) def FlagsForFile(filename, **kwargs): - global flags - if database: - compilation_info = GetCompilationInfoForFile(filename) - if not compilation_info: - return None - final_flags = MakeRelativePathsInFlagsAbsolute( - compilation_info.compiler_flags_, - compilation_info.compiler_working_dir_) - else: - relative_to = current_path() - final_flags = MakeRelativePathsInFlagsAbsolute(flags, relative_to) - - final_flags.extend(flags) - - return {'flags': final_flags, 'do_cache': True} + global flags + if database: + compilation_info = GetCompilationInfoForFile(filename) + if not compilation_info: + return None + final_flags = MakeRelativePathsInFlagsAbsolute( + compilation_info.compiler_flags_, + compilation_info.compiler_working_dir_) + else: + relative_to = current_path() + final_flags = MakeRelativePathsInFlagsAbsolute(flags, relative_to) + + final_flags.extend(flags) + + return {'flags': final_flags, 'do_cache': True} diff --git a/CHANGELOG.md b/CHANGELOG.md index a7ef91a5..cf99269f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +### 2.2.1 + +This release fixed a crash on Windows. + +*Fixes*: + + * Fixed client crashing when `suppress_exceptions` was on + and the server threw an exception. + ### 2.2.0 This release fixed a number of long-standing issues. diff --git a/CMakeLists.txt b/CMakeLists.txt index 58e7827d..4294eade 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.9.0) -project(rpc VERSION 2.2.0) +project(rpc VERSION 2.2.1) set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") @@ -8,46 +8,51 @@ include(msvc_support) include(coverage) include(check_warning_flag) +include_directories(SYSTEM "/usr/local/Cellar/libuv/1.18.0/include") +link_directories("/usr/local/Cellar/libuv/1.18.0/lib") + # # Options # -option(RPCLIB_BUILD_TESTS - "Build unit RPCLIB_BUILD_TESTS." +option(RPCLIB_BUILD_TESTS + "Build unit RPCLIB_BUILD_TESTS." OFF) -option(RPCLIB_GENERATE_COMPDB - "Generate compilation database. Useful for YCM." +option(RPCLIB_GENERATE_COMPDB + "Generate compilation database. Useful for YCM." OFF) -option(RPCLIB_BUILD_EXAMPLES - "Build examples." +option(RPCLIB_BUILD_EXAMPLES + "Build examples." OFF) -option(RPCLIB_ENABLE_LOGGING +option(RPCLIB_ENABLE_LOGGING "ALlow logging in the library for debug purposes." OFF) -option(RPCLIB_ENABLE_COVERAGE - "Generate coverage information" +option(RPCLIB_ENABLE_COVERAGE + "Generate coverage information" + OFF) +option(RPCLIB_MSVC_STATIC_RUNTIME + "MSVC only: build with /MT instead of /MD" + OFF) +option(RPCLIB_ASAN + "Enable AddressSanitizer. Intended for rpclib development" OFF) -option(RPCLIB_MSVC_STATIC_RUNTIME - "MSVC only: build with /MT instead of /MD" +option(RPCLIB_TSAN + "Enable ThreadSanitizer. Intended for rpclib development" + OFF) +option(RPCLIB_MSAN + "Enable MemorySanitizer. Intended for rpclib development" OFF) # # Other configuration values # -set(RPCLIB_DEFAULT_PORT 8080 +set(RPCLIB_DEFAULT_PORT 8080 CACHE STRING "Default port used for running tests and examples") -set(RPCLIB_DEFAULT_BUFFER_SIZE "1024 << 10" +set(RPCLIB_DEFAULT_BUFFER_SIZE "1024 << 10" CACHE STRING "Default buffer size") -set(RPCLIB_CXX_STANDARD 11 CACHE STRING +set(RPCLIB_CXX_STANDARD 11 CACHE STRING "C++ version used to build rpclib (Currently: Only 11 and 14 supported)") -if(RPCLIB_GENERATE_COMPDB) - set(CMAKE_EXPORT_COMPILE_COMMANDS "ON") # for YCM - add_custom_command(PROJECT_NAME ${PROJECT_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E - copy ${CMAKE_BINARY_DIR}/compile_commands.json ${CMAKE_BINARY_DIR}/../compile_commands.json) -endif() - -if(NOT ${RPCLIB_CXX_STANDARD} EQUAL 14 AND +if(NOT ${RPCLIB_CXX_STANDARD} EQUAL 14 AND NOT ${RPCLIB_CXX_STANDARD} EQUAL 11) message(fatal_error "Unsupported C++ standard: ${RPCLIB_CXX_STANDARD}") endif() @@ -81,6 +86,8 @@ configure_file( file(GLOB_RECURSE RPCLIB_HEADERS include/rpc/*.h + include/rpc/*.hpp + include/rpc/*.inl include/msgpack/*.hpp) file(GLOB_RECURSE DEP_HEADERS ${RPCLIB_DEPENDENCIES}/include/*.h @@ -101,6 +108,7 @@ add_library(${PROJECT_NAME} lib/rpc/detail/server_session.cc lib/rpc/detail/response.cc lib/rpc/detail/client_error.cc + lib/rpc/detail/uv_tools.cc lib/rpc/nonstd/optional.cc ${DEP_SOURCES} ${DEP_HEADERS} @@ -115,6 +123,13 @@ if(RPCLIB_ENABLE_COVERAGE) enable_coverage(${PROJECT_NAME}) endif() +if(RPCLIB_GENERATE_COMPDB) + set(CMAKE_EXPORT_COMPILE_COMMANDS "ON") # for YCM + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E + copy ${CMAKE_BINARY_DIR}/compile_commands.json ${CMAKE_BINARY_DIR}/../compile_commands.json) +endif() + if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # clang is the compiler used for developing mainly, so # this is where I set the highest warning level @@ -126,7 +141,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") set(UNUSED_LAMBDA_CAPTURE_WARN_SUPPORTED) check_warning_flag("unused-lambda-capture" UNUSED_LAMBDA_CAPTURE_WARN_SUPPORTED) if(${UNUSED_LAMBDA_CAPTURE_WARN_SUPPORTED}) - list(APPEND RPCLIB_BUILD_FLAGS -Wno-no-unused-lambda-capture) + list(APPEND RPCLIB_BUILD_FLAGS -Wno-unused-lambda-capture) endif() check_warning_flag("zero-as-null-pointer-constant" ZERO_AS_NULL_POINTER_CONSTANT_WARN_SUPPORTED) @@ -136,11 +151,26 @@ if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") endif() endif() +if (RPCLIB_TSAN) + list(APPEND RPCLIB_BUILD_FLAGS -fsanitize=thread) + list(APPEND RPCLIB_DEP_LIBRARIES tsan) +endif() + +if (RPCLIB_ASAN) + list(APPEND RPCLIB_BUILD_FLAGS -fsanitize=address) + #list(APPEND RPCLIB_DEP_LIBRARIES asan) +endif() + +if (RPCLIB_MSAN) + list(APPEND RPCLIB_BUILD_FLAGS -fsanitize=memory) + #list(APPEND RPCLIB_DEP_LIBRARIES asan) +endif() + if (RPCLIB_EXTRA_BUILD_FLAGS) list(APPEND RPCLIB_BUILD_FLAGS ${RPCLIB_EXTRA_BUILD_FLAGS}) endif() -target_compile_definitions(${PROJECT_NAME} +target_compile_definitions(${PROJECT_NAME} PRIVATE "${RPCLIB_COMPILE_DEFINITIONS}" "${RPCLIB_ARCH_DEF}" @@ -157,7 +187,7 @@ if(RPCLIB_ENABLE_LOGGING) endif() if(RPCLIB_BUILD_FLAGS) - target_compile_options(${PROJECT_NAME} PRIVATE ${RPCLIB_BUILD_FLAGS}) + target_compile_options(${PROJECT_NAME} PUBLIC ${RPCLIB_BUILD_FLAGS}) endif() if(RPCLIB_COMPILE_DEFINITIONS) diff --git a/doc/pages/cookbook.md b/doc/pages/cookbook.md index 8de2879a..9476d888 100644 --- a/doc/pages/cookbook.md +++ b/doc/pages/cookbook.md @@ -365,7 +365,7 @@ int main() { int main() { rpc::client c("127.0.0.1", 8080); - client::connection_state cs = c.get_connection_state(); + connection_state cs = c.get_connection_state(); return 0; } diff --git a/doc/pages/versions.md b/doc/pages/versions.md index cde88106..f2874691 100644 --- a/doc/pages/versions.md +++ b/doc/pages/versions.md @@ -1,4 +1,4 @@ -You are reading the documentation of 2.2.0. +You are reading the documentation of 2.2.1. If, for some reason you need the documentation of older versions, you can download them from this page. * [1.0.0](/archive/rpclib_docs_1.0.0.zip) diff --git a/include/rpc/client.h b/include/rpc/client.h index 837e85c9..d2056b29 100644 --- a/include/rpc/client.h +++ b/include/rpc/client.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -12,136 +13,167 @@ namespace rpc { -//! \brief Implements a client that connects to a msgpack-rpc server and is -//! able to call functions synchronously or asynchronously. This is the main -//! interfacing point for implementing client applications. +class client; + +//! \brief Enum representing the connection states of the client. +enum class connection_state { initial, connected, disconnected, reset }; + +using state_handler_t = + std::function; + +//! \brief Implements a client that connects to a +//! msgpack-rpc server and is able to call functions +//! synchronously or asynchronously. This is the main +//! interfacing point for implementing client +//! applications. //! -//! Use this class to connect to msgpack-rpc servers and call their exposed -//! functions. This class supports calling functions synchronously and -//! asynchronously. When the client object is created, it initiates connecting -//! to the given server asynchronically and disconnects when it is destroyed. +//! Use this class to connect to msgpack-rpc servers and +//! call their exposed functions. This class supports +//! calling functions synchronously and asynchronously. +//! When the client object is created, it initiates +//! connecting to the given server asynchronically and +//! disconnects when it is destroyed. class client { public: - //! \brief Constructs a client. - //! - //! When a client is constructed, it initiates a connection - //! asynchronically. This means that it will not block while the connection - //! is established. However, when the first call is performed, it *might* - //! block if the connection was not already established. - //! - //! \param addr The address of the server to connect to. This might be an - //! IP address or a host name, too. - //! \param port The port on the server to connect to. - client(std::string const &addr, uint16_t port); - - //! \cond DOXYGEN_SKIP - client(client const &) = delete; - //! \endcond - - //! \brief Destructor. - //! - //! During destruction, the connection to the server is gracefully closed. - //! This means that any outstanding reads and writes are completed first. - ~client(); - - //! \brief Calls a function with the given name and arguments (if any). - //! - //! \param func_name The name of the function to call on the server. - //! \param args A variable number of arguments to pass to the called - //! function. - //! - //! \tparam Args The types of the arguments. Each type in this parameter - //! pack have to be serializable by msgpack. - //! - //! \returns A RPCLIB_MSGPACK::object containing the result of the function (if - //! any). To obtain a typed value, use the msgpack API. - //! - //! \throws rpc::rpc_error if the server responds with an error. - template - RPCLIB_MSGPACK::object_handle call(std::string const &func_name, Args... args); - - //! \brief Calls a function asynchronously with the given name and - //! arguments. - //! - //! A call is performed asynchronously in the context of the client, i.e. - //! this is not to be confused with parallel execution on the server. - //! This function differs from `call` in that it does not wait for the - //! result of the function. Instead, it returns a std::future that - //! can be used to retrieve the result later. - //! - //! \param func_name The name of the function to call. - //! \param args The arguments to pass to the function. - //! - //! \tparam Args The types of the arguments. - //! - //! \returns A std::future, possibly holding a future result - //! (which is a RPCLIB_MSGPACK::object). - template - std::future async_call(std::string const &func_name, - Args... args); - - //! \brief Sends a notification with the given name and arguments (if any). - //! - //! Notifications are a special kind of calls. They can be used to notify - //! the server, while not expecting a response. In `rpclib` terminology, - //! a notification is like an `async_call` without a return value. - //! - //! \param func_name The name of the notification to call. - //! \param args The arguments to pass to the function. - //! \tparam Args THe types of the arguments. - //! - //! \note This function returns immediately (possibly before the - //! notification is written to the socket). - template - void send(std::string const &func_name, Args... args); - - //! \brief Returns the timeout setting of this client in milliseconds. - //! - //! The timeout is applied to synchronous calls. If the timeout expires - //! without receiving a response from the server, rpc::timeout exception - //! will be thrown. - //! - //! \note The timeout has no effect on async calls. For those, - //! the preferred timeout mechanism remains using std::future. - //! - //! The default value for timeout is 5000ms (5 seconds). - nonstd::optional get_timeout() const; - - //! \brief Sets the timeout for synchronous calls. For more information, - //! see get_timeout(). - void set_timeout(int64_t value); - - //! \brief Clears the timeout for synchronous calls. For more information, - //! see get_timeout(). - void clear_timeout(); - - //! \brief Enum representing the connection states of the client. - enum class connection_state { initial, connected, disconnected, reset }; - - //! \brief Returns the current connection state. - connection_state get_connection_state() const; - - //! \brief Waits for the completion of all ongoing calls. - void wait_all_responses(); + //! \brief Constructs a client. + //! + //! When a client is constructed, it initiates a connection + //! asynchronically. This means that it will not block while the connection + //! is established. However, when the first call is performed, it *might* + //! block if the connection was not already established. + //! + //! \param addr The address of the server to connect to. This might be an + //! IP address or a host name, too. + //! \param port The port on the server to connect to. + client(std::string const &addr, uint16_t port); + + //! \brief Constructs a client with a connection state callback. + //! Without this constructor, it is possible that the handler + //! set via set_state_handler() never receives the initial->connected + //! state change because the connection is initiated during construction. + //! This constructor overload guarantees that the callback is set + //! before the connection is initiated. + client(std::string const &addr, uint16_t port, state_handler_t f); + + //! \cond DOXYGEN_SKIP + client(client const &) = delete; + //! \endcond + + //! \brief Destructor. + //! + //! During destruction, the connection to the server is gracefully closed. + //! This means that any outstanding reads and writes are completed first. + ~client(); + + //! \brief Calls a function with the given name and arguments (if any). + //! + //! \param func_name The name of the function to call on the server. + //! \param args A variable number of arguments to pass to the called + //! function. + //! + //! \tparam Args The types of the arguments. Each type in this parameter + //! pack have to be serializable by msgpack. + //! + //! \returns A RPCLIB_MSGPACK::object containing the result of the function + //! (if any). To obtain a typed value, use the msgpack API. + //! + //! \throws rpc::rpc_error if the server responds with an error. + template + RPCLIB_MSGPACK::object_handle call(std::string const &func_name, + Args... args); + + //! \brief Calls a function asynchronously with the given name and + //! arguments. + //! + //! A call is performed asynchronously in the context of the client, i.e. + //! this is not to be confused with parallel execution on the server. + //! This function differs from `call` in that it does not wait for the + //! result of the function. Instead, it returns a std::future that + //! can be used to retrieve the result later. + //! + //! \param func_name The name of the function to call. + //! \param args The arguments to pass to the function. + //! + //! \tparam Args The types of the arguments. + //! + //! \returns A std::future, possibly holding a future result + //! (which is a RPCLIB_MSGPACK::object). + template + std::future async_call( + std::string const &func_name, + Args... args); + + //! \brief Sends a notification with the given name and arguments (if any). + //! + //! Notifications are a special kind of calls. They can be used to notify + //! the server, while not expecting a response. In `rpclib` terminology, + //! a notification is like an `async_call` without a return value. + //! + //! \param func_name The name of the notification to call. + //! \param args The arguments to pass to the function. + //! \tparam Args THe types of the arguments. + //! + //! \note This function returns immediately (possibly before the + //! notification is written to the socket). + template + void send(std::string const &func_name, Args... args); + + //! \brief Returns the timeout setting of this client in milliseconds. + //! + //! The timeout is applied to synchronous calls. If the timeout expires + //! without receiving a response from the server, rpc::timeout exception + //! will be thrown. + //! + //! \note The timeout has no effect on async calls. For those, + //! the preferred timeout mechanism remains using std::future. + //! + //! The default value for timeout is 5000ms (5 seconds). + nonstd::optional get_timeout() const; + + //! \brief Sets the timeout for synchronous calls. For more information, + //! see get_timeout(). + void set_timeout(int64_t value); + + //! \brief Clears the timeout for synchronous calls. For more information, + //! see get_timeout(). + void clear_timeout(); + + //! \brief Returns the current connection state. + connection_state get_connection_state() const; + + //! \brief Waits for the completion of all ongoing calls. + void wait_all_responses(); + + void set_state_handler(state_handler_t callback); + + std::future async_reconnect(); + + connection_state reconnect(); private: - //! \brief Type of a promise holding a future response. - using rsp_promise = std::promise; + void common_init(); - enum class request_type { call = 0, notification = 2 }; + //! \brief Type of a promise holding a future response. + using rsp_promise = std::promise; - void wait_conn(); - void post(std::shared_ptr buffer, int idx, - std::string const& func_name, - std::shared_ptr p); - void post(RPCLIB_MSGPACK::sbuffer *buffer); - int get_next_call_idx(); - RPCLIB_NORETURN void throw_timeout(std::string const& func_name); + enum class request_type { call = 0, notification = 2 }; + + void wait_conn(); + void post(std::shared_ptr buffer, + int idx, + std::string const &func_name, + std::shared_ptr p); + void post(RPCLIB_MSGPACK::sbuffer *buffer); + int get_next_call_idx(); + RPCLIB_NORETURN void throw_timeout(std::string const &func_name); private: - static constexpr double buffer_grow_factor = 1.8; - RPCLIB_DECLARE_PIMPL() + static constexpr double buffer_grow_factor = 1.8; + RPCLIB_DECLARE_PIMPL() }; -} + +bool is_connected(client const &c); + +} // namespace rpc #include "rpc/client.inl" diff --git a/include/rpc/client.inl b/include/rpc/client.inl index 04dc825b..cdf7373b 100644 --- a/include/rpc/client.inl +++ b/include/rpc/client.inl @@ -2,44 +2,45 @@ namespace rpc { template RPCLIB_MSGPACK::object_handle client::call(std::string const &func_name, - Args... args) { - RPCLIB_CREATE_LOG_CHANNEL(client) - auto future = async_call(func_name, std::forward(args)...); - if (auto timeout = get_timeout()) { - auto wait_result = future.wait_for(std::chrono::milliseconds(*timeout)); - if (wait_result == std::future_status::timeout) { - throw_timeout(func_name); - } + Args... args) { + RPCLIB_CREATE_LOG_CHANNEL(client) + auto future = async_call(func_name, std::forward(args)...); + if (auto timeout = get_timeout()) { + auto wait_result = future.wait_for(std::chrono::milliseconds(*timeout)); + if (wait_result == std::future_status::timeout) { + throw_timeout(func_name); } + } - return future.get(); + return future.get(); } template -std::future -client::async_call(std::string const &func_name, Args... args) { - RPCLIB_CREATE_LOG_CHANNEL(client) - wait_conn(); - using RPCLIB_MSGPACK::object; - LOG_DEBUG("Calling {}", func_name); +std::future client::async_call( + std::string const &func_name, + Args... args) { + RPCLIB_CREATE_LOG_CHANNEL(client) + wait_conn(); + using RPCLIB_MSGPACK::object; + LOG_DEBUG("Calling {}", func_name); - auto args_obj = std::make_tuple(args...); - const int idx = get_next_call_idx(); - auto call_obj = - std::make_tuple(static_cast(client::request_type::call), idx, - func_name, args_obj); + auto args_obj = std::make_tuple(args...); + const int idx = get_next_call_idx(); + auto call_obj = + std::make_tuple(static_cast(client::request_type::call), idx, + func_name, args_obj); - auto buffer = std::make_shared(); - RPCLIB_MSGPACK::pack(*buffer, call_obj); + auto buffer = std::make_shared(); + RPCLIB_MSGPACK::pack(*buffer, call_obj); - // TODO: Change to move semantics when asio starts supporting move-only - // handlers in post(). [sztomi, 2016-02-14] - auto p = std::make_shared>(); - auto ft = p->get_future(); + // TODO: Change to move semantics when asio starts supporting move-only + // handlers in post(). [sztomi, 2016-02-14] + auto p = std::make_shared>(); + auto ft = p->get_future(); - post(buffer, idx, func_name, p); + post(buffer, idx, func_name, p); - return ft; + return ft; } //! \brief Sends a notification with the given name and arguments (if any). @@ -50,17 +51,18 @@ client::async_call(std::string const &func_name, Args... args) { //! \tparam Args THe types of the arguments. template void client::send(std::string const &func_name, Args... args) { - RPCLIB_CREATE_LOG_CHANNEL(client) - LOG_DEBUG("Sending notification {}", func_name); + RPCLIB_CREATE_LOG_CHANNEL(client) + LOG_DEBUG("Sending notification {}", func_name); - auto args_obj = std::make_tuple(args...); - auto call_obj = std::make_tuple( - static_cast(client::request_type::notification), func_name, - args_obj); + auto args_obj = std::make_tuple(args...); + auto call_obj = + std::make_tuple(static_cast(client::request_type::notification), + func_name, args_obj); - auto buffer = new RPCLIB_MSGPACK::sbuffer; - RPCLIB_MSGPACK::pack(*buffer, call_obj); + auto buffer = new RPCLIB_MSGPACK::sbuffer; + RPCLIB_MSGPACK::pack(*buffer, call_obj); - post(buffer); -} + post(buffer); } + +} // namespace rpc diff --git a/include/rpc/compatibility.h b/include/rpc/compatibility.h index 19baa201..ac10501d 100644 --- a/include/rpc/compatibility.h +++ b/include/rpc/compatibility.h @@ -12,9 +12,9 @@ #else #define RPCLIB_NORETURN __declspec(noreturn) -#define RPCLIB_CONSTEXPR const // bad replacement, but gets the job done +#define RPCLIB_CONSTEXPR const // bad replacement, but gets the job done #define RPCLIB_FINAL -#endif // !_MSC_VER +#endif // !_MSC_VER -#endif // COMPATIBILITY_H_PODKJ3 +#endif // COMPATIBILITY_H_PODKJ3 diff --git a/include/rpc/detail/all.h b/include/rpc/detail/all.h index 5a060bfb..f95088de 100644 --- a/include/rpc/detail/all.h +++ b/include/rpc/detail/all.h @@ -3,24 +3,22 @@ #ifndef ALL_H_H8MAAYCG #define ALL_H_H8MAAYCG -#include "rpc/detail/invoke.h" -#include "rpc/detail/if.h" #include "rpc/detail/bool.h" +#include "rpc/detail/if.h" +#include "rpc/detail/invoke.h" namespace rpc { namespace detail { //! \brief This type can be used to check multiple conditions. //! It will be true_type if all its arguments are true. -template struct all : true_ {}; +template +struct all : true_ {}; template -struct all - : if_, false_> {}; - -} -} - +struct all : if_, false_> {}; +} // namespace detail +} // namespace rpc #endif /* end of include guard: ALL_H_H8MAAYCG */ diff --git a/include/rpc/detail/any.h b/include/rpc/detail/any.h index 09ffb81c..2a2c5d72 100644 --- a/include/rpc/detail/any.h +++ b/include/rpc/detail/any.h @@ -3,19 +3,20 @@ #ifndef ANY_H_4G3QUOAN #define ANY_H_4G3QUOAN -#include "rpc/detail/invoke.h" -#include "rpc/detail/if.h" #include "rpc/detail/bool.h" +#include "rpc/detail/if.h" +#include "rpc/detail/invoke.h" namespace rpc { namespace detail { //! \brief Evaluates to true_type if any of its arguments is true_type. -template struct any : false_ {}; +template +struct any : false_ {}; template struct any : if_> {}; -} -} +} // namespace detail +} // namespace rpc #endif /* end of include guard: ANY_H_4G3QUOAN */ diff --git a/include/rpc/detail/async_writer.h b/include/rpc/detail/async_writer.h index 66488ff6..d2e0619b 100644 --- a/include/rpc/detail/async_writer.h +++ b/include/rpc/detail/async_writer.h @@ -3,12 +3,13 @@ #ifndef ASYNC_WRITER_H_HQIRH28I #define ASYNC_WRITER_H_HQIRH28I -#include "asio.hpp" -#include "rpc/msgpack.hpp" #include #include #include #include +#include "asio.hpp" +#include "rpc/msgpack.hpp" +#include "rpc/detail/log.h" namespace rpc { @@ -19,81 +20,80 @@ namespace detail { //! \brief Common logic for classes that have a write queue with async writing. class async_writer : public std::enable_shared_from_this { public: - async_writer(RPCLIB_ASIO::io_service *io, - RPCLIB_ASIO::ip::tcp::socket socket) - : socket_(std::move(socket)), write_strand_(*io), exit_(false) {} - - void do_write() { - if (exit_) { - return; - } - auto self(shared_from_this()); - auto &item = write_queue_.front(); - // the data in item remains valid until the handler is called - // since it will still be in the queue physically until then. - RPCLIB_ASIO::async_write( - socket_, RPCLIB_ASIO::buffer(item.data(), item.size()), - write_strand_.wrap( - [this, self](std::error_code ec, std::size_t transferred) { - (void)transferred; - if (!ec) { - write_queue_.pop_front(); - if (write_queue_.size() > 0) { - if (!exit_) { - do_write(); - } - } - } else { - LOG_ERROR("Error while writing to socket: {}", ec); - } - - if (exit_) { - LOG_INFO("Closing socket"); - try { - socket_.shutdown( - RPCLIB_ASIO::ip::tcp::socket::shutdown_both); - } - catch (std::system_error &e) { - (void)e; - LOG_WARN("std::system_error during socket shutdown. " - "Code: {}. Message: {}", e.code(), e.what()); - } - socket_.close(); - } - })); - } + async_writer(RPCLIB_ASIO::io_service *io, RPCLIB_ASIO::ip::tcp::socket socket) + : socket_(std::move(socket)), write_strand_(*io), exit_(false) {} - void write(RPCLIB_MSGPACK::sbuffer &&data) { - write_queue_.push_back(std::move(data)); - if (write_queue_.size() > 1) { - return; // there is an ongoing write chain so don't start another - } - - do_write(); + void do_write() { + if (exit_) { + return; + } + auto self(shared_from_this()); + auto &item = write_queue_.front(); + // the data in item remains valid until the handler is called + // since it will still be in the queue physically until then. + RPCLIB_ASIO::async_write( + socket_, RPCLIB_ASIO::buffer(item.data(), item.size()), + write_strand_.wrap( + [this, self](std::error_code ec, std::size_t transferred) { + (void)transferred; + if (!ec) { + write_queue_.pop_front(); + if (write_queue_.size() > 0) { + if (!exit_) { + do_write(); + } + } + } else { + LOG_ERROR("Error while writing to socket: {}", ec); + } + + if (exit_) { + LOG_INFO("Closing socket"); + try { + socket_.shutdown(RPCLIB_ASIO::ip::tcp::socket::shutdown_both); + } catch (std::system_error &e) { + (void)e; + LOG_WARN( + "std::system_error during socket shutdown. " + "Code: {}. Message: {}", + e.code(), e.what()); + } + socket_.close(); + } + })); + } + + void write(RPCLIB_MSGPACK::sbuffer &&data) { + write_queue_.push_back(std::move(data)); + if (write_queue_.size() > 1) { + return; // there is an ongoing write chain so don't start another } - friend class rpc::client; + do_write(); + } + + friend class rpc::client; protected: - template - std::shared_ptr shared_from_base() { - return std::static_pointer_cast(shared_from_this()); - } + template + std::shared_ptr shared_from_base() { + return std::static_pointer_cast(shared_from_this()); + } protected: - RPCLIB_ASIO::ip::tcp::socket socket_; - RPCLIB_ASIO::strand write_strand_; - std::atomic_bool exit_{false}; - bool exited_ = false; - std::mutex m_exit_; - std::condition_variable cv_exit_; + RPCLIB_ASIO::ip::tcp::socket socket_; + RPCLIB_ASIO::strand write_strand_; + std::atomic_bool exit_{false}; + bool exited_ = false; + std::mutex m_exit_; + std::condition_variable cv_exit_; private: - std::deque write_queue_; - RPCLIB_CREATE_LOG_CHANNEL(async_writer) + std::deque write_queue_; + RPCLIB_CREATE_LOG_CHANNEL(async_writer) }; -} /* detail */ -} /* rpc */ +} // namespace detail +} // namespace rpc #endif /* end of include guard: ASYNC_WRITER_H_HQIRH28I */ diff --git a/include/rpc/detail/bool.h b/include/rpc/detail/bool.h index 65f37d37..3111ba15 100644 --- a/include/rpc/detail/bool.h +++ b/include/rpc/detail/bool.h @@ -8,14 +8,14 @@ namespace rpc { namespace detail { -template +template using bool_ = constant; using true_ = bool_; using false_ = bool_; -} -} +} // namespace detail +} // namespace rpc #endif /* end of include guard: BOOL_H_QLG6S5XZ */ diff --git a/include/rpc/detail/call.h b/include/rpc/detail/call.h index b5c9bdce..f5e531b3 100644 --- a/include/rpc/detail/call.h +++ b/include/rpc/detail/call.h @@ -13,28 +13,26 @@ namespace detail { //! \brief Calls a functor with argument provided directly template -auto call(Functor f, Arg &&arg) - -> decltype(f(std::forward(arg))) -{ - return f(std::forward(arg)); +auto call(Functor f, Arg &&arg) -> decltype(f(std::forward(arg))) { + return f(std::forward(arg)); } - // Default behaviour is to assume C++11, overriding RPCLIB_CXX_STANDARD can use // newer standards: #if RPCLIB_CXX_STANDARD >= 14 template -decltype(auto) call_helper(Functor func, std::tuple &¶ms, +decltype(auto) call_helper(Functor func, + std::tuple &¶ms, std::index_sequence) { - return func(std::get(params)...); + return func(std::get(params)...); } //! \brief Calls a functor with arguments provided as a tuple template decltype(auto) call(Functor f, std::tuple &args) { - return call_helper(f, std::forward>(args), - std::index_sequence_for{}); + return call_helper(f, std::forward>(args), + std::index_sequence_for{}); } #else @@ -42,51 +40,37 @@ decltype(auto) call(Functor f, std::tuple &args) { // N is number of arguments left in tuple to unpack template -struct call_helper -{ - template - static auto call( - Functor f, - std::tuple& args_t, - ArgsF&&... args_f) - -> decltype(call_helper::call( - f, args_t, std::get(args_t), - std::forward(args_f)...)) - { - return call_helper::call( - f, - args_t, - std::get(args_t), - std::forward(args_f)... - ); - } +struct call_helper { + template + static auto call(Functor f, std::tuple &args_t, ArgsF &&... args_f) + -> decltype(call_helper::call(f, + args_t, + std::get(args_t), + std::forward(args_f)...)) { + return call_helper::call(f, args_t, std::get(args_t), + std::forward(args_f)...); + } }; template <> -struct call_helper<0> -{ - template - static auto call( - Functor f, - std::tuple&, - ArgsF&&... args_f) - -> decltype(f(std::forward(args_f)...)) - { - return f(std::forward(args_f)...); - } +struct call_helper<0> { + template + static auto call(Functor f, std::tuple &, ArgsF &&... args_f) + -> decltype(f(std::forward(args_f)...)) { + return f(std::forward(args_f)...); + } }; //! \brief Calls a functor with arguments provided as a tuple template -auto call(Functor f, std::tuple& args_t) - -> decltype(call_helper::call(f, args_t)) -{ - return call_helper::call(f, args_t); +auto call(Functor f, std::tuple &args_t) + -> decltype(call_helper::call(f, args_t)) { + return call_helper::call(f, args_t); } #endif -} -} +} // namespace detail +} // namespace rpc #endif /* end of include guard: CALL_H_ZXFACADH */ diff --git a/include/rpc/detail/client_error.h b/include/rpc/detail/client_error.h index 73edd2eb..99c441dd 100644 --- a/include/rpc/detail/client_error.h +++ b/include/rpc/detail/client_error.h @@ -3,8 +3,8 @@ #ifndef RPC_CLIENT_ERROR_H #define RPC_CLIENT_ERROR_H -#include #include +#include namespace rpc { namespace detail { @@ -14,26 +14,26 @@ namespace detail { //! wrong number of arguments, etc.) class client_error : public std::exception { public: - //! \brief Error codes used for signaling back to clients. These are used - //! to produce google-able error messages (since the msgpack-rpc protocol - //! does not define error handling in any more detail than sending an - //! object). - //! \note Care must be taken to keep these codes stable even between major - //! versions. - enum class code : uint16_t { - no_such_function = 1, - wrong_arity = 2, - protocol_error = 4 - }; - - client_error(code c, const std::string &msg); - - const char *what() const noexcept; + //! \brief Error codes used for signaling back to clients. These are used + //! to produce google-able error messages (since the msgpack-rpc protocol + //! does not define error handling in any more detail than sending an + //! object). + //! \note Care must be taken to keep these codes stable even between major + //! versions. + enum class code : uint16_t { + no_such_function = 1, + wrong_arity = 2, + protocol_error = 4 + }; + + client_error(code c, const std::string &msg); + + const char *what() const noexcept; private: - std::string what_; + std::string what_; }; -} -} +} // namespace detail +} // namespace rpc -#endif // RPC_CLIENT_ERROR_H +#endif // RPC_CLIENT_ERROR_H diff --git a/include/rpc/detail/constant.h b/include/rpc/detail/constant.h index 1c373b3a..4f4617d0 100644 --- a/include/rpc/detail/constant.h +++ b/include/rpc/detail/constant.h @@ -8,10 +8,10 @@ namespace rpc { namespace detail { -template +template struct constant : std::integral_constant {}; -} -} +} // namespace detail +} // namespace rpc #endif /* end of include guard: CONSTANT_H_5CXUYJEW */ diff --git a/include/rpc/detail/dev_utils.h b/include/rpc/detail/dev_utils.h index 32532097..444d4439 100644 --- a/include/rpc/detail/dev_utils.h +++ b/include/rpc/detail/dev_utils.h @@ -10,12 +10,12 @@ namespace rpc { namespace detail { inline void name_thread(std::string const &name) { - (void)name; + (void)name; #ifdef RPCLIB_LINUX - pthread_setname_np(pthread_self(), name.c_str()); + pthread_setname_np(pthread_self(), name.c_str()); #endif } -} /* detail */ -} /* rpc */ +} // namespace detail +} // namespace rpc #endif /* end of include guard: DEV_UTILS_H_JQSWE2OS */ diff --git a/include/rpc/detail/func_tools.h b/include/rpc/detail/func_tools.h index 4f18e482..60ae788a 100644 --- a/include/rpc/detail/func_tools.h +++ b/include/rpc/detail/func_tools.h @@ -3,14 +3,13 @@ #ifndef FUNC_TOOLS_H_9FNRD4G2 #define FUNC_TOOLS_H_9FNRD4G2 -#include "rpc/detail/invoke.h" #include "rpc/detail/all.h" #include "rpc/detail/any.h" +#include "rpc/detail/invoke.h" namespace rpc { namespace detail { - enum class enabled {}; template @@ -19,8 +18,7 @@ using enable_if = invoke::value, enabled>>; template using disable_if = invoke::value, enabled>>; - -} -} +} // namespace detail +} // namespace rpc #endif /* end of include guard: FUNC_TOOLS_H_9FNRD4G2 */ diff --git a/include/rpc/detail/func_traits.h b/include/rpc/detail/func_traits.h index 1847d99d..3650524c 100644 --- a/include/rpc/detail/func_traits.h +++ b/include/rpc/detail/func_traits.h @@ -22,14 +22,26 @@ struct nonzero_arg {}; struct void_result {}; struct nonvoid_result {}; -template struct arg_count_trait { typedef nonzero_arg type; }; +template +struct arg_count_trait { + typedef nonzero_arg type; +}; -template <> struct arg_count_trait<0> { typedef zero_arg type; }; +template <> +struct arg_count_trait<0> { + typedef zero_arg type; +}; -template struct result_trait { typedef nonvoid_result type; }; +template +struct result_trait { + typedef nonvoid_result type; +}; -template <> struct result_trait { typedef void_result type; }; -} +template <> +struct result_trait { + typedef void_result type; +}; +} // namespace tags //! \brief Provides a small function traits implementation that //! works with a reasonably large set of functors. @@ -42,10 +54,11 @@ struct func_traits : func_traits {}; template struct func_traits : func_traits {}; -template struct func_traits { - using result_type = R; - using arg_count = std::integral_constant; - using args_type = std::tuple::type...>; +template +struct func_traits { + using result_type = R; + using arg_count = std::integral_constant; + using args_type = std::tuple::type...>; }; template @@ -58,12 +71,14 @@ template struct func_kind_info : func_kind_info {}; -template struct func_kind_info { - typedef typename tags::arg_count_trait::type args_kind; - typedef typename tags::result_trait::type result_kind; +template +struct func_kind_info { + typedef typename tags::arg_count_trait::type args_kind; + typedef typename tags::result_trait::type result_kind; }; -template using is_zero_arg = is_zero::arg_count>; +template +using is_zero_arg = is_zero::arg_count>; template using is_single_arg = @@ -71,7 +86,7 @@ using is_single_arg = template using is_void_result = std::is_void::result_type>; -} -} +} // namespace detail +} // namespace rpc #endif /* end of include guard: FUNC_TRAITS_H_HWIWA6G0 */ diff --git a/include/rpc/detail/if.h b/include/rpc/detail/if.h index 7e4cab4f..83ea604e 100644 --- a/include/rpc/detail/if.h +++ b/include/rpc/detail/if.h @@ -11,6 +11,6 @@ namespace detail { template using if_ = invoke>; } -} +} // namespace rpc #endif /* end of include guard: IF_H_1OW9DR7G */ diff --git a/include/rpc/detail/invoke.h b/include/rpc/detail/invoke.h index 4a491582..534b6256 100644 --- a/include/rpc/detail/invoke.h +++ b/include/rpc/detail/invoke.h @@ -6,10 +6,9 @@ namespace rpc { namespace detail { -template +template using invoke = typename T::type; - -}} - +} +} // namespace rpc #endif /* end of include guard: INVOKE_H_0CWMPLUE */ diff --git a/include/rpc/detail/is_specialization_of.h b/include/rpc/detail/is_specialization_of.h index c4003cee..1a10988f 100644 --- a/include/rpc/detail/is_specialization_of.h +++ b/include/rpc/detail/is_specialization_of.h @@ -14,7 +14,7 @@ struct is_specialization_of : false_ {}; template