From 6e477c6d2b0e9a5be4f5bf16cd967b93b4d5680d Mon Sep 17 00:00:00 2001 From: Daniel Lyubomirov <daniel.lyubomirov@vereign.com> Date: Tue, 26 May 2020 11:45:34 +0300 Subject: [PATCH] [OPTIMIZATION] Use pimpl in the gRPC server --- cpp/.clang-tidy | 35 +++++++ cpp/CMakeLists.txt | 27 ++++-- cpp/src/CMakeLists.txt | 18 +++- cpp/src/csandbox.cc | 108 +--------------------- cpp/src/vereign/core/scope_guard.hh | 7 +- cpp/src/vereign/grpc/server.cc | 137 +++++++++++++++++----------- cpp/src/vereign/grpc/server.hh | 32 ++----- cpp/tests/vereign/CMakeLists.txt | 6 ++ 8 files changed, 172 insertions(+), 198 deletions(-) create mode 100644 cpp/.clang-tidy diff --git a/cpp/.clang-tidy b/cpp/.clang-tidy new file mode 100644 index 0000000..e57be52 --- /dev/null +++ b/cpp/.clang-tidy @@ -0,0 +1,35 @@ +--- +Checks: 'clang-diagnostic-*,clang-analyzer-*,modernize-*,readability-*,-readability-container-size-empty' +WarningsAsErrors: '' +HeaderFilterRegex: '' +AnalyzeTemporaryDtors: false +FormatStyle: none +CheckOptions: + - key: cert-dcl16-c.NewSuffixes + value: 'L;LL;LU;LLU' + - key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField + value: '0' + - key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors + value: '1' + - key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic + value: '1' + - key: google-readability-braces-around-statements.ShortStatementLines + value: '1' + - key: google-readability-function-size.StatementThreshold + value: '800' + - key: google-readability-namespace-comments.ShortNamespaceLines + value: '10' + - key: google-readability-namespace-comments.SpacesBeforeComments + value: '2' + - key: modernize-loop-convert.MaxCopySize + value: '16' + - key: modernize-loop-convert.MinConfidence + value: reasonable + - key: modernize-loop-convert.NamingStyle + value: CamelCase + - key: modernize-pass-by-value.IncludeStyle + value: llvm + - key: modernize-replace-auto-ptr.IncludeStyle + value: llvm + - key: modernize-use-nullptr.NullMacros + value: 'NULL' diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index a07568a..8ad13ac 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -6,6 +6,11 @@ endif() project (vereign) +# Options +option(VEREIGN_USE_LLD "Use the lld linker" OFF) +option(VEREIGN_USE_PRECOMPILED_HEADERS "Use precompiled headers" OFF) +option(VEREIGN_USE_TIME_TRACE OFF) + if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "19.0.24215.1") message(FATAL_ERROR "Microsoft Visual C++ version MSVC 19.0.24215.1 required") @@ -46,6 +51,9 @@ endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(CMAKE_CXX_FLAGS_RELEASE "-g -O3 -Wall -Wextra -pedantic") set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -Wall -Wextra -pedantic") + if (VEREIGN_USE_LLD) + set(CMAKE_EXE_LINKER_FLAGS "-fuse-ld=lld") + endif() endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") @@ -141,13 +149,14 @@ message(STATUS "summary of build options: Install prefix: ${CMAKE_INSTALL_PREFIX} Target system: ${CMAKE_SYSTEM_NAME} Compiler: - Build type: ${CMAKE_BUILD_TYPE} - C compiler: ${CMAKE_C_COMPILER} - CFLAGS: ${CMAKE_C_FLAGS_${_build_type}} ${CMAKE_C_FLAGS} - C++ compiler: ${CMAKE_CXX_COMPILER} - CXXFLAGS: ${CMAKE_CXX_FLAGS_${_build_type}} ${CMAKE_CXX_FLAGS} - WARNCFLAGS: ${WARNCFLAGS} - CXX1XCXXFLAGS: ${CXX1XCXXFLAGS} + Build type: ${CMAKE_BUILD_TYPE} + C compiler: ${CMAKE_C_COMPILER} + CFLAGS: ${CMAKE_C_FLAGS_${_build_type}} ${CMAKE_C_FLAGS} + C++ compiler: ${CMAKE_CXX_COMPILER} + CXXFLAGS: ${CMAKE_CXX_FLAGS_${_build_type}} ${CMAKE_CXX_FLAGS} + EXE_LINKER_FLAGS: ${CMAKE_EXE_LINKER_FLAGS_${_build_type}} ${CMAKE_EXE_LINKER_FLAGS} + WARNCFLAGS: ${WARNCFLAGS} + CXX1XCXXFLAGS: ${CXX1XCXXFLAGS} Libs: fmt: ${fmt_FOUND} [${fmt_VERSION}] (DIR='${fmt_DIR}') OpenSSL: ${OpenSSL_FOUND} [${OPENSSL_VERSION}] (LIBS='${OPENSSL_LIBRARIES}') @@ -155,4 +164,8 @@ message(STATUS "summary of build options: Boost ${Boost_FOUND} [${Boost_VERSION_STRING}] (DIR='${Boost_DIR}') Boost libs ${Boost_LIBRARIES} gRPC ${gRPC_FOUND} [${gRPC_VERSION}] (LIBS='${_grpc_libs}') + Options: + VEREIGN_USE_LLD ${VEREIGN_USE_LLD} + VEREIGN_USE_PRECOMPILED_HEADERS ${VEREIGN_USE_PRECOMPILED_HEADERS} + VEREIGN_USE_TIME_TRACE ${VEREIGN_USE_TIME_TRACE} ") diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt index 1c7b906..2defa3f 100644 --- a/cpp/src/CMakeLists.txt +++ b/cpp/src/CMakeLists.txt @@ -35,6 +35,8 @@ target_link_libraries( OpenSSL::Crypto $<$<CXX_COMPILER_ID:MSVC>:CRYPT32.LIB> ) +file(GLOB PROTO_HEADERS ${CMAKE_SOURCE_DIR}/proto/cpp/vereign/client_library/*pb.h) +target_precompile_headers(vereignproto PUBLIC ${PROTO_HEADERS}) set(VEREIGNLIB_SRC vereign/restapi/detail/http_reader.cc @@ -66,12 +68,18 @@ set(csandbox_sources add_executable(csandbox ${csandbox_sources}) target_link_libraries(csandbox - fmt::fmt - Boost::regex - Threads::Threads - OpenSSL::SSL - $<$<CXX_COMPILER_ID:MSVC>:CRYPT32.LIB> + vereignlib + # fmt::fmt + # Boost::regex + # Threads::Threads + # OpenSSL::SSL + # $<$<CXX_COMPILER_ID:MSVC>:CRYPT32.LIB> ) +if (VEREIGN_USE_TIME_TRACE AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + target_compile_options(csandbox + PRIVATE "-ftime-trace" + ) +endif() # Generates the gRPC and Vereign services source code. add_custom_target( diff --git a/cpp/src/csandbox.cc b/cpp/src/csandbox.cc index 01b0beb..3c3bf93 100644 --- a/cpp/src/csandbox.cc +++ b/cpp/src/csandbox.cc @@ -1,108 +1,2 @@ -#include <functional> -#include <memory> -#include <thread> -#include <future> -#include <iostream> -#include <type_traits> -#include <vector> -#include <boost/bind.hpp> -#include <boost/optional.hpp> - -namespace experiment { - -/** - * Array with c++11 move semantics for research purposes. - * - * @note Do not use this in real code. - */ -class Array { - public: - Array() : arr_{nullptr}, size_{0} { - std::cout << "default construct " << to_s() << "\n"; - } - - Array(std::initializer_list<int> l) - : arr_(new int[l.size()]{}) - , size_(l.size()) { - - std::copy(l.begin(), l.end(), arr_); - std::cout << "create list " << to_s() << "\n"; - } - - Array(const std::vector<int>& src) { - arr_ = new int[src.size()]{}; - size_ = src.size(); - std::copy(src.begin(), src.end(), arr_); - std::cout << "create " << to_s() << "\n"; - } - - Array(const Array& other) : arr_(new int[other.size_]{}), size_{other.size_} { - std::copy(other.arr_, other.arr_ + other.size_, arr_); - std::cout << "copy construct " << to_s() << "\n"; - } - - Array& operator=(const Array& other) { - Array tmp{other}; - swap(*this, tmp); - std::cout << "copy assign " << to_s() << "\n"; - return *this; - } - - Array(Array&& other) noexcept : arr_{other.arr_}, size_(other.size_) { - other.arr_ = nullptr; - other.size_ = 0; - std::cout << "move construct " << to_s() << "\n"; - } - - Array& operator=(Array&& other) noexcept { - swap(*this, other); - std::cout << "move assign " << to_s() << "\n"; - return *this; - } - - ~Array() { - std::cout << "destroy " << to_s() << "\n"; - if (arr_) { - delete [] arr_; - } - } - - void reset() { - if (arr_) { - delete [] arr_; - arr_ = nullptr; - } - size_ = 0; - } - - std::string to_s() const { - std::string ret = std::to_string(intptr_t(this)) + " "; - if (!arr_) { - return ret; - } - - for (int i = 0; i < size_; i++) { - ret += std::to_string(arr_[i]) + " "; - } - - return ret; - } - - friend void swap(Array& lhs, Array& rhs) noexcept; - - private: - int* arr_; - int size_; -}; - -void swap(Array& lhs, Array& rhs) noexcept { - using std::swap; - swap(lhs.arr_, rhs.arr_); - swap(lhs.size_, rhs.size_); -} -} - -using namespace experiment; - -int main(int , char* []) { +auto main(int argc, char* argv[]) -> int { } diff --git a/cpp/src/vereign/core/scope_guard.hh b/cpp/src/vereign/core/scope_guard.hh index 72e4086..7c45af0 100644 --- a/cpp/src/vereign/core/scope_guard.hh +++ b/cpp/src/vereign/core/scope_guard.hh @@ -1,6 +1,9 @@ #ifndef __VEREIGN_CORE_SCOPE_GUARD_HH #define __VEREIGN_CORE_SCOPE_GUARD_HH +#include <type_traits> +#include <utility> + namespace vereign { namespace core { @@ -23,11 +26,11 @@ private: }; template<typename Func> -ScopeGuard<Func> MakeScopeGuard(Func&& func) { +auto MakeScopeGuard(Func&& func) -> ScopeGuard<Func> { return ScopeGuard<Func>{std::move(func)}; } } } -#endif \ No newline at end of file +#endif diff --git a/cpp/src/vereign/grpc/server.cc b/cpp/src/vereign/grpc/server.cc index 902cabf..c89a274 100644 --- a/cpp/src/vereign/grpc/server.cc +++ b/cpp/src/vereign/grpc/server.cc @@ -1,18 +1,18 @@ -#include "vereign/grpc/gen/conversation_api.hh" -#include "vereign/service/gen/conversation_service.hh" #include <vereign/grpc/server.hh> -#include <vereign/grpc/gen/passport_api.hh> -#include <vereign/service/gen/passport_service.hh> #include <vereign/restapi/client.hh> #include <vereign/restapi/client_session.hh> - #include <vereign/service/gen/gen.hh> -#include <vereign/service/passport_service.hh> - #include <vereign/grpc/gen/gen.hh> +#include <vereign/grpc/service_registry.hh> + +#include <vereign/service/passport_service.hh> #include <vereign/grpc/passport_api.hh> +#include <grpcpp/server.h> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ssl/context.hpp> +#include <boost/asio/executor_work_guard.hpp> #include <grpcpp/server_builder.h> #include <grpcpp/server_context.h> #include <grpcpp/security/server_credentials.h> @@ -22,14 +22,17 @@ namespace vereign { namespace grpc { -Server::Server( - const std::string& listenAddress, - const std::string& vereignHost, - const std::string& vereignPort, - // FIXME: the public key must come from a storage - const std::string& publicKey -) - : work_guard_{asio::make_work_guard(ioc_)}, +namespace asio = boost::asio; + +class Server::Impl { +public: + Impl( + const std::string& listenAddress, + const std::string& vereignHost, + const std::string& vereignPort, + // FIXME: the public key must come from a storage + const std::string& publicKey + ) : work_guard_{asio::make_work_guard(ioc_)}, ssl_context_{asio::ssl::context::tlsv12_client}, client_{std::make_unique<restapi::Client>( ioc_, ssl_context_, vereignHost, vereignPort @@ -37,37 +40,79 @@ Server::Server( client_session_{std::make_unique<restapi::ClientSession>( *client_, publicKey )} -{ - // FIXME: Verify the remote server's certificate - // ssl_context.set_verify_mode(ssl::verify_peer); + { + + // FIXME: Verify the remote server's certificate + // ssl_context.set_verify_mode(ssl::verify_peer); + ::grpc::ServerBuilder builder; + builder.AddListeningPort( + listenAddress, + ::grpc::InsecureServerCredentials(), + &selected_port_ + ); + + // register manually written services + services_registry_.RegisterIfNotExist<PassportAPI<service::PassportService>>(*client_session_); + + // register all generated services + grpc::gen::RegisterAll(*client_session_, services_registry_); - ::grpc::ServerBuilder builder; - builder.AddListeningPort( - listenAddress, - ::grpc::InsecureServerCredentials(), - &selected_port_ - ); + services_registry_.RegisterIntoBuilder(builder); - // register manually written services - services_registry_.RegisterIfNotExist<PassportAPI<service::PassportService>>(*client_session_); + server_ = builder.BuildAndStart(); + if (server_ == nullptr) { + throw std::runtime_error("server start failed"); + } - // register all generated services - grpc::gen::RegisterAll(*client_session_, services_registry_); + server_thread_ = std::thread([this]() { + server_->Wait(); + }); - services_registry_.RegisterIntoBuilder(builder); + service_thread_ = std::thread([this]() { + ioc_.run(); + }); + } + + void Shutdown() { + client_session_->Close(); + + server_->Shutdown(); + if (server_thread_.joinable()) { + server_thread_.join(); + } + + work_guard_.reset(); + if (service_thread_.joinable()) { + service_thread_.join(); + } + } - server_ = builder.BuildAndStart(); - if (server_ == nullptr) { - throw std::runtime_error("server start failed"); + auto SelectedPort() const -> int { + return selected_port_; } - server_thread_ = std::thread([this]() { - server_->Wait(); - }); +private: + int selected_port_; + asio::io_context ioc_; + asio::executor_work_guard<asio::io_context::executor_type> work_guard_; + asio::ssl::context ssl_context_; + std::unique_ptr<vereign::restapi::Client> client_; + std::unique_ptr<vereign::restapi::ClientSession> client_session_; + ServiceRegistry services_registry_; + std::unique_ptr<::grpc::Server> server_; + std::thread server_thread_; + std::thread service_thread_; +}; - service_thread_ = std::thread([this]() { - ioc_.run(); - }); +Server::Server( + const std::string& listenAddress, + const std::string& vereignHost, + const std::string& vereignPort, + // FIXME: the public key must come from a storage + const std::string& publicKey +) + : impl_{std::make_unique<Impl>(listenAddress, vereignHost, vereignPort, publicKey)} +{ } Server::~Server() { @@ -75,21 +120,11 @@ Server::~Server() { } void Server::Shutdown() { - client_session_->Close(); - - server_->Shutdown(); - if (server_thread_.joinable()) { - server_thread_.join(); - } - - work_guard_.reset(); - if (service_thread_.joinable()) { - service_thread_.join(); - } + impl_->Shutdown(); } -int Server::SelectedPort() const { - return selected_port_; +auto Server::SelectedPort() const -> int { + return impl_->SelectedPort(); } } // namespace grpc diff --git a/cpp/src/vereign/grpc/server.hh b/cpp/src/vereign/grpc/server.hh index 7ba60bb..d2c4cba 100644 --- a/cpp/src/vereign/grpc/server.hh +++ b/cpp/src/vereign/grpc/server.hh @@ -1,24 +1,12 @@ #ifndef __VEREIGN_GRPC_SERVER_API_HH #define __VEREIGN_GRPC_SERVER_API_HH -#include <thread> -#include <grpcpp/server.h> -#include <boost/asio/io_context.hpp> -#include <boost/asio/ssl/context.hpp> -#include <boost/asio/executor_work_guard.hpp> -#include <vereign/grpc/service_registry.hh> +#include <string> +#include <memory> namespace vereign { - -namespace restapi { -class Client; -class ClientSession; -} - namespace grpc { -namespace asio = boost::asio; - /** * Server is a grpc server that provides the Vereign services. * @@ -51,7 +39,7 @@ public: // Disable copying Server(const Server&) = delete; - Server& operator=(const Server&) = delete; + auto operator=(const Server&) -> Server& = delete; /** * Shutdown the server. @@ -68,19 +56,11 @@ public: * Then this method will return the port that the OS assigned to the gRPC * socket. */ - int SelectedPort() const; + auto SelectedPort() const -> int; private: - int selected_port_; - asio::io_context ioc_; - asio::executor_work_guard<asio::io_context::executor_type> work_guard_; - asio::ssl::context ssl_context_; - std::unique_ptr<vereign::restapi::Client> client_; - std::unique_ptr<vereign::restapi::ClientSession> client_session_; - ServiceRegistry services_registry_; - std::unique_ptr<::grpc::Server> server_; - std::thread server_thread_; - std::thread service_thread_; + class Impl; + std::unique_ptr<Impl> impl_; }; } // namespace grpc diff --git a/cpp/tests/vereign/CMakeLists.txt b/cpp/tests/vereign/CMakeLists.txt index d170416..16cfb8c 100644 --- a/cpp/tests/vereign/CMakeLists.txt +++ b/cpp/tests/vereign/CMakeLists.txt @@ -23,6 +23,12 @@ target_link_libraries(tests Threads::Threads ) +if (VEREIGN_USE_TIME_TRACE AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + target_compile_options(tests + PRIVATE "-ftime-trace" + ) +endif() + #add_custom_command( # TARGET protobuf_tests # COMMENT "Run protobuf tests" -- GitLab