From 49eff5f6952f24713c22d537a0bd34f0521b8bde Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Tue, 19 Feb 2019 00:05:56 +0300 Subject: [PATCH 01/48] Asio custom handler allocation --- c++-virtan/server.cc | 183 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 152 insertions(+), 31 deletions(-) diff --git a/c++-virtan/server.cc b/c++-virtan/server.cc index ebb2bc8..8dbaad3 100644 --- a/c++-virtan/server.cc +++ b/c++-virtan/server.cc @@ -1,58 +1,175 @@ #include #include +#include +#include +#include +#include +#include #include #include -#include #include -using namespace std; -using namespace boost; -using namespace boost::asio; -using namespace boost::asio::ip; +template +void* allocate(std::size_t size, Context& context) { + using namespace boost::asio; + return asio_handler_allocate(size, std::addressof(context)); +} + +template +void deallocate(void* pointer, std::size_t size, Context& context) { + using namespace boost::asio; + asio_handler_deallocate(pointer, size, std::addressof(context)); +} + +template +void invoke(Function&& function, Context& context) { + using namespace boost::asio; + asio_handler_invoke(std::forward(function), std::addressof(context)); +} + +template +class handler_allocator { +private: + handler_allocator(const handler_allocator&); + handler_allocator& operator=(const handler_allocator&); + +public: + handler_allocator(const handler_allocator&) : in_use_(false) {} + + bool owns(void* p) const { + return std::addressof(storage_) == p; + } + + void* allocate(std::size_t size) { + if (in_use_ || size > alloc_size) + return 0; + in_use_ = true; + return std::addressof(storage_); + } + + void deallocate(void* p) { + if (p) + in_use_ = false; + } + +private: + typename std::aligned_storage::type storage_; + bool in_use_; +}; + +template +class custom_alloc_handler { +private: + typedef custom_alloc_handler this_type; + +public: + typedef void result_type; + + template + custom_alloc_handler(Allocator& allocator, H&& handler): + allocator_(std::addressof(allocator)), handler_(std::forward(handler)) {} + + friend void* asio_handler_allocate(std::size_t size, this_type* context) { + if (void* p = context->allocator_->allocate(size)) + return p; + return allocate(size, context->handler_); + } + + friend void asio_handler_deallocate(void* pointer, std::size_t size, this_type* context) { + if (context->allocator_->owns(pointer)) + context->allocator_->deallocate(pointer); + else + deallocate(pointer, size, context->handler_); + } + + template + friend void asio_handler_invoke(Function&& function, this_type* context) { + invoke(detail::forward(function), context->handler_); + } + + template + friend void asio_handler_invoke(Function& function, this_type* context) { + invoke(function, context->handler_); + } + + template + friend void asio_handler_invoke(const Function& function, this_type* context) { + invoke(function, context->handler_); + } + + template + void operator()(Arg&&... arg) { + handler_(std::forward(arg)...); + } + + template + void operator()(Arg&&... arg) const { + handler_(std::forward(arg)...); + } + +private: + Allocator* allocator_; + Handler handler_; +}; + +template +inline custom_alloc_handler::type> +make_custom_alloc_handler(Allocator& allocator, Handler&& handler) { + typedef typename std::decay::type handler_type; + return custom_alloc_handler(allocator, std::forward(handler)); +} struct connection { - connection(io_service &s) : - sock(s) {} + connection(io_service &s) : sock(s), pending_ops(0) {} void start() { - //sock.set_option(tcp::no_delay(true)); - sock.async_read_some(buffer(data, max_length), - bind(&connection::read, this, asio::placeholders::error, asio::placeholders::bytes_transferred)); + sock.async_read_some(boost::asio::buffer(data, max_length), make_custom_alloc_handler(allocator, + std::bind(&connection::read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred))); } - void read(const system::error_code &e, size_t bytes_transferred) { - if(!e) { async_write(sock, buffer(data, bytes_transferred), - bind(&connection::write, this, asio::placeholders::error)); } - else delete this; + void read(const boost::system::error_code &e, std::size_t bytes_transferred) { + if (e) + delete this; + else + async_write(sock, boost::asio::buffer(data, bytes_transferred), make_custom_alloc_handler(allocator, + std::bind(&connection::write, this, boost::asio::placeholders::error)); } - void write(const system::error_code &e) { - if(!e) start(); - else delete this; + void write(const boost::system::error_code &e) { + if (e) + delete this; + else + start(); } - tcp::socket sock; + boost::asio::ip::tcp::socket sock; enum { max_length = 4096 }; char data[max_length]; + handler_allocator<128> allocator; }; struct acceptor { - acceptor(int native_sock) : - a(s, tcp::v4(), native_sock) { start_accept(); } + acceptor(int native_sock) : a(s, boost::asio::ip::tcp::v4(), native_sock) { + start_accept(); + } void start_accept() { connection *c = new connection(s); - a.async_accept(c->sock, bind(&acceptor::accept, this, c, boost::asio::placeholders::error)); + a.async_accept(c->sock, make_custom_alloc_handler(allocator, + std::bind(&acceptor::accept, this, c, boost::asio::placeholders::error))); } void accept(connection *c, const system::error_code &e) { - if(!e) c->start(); - else delete c; + if (e) + delete c; + else + c->start(); start_accept(); } - io_service s; - tcp::acceptor a; + boost::asio::io_service s; + boost::asio::ip::tcp::acceptor a; + handler_allocator<256> allocator; }; void servicing(unsigned short port) { @@ -67,13 +184,17 @@ int main(int args, char **argv) { return 1; } unsigned short port = atoi(argv[1]); - size_t threads = args > 2 ? atoi(argv[2]) : 24; - thread *vthreads = new thread[threads]; - io_service fake_s; - tcp::acceptor fake_a(fake_s, tcp::endpoint(tcp::v4(), port)); + std::size_t threads = args > 2 ? atoi(argv[2]) : 24; + std::vector vthreads; + vthreads.reserve(threads) + boost::asio::io_service fake_s; + boost::asio::ip::tcp::acceptor fake_a(fake_s, boost::asio::ip::tcp::endpoint( + boost::asio::ip::tcp::v4(), port)); //int defer_accept = 1; //setsockopt(fake_a.native_handle(), IPPROTO_TCP, TCP_DEFER_ACCEPT, &defer_accept, sizeof(defer_accept)); - for(size_t i = 0; i < threads; ++i) vthreads[i] = thread(servicing, fake_a.native_handle()); - sleep((unsigned int) 0 - 1); + for (std::size_t i = 0; i < threads; ++i) + vthreads.emplace_back(std::bind(servicing, fake_a.native_handle())); + for (std::size_t i = 0; i < threads; ++i) + vthreads[i].join(); return 0; } From 6bda46fd64c02c48a41b4ac2764673f7c9532d87 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Tue, 19 Feb 2019 00:32:35 +0300 Subject: [PATCH 02/48] Fixed build errors and makefile --- c++-virtan/Makefile | 4 +- c++-virtan/server.cc | 311 +++++++++++++++++++++++-------------------- 2 files changed, 168 insertions(+), 147 deletions(-) diff --git a/c++-virtan/Makefile b/c++-virtan/Makefile index b247aef..721bbf7 100644 --- a/c++-virtan/Makefile +++ b/c++-virtan/Makefile @@ -1,7 +1,7 @@ -CXXFLAGS = -Os -march=native -mtune=native -std=c++0x -Wall +CXXFLAGS = -Os -march=native -mtune=native -std=c++17 -Wall DEBUG_CXXFLAGS = -g -O0 CPPFLAGS = -I/opt/local/include -LDFLAGS = -L/opt/local/lib -lboost_thread-mt -lboost_system-mt -pthread +LDFLAGS = -L/opt/local/lib -lboost_system-mt -pthread SERVER = server default: $(SERVER) diff --git a/c++-virtan/server.cc b/c++-virtan/server.cc index 8dbaad3..da94e6b 100644 --- a/c++-virtan/server.cc +++ b/c++-virtan/server.cc @@ -5,196 +5,217 @@ #include #include #include -#include -#include #include template void* allocate(std::size_t size, Context& context) { - using namespace boost::asio; - return asio_handler_allocate(size, std::addressof(context)); + using namespace boost::asio; + return asio_handler_allocate(size, std::addressof(context)); } template void deallocate(void* pointer, std::size_t size, Context& context) { - using namespace boost::asio; - asio_handler_deallocate(pointer, size, std::addressof(context)); + using namespace boost::asio; + asio_handler_deallocate(pointer, size, std::addressof(context)); } template void invoke(Function&& function, Context& context) { - using namespace boost::asio; - asio_handler_invoke(std::forward(function), std::addressof(context)); + using namespace boost::asio; + asio_handler_invoke(std::forward(function), std::addressof(context)); } template class handler_allocator { private: - handler_allocator(const handler_allocator&); - handler_allocator& operator=(const handler_allocator&); + handler_allocator(const handler_allocator&) = delete; + handler_allocator& operator=(const handler_allocator&) = delete; public: - handler_allocator(const handler_allocator&) : in_use_(false) {} + handler_allocator(const handler_allocator&) : in_use_(false) {} + ~handler_allocator() = default; - bool owns(void* p) const { - return std::addressof(storage_) == p; - } + bool owns(void* p) const { + return std::addressof(storage_) == p; + } - void* allocate(std::size_t size) { - if (in_use_ || size > alloc_size) - return 0; - in_use_ = true; - return std::addressof(storage_); - } + void* allocate(std::size_t size) { + if (in_use_ || size > alloc_size) + return 0; + in_use_ = true; + return std::addressof(storage_); + } - void deallocate(void* p) { - if (p) - in_use_ = false; - } + void deallocate(void* p) { + if (p) + in_use_ = false; + } private: - typename std::aligned_storage::type storage_; - bool in_use_; + typename std::aligned_storage::type storage_; + bool in_use_; }; template class custom_alloc_handler { private: - typedef custom_alloc_handler this_type; + typedef custom_alloc_handler this_type; public: - typedef void result_type; - - template - custom_alloc_handler(Allocator& allocator, H&& handler): - allocator_(std::addressof(allocator)), handler_(std::forward(handler)) {} - - friend void* asio_handler_allocate(std::size_t size, this_type* context) { - if (void* p = context->allocator_->allocate(size)) - return p; - return allocate(size, context->handler_); - } - - friend void asio_handler_deallocate(void* pointer, std::size_t size, this_type* context) { - if (context->allocator_->owns(pointer)) - context->allocator_->deallocate(pointer); - else - deallocate(pointer, size, context->handler_); - } - - template - friend void asio_handler_invoke(Function&& function, this_type* context) { - invoke(detail::forward(function), context->handler_); - } - - template - friend void asio_handler_invoke(Function& function, this_type* context) { - invoke(function, context->handler_); - } - - template - friend void asio_handler_invoke(const Function& function, this_type* context) { - invoke(function, context->handler_); - } - - template - void operator()(Arg&&... arg) { - handler_(std::forward(arg)...); - } - - template - void operator()(Arg&&... arg) const { - handler_(std::forward(arg)...); - } + typedef void result_type; + + template + custom_alloc_handler(Allocator& allocator, H&& handler): + allocator_(std::addressof(allocator)), handler_(std::forward(handler)) {} + + friend void* asio_handler_allocate(std::size_t size, this_type* context) { + if (void* p = context->allocator_->allocate(size)) + return p; + return allocate(size, context->handler_); + } + + friend void asio_handler_deallocate(void* pointer, std::size_t size, this_type* context) { + if (context->allocator_->owns(pointer)) + context->allocator_->deallocate(pointer); + else + deallocate(pointer, size, context->handler_); + } + + template + friend void asio_handler_invoke(Function&& function, this_type* context) { + invoke(detail::forward(function), context->handler_); + } + + template + friend void asio_handler_invoke(Function& function, this_type* context) { + invoke(function, context->handler_); + } + + template + friend void asio_handler_invoke(const Function& function, this_type* context) { + invoke(function, context->handler_); + } + + template + void operator()(Arg&&... arg) { + handler_(std::forward(arg)...); + } + + template + void operator()(Arg&&... arg) const { + handler_(std::forward(arg)...); + } private: - Allocator* allocator_; - Handler handler_; + Allocator* allocator_; + Handler handler_; }; template inline custom_alloc_handler::type> make_custom_alloc_handler(Allocator& allocator, Handler&& handler) { - typedef typename std::decay::type handler_type; - return custom_alloc_handler(allocator, std::forward(handler)); + typedef typename std::decay::type handler_type; + return custom_alloc_handler(allocator, std::forward(handler)); } -struct connection { - connection(io_service &s) : sock(s), pending_ops(0) {} - - void start() { - sock.async_read_some(boost::asio::buffer(data, max_length), make_custom_alloc_handler(allocator, - std::bind(&connection::read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred))); - } - - void read(const boost::system::error_code &e, std::size_t bytes_transferred) { - if (e) - delete this; - else - async_write(sock, boost::asio::buffer(data, bytes_transferred), make_custom_alloc_handler(allocator, - std::bind(&connection::write, this, boost::asio::placeholders::error)); - } - - void write(const boost::system::error_code &e) { - if (e) - delete this; - else - start(); - } - - boost::asio::ip::tcp::socket sock; - enum { max_length = 4096 }; - char data[max_length]; - handler_allocator<128> allocator; +class connection { +private: + connection(const connection&) = delete; + connection& operator=(const connection&) = delete; + +public: + explicit connection(boost::asio::io_service& service) : socket_(service) {} + ~connection() = default; + + void start() { + socket_.async_read_some(boost::asio::buffer(data_, max_length), + make_custom_alloc_handler(allocator_, std::bind(&connection::read, this, + boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred))); + } + + boost::asio::ip::tcp::socket& socket() { + return socket_; + } + +private: + void read(const boost::system::error_code& e, std::size_t bytes_transferred) { + if (e) + delete this; + else + boost::asio::async_write(socket_, boost::asio::buffer(data_, bytes_transferred), + make_custom_alloc_handler(allocator_, std::bind(&connection::write, this, + boost::asio::placeholders::error)); + } + + void write(const boost::system::error_code &e) { + if (e) + delete this; + else + start(); + } + + handler_allocator<128> allocator_; + enum { max_length = 4096 }; + char data_[max_length]; + boost::asio::ip::tcp::socket socket_; }; -struct acceptor { - acceptor(int native_sock) : a(s, boost::asio::ip::tcp::v4(), native_sock) { - start_accept(); - } - - void start_accept() { - connection *c = new connection(s); - a.async_accept(c->sock, make_custom_alloc_handler(allocator, - std::bind(&acceptor::accept, this, c, boost::asio::placeholders::error))); - } - - void accept(connection *c, const system::error_code &e) { - if (e) - delete c; - else - c->start(); - start_accept(); - } - - boost::asio::io_service s; - boost::asio::ip::tcp::acceptor a; - handler_allocator<256> allocator; +class acceptor { +private: + acceptor(const acceptor&) = delete; + acceptor& operator=(const acceptor&) = delete; + +public: + acceptor(boost::asio::io_service& service, + boost::asio::ip::tcp::acceptor::native_handle_type native_acceptor) : + service_(service), acceptor_(service_, boost::asio::ip::tcp::v4(), native_acceptor) { + start_accept(); + } + + ~acceptor() = default; + +private: + void start_accept() { + connection *c = new connection(service_); + acceptor_.async_accept(c->socket(), make_custom_alloc_handler(allocator_, + std::bind(&acceptor::accept, this, c, boost::asio::placeholders::error))); + } + + void accept(connection *c, const boost::system::error_code& e) { + if (e) + delete c; + else + c->start(); + start_accept(); + } + + boost::asio::io_service& service_; + boost::asio::ip::tcp::acceptor acceptor_; + handler_allocator<256> allocator_; }; -void servicing(unsigned short port) { - acceptor accpt(port); - //io_service::work worker(accpt.s); - accpt.s.run(); +void servicing(boost::asio::ip::tcp::acceptor::native_handle_type native_acceptor) { + boost::asio::io_service service(BOOST_ASIO_CONCURRENCY_HINT_UNSAFE_IO) + acceptor a(native_acceptor); + service.run(); } int main(int args, char **argv) { - if(args < 2) { - cout << "Usage: " << argv[0] << " [threads = 24]" << endl; - return 1; - } - unsigned short port = atoi(argv[1]); - std::size_t threads = args > 2 ? atoi(argv[2]) : 24; - std::vector vthreads; - vthreads.reserve(threads) - boost::asio::io_service fake_s; - boost::asio::ip::tcp::acceptor fake_a(fake_s, boost::asio::ip::tcp::endpoint( - boost::asio::ip::tcp::v4(), port)); - //int defer_accept = 1; - //setsockopt(fake_a.native_handle(), IPPROTO_TCP, TCP_DEFER_ACCEPT, &defer_accept, sizeof(defer_accept)); - for (std::size_t i = 0; i < threads; ++i) - vthreads.emplace_back(std::bind(servicing, fake_a.native_handle())); - for (std::size_t i = 0; i < threads; ++i) - vthreads[i].join(); - return 0; + if(args < 2) { + cout << "Usage: " << argv[0] << " [threads = 24]" << endl; + return 1; + } + unsigned short port = atoi(argv[1]); + std::size_t threads = args > 2 ? atoi(argv[2]) : 24; + std::vector vthreads; + vthreads.reserve(threads) + boost::asio::io_service fake_s; + boost::asio::ip::tcp::acceptor fake_a(fake_s, boost::asio::ip::tcp::endpoint( + boost::asio::ip::tcp::v4(), port)); + auto native_handle = fake_a.native_handle(); + for (std::size_t i = 0; i < threads; ++i) + vthreads.emplace_back(std::bind(servicing, native_handle)); + for (std::size_t i = 0; i < threads; ++i) + vthreads[i].join(); + return 0; } From bf8db6e86f053c36defd5868bdc9cbdc16d269dd Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Tue, 19 Feb 2019 01:10:05 +0300 Subject: [PATCH 03/48] Fixed build errors and migrated Makefile to CMake --- c++-virtan/CMakeLists.txt | 27 +++++++++++++++++++++++++++ c++-virtan/Makefile | 16 ---------------- c++-virtan/server.cc | 24 ++++++++++++------------ 3 files changed, 39 insertions(+), 28 deletions(-) create mode 100644 c++-virtan/CMakeLists.txt delete mode 100644 c++-virtan/Makefile diff --git a/c++-virtan/CMakeLists.txt b/c++-virtan/CMakeLists.txt new file mode 100644 index 0000000..baf029e --- /dev/null +++ b/c++-virtan/CMakeLists.txt @@ -0,0 +1,27 @@ +# +# Copyright (c) 2015-2016 Marat Abrarov (abrarov@gmail.com) +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) +# + +cmake_minimum_required(VERSION 3.0) +project(server) + +set(Boost_USE_STATIC_LIBS ON) +set(Boost_USE_MULTITHREADED ON) +find_package(Boost REQUIRED system) +find_package(Threads REQUIRED) + +add_executable(${PROJECT_NAME} "server.cc") +target_compile_options(${PROJECT_NAME} + PRIVATE + "-Os" + "-march=native" + "-mtune=native" + "-std=c++17" + "-Wall") +target_link_libraries(${PROJECT_NAME} + PRIVATE + ${Boost_LIBRARIES} + Threads::Threads) diff --git a/c++-virtan/Makefile b/c++-virtan/Makefile deleted file mode 100644 index 721bbf7..0000000 --- a/c++-virtan/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -CXXFLAGS = -Os -march=native -mtune=native -std=c++17 -Wall -DEBUG_CXXFLAGS = -g -O0 -CPPFLAGS = -I/opt/local/include -LDFLAGS = -L/opt/local/lib -lboost_system-mt -pthread -SERVER = server - -default: $(SERVER) - -$(SERVER): $(addsuffix .cc, $(SERVER)) - -debug: - @CXXFLAGS="$(CXXFLAGS) $(DEBUG_CXXFLAGS)" make -e $(SERVER) - -.PHONY: clean -clean: - @rm -rf server server.dSYM diff --git a/c++-virtan/server.cc b/c++-virtan/server.cc index da94e6b..5ac58bb 100644 --- a/c++-virtan/server.cc +++ b/c++-virtan/server.cc @@ -32,7 +32,7 @@ class handler_allocator { handler_allocator& operator=(const handler_allocator&) = delete; public: - handler_allocator(const handler_allocator&) : in_use_(false) {} + handler_allocator() : in_use_(false) {} ~handler_allocator() = default; bool owns(void* p) const { @@ -83,7 +83,7 @@ class custom_alloc_handler { template friend void asio_handler_invoke(Function&& function, this_type* context) { - invoke(detail::forward(function), context->handler_); + invoke(std::forward(function), context->handler_); } template @@ -130,7 +130,7 @@ class connection { void start() { socket_.async_read_some(boost::asio::buffer(data_, max_length), make_custom_alloc_handler(allocator_, std::bind(&connection::read, this, - boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred))); + std::placeholders::_1, std::placeholders::_2))); } boost::asio::ip::tcp::socket& socket() { @@ -144,7 +144,7 @@ class connection { else boost::asio::async_write(socket_, boost::asio::buffer(data_, bytes_transferred), make_custom_alloc_handler(allocator_, std::bind(&connection::write, this, - boost::asio::placeholders::error)); + std::placeholders::_1))); } void write(const boost::system::error_code &e) { @@ -178,7 +178,7 @@ class acceptor { void start_accept() { connection *c = new connection(service_); acceptor_.async_accept(c->socket(), make_custom_alloc_handler(allocator_, - std::bind(&acceptor::accept, this, c, boost::asio::placeholders::error))); + std::bind(&acceptor::accept, this, c, std::placeholders::_1))); } void accept(connection *c, const boost::system::error_code& e) { @@ -195,20 +195,20 @@ class acceptor { }; void servicing(boost::asio::ip::tcp::acceptor::native_handle_type native_acceptor) { - boost::asio::io_service service(BOOST_ASIO_CONCURRENCY_HINT_UNSAFE_IO) - acceptor a(native_acceptor); + boost::asio::io_service service(1); + acceptor a(service, native_acceptor); service.run(); } -int main(int args, char **argv) { +int main(int args, char** argv) { if(args < 2) { - cout << "Usage: " << argv[0] << " [threads = 24]" << endl; + std::cout << "Usage: " << argv[0] << " [threads = 24]" << std::endl; return 1; } - unsigned short port = atoi(argv[1]); - std::size_t threads = args > 2 ? atoi(argv[2]) : 24; + unsigned short port = std::atoi(argv[1]); + std::size_t threads = args > 2 ? std::atoi(argv[2]) : 24; std::vector vthreads; - vthreads.reserve(threads) + vthreads.reserve(threads); boost::asio::io_service fake_s; boost::asio::ip::tcp::acceptor fake_a(fake_s, boost::asio::ip::tcp::endpoint( boost::asio::ip::tcp::v4(), port)); From 13671533d664d505cfd44abb8009d0d15601b818 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Tue, 19 Feb 2019 01:23:18 +0300 Subject: [PATCH 04/48] Fixed formatting to make code looks more production like --- c++-virtan/server.cc | 58 ++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/c++-virtan/server.cc b/c++-virtan/server.cc index 5ac58bb..159196d 100644 --- a/c++-virtan/server.cc +++ b/c++-virtan/server.cc @@ -5,6 +5,7 @@ #include #include #include +#include #include template @@ -40,15 +41,17 @@ class handler_allocator { } void* allocate(std::size_t size) { - if (in_use_ || size > alloc_size) - return 0; + if (in_use_ || size > alloc_size) { + return 0; + } in_use_ = true; return std::addressof(storage_); } void deallocate(void* p) { - if (p) - in_use_ = false; + if (p) { + in_use_ = false; + } } private: @@ -69,16 +72,18 @@ class custom_alloc_handler { allocator_(std::addressof(allocator)), handler_(std::forward(handler)) {} friend void* asio_handler_allocate(std::size_t size, this_type* context) { - if (void* p = context->allocator_->allocate(size)) + if (void* p = context->allocator_->allocate(size)) { return p; + } return allocate(size, context->handler_); } friend void asio_handler_deallocate(void* pointer, std::size_t size, this_type* context) { - if (context->allocator_->owns(pointer)) + if (context->allocator_->owns(pointer)) { context->allocator_->deallocate(pointer); - else + } else { deallocate(pointer, size, context->handler_); + } } template @@ -139,19 +144,21 @@ class connection { private: void read(const boost::system::error_code& e, std::size_t bytes_transferred) { - if (e) + if (e) { delete this; - else + } else { boost::asio::async_write(socket_, boost::asio::buffer(data_, bytes_transferred), make_custom_alloc_handler(allocator_, std::bind(&connection::write, this, std::placeholders::_1))); + } } void write(const boost::system::error_code &e) { - if (e) + if (e) { delete this; - else + } else { start(); + } } handler_allocator<128> allocator_; @@ -182,10 +189,11 @@ class acceptor { } void accept(connection *c, const boost::system::error_code& e) { - if (e) + if (e) { delete c; - else + } else { c->start(); + } start_accept(); } @@ -194,28 +202,26 @@ class acceptor { handler_allocator<256> allocator_; }; -void servicing(boost::asio::ip::tcp::acceptor::native_handle_type native_acceptor) { - boost::asio::io_service service(1); - acceptor a(service, native_acceptor); - service.run(); -} - int main(int args, char** argv) { if(args < 2) { std::cout << "Usage: " << argv[0] << " [threads = 24]" << std::endl; return 1; } unsigned short port = std::atoi(argv[1]); - std::size_t threads = args > 2 ? std::atoi(argv[2]) : 24; - std::vector vthreads; - vthreads.reserve(threads); + std::size_t thread_num = args > 2 ? std::atoi(argv[2]) : 24; + std::vector threads; + threads.reserve(thread_num); boost::asio::io_service fake_s; boost::asio::ip::tcp::acceptor fake_a(fake_s, boost::asio::ip::tcp::endpoint( boost::asio::ip::tcp::v4(), port)); auto native_handle = fake_a.native_handle(); - for (std::size_t i = 0; i < threads; ++i) - vthreads.emplace_back(std::bind(servicing, native_handle)); - for (std::size_t i = 0; i < threads; ++i) - vthreads[i].join(); + for (std::size_t i = 0; i < thread_num; ++i) { + threads.emplace_back([native_handle]() { + boost::asio::io_service service(1); + acceptor a(service, native_handle); + service.run(); + }); + } + std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread::join)); return 0; } From 06b86436ae4203a04a7c9a00188405c6235eb62f Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Tue, 19 Feb 2019 03:41:17 +0300 Subject: [PATCH 05/48] Removed unused stuff from CMake project --- c++-virtan/CMakeLists.txt | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/c++-virtan/CMakeLists.txt b/c++-virtan/CMakeLists.txt index baf029e..8352f88 100644 --- a/c++-virtan/CMakeLists.txt +++ b/c++-virtan/CMakeLists.txt @@ -1,10 +1,3 @@ -# -# Copyright (c) 2015-2016 Marat Abrarov (abrarov@gmail.com) -# -# Distributed under the Boost Software License, Version 1.0. (See accompanying -# file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) -# - cmake_minimum_required(VERSION 3.0) project(server) @@ -16,9 +9,6 @@ find_package(Threads REQUIRED) add_executable(${PROJECT_NAME} "server.cc") target_compile_options(${PROJECT_NAME} PRIVATE - "-Os" - "-march=native" - "-mtune=native" "-std=c++17" "-Wall") target_link_libraries(${PROJECT_NAME} From efdc636a6bf10185cce8b322673fdf6d5b14c196 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Tue, 19 Feb 2019 12:50:23 +0300 Subject: [PATCH 06/48] Migrated test client to CMake --- client-c++-virtan/CMakeLists.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 client-c++-virtan/CMakeLists.txt diff --git a/client-c++-virtan/CMakeLists.txt b/client-c++-virtan/CMakeLists.txt new file mode 100644 index 0000000..b53ae4a --- /dev/null +++ b/client-c++-virtan/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.0) +project(client) + +set(Boost_USE_STATIC_LIBS ON) +set(Boost_USE_MULTITHREADED ON) +find_package(Boost REQUIRED thread system) +find_package(Threads REQUIRED) + +add_executable(${PROJECT_NAME} "server.cc") +target_compile_options(${PROJECT_NAME} + PRIVATE + "-std=c++17" + "-Wall") +target_link_libraries(${PROJECT_NAME} + PRIVATE + ${Boost_LIBRARIES} + Threads::Threads) From 8a982f8fe26b1513c29071dfe6ebe127d3e7c56e Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Tue, 19 Feb 2019 12:51:12 +0300 Subject: [PATCH 07/48] Migrated test client to CMake --- client-c++-virtan/Makefile | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 client-c++-virtan/Makefile diff --git a/client-c++-virtan/Makefile b/client-c++-virtan/Makefile deleted file mode 100644 index d27f9dc..0000000 --- a/client-c++-virtan/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -CXXFLAGS = -Os -march=native -mtune=native -std=c++0x -Wall -DEBUG_CXXFLAGS = -g -O0 -CPPFLAGS = -I/opt/local/include -LDFLAGS = -L/opt/local/lib -lboost_thread-mt -lboost_system-mt -pthread -CLIENT = client - -default: $(CLIENT) - -$(CLIENT): $(addsuffix .cc, $(CLIENT)) - -debug: - @CXXFLAGS="$(CXXFLAGS) $(DEBUG_CXXFLAGS)" make -e $(CLIENT) - -.PHONY: clean -clean: - @rm -rf client client.dSYM From ec0e84bbc3e4f3df85b9699562920e2a93432433 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Tue, 19 Feb 2019 12:52:02 +0300 Subject: [PATCH 08/48] Migrated test client to CMake --- client-c++-virtan/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client-c++-virtan/CMakeLists.txt b/client-c++-virtan/CMakeLists.txt index b53ae4a..2196237 100644 --- a/client-c++-virtan/CMakeLists.txt +++ b/client-c++-virtan/CMakeLists.txt @@ -6,7 +6,7 @@ set(Boost_USE_MULTITHREADED ON) find_package(Boost REQUIRED thread system) find_package(Threads REQUIRED) -add_executable(${PROJECT_NAME} "server.cc") +add_executable(${PROJECT_NAME} "client.cc") target_compile_options(${PROJECT_NAME} PRIVATE "-std=c++17" From 216f1ba8473cf5b5bc805143d0c811e02cf7f24b Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Tue, 19 Feb 2019 13:05:03 +0300 Subject: [PATCH 09/48] Fixed build errors of test client --- client-c++-virtan/CMakeLists.txt | 2 +- client-c++-virtan/client.cc | 148 +++++++++++++++---------------- 2 files changed, 73 insertions(+), 77 deletions(-) diff --git a/client-c++-virtan/CMakeLists.txt b/client-c++-virtan/CMakeLists.txt index 2196237..3b84193 100644 --- a/client-c++-virtan/CMakeLists.txt +++ b/client-c++-virtan/CMakeLists.txt @@ -3,7 +3,7 @@ project(client) set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_MULTITHREADED ON) -find_package(Boost REQUIRED thread system) +find_package(Boost REQUIRED date_time system) find_package(Threads REQUIRED) add_executable(${PROJECT_NAME} "client.cc") diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index 1bc3fe9..fb909d4 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -2,32 +2,28 @@ #include #include #include -#include +#include +#include #include -using namespace std; -using namespace boost; -using namespace boost::asio; -using namespace boost::asio::ip; - -const size_t clients_max = 10000; -const size_t bear_after_mcs = 1000; -const size_t send_each_mcs = 10000; -const size_t client_timeout_mcs = 30*1000*1000; - -size_t threads_; -thread *vthreads; -io_service *services; -size_t services_i = 0; -tcp::resolver::iterator connect_to; - -volatile size_t children = 0; -volatile size_t connection_active = 0; -volatile size_t connection_errors = 0; -volatile size_t exchange_errors = 0; -volatile size_t connection_summ_mcs = 0; -volatile size_t latency_summ_mcs = 0; -volatile size_t msgs = 0; +const std::size_t clients_max = 10000; +const std::size_t bear_after_mcs = 1000; +const std::size_t send_each_mcs = 10000; +const std::size_t client_timeout_mcs = 30*1000*1000; + +std::size_t threads_; +std::thread *vthreads; +boost::asio::io_service *services; +std::size_t services_i = 0; +boost::asio::ip::tcp::resolver::iterator connect_to; + +volatile std::size_t children = 0; +volatile std::size_t connection_active = 0; +volatile std::size_t connection_errors = 0; +volatile std::size_t exchange_errors = 0; +volatile std::size_t connection_summ_mcs = 0; +volatile std::size_t latency_summ_mcs = 0; +volatile std::size_t msgs = 0; class timer { public: @@ -35,10 +31,10 @@ class timer { void start() { gettimeofday(&start_time, NULL); } - size_t current() { + std::size_t current() { struct timeval now; gettimeofday(&now, NULL); - size_t diff = (((size_t) now.tv_sec) * 1000000) + now.tv_usec - (((size_t) start_time.tv_sec) * 1000000) - start_time.tv_usec; + std::size_t diff = (((std::size_t) now.tv_sec) * 1000000) + now.tv_usec - (((std::size_t) start_time.tv_sec) * 1000000) - start_time.tv_usec; return diff; } @@ -50,8 +46,8 @@ class timer { struct timeval start_time; }; -void servicing(size_t i) { - io_service::work worker(services[i]); +void servicing(std::size_t i) { + boost::asio::io_service::work worker(services[i]); services[i].run(); } @@ -60,25 +56,25 @@ struct connection_handler { service_id(__sync_fetch_and_add(&services_i, 1)), my_service(services[service_id % threads_]), s(my_service), - bear_tmr(my_service, posix_time::microseconds(bear_after_mcs)), + bear_tmr(my_service, boost::posix_time::microseconds(bear_after_mcs)), exch_tmr(my_service), errored(false) { if(service_id >= clients_max) return; - bear_tmr.async_wait(bind(&connection_handler::new_connection_handler, this, asio::placeholders::error)); + bear_tmr.async_wait(std::bind(&connection_handler::new_connection_handler, this, std::placeholders::_1)); connect_timer.reset(); __sync_fetch_and_add(&connection_active, 1); - async_connect(s, connect_to, bind(&connection_handler::connect, this, asio::placeholders::error)); + async_connect(s, connect_to, std::bind(&connection_handler::connect, this, std::placeholders::_1)); } - void new_connection_handler(const system::error_code &e) { - // cout << "new connection\n"; + void new_connection_handler(const boost::system::error_code &e) { + // std::cout << "new connection\n"; assert(!e); new connection_handler; } - void connect(const system::error_code &e) { - // cout << "connect_staff\n"; + void connect(const boost::system::error_code &e) { + // std::cout << "connect_staff\n"; __sync_fetch_and_add(&children, 1); __sync_fetch_and_sub(&connection_active, 1); if(e) { @@ -86,7 +82,7 @@ struct connection_handler { __sync_fetch_and_add(&connection_errors, 1); return; } - size_t spent_mcs = connect_timer.current(); + std::size_t spent_mcs = connect_timer.current(); if(spent_mcs > client_timeout_mcs) { errored = true; __sync_fetch_and_add(&connection_errors, 1); @@ -97,32 +93,32 @@ struct connection_handler { snprintf(send_buf, 32, "%d", rand()); send_buf[31] = '\n'; send_buf[32] = 0; - s.set_option(tcp::no_delay(true)); + s.set_option(boost::asio::ip::tcp::no_delay(true)); //typedef boost::asio::detail::socket_option::integer snd_buf; //typedef boost::asio::detail::socket_option::integer rcv_buf; //s.set_option(snd_buf(18350)); //s.set_option(rcv_buf(18350)); - read_staff(system::error_code()); - send_staff(system::error_code()); + read_staff(boost::system::error_code()); + send_staff(boost::system::error_code()); } - void exchange(const system::error_code &e) { - // cout << "exchange_staff\n"; + void exchange(const boost::system::error_code &e) { + // std::cout << "exchange_staff\n"; assert(!e); } - void send_staff(const system::error_code &e) { - // cout << "send_staff\n"; + void send_staff(const boost::system::error_code &e) { + // std::cout << "send_staff\n"; assert(!e); if(errored) return; - exch_tmr.expires_from_now(posix_time::microseconds(rand() % (2 * send_each_mcs))); - exch_tmr.async_wait(bind(&connection_handler::send_staff, this, asio::placeholders::error)); + exch_tmr.expires_from_now(boost::posix_time::microseconds(rand() % (2 * send_each_mcs))); + exch_tmr.async_wait(std::bind(&connection_handler::send_staff, this, std::placeholders::_1)); tqueue.push(timer()); - async_write(s, mutable_buffers_1(send_buf, 32), bind(&connection_handler::stub, this, asio::placeholders::error)); + async_write(s, boost::asio::mutable_buffers_1(send_buf, 32), std::bind(&connection_handler::stub, this, std::placeholders::_1)); } - void stub(const system::error_code &e) { - // cout << "stub\n"; + void stub(const boost::system::error_code &e) { + // std::cout << "stub\n"; if(errored) return; if(e) { // cerr << "send error\n"; @@ -132,8 +128,8 @@ struct connection_handler { } } - void read_staff(const system::error_code &e) { - // cout << "read_staff\n"; + void read_staff(const boost::system::error_code &e) { + // std::cout << "read_staff\n"; if(errored) return; if(e) { errored = true; @@ -141,19 +137,19 @@ struct connection_handler { return; } memset(read_buf, 0, 33); - async_read(s, mutable_buffers_1(read_buf, 32), bind(&connection_handler::compare_staff, this, asio::placeholders::error, asio::placeholders::bytes_transferred)); + async_read(s, boost::asio::mutable_buffers_1(read_buf, 32), std::bind(&connection_handler::compare_staff, this, std::placeholders::_1, std::placeholders::_2)); } - void compare_staff(const system::error_code &e, size_t transferred) { - // cout << "compare_staff\n"; + void compare_staff(const boost::system::error_code &e, std::size_t transferred) { + // std::cout << "compare_staff\n"; if(errored) return; if(e || transferred != 32 || strncmp(send_buf, read_buf, 32)) { - //if(strncmp(send_buf, read_buf, 32)) cerr << transferred << " \"" << send_buf << "\" != \"" << read_buf << "\"" << endl; + //if(strncmp(send_buf, read_buf, 32)) cerr << transferred << " \"" << send_buf << "\" != \"" << read_buf << "\"" << std::endl; errored = true; __sync_fetch_and_add(&exchange_errors, 1); return; } - size_t spent_mcs = tqueue.front().current(); + std::size_t spent_mcs = tqueue.front().current(); tqueue.pop(); if(spent_mcs > client_timeout_mcs) { errored = true; @@ -165,45 +161,45 @@ struct connection_handler { read_staff(e); } - size_t service_id; - io_service &my_service; - tcp::socket s; - deadline_timer bear_tmr; - deadline_timer exch_tmr; + std::size_t service_id; + boost::asio::io_service &my_service; + boost::asio::ip::tcp::socket s; + boost::asio::deadline_timer bear_tmr; + boost::asio::deadline_timer exch_tmr; char send_buf[33]; char read_buf[33]; timer connect_timer; timer exchange_timer; bool errored; - queue tqueue; + std::queue tqueue; }; void print_stat(bool final = false) { - if(final) cout << "\nFinal statistics:\n"; - size_t conn_avg = connection_summ_mcs / max((size_t) children - connection_errors, (size_t) 1); - size_t lat_avg = latency_summ_mcs / max((size_t) msgs, (size_t) 1); - cout << "Chld: " << children << " ConnErr: " << (final ? connection_errors + connection_active : connection_errors) << " ExchErr: " << exchange_errors << " ConnAvg: " << (((double) conn_avg) / 1000) << "ms LatAvg: " << (((double) lat_avg) / 1000) << "ms Msgs: " << msgs << "\n"; + if(final) std::cout << "\nFinal statistics:\n"; + std::size_t conn_avg = connection_summ_mcs / std::max((std::size_t) children - connection_errors, (std::size_t) 1); + std::size_t lat_avg = latency_summ_mcs / std::max((std::size_t) msgs, (std::size_t) 1); + std::cout << "Chld: " << children << " ConnErr: " << (final ? connection_errors + connection_active : connection_errors) << " ExchErr: " << exchange_errors << " ConnAvg: " << (((double) conn_avg) / 1000) << "ms LatAvg: " << (((double) lat_avg) / 1000) << "ms Msgs: " << msgs << "\n"; } int main(int args, char **argv) { if(args < 2) { - cout << "Usage: " << argv[0] << " [port = 32000 [threads = 24]]" << endl; + std::cout << "Usage: " << argv[0] << " [port = 32000 [threads = 24]]" << std::endl; return 1; } - string host(argv[1]); - string port(args > 2 ? argv[2] : "32000"); - string threads(args > 3 ? argv[3] : "24"); + std::string host(argv[1]); + std::string port(args > 2 ? argv[2] : "32000"); + std::string threads(args > 3 ? argv[3] : "24"); threads_ = atoi(threads.c_str()); - vthreads = new thread[threads_]; - services = new io_service[threads_]; - for(size_t i = 0; i < threads_; ++i) vthreads[i] = thread(servicing, i); + vthreads = new std::thread[threads_]; + services = new boost::asio::io_service[threads_]; + for(std::size_t i = 0; i < threads_; ++i) vthreads[i] = std::thread(servicing, i); sleep(1); - cout << "Starting tests" << endl; - tcp::resolver resolver(services[0]); - tcp::resolver::query query(host, port); + std::cout << "Starting tests" << std::endl; + boost::asio::ip::tcp::resolver resolver(services[0]); + boost::asio::ip::tcp::resolver::query query(host, port); connect_to = resolver.resolve(query); new connection_handler; - for(size_t i = 0; i < 60; i += 5) { + for(std::size_t i = 0; i < 60; i += 5) { print_stat(); sleep(5); } From a5af6219be4ceb98b3cf7286ca125b0728d8380a Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Tue, 19 Feb 2019 14:08:03 +0300 Subject: [PATCH 10/48] Docker images for test client and c++-virtan server --- c++-virtan/Dockerfile | 20 ++++++++++++++++++++ client-c++-virtan/Dockerfile | 20 ++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 c++-virtan/Dockerfile create mode 100644 client-c++-virtan/Dockerfile diff --git a/c++-virtan/Dockerfile b/c++-virtan/Dockerfile new file mode 100644 index 0000000..dea9d29 --- /dev/null +++ b/c++-virtan/Dockerfile @@ -0,0 +1,20 @@ +FROM alpine:3.9 + +LABEL name="abrarov/cpp-virtan" + +ADD ["CMakeLists.txt", "/tmp/cpp-virtan/"] +ADD ["server.cc", "/tmp/cpp-virtan/"] + +RUN apk add --no-cache libstdc++ &&\ + apk add --no-cache --virtual build-dependencies g++ make cmake boost-dev boost-static libstdc++ &&\ + cd /tmp/cpp-virtan &&\ + mkdir build && cd build &&\ + cmake -D CMAKE_SKIP_BUILD_RPATH=ON -D CMAKE_BUILD_TYPE=Release .. &&\ + cmake --build . &&\ + mkdir -p /opt/cpp-virtan &&\ + mv -f client /opt/cpp-virtan/client &&\ + cd /tmp &&\ + rm -rf cpp-virtan &&\ + apk del build-dependencies + +ENTRYPOINT ["/opt/cpp-virtan/client"] diff --git a/client-c++-virtan/Dockerfile b/client-c++-virtan/Dockerfile new file mode 100644 index 0000000..6c4a176 --- /dev/null +++ b/client-c++-virtan/Dockerfile @@ -0,0 +1,20 @@ +FROM alpine:3.9 + +LABEL name="abrarov/client-cpp-virtan" + +ADD ["CMakeLists.txt", "/tmp/client-cpp-virtan/"] +ADD ["client.cc", "/tmp/client-cpp-virtan/"] + +RUN apk add --no-cache libstdc++ &&\ + apk add --no-cache --virtual build-dependencies g++ make cmake boost-dev boost-static libstdc++ &&\ + cd /tmp/client-cpp-virtan &&\ + mkdir build && cd build &&\ + cmake -D CMAKE_SKIP_BUILD_RPATH=ON -D CMAKE_BUILD_TYPE=Release .. &&\ + cmake --build . &&\ + mkdir -p /opt/client-cpp-virtan &&\ + mv -f client /opt/client-cpp-virtan/client &&\ + cd /tmp &&\ + rm -rf client-cpp-virtan &&\ + apk del build-dependencies + +ENTRYPOINT ["/opt/client-cpp-virtan/client"] From 7979097ae7345a12f4316f7a80435673c56ee9c4 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Tue, 19 Feb 2019 14:10:05 +0300 Subject: [PATCH 11/48] Docker images for test client and c++-virtan server --- c++-virtan/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/c++-virtan/Dockerfile b/c++-virtan/Dockerfile index dea9d29..fb4844b 100644 --- a/c++-virtan/Dockerfile +++ b/c++-virtan/Dockerfile @@ -12,9 +12,9 @@ RUN apk add --no-cache libstdc++ &&\ cmake -D CMAKE_SKIP_BUILD_RPATH=ON -D CMAKE_BUILD_TYPE=Release .. &&\ cmake --build . &&\ mkdir -p /opt/cpp-virtan &&\ - mv -f client /opt/cpp-virtan/client &&\ + mv -f server /opt/cpp-virtan/server &&\ cd /tmp &&\ rm -rf cpp-virtan &&\ apk del build-dependencies -ENTRYPOINT ["/opt/cpp-virtan/client"] +ENTRYPOINT ["/opt/cpp-virtan/server"] From 39ada51d5c819ef3a4e4372caa3bd7c9304e3a79 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Tue, 19 Feb 2019 21:38:13 +0300 Subject: [PATCH 12/48] Fixed CMake project for client-c++-virtan and c++-virtan, added .gitignore for CMake and CLion --- c++-virtan/.gitignore | 2 ++ c++-virtan/CMakeLists.txt | 16 ++++++++-------- client-c++-virtan/.gitignore | 2 ++ client-c++-virtan/CMakeLists.txt | 16 ++++++++-------- 4 files changed, 20 insertions(+), 16 deletions(-) create mode 100644 c++-virtan/.gitignore create mode 100644 client-c++-virtan/.gitignore diff --git a/c++-virtan/.gitignore b/c++-virtan/.gitignore new file mode 100644 index 0000000..f1c6a7c --- /dev/null +++ b/c++-virtan/.gitignore @@ -0,0 +1,2 @@ +/build/ +/.idea/ \ No newline at end of file diff --git a/c++-virtan/CMakeLists.txt b/c++-virtan/CMakeLists.txt index 8352f88..081e6e1 100644 --- a/c++-virtan/CMakeLists.txt +++ b/c++-virtan/CMakeLists.txt @@ -6,12 +6,12 @@ set(Boost_USE_MULTITHREADED ON) find_package(Boost REQUIRED system) find_package(Threads REQUIRED) +set(cxx_libraries ${Boost_LIBRARIES} Threads::Threads) +if(WIN32) + list(APPEND cxx_libraries "ws2_32" "mswsock") +endif() + add_executable(${PROJECT_NAME} "server.cc") -target_compile_options(${PROJECT_NAME} - PRIVATE - "-std=c++17" - "-Wall") -target_link_libraries(${PROJECT_NAME} - PRIVATE - ${Boost_LIBRARIES} - Threads::Threads) +target_compile_options(${PROJECT_NAME} PRIVATE "-std=c++17" "-Wall") +target_include_directories(${PROJECT_NAME} PRIVATE ${Boost_INCLUDE_DIRS}) +target_link_libraries(${PROJECT_NAME} PRIVATE ${cxx_libraries}) diff --git a/client-c++-virtan/.gitignore b/client-c++-virtan/.gitignore new file mode 100644 index 0000000..f1c6a7c --- /dev/null +++ b/client-c++-virtan/.gitignore @@ -0,0 +1,2 @@ +/build/ +/.idea/ \ No newline at end of file diff --git a/client-c++-virtan/CMakeLists.txt b/client-c++-virtan/CMakeLists.txt index 3b84193..e1eb0e9 100644 --- a/client-c++-virtan/CMakeLists.txt +++ b/client-c++-virtan/CMakeLists.txt @@ -6,12 +6,12 @@ set(Boost_USE_MULTITHREADED ON) find_package(Boost REQUIRED date_time system) find_package(Threads REQUIRED) +set(cxx_libraries ${Boost_LIBRARIES} Threads::Threads) +if(WIN32) + list(APPEND cxx_libraries "ws2_32" "mswsock") +endif() + add_executable(${PROJECT_NAME} "client.cc") -target_compile_options(${PROJECT_NAME} - PRIVATE - "-std=c++17" - "-Wall") -target_link_libraries(${PROJECT_NAME} - PRIVATE - ${Boost_LIBRARIES} - Threads::Threads) +target_compile_options(${PROJECT_NAME} PRIVATE "-std=c++17" "-Wall") +target_include_directories(${PROJECT_NAME} PRIVATE ${Boost_INCLUDE_DIRS}) +target_link_libraries(${PROJECT_NAME} PRIVATE ${cxx_libraries}) From ca0baf547c84bf00e819cdf8e8ef8ddb7413c28a Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Wed, 20 Feb 2019 00:22:37 +0300 Subject: [PATCH 13/48] Fixed building ot test client on Windows - switched from pthread sleep to C++ standard library --- client-c++-virtan/client.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index fb909d4..81d8897 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -4,6 +4,7 @@ #include #include #include +#include #include const std::size_t clients_max = 10000; @@ -193,7 +194,7 @@ int main(int args, char **argv) { vthreads = new std::thread[threads_]; services = new boost::asio::io_service[threads_]; for(std::size_t i = 0; i < threads_; ++i) vthreads[i] = std::thread(servicing, i); - sleep(1); + std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "Starting tests" << std::endl; boost::asio::ip::tcp::resolver resolver(services[0]); boost::asio::ip::tcp::resolver::query query(host, port); @@ -201,7 +202,7 @@ int main(int args, char **argv) { new connection_handler; for(std::size_t i = 0; i < 60; i += 5) { print_stat(); - sleep(5); + std::this_thread::sleep_for(std::chrono::seconds(5)); } print_stat(true); return 0; From 8c811f681591065e3f6c6f69dee020ff63df4d58 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Thu, 21 Feb 2019 04:29:02 +0300 Subject: [PATCH 14/48] More standard C++ in test client --- c++-virtan/server.cc | 5 +- client-c++-virtan/client.cc | 345 ++++++++++++++++++------------------ 2 files changed, 179 insertions(+), 171 deletions(-) diff --git a/c++-virtan/server.cc b/c++-virtan/server.cc index 159196d..f0c8b90 100644 --- a/c++-virtan/server.cc +++ b/c++-virtan/server.cc @@ -6,6 +6,7 @@ #include #include #include +#include #include template @@ -205,7 +206,7 @@ class acceptor { int main(int args, char** argv) { if(args < 2) { std::cout << "Usage: " << argv[0] << " [threads = 24]" << std::endl; - return 1; + return EXIT_FAILURE; } unsigned short port = std::atoi(argv[1]); std::size_t thread_num = args > 2 ? std::atoi(argv[2]) : 24; @@ -223,5 +224,5 @@ int main(int args, char** argv) { }); } std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread::join)); - return 0; + return EXIT_SUCCESS; } diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index 81d8897..f8fc0dd 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -5,6 +5,9 @@ #include #include #include +#include +#include +#include #include const std::size_t clients_max = 10000; @@ -12,198 +15,202 @@ const std::size_t bear_after_mcs = 1000; const std::size_t send_each_mcs = 10000; const std::size_t client_timeout_mcs = 30*1000*1000; -std::size_t threads_; -std::thread *vthreads; -boost::asio::io_service *services; -std::size_t services_i = 0; +std::size_t thread_num; + +std::vector> services; +std::atomic_size_t services_i(0); boost::asio::ip::tcp::resolver::iterator connect_to; -volatile std::size_t children = 0; -volatile std::size_t connection_active = 0; -volatile std::size_t connection_errors = 0; -volatile std::size_t exchange_errors = 0; -volatile std::size_t connection_summ_mcs = 0; -volatile std::size_t latency_summ_mcs = 0; -volatile std::size_t msgs = 0; +std::atomic_size_t children(0); +std::atomic_size_t connection_active(0); +std::atomic_size_t connection_errors(0); +std::atomic_size_t exchange_errors(0); +std::atomic_size_t connection_summ_mcs(0); +std::atomic_size_t latency_summ_mcs(0); +std::atomic_size_t msgs(0); class timer { - public: - timer() { reset(); } + public: + timer() { + reset(); + } - void start() { gettimeofday(&start_time, NULL); } + void start() { + gettimeofday(&start_time, NULL); + } - std::size_t current() { - struct timeval now; - gettimeofday(&now, NULL); - std::size_t diff = (((std::size_t) now.tv_sec) * 1000000) + now.tv_usec - (((std::size_t) start_time.tv_sec) * 1000000) - start_time.tv_usec; - return diff; - } + std::size_t current() { + struct timeval now; + gettimeofday(&now, NULL); + std::size_t diff = (((std::size_t) now.tv_sec) * 1000000) + now.tv_usec - (((std::size_t) start_time.tv_sec) * 1000000) - start_time.tv_usec; + return diff; + } - void reset() { - start(); - } + void reset() { + start(); + } - private: - struct timeval start_time; + private: + struct timeval start_time; }; -void servicing(std::size_t i) { - boost::asio::io_service::work worker(services[i]); - services[i].run(); -} - struct connection_handler { - connection_handler() : - service_id(__sync_fetch_and_add(&services_i, 1)), - my_service(services[service_id % threads_]), - s(my_service), - bear_tmr(my_service, boost::posix_time::microseconds(bear_after_mcs)), - exch_tmr(my_service), - errored(false) - { - if(service_id >= clients_max) return; - bear_tmr.async_wait(std::bind(&connection_handler::new_connection_handler, this, std::placeholders::_1)); - connect_timer.reset(); - __sync_fetch_and_add(&connection_active, 1); - async_connect(s, connect_to, std::bind(&connection_handler::connect, this, std::placeholders::_1)); + connection_handler() : + service_id(++services_i), + my_service(*services[service_id % thread_num]), + s(my_service), + bear_tmr(my_service, boost::posix_time::microseconds(bear_after_mcs)), + exch_tmr(my_service), + errored(false) + { + if (service_id >= clients_max) { + return; } - - void new_connection_handler(const boost::system::error_code &e) { - // std::cout << "new connection\n"; - assert(!e); - new connection_handler; + bear_tmr.async_wait(std::bind( + &connection_handler::new_connection_handler, this, std::placeholders::_1)); + connect_timer.reset(); + ++connection_active; + async_connect(s, connect_to, std::bind( + &connection_handler::connect, this, std::placeholders::_1)); + } + + void new_connection_handler(const boost::system::error_code& e) { + assert(!e); + new connection_handler; + } + + void connect(const boost::system::error_code& e) { + ++children; + --connection_active; + if (e) { + errored = true; + ++connection_errors; + return; } - - void connect(const boost::system::error_code &e) { - // std::cout << "connect_staff\n"; - __sync_fetch_and_add(&children, 1); - __sync_fetch_and_sub(&connection_active, 1); - if(e) { - errored = true; - __sync_fetch_and_add(&connection_errors, 1); - return; - } - std::size_t spent_mcs = connect_timer.current(); - if(spent_mcs > client_timeout_mcs) { - errored = true; - __sync_fetch_and_add(&connection_errors, 1); - return; - } - __sync_fetch_and_add(&connection_summ_mcs, spent_mcs); - memset(send_buf, ' ', 32); - snprintf(send_buf, 32, "%d", rand()); - send_buf[31] = '\n'; - send_buf[32] = 0; - s.set_option(boost::asio::ip::tcp::no_delay(true)); - //typedef boost::asio::detail::socket_option::integer snd_buf; - //typedef boost::asio::detail::socket_option::integer rcv_buf; - //s.set_option(snd_buf(18350)); - //s.set_option(rcv_buf(18350)); - read_staff(boost::system::error_code()); - send_staff(boost::system::error_code()); + std::size_t spent_mcs = connect_timer.current(); + if (spent_mcs > client_timeout_mcs) { + errored = true; + ++connection_errors; + return; } - - void exchange(const boost::system::error_code &e) { - // std::cout << "exchange_staff\n"; - assert(!e); + connection_summ_mcs += spent_mcs; + memset(send_buf, ' ', 32); + snprintf(send_buf, 32, "%d", rand()); + send_buf[31] = '\n'; + send_buf[32] = 0; + s.set_option(boost::asio::ip::tcp::no_delay(true)); + read_staff(boost::system::error_code()); + send_staff(boost::system::error_code()); + } + + void send_staff(const boost::system::error_code& e) { + assert(!e); + if (errored) { + return; } - - void send_staff(const boost::system::error_code &e) { - // std::cout << "send_staff\n"; - assert(!e); - if(errored) return; - exch_tmr.expires_from_now(boost::posix_time::microseconds(rand() % (2 * send_each_mcs))); - exch_tmr.async_wait(std::bind(&connection_handler::send_staff, this, std::placeholders::_1)); - tqueue.push(timer()); - async_write(s, boost::asio::mutable_buffers_1(send_buf, 32), std::bind(&connection_handler::stub, this, std::placeholders::_1)); + exch_tmr.expires_from_now(boost::posix_time::microseconds(rand() % (2 * send_each_mcs))); + exch_tmr.async_wait(std::bind( + &connection_handler::send_staff, this, std::placeholders::_1)); + tqueue.push(timer()); + async_write(s, boost::asio::mutable_buffers_1(send_buf, 32), std::bind( + &connection_handler::stub, this, std::placeholders::_1)); + } + + void stub(const boost::system::error_code& e) { + if (errored) { + return; } - - void stub(const boost::system::error_code &e) { - // std::cout << "stub\n"; - if(errored) return; - if(e) { - // cerr << "send error\n"; - errored = true; - __sync_fetch_and_add(&exchange_errors, 1); - return; - } + if (e) { + errored = true; + ++exchange_errors; + return; } + } - void read_staff(const boost::system::error_code &e) { - // std::cout << "read_staff\n"; - if(errored) return; - if(e) { - errored = true; - __sync_fetch_and_add(&exchange_errors, 1); - return; - } - memset(read_buf, 0, 33); - async_read(s, boost::asio::mutable_buffers_1(read_buf, 32), std::bind(&connection_handler::compare_staff, this, std::placeholders::_1, std::placeholders::_2)); + void read_staff(const boost::system::error_code& e) { + if (errored) { + return; } - - void compare_staff(const boost::system::error_code &e, std::size_t transferred) { - // std::cout << "compare_staff\n"; - if(errored) return; - if(e || transferred != 32 || strncmp(send_buf, read_buf, 32)) { - //if(strncmp(send_buf, read_buf, 32)) cerr << transferred << " \"" << send_buf << "\" != \"" << read_buf << "\"" << std::endl; - errored = true; - __sync_fetch_and_add(&exchange_errors, 1); - return; - } - std::size_t spent_mcs = tqueue.front().current(); - tqueue.pop(); - if(spent_mcs > client_timeout_mcs) { - errored = true; - __sync_fetch_and_add(&exchange_errors, 1); - return; - } - __sync_fetch_and_add(&msgs, 1); - __sync_fetch_and_add(&latency_summ_mcs, spent_mcs); - read_staff(e); + if (e) { + errored = true; + ++exchange_errors; + return; } - - std::size_t service_id; - boost::asio::io_service &my_service; - boost::asio::ip::tcp::socket s; - boost::asio::deadline_timer bear_tmr; - boost::asio::deadline_timer exch_tmr; - char send_buf[33]; - char read_buf[33]; - timer connect_timer; - timer exchange_timer; - bool errored; - std::queue tqueue; + memset(read_buf, 0, 33); + async_read(s, boost::asio::mutable_buffers_1(read_buf, 32), std::bind( + &connection_handler::compare_staff, this, std::placeholders::_1, std::placeholders::_2)); + } + + void compare_staff(const boost::system::error_code& e, std::size_t transferred) { + if (errored) { + return; + } + if (e || transferred != 32 || strncmp(send_buf, read_buf, 32)) { + errored = true; + ++exchange_errors; + return; + } + std::size_t spent_mcs = tqueue.front().current(); + tqueue.pop(); + if (spent_mcs > client_timeout_mcs) { + errored = true; + ++exchange_errors; + return; + } + ++msgs; + latency_summ_mcs += spent_mcs; + read_staff(e); + } + + std::size_t service_id; + boost::asio::io_service& my_service; + boost::asio::ip::tcp::socket s; + boost::asio::deadline_timer bear_tmr; + boost::asio::deadline_timer exch_tmr; + char send_buf[33]; + char read_buf[33]; + timer connect_timer; + bool errored; + std::queue tqueue; }; void print_stat(bool final = false) { - if(final) std::cout << "\nFinal statistics:\n"; - std::size_t conn_avg = connection_summ_mcs / std::max((std::size_t) children - connection_errors, (std::size_t) 1); - std::size_t lat_avg = latency_summ_mcs / std::max((std::size_t) msgs, (std::size_t) 1); - std::cout << "Chld: " << children << " ConnErr: " << (final ? connection_errors + connection_active : connection_errors) << " ExchErr: " << exchange_errors << " ConnAvg: " << (((double) conn_avg) / 1000) << "ms LatAvg: " << (((double) lat_avg) / 1000) << "ms Msgs: " << msgs << "\n"; + if (final) { + std::cout << "\nFinal statistics:\n"; + } + std::size_t conn_avg = connection_summ_mcs / std::max((std::size_t) children - connection_errors, (std::size_t) 1); + std::size_t lat_avg = latency_summ_mcs / std::max((std::size_t) msgs, (std::size_t) 1); + std::cout << "Chld: " << children << " ConnErr: " << (final ? connection_errors + connection_active : static_cast(connection_errors)) << " ExchErr: " << exchange_errors << " ConnAvg: " << (((double) conn_avg) / 1000) << "ms LatAvg: " << (((double) lat_avg) / 1000) << "ms Msgs: " << msgs << "\n"; } -int main(int args, char **argv) { - if(args < 2) { - std::cout << "Usage: " << argv[0] << " [port = 32000 [threads = 24]]" << std::endl; - return 1; - } - std::string host(argv[1]); - std::string port(args > 2 ? argv[2] : "32000"); - std::string threads(args > 3 ? argv[3] : "24"); - threads_ = atoi(threads.c_str()); - vthreads = new std::thread[threads_]; - services = new boost::asio::io_service[threads_]; - for(std::size_t i = 0; i < threads_; ++i) vthreads[i] = std::thread(servicing, i); - std::this_thread::sleep_for(std::chrono::seconds(1)); - std::cout << "Starting tests" << std::endl; - boost::asio::ip::tcp::resolver resolver(services[0]); - boost::asio::ip::tcp::resolver::query query(host, port); - connect_to = resolver.resolve(query); - new connection_handler; - for(std::size_t i = 0; i < 60; i += 5) { - print_stat(); - std::this_thread::sleep_for(std::chrono::seconds(5)); - } - print_stat(true); - return 0; +int main(int args, char** argv) { + if (args < 2) { + std::cout << "Usage: " << argv[0] << " [port = 32000 [threads = 24]]" << std::endl; + return EXIT_FAILURE; + } + std::string host = argv[1]; + std::string port = args > 2 ? argv[2] : "32000"; + thread_num = args > 3 ? std::atoi(argv[3]) : 24; + std::vector threads; + threads.reserve(thread_num); + services.reserve(thread_num); + for(std::size_t i = 0; i < thread_num; ++i) { + services.emplace_back(std::make_unique(1)); + threads.emplace_back([i] { + boost::asio::io_service::work worker(*services[i]); + services[i]->run(); + }); + } + std::this_thread::sleep_for(std::chrono::seconds(1)); + std::cout << "Starting tests" << std::endl; + boost::asio::ip::tcp::resolver resolver(*services[0]); + boost::asio::ip::tcp::resolver::query query(host, port); + connect_to = resolver.resolve(query); + new connection_handler; + for (std::size_t i = 0; i < 60; i += 5) { + print_stat(); + std::this_thread::sleep_for(std::chrono::seconds(5)); + } + print_stat(true); + return EXIT_SUCCESS; } From 49334b945087c7c4a3d9e9afb82eb77ff88a6197 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Thu, 21 Feb 2019 04:55:07 +0300 Subject: [PATCH 15/48] More standard C++ in test client and c++-virtan --- c++-virtan/server.cc | 163 +++++++++++++++++++++++++----------- client-c++-virtan/client.cc | 12 ++- 2 files changed, 120 insertions(+), 55 deletions(-) diff --git a/c++-virtan/server.cc b/c++-virtan/server.cc index f0c8b90..5ac4366 100644 --- a/c++-virtan/server.cc +++ b/c++-virtan/server.cc @@ -9,49 +9,64 @@ #include #include +namespace { + template -void* allocate(std::size_t size, Context& context) { +void* allocate(std::size_t size, Context& context) +{ using namespace boost::asio; return asio_handler_allocate(size, std::addressof(context)); } template -void deallocate(void* pointer, std::size_t size, Context& context) { +void deallocate(void* pointer, std::size_t size, Context& context) +{ using namespace boost::asio; asio_handler_deallocate(pointer, size, std::addressof(context)); } template -void invoke(Function&& function, Context& context) { +void invoke(Function&& function, Context& context) +{ using namespace boost::asio; - asio_handler_invoke(std::forward(function), std::addressof(context)); + asio_handler_invoke(std::forward(function), + std::addressof(context)); } template -class handler_allocator { +class handler_allocator +{ private: handler_allocator(const handler_allocator&) = delete; + handler_allocator& operator=(const handler_allocator&) = delete; public: - handler_allocator() : in_use_(false) {} + handler_allocator() : in_use_(false) + {} + ~handler_allocator() = default; - bool owns(void* p) const { + bool owns(void* p) const + { return std::addressof(storage_) == p; } - void* allocate(std::size_t size) { - if (in_use_ || size > alloc_size) { - return 0; + void* allocate(std::size_t size) + { + if (in_use_ || size > alloc_size) + { + return 0; } in_use_ = true; return std::addressof(storage_); } - void deallocate(void* p) { - if (p) { - in_use_ = false; + void deallocate(void* p) + { + if (p) + { + in_use_ = false; } } @@ -61,7 +76,8 @@ class handler_allocator { }; template -class custom_alloc_handler { +class custom_alloc_handler +{ private: typedef custom_alloc_handler this_type; @@ -70,45 +86,58 @@ class custom_alloc_handler { template custom_alloc_handler(Allocator& allocator, H&& handler): - allocator_(std::addressof(allocator)), handler_(std::forward(handler)) {} + allocator_(std::addressof(allocator)), handler_(std::forward(handler)) + {} - friend void* asio_handler_allocate(std::size_t size, this_type* context) { - if (void* p = context->allocator_->allocate(size)) { + friend void* asio_handler_allocate(std::size_t size, this_type* context) + { + if (void* p = context->allocator_->allocate(size)) + { return p; } return allocate(size, context->handler_); } - friend void asio_handler_deallocate(void* pointer, std::size_t size, this_type* context) { - if (context->allocator_->owns(pointer)) { + friend void + asio_handler_deallocate(void* pointer, std::size_t size, this_type* context) + { + if (context->allocator_->owns(pointer)) + { context->allocator_->deallocate(pointer); - } else { + } + else + { deallocate(pointer, size, context->handler_); } } template - friend void asio_handler_invoke(Function&& function, this_type* context) { + friend void asio_handler_invoke(Function&& function, this_type* context) + { invoke(std::forward(function), context->handler_); } template - friend void asio_handler_invoke(Function& function, this_type* context) { + friend void asio_handler_invoke(Function& function, this_type* context) + { invoke(function, context->handler_); } template - friend void asio_handler_invoke(const Function& function, this_type* context) { + friend void asio_handler_invoke(const Function& function, this_type* context) + { invoke(function, context->handler_); } template - void operator()(Arg&&... arg) { + void operator()(Arg&& ... arg) + { handler_(std::forward(arg)...); } template - void operator()(Arg&&... arg) const { + void operator()(Arg&& ... arg) const + { handler_(std::forward(arg)...); } @@ -119,80 +148,110 @@ class custom_alloc_handler { template inline custom_alloc_handler::type> -make_custom_alloc_handler(Allocator& allocator, Handler&& handler) { +make_custom_alloc_handler(Allocator& allocator, Handler&& handler) +{ typedef typename std::decay::type handler_type; - return custom_alloc_handler(allocator, std::forward(handler)); + return custom_alloc_handler(allocator, + std::forward(handler)); } -class connection { +class connection +{ private: connection(const connection&) = delete; + connection& operator=(const connection&) = delete; public: - explicit connection(boost::asio::io_service& service) : socket_(service) {} + explicit connection(boost::asio::io_service& service) : socket_(service) + {} + ~connection() = default; - void start() { + void start() + { socket_.async_read_some(boost::asio::buffer(data_, max_length), - make_custom_alloc_handler(allocator_, std::bind(&connection::read, this, - std::placeholders::_1, std::placeholders::_2))); + make_custom_alloc_handler(allocator_, + std::bind(&connection::read, this, std::placeholders::_1, + std::placeholders::_2))); } - boost::asio::ip::tcp::socket& socket() { + boost::asio::ip::tcp::socket& socket() + { return socket_; } private: - void read(const boost::system::error_code& e, std::size_t bytes_transferred) { - if (e) { + void read(const boost::system::error_code& e, std::size_t bytes_transferred) + { + if (e) + { delete this; - } else { - boost::asio::async_write(socket_, boost::asio::buffer(data_, bytes_transferred), - make_custom_alloc_handler(allocator_, std::bind(&connection::write, this, - std::placeholders::_1))); + } + else + { + boost::asio::async_write(socket_, + boost::asio::buffer(data_, bytes_transferred), + make_custom_alloc_handler(allocator_, + std::bind(&connection::write, this, std::placeholders::_1))); } } - void write(const boost::system::error_code &e) { - if (e) { + void write(const boost::system::error_code& e) + { + if (e) + { delete this; - } else { + } + else + { start(); } } handler_allocator<128> allocator_; - enum { max_length = 4096 }; + enum + { + max_length = 4096 + }; char data_[max_length]; boost::asio::ip::tcp::socket socket_; }; -class acceptor { +class acceptor +{ private: acceptor(const acceptor&) = delete; + acceptor& operator=(const acceptor&) = delete; public: acceptor(boost::asio::io_service& service, - boost::asio::ip::tcp::acceptor::native_handle_type native_acceptor) : - service_(service), acceptor_(service_, boost::asio::ip::tcp::v4(), native_acceptor) { + boost::asio::ip::tcp::acceptor::native_handle_type native_acceptor) + : service_(service), + acceptor_(service_, boost::asio::ip::tcp::v4(), native_acceptor) + { start_accept(); } ~acceptor() = default; private: - void start_accept() { - connection *c = new connection(service_); + void start_accept() + { + connection* c = new connection(service_); acceptor_.async_accept(c->socket(), make_custom_alloc_handler(allocator_, std::bind(&acceptor::accept, this, c, std::placeholders::_1))); } - void accept(connection *c, const boost::system::error_code& e) { - if (e) { + void accept(connection* c, const boost::system::error_code& e) + { + if (e) + { delete c; - } else { + } + else + { c->start(); } start_accept(); @@ -203,6 +262,8 @@ class acceptor { handler_allocator<256> allocator_; }; +} // anonymous namespace + int main(int args, char** argv) { if(args < 2) { std::cout << "Usage: " << argv[0] << " [threads = 24]" << std::endl; diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index f8fc0dd..5f25ece 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -10,6 +10,8 @@ #include #include +namespace { + const std::size_t clients_max = 10000; const std::size_t bear_after_mcs = 1000; const std::size_t send_each_mcs = 10000; @@ -70,7 +72,7 @@ struct connection_handler { &connection_handler::new_connection_handler, this, std::placeholders::_1)); connect_timer.reset(); ++connection_active; - async_connect(s, connect_to, std::bind( + boost::asio::async_connect(s, connect_to, std::bind( &connection_handler::connect, this, std::placeholders::_1)); } @@ -112,7 +114,7 @@ struct connection_handler { exch_tmr.async_wait(std::bind( &connection_handler::send_staff, this, std::placeholders::_1)); tqueue.push(timer()); - async_write(s, boost::asio::mutable_buffers_1(send_buf, 32), std::bind( + boost::asio::async_write(s, boost::asio::mutable_buffers_1(send_buf, 32), std::bind( &connection_handler::stub, this, std::placeholders::_1)); } @@ -137,7 +139,7 @@ struct connection_handler { return; } memset(read_buf, 0, 33); - async_read(s, boost::asio::mutable_buffers_1(read_buf, 32), std::bind( + boost::asio::async_read(s, boost::asio::mutable_buffers_1(read_buf, 32), std::bind( &connection_handler::compare_staff, this, std::placeholders::_1, std::placeholders::_2)); } @@ -183,6 +185,8 @@ void print_stat(bool final = false) { std::cout << "Chld: " << children << " ConnErr: " << (final ? connection_errors + connection_active : static_cast(connection_errors)) << " ExchErr: " << exchange_errors << " ConnAvg: " << (((double) conn_avg) / 1000) << "ms LatAvg: " << (((double) lat_avg) / 1000) << "ms Msgs: " << msgs << "\n"; } +} // anonymous namespace + int main(int args, char** argv) { if (args < 2) { std::cout << "Usage: " << argv[0] << " [port = 32000 [threads = 24]]" << std::endl; @@ -190,7 +194,7 @@ int main(int args, char** argv) { } std::string host = argv[1]; std::string port = args > 2 ? argv[2] : "32000"; - thread_num = args > 3 ? std::atoi(argv[3]) : 24; + thread_num = static_cast(args > 3 ? std::atoi(argv[3]) : 24); std::vector threads; threads.reserve(thread_num); services.reserve(thread_num); From 8a2e9da8c8dbaad5067d1f67317a045737fff334 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Thu, 21 Feb 2019 11:01:11 +0300 Subject: [PATCH 16/48] std::steady::clock in test client --- client-c++-virtan/client.cc | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index 5f25ece..5153ad9 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -32,28 +32,26 @@ std::atomic_size_t latency_summ_mcs(0); std::atomic_size_t msgs(0); class timer { - public: - timer() { - reset(); - } +public: + timer() { + reset(); + } - void start() { - gettimeofday(&start_time, NULL); - } + void start() { + start_time = std::chrono::steady_clock::now(); + } - std::size_t current() { - struct timeval now; - gettimeofday(&now, NULL); - std::size_t diff = (((std::size_t) now.tv_sec) * 1000000) + now.tv_usec - (((std::size_t) start_time.tv_sec) * 1000000) - start_time.tv_usec; - return diff; - } + std::size_t current() { + auto now = std::chrono::steady_clock::now(); + return std::chrono::duration_cast(now - start_time).count(); + } - void reset() { - start(); - } + void reset() { + start(); + } - private: - struct timeval start_time; +private: + std::chrono::steady_clock::time_point start_time; }; struct connection_handler { From e5aee092241f37e2bb961c4838354ea2eaee87bd Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Thu, 21 Feb 2019 11:01:11 +0300 Subject: [PATCH 17/48] std::steady::clock in test client --- client-c++-virtan/client.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index 5153ad9..7a71129 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -192,7 +192,7 @@ int main(int args, char** argv) { } std::string host = argv[1]; std::string port = args > 2 ? argv[2] : "32000"; - thread_num = static_cast(args > 3 ? std::atoi(argv[3]) : 24); + thread_num = args > 3 ? std::atoi(argv[3]) : 24; std::vector threads; threads.reserve(thread_num); services.reserve(thread_num); From f35684ecbb529fa1d6efd64f3a2012bd1638062b Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Thu, 21 Feb 2019 11:15:18 +0300 Subject: [PATCH 18/48] Fixed stop of test client --- client-c++-virtan/client.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index 7a71129..fa355b7 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -8,6 +8,7 @@ #include #include #include +#include #include namespace { @@ -213,6 +214,10 @@ int main(int args, char** argv) { print_stat(); std::this_thread::sleep_for(std::chrono::seconds(5)); } + std::for_each(services.begin(), services.end(), [](std::unique_ptr& s) { + s->stop(); + }); + std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread::join)); print_stat(true); return EXIT_SUCCESS; } From 001625dc21d85ae66862ffd9ed1bbab46aa2c78d Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Thu, 21 Feb 2019 11:30:00 +0300 Subject: [PATCH 19/48] Reverted changes in formatting --- c++-virtan/server.cc | 157 ++++++++++++++----------------------------- 1 file changed, 50 insertions(+), 107 deletions(-) diff --git a/c++-virtan/server.cc b/c++-virtan/server.cc index 5ac4366..4097f92 100644 --- a/c++-virtan/server.cc +++ b/c++-virtan/server.cc @@ -12,60 +12,47 @@ namespace { template -void* allocate(std::size_t size, Context& context) -{ +void* allocate(std::size_t size, Context& context) { using namespace boost::asio; return asio_handler_allocate(size, std::addressof(context)); } template -void deallocate(void* pointer, std::size_t size, Context& context) -{ +void deallocate(void* pointer, std::size_t size, Context& context) { using namespace boost::asio; asio_handler_deallocate(pointer, size, std::addressof(context)); } template -void invoke(Function&& function, Context& context) -{ +void invoke(Function&& function, Context& context) { using namespace boost::asio; - asio_handler_invoke(std::forward(function), - std::addressof(context)); + asio_handler_invoke(std::forward(function), std::addressof(context)); } template -class handler_allocator -{ +class handler_allocator { private: handler_allocator(const handler_allocator&) = delete; - handler_allocator& operator=(const handler_allocator&) = delete; public: - handler_allocator() : in_use_(false) - {} - + handler_allocator() : in_use_(false) {} ~handler_allocator() = default; - bool owns(void* p) const - { + bool owns(void* p) const { return std::addressof(storage_) == p; } - void* allocate(std::size_t size) - { - if (in_use_ || size > alloc_size) - { + void* allocate(std::size_t size) { + if (in_use_ || size > alloc_size) { return 0; } in_use_ = true; return std::addressof(storage_); } - void deallocate(void* p) - { - if (p) - { + void deallocate(void* p) { + if (p) { in_use_ = false; } } @@ -76,8 +63,7 @@ class handler_allocator }; template -class custom_alloc_handler -{ +class custom_alloc_handler { private: typedef custom_alloc_handler this_type; @@ -86,58 +72,45 @@ class custom_alloc_handler template custom_alloc_handler(Allocator& allocator, H&& handler): - allocator_(std::addressof(allocator)), handler_(std::forward(handler)) - {} + allocator_(std::addressof(allocator)), handler_(std::forward(handler)) {} - friend void* asio_handler_allocate(std::size_t size, this_type* context) - { - if (void* p = context->allocator_->allocate(size)) - { + friend void* asio_handler_allocate(std::size_t size, this_type* context) { + if (void* p = context->allocator_->allocate(size)) { return p; } return allocate(size, context->handler_); } - friend void - asio_handler_deallocate(void* pointer, std::size_t size, this_type* context) - { - if (context->allocator_->owns(pointer)) - { + friend void asio_handler_deallocate(void* pointer, std::size_t size, this_type* context) { + if (context->allocator_->owns(pointer)) { context->allocator_->deallocate(pointer); - } - else - { + } else { deallocate(pointer, size, context->handler_); } } template - friend void asio_handler_invoke(Function&& function, this_type* context) - { + friend void asio_handler_invoke(Function&& function, this_type* context) { invoke(std::forward(function), context->handler_); } template - friend void asio_handler_invoke(Function& function, this_type* context) - { + friend void asio_handler_invoke(Function& function, this_type* context) { invoke(function, context->handler_); } template - friend void asio_handler_invoke(const Function& function, this_type* context) - { + friend void asio_handler_invoke(const Function& function, this_type* context) { invoke(function, context->handler_); } template - void operator()(Arg&& ... arg) - { + void operator()(Arg&&... arg) { handler_(std::forward(arg)...); } template - void operator()(Arg&& ... arg) const - { + void operator()(Arg&&... arg) const { handler_(std::forward(arg)...); } @@ -147,111 +120,81 @@ class custom_alloc_handler }; template -inline custom_alloc_handler::type> -make_custom_alloc_handler(Allocator& allocator, Handler&& handler) -{ +custom_alloc_handler::type> +make_custom_alloc_handler(Allocator& allocator, Handler&& handler) { typedef typename std::decay::type handler_type; - return custom_alloc_handler(allocator, - std::forward(handler)); + return custom_alloc_handler(allocator, std::forward(handler)); } -class connection -{ +class connection { private: connection(const connection&) = delete; - connection& operator=(const connection&) = delete; public: - explicit connection(boost::asio::io_service& service) : socket_(service) - {} - + explicit connection(boost::asio::io_service& service) : socket_(service) {} ~connection() = default; - void start() - { + void start() { socket_.async_read_some(boost::asio::buffer(data_, max_length), - make_custom_alloc_handler(allocator_, - std::bind(&connection::read, this, std::placeholders::_1, - std::placeholders::_2))); + make_custom_alloc_handler(allocator_, std::bind(&connection::read, this, + std::placeholders::_1, std::placeholders::_2))); } - boost::asio::ip::tcp::socket& socket() - { + boost::asio::ip::tcp::socket& socket() { return socket_; } private: - void read(const boost::system::error_code& e, std::size_t bytes_transferred) - { - if (e) - { + void read(const boost::system::error_code& e, std::size_t bytes_transferred) { + if (e) { delete this; - } - else - { - boost::asio::async_write(socket_, - boost::asio::buffer(data_, bytes_transferred), - make_custom_alloc_handler(allocator_, - std::bind(&connection::write, this, std::placeholders::_1))); + } else { + boost::asio::async_write(socket_, boost::asio::buffer(data_, bytes_transferred), + make_custom_alloc_handler(allocator_, std::bind(&connection::write, this, + std::placeholders::_1))); } } - void write(const boost::system::error_code& e) - { - if (e) - { + void write(const boost::system::error_code& e) { + if (e) { delete this; - } - else - { + } else { start(); } } handler_allocator<128> allocator_; - enum - { - max_length = 4096 - }; + enum { max_length = 4096 }; char data_[max_length]; boost::asio::ip::tcp::socket socket_; }; -class acceptor -{ +class acceptor { private: acceptor(const acceptor&) = delete; - acceptor& operator=(const acceptor&) = delete; public: acceptor(boost::asio::io_service& service, - boost::asio::ip::tcp::acceptor::native_handle_type native_acceptor) - : service_(service), - acceptor_(service_, boost::asio::ip::tcp::v4(), native_acceptor) - { + boost::asio::ip::tcp::acceptor::native_handle_type native_acceptor) : + service_(service), acceptor_(service_, boost::asio::ip::tcp::v4(), native_acceptor) { start_accept(); } ~acceptor() = default; private: - void start_accept() - { + void start_accept() { connection* c = new connection(service_); acceptor_.async_accept(c->socket(), make_custom_alloc_handler(allocator_, std::bind(&acceptor::accept, this, c, std::placeholders::_1))); } - void accept(connection* c, const boost::system::error_code& e) - { - if (e) - { + void accept(connection* c, const boost::system::error_code& e) { + if (e) { delete c; - } - else - { + } else { c->start(); } start_accept(); @@ -265,7 +208,7 @@ class acceptor } // anonymous namespace int main(int args, char** argv) { - if(args < 2) { + if (args < 2) { std::cout << "Usage: " << argv[0] << " [threads = 24]" << std::endl; return EXIT_FAILURE; } From b0b23f109feee93cf0456703be664e53291b0aa4 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Thu, 21 Feb 2019 13:31:17 +0300 Subject: [PATCH 20/48] Minor formatting of test client --- client-c++-virtan/client.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index fa355b7..be391f4 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -16,7 +16,7 @@ namespace { const std::size_t clients_max = 10000; const std::size_t bear_after_mcs = 1000; const std::size_t send_each_mcs = 10000; -const std::size_t client_timeout_mcs = 30*1000*1000; +const std::size_t client_timeout_mcs = 30 * 1000 * 1000; std::size_t thread_num; From 31baa24ea88f801e0b66da1a46924532dbea5d21 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Thu, 21 Feb 2019 13:58:09 +0300 Subject: [PATCH 21/48] Switched test client to std::shared_from_this in order to keep memory cleaned when stopping application --- client-c++-virtan/client.cc | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index be391f4..95e50cc 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -55,29 +55,32 @@ class timer { std::chrono::steady_clock::time_point start_time; }; -struct connection_handler { +class connection_handler : public std::enable_shared_from_this { +public: connection_handler() : service_id(++services_i), my_service(*services[service_id % thread_num]), s(my_service), bear_tmr(my_service, boost::posix_time::microseconds(bear_after_mcs)), exch_tmr(my_service), - errored(false) - { + errored(false) {} + + void start() { if (service_id >= clients_max) { return; } bear_tmr.async_wait(std::bind( - &connection_handler::new_connection_handler, this, std::placeholders::_1)); + &connection_handler::new_connection_handler, shared_from_this(), std::placeholders::_1)); connect_timer.reset(); ++connection_active; boost::asio::async_connect(s, connect_to, std::bind( - &connection_handler::connect, this, std::placeholders::_1)); + &connection_handler::connect, shared_from_this(), std::placeholders::_1)); } +private: void new_connection_handler(const boost::system::error_code& e) { assert(!e); - new connection_handler; + std::make_shared()->start(); } void connect(const boost::system::error_code& e) { @@ -111,10 +114,10 @@ struct connection_handler { } exch_tmr.expires_from_now(boost::posix_time::microseconds(rand() % (2 * send_each_mcs))); exch_tmr.async_wait(std::bind( - &connection_handler::send_staff, this, std::placeholders::_1)); + &connection_handler::send_staff, shared_from_this(), std::placeholders::_1)); tqueue.push(timer()); boost::asio::async_write(s, boost::asio::mutable_buffers_1(send_buf, 32), std::bind( - &connection_handler::stub, this, std::placeholders::_1)); + &connection_handler::stub, shared_from_this(), std::placeholders::_1)); } void stub(const boost::system::error_code& e) { @@ -139,7 +142,7 @@ struct connection_handler { } memset(read_buf, 0, 33); boost::asio::async_read(s, boost::asio::mutable_buffers_1(read_buf, 32), std::bind( - &connection_handler::compare_staff, this, std::placeholders::_1, std::placeholders::_2)); + &connection_handler::compare_staff, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); } void compare_staff(const boost::system::error_code& e, std::size_t transferred) { @@ -209,7 +212,7 @@ int main(int args, char** argv) { boost::asio::ip::tcp::resolver resolver(*services[0]); boost::asio::ip::tcp::resolver::query query(host, port); connect_to = resolver.resolve(query); - new connection_handler; + std::make_shared()->start(); for (std::size_t i = 0; i < 60; i += 5) { print_stat(); std::this_thread::sleep_for(std::chrono::seconds(5)); From 143ba8ba676ecd7c145954225a053db61f138522 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Sat, 23 Feb 2019 22:55:12 +0300 Subject: [PATCH 22/48] Replaced C rand with C++11 random device and distribution --- client-c++-virtan/client.cc | 116 +++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 54 deletions(-) diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index 95e50cc..847621c 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -39,12 +40,12 @@ class timer { } void start() { - start_time = std::chrono::steady_clock::now(); + start_time_ = std::chrono::steady_clock::now(); } - std::size_t current() { + std::size_t current() const { auto now = std::chrono::steady_clock::now(); - return std::chrono::duration_cast(now - start_time).count(); + return std::chrono::duration_cast(now - start_time_).count(); } void reset() { @@ -52,112 +53,115 @@ class timer { } private: - std::chrono::steady_clock::time_point start_time; + std::chrono::steady_clock::time_point start_time_; }; class connection_handler : public std::enable_shared_from_this { public: - connection_handler() : - service_id(++services_i), - my_service(*services[service_id % thread_num]), - s(my_service), - bear_tmr(my_service, boost::posix_time::microseconds(bear_after_mcs)), - exch_tmr(my_service), - errored(false) {} + explicit connection_handler(const std::mt19937& rand_engine) : + service_id_(++services_i), + my_service_(*services[service_id_ % thread_num]), + socket_(my_service_), + bear_timer_(my_service_, boost::posix_time::microseconds(bear_after_mcs)), + exch_timer_(my_service_), + errored_(false), + rand_engine_(rand_engine), + data_rand_(0, RAND_MAX), + exch_rand_(0, 2 * send_each_mcs) {} void start() { - if (service_id >= clients_max) { + if (service_id_ >= clients_max) { return; } - bear_tmr.async_wait(std::bind( + bear_timer_.async_wait(std::bind( &connection_handler::new_connection_handler, shared_from_this(), std::placeholders::_1)); - connect_timer.reset(); + connect_timer_.reset(); ++connection_active; - boost::asio::async_connect(s, connect_to, std::bind( + boost::asio::async_connect(socket_, connect_to, std::bind( &connection_handler::connect, shared_from_this(), std::placeholders::_1)); } private: void new_connection_handler(const boost::system::error_code& e) { assert(!e); - std::make_shared()->start(); + std::make_shared(rand_engine_)->start(); } void connect(const boost::system::error_code& e) { ++children; --connection_active; if (e) { - errored = true; + errored_ = true; ++connection_errors; return; } - std::size_t spent_mcs = connect_timer.current(); + std::size_t spent_mcs = connect_timer_.current(); if (spent_mcs > client_timeout_mcs) { - errored = true; + errored_ = true; ++connection_errors; return; } connection_summ_mcs += spent_mcs; - memset(send_buf, ' ', 32); - snprintf(send_buf, 32, "%d", rand()); - send_buf[31] = '\n'; - send_buf[32] = 0; - s.set_option(boost::asio::ip::tcp::no_delay(true)); + memset(send_buf_, ' ', 32); + snprintf(send_buf_, 32, "%d", data_rand_(rand_engine_)); + send_buf_[31] = '\n'; + send_buf_[32] = 0; + socket_.set_option(boost::asio::ip::tcp::no_delay(true)); read_staff(boost::system::error_code()); send_staff(boost::system::error_code()); } void send_staff(const boost::system::error_code& e) { assert(!e); - if (errored) { + if (errored_) { return; } - exch_tmr.expires_from_now(boost::posix_time::microseconds(rand() % (2 * send_each_mcs))); - exch_tmr.async_wait(std::bind( + exch_timer_.expires_from_now(boost::posix_time::microseconds(exch_rand_(rand_engine_) % (2 * send_each_mcs))); + exch_timer_.async_wait(std::bind( &connection_handler::send_staff, shared_from_this(), std::placeholders::_1)); - tqueue.push(timer()); - boost::asio::async_write(s, boost::asio::mutable_buffers_1(send_buf, 32), std::bind( + tqueue_.push(timer()); + boost::asio::async_write(socket_, boost::asio::mutable_buffers_1(send_buf_, 32), std::bind( &connection_handler::stub, shared_from_this(), std::placeholders::_1)); } void stub(const boost::system::error_code& e) { - if (errored) { + if (errored_) { return; } if (e) { - errored = true; + errored_ = true; ++exchange_errors; return; } } void read_staff(const boost::system::error_code& e) { - if (errored) { + if (errored_) { return; } if (e) { - errored = true; + errored_ = true; ++exchange_errors; return; } - memset(read_buf, 0, 33); - boost::asio::async_read(s, boost::asio::mutable_buffers_1(read_buf, 32), std::bind( + memset(read_buf_, 0, 33); + boost::asio::async_read(socket_, boost::asio::mutable_buffers_1(read_buf_, 32), std::bind( &connection_handler::compare_staff, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); } void compare_staff(const boost::system::error_code& e, std::size_t transferred) { - if (errored) { + if (errored_) { return; } - if (e || transferred != 32 || strncmp(send_buf, read_buf, 32)) { - errored = true; + if (e || transferred != 32 || strncmp(send_buf_, read_buf_, 32)) { + errored_ = true; ++exchange_errors; return; } - std::size_t spent_mcs = tqueue.front().current(); - tqueue.pop(); + std::size_t spent_mcs = tqueue_.front().current(); + tqueue_.pop(); if (spent_mcs > client_timeout_mcs) { - errored = true; + errored_ = true; ++exchange_errors; return; } @@ -166,24 +170,27 @@ class connection_handler : public std::enable_shared_from_this tqueue; + std::size_t service_id_; + boost::asio::io_service& my_service_; + boost::asio::ip::tcp::socket socket_; + boost::asio::deadline_timer bear_timer_; + boost::asio::deadline_timer exch_timer_; + char send_buf_[33]; + char read_buf_[33]; + timer connect_timer_; + bool errored_; + std::queue tqueue_; + std::mt19937 rand_engine_; + std::uniform_int_distribution data_rand_; + std::uniform_int_distribution exch_rand_; }; void print_stat(bool final = false) { if (final) { std::cout << "\nFinal statistics:\n"; } - std::size_t conn_avg = connection_summ_mcs / std::max((std::size_t) children - connection_errors, (std::size_t) 1); - std::size_t lat_avg = latency_summ_mcs / std::max((std::size_t) msgs, (std::size_t) 1); + std::size_t conn_avg = connection_summ_mcs / (std::max)((std::size_t) children - connection_errors, (std::size_t) 1); + std::size_t lat_avg = latency_summ_mcs / (std::max)((std::size_t) msgs, (std::size_t) 1); std::cout << "Chld: " << children << " ConnErr: " << (final ? connection_errors + connection_active : static_cast(connection_errors)) << " ExchErr: " << exchange_errors << " ConnAvg: " << (((double) conn_avg) / 1000) << "ms LatAvg: " << (((double) lat_avg) / 1000) << "ms Msgs: " << msgs << "\n"; } @@ -197,6 +204,7 @@ int main(int args, char** argv) { std::string host = argv[1]; std::string port = args > 2 ? argv[2] : "32000"; thread_num = args > 3 ? std::atoi(argv[3]) : 24; + std::mt19937 rand_engine; std::vector threads; threads.reserve(thread_num); services.reserve(thread_num); @@ -212,7 +220,7 @@ int main(int args, char** argv) { boost::asio::ip::tcp::resolver resolver(*services[0]); boost::asio::ip::tcp::resolver::query query(host, port); connect_to = resolver.resolve(query); - std::make_shared()->start(); + std::make_shared(rand_engine)->start(); for (std::size_t i = 0; i < 60; i += 5) { print_stat(); std::this_thread::sleep_for(std::chrono::seconds(5)); From f348d666545b3e386c648824de7dc01af3377152 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Sun, 24 Feb 2019 13:04:02 +0300 Subject: [PATCH 23/48] Replaced C rand with C++11 random device and distribution --- client-c++-virtan/client.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index 847621c..962c16f 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -203,8 +203,9 @@ int main(int args, char** argv) { } std::string host = argv[1]; std::string port = args > 2 ? argv[2] : "32000"; - thread_num = args > 3 ? std::atoi(argv[3]) : 24; - std::mt19937 rand_engine; + thread_num = static_cast(args > 3 ? std::atol(argv[3]) : 24); + std::random_device random_device; + std::mt19937 rand_engine(random_device()); std::vector threads; threads.reserve(thread_num); services.reserve(thread_num); From 5017c2647f7c8ae93229eee6072df54370c00bc7 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Sun, 24 Feb 2019 18:42:25 +0300 Subject: [PATCH 24/48] Replaced C rand with C++11 random device and distribution, replaced raw arrays used for buffers with std::array --- client-c++-virtan/client.cc | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index 962c16f..07f5de6 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -18,6 +18,7 @@ const std::size_t clients_max = 10000; const std::size_t bear_after_mcs = 1000; const std::size_t send_each_mcs = 10000; const std::size_t client_timeout_mcs = 30 * 1000 * 1000; +const std::size_t buffer_size = 32; std::size_t thread_num; @@ -102,10 +103,8 @@ class connection_handler : public std::enable_shared_from_this send_buf_; + std::array read_buf_; timer connect_timer_; bool errored_; std::queue tqueue_; @@ -203,7 +203,7 @@ int main(int args, char** argv) { } std::string host = argv[1]; std::string port = args > 2 ? argv[2] : "32000"; - thread_num = static_cast(args > 3 ? std::atol(argv[3]) : 24); + thread_num = static_cast(args > 3 ? std::atoi(argv[3]) : 24); std::random_device random_device; std::mt19937 rand_engine(random_device()); std::vector threads; From 2532047138755a76c0c826ea973b309879b1cada Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Sun, 24 Feb 2019 18:57:52 +0300 Subject: [PATCH 25/48] Fixed usage of boost::asio::async_write composed operation --- client-c++-virtan/client.cc | 48 ++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index 07f5de6..8df499d 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -66,9 +66,11 @@ class connection_handler : public std::enable_shared_from_this= clients_max) { @@ -106,24 +108,30 @@ class connection_handler : public std::enable_shared_from_this client_timeout_mcs) { errored_ = true; ++exchange_errors; @@ -167,7 +179,7 @@ class connection_handler : public std::enable_shared_from_this read_buf_; timer connect_timer_; bool errored_; - std::queue tqueue_; + bool send_in_progress_; + bool start_send_after_send_completes_; + std::queue timer_queue_; std::mt19937 rand_engine_; std::uniform_int_distribution data_rand_; - std::uniform_int_distribution exch_rand_; + std::uniform_int_distribution exchange_rand_; }; void print_stat(bool final = false) { From 633ecc41b91ef9898d4a64537e3aa0fab79baa9b Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Sun, 24 Feb 2019 20:16:02 +0300 Subject: [PATCH 26/48] Re-worked work with timer to support custom handler allocation in test client --- client-c++-virtan/client.cc | 92 ++++++++++++++++++++++++++----------- 1 file changed, 64 insertions(+), 28 deletions(-) diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index 8df499d..785570b 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -30,8 +30,8 @@ std::atomic_size_t children(0); std::atomic_size_t connection_active(0); std::atomic_size_t connection_errors(0); std::atomic_size_t exchange_errors(0); -std::atomic_size_t connection_summ_mcs(0); -std::atomic_size_t latency_summ_mcs(0); +std::atomic_size_t connection_sum_mcs(0); +std::atomic_size_t latency_sum_mcs(0); std::atomic_size_t msgs(0); class timer { @@ -67,10 +67,15 @@ class connection_handler : public std::enable_shared_from_this= clients_max) { @@ -102,28 +107,23 @@ class connection_handler : public std::enable_shared_from_this client_timeout_mcs) { errored_ = true; ++connection_errors; + stop_operations(); return; } - connection_summ_mcs += spent_mcs; - std::fill(send_buf_.begin(), send_buf_.end() - 1, data_rand_(rand_engine_)); - *(send_buf_.rbegin()) = '\n'; + connection_sum_mcs += spent_mcs; socket_.set_option(boost::asio::ip::tcp::no_delay(true)); - start_read(boost::system::error_code()); - start_send(boost::system::error_code()); + socket_.set_option(boost::asio::socket_base::linger(true, 0)); + start_read(); + start_send(); } - void start_send(const boost::system::error_code& e) { - assert(!e); - if (errored_) { - return; - } + void start_send() { if (send_in_progress_) { - start_send_after_send_completes_ = true; + start_send_when_send_completes_ = true; return; } exch_timer_.expires_from_now(boost::posix_time::microseconds(exchange_rand_(rand_engine_))); - exch_timer_.async_wait(std::bind( - &connection_handler::start_send, shared_from_this(), std::placeholders::_1)); + start_exchange_timer_wait(); timer_queue_.push(timer()); boost::asio::async_write(socket_, boost::asio::buffer(send_buf_), std::bind( &connection_handler::handle_send, shared_from_this(), std::placeholders::_1)); @@ -138,21 +138,47 @@ class connection_handler : public std::enable_shared_from_this client_timeout_mcs) { errored_ = true; ++exchange_errors; + stop_operations(); return; } ++msgs; - latency_summ_mcs += spent_mcs; - start_read(e); + latency_sum_mcs += spent_mcs; + start_read(); + } + + void stop_operations() { + boost::system::error_code ignored; + socket_.close(ignored); + exch_timer_.cancel(ignored); } std::size_t service_id_; @@ -192,7 +226,9 @@ class connection_handler : public std::enable_shared_from_this timer_queue_; std::mt19937 rand_engine_; std::uniform_int_distribution data_rand_; @@ -203,8 +239,8 @@ void print_stat(bool final = false) { if (final) { std::cout << "\nFinal statistics:\n"; } - std::size_t conn_avg = connection_summ_mcs / (std::max)((std::size_t) children - connection_errors, (std::size_t) 1); - std::size_t lat_avg = latency_summ_mcs / (std::max)((std::size_t) msgs, (std::size_t) 1); + std::size_t conn_avg = connection_sum_mcs / (std::max)((std::size_t) children - connection_errors, (std::size_t) 1); + std::size_t lat_avg = latency_sum_mcs / (std::max)((std::size_t) msgs, (std::size_t) 1); std::cout << "Chld: " << children << " ConnErr: " << (final ? connection_errors + connection_active : static_cast(connection_errors)) << " ExchErr: " << exchange_errors << " ConnAvg: " << (((double) conn_avg) / 1000) << "ms LatAvg: " << (((double) lat_avg) / 1000) << "ms Msgs: " << msgs << "\n"; } From 65c42242e3be5a39dcd51bc5e4df09a5ffb3e916 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Sun, 24 Feb 2019 20:50:34 +0300 Subject: [PATCH 27/48] Asio custom handler allocation in test client --- client-c++-virtan/client.cc | 208 +++++++++++++++++++++++++++++------- 1 file changed, 169 insertions(+), 39 deletions(-) diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index 785570b..4063139 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -14,6 +15,121 @@ namespace { +template +void* allocate(std::size_t size, Context& context) { + using namespace boost::asio; + return asio_handler_allocate(size, std::addressof(context)); +} + +template +void deallocate(void* pointer, std::size_t size, Context& context) { + using namespace boost::asio; + asio_handler_deallocate(pointer, size, std::addressof(context)); +} + +template +void invoke(Function&& function, Context& context) { + using namespace boost::asio; + asio_handler_invoke(std::forward(function), std::addressof(context)); +} + +template +class handler_allocator { +private: + handler_allocator(const handler_allocator&) = delete; + handler_allocator& operator=(const handler_allocator&) = delete; + +public: + handler_allocator() : in_use_(false) {} + ~handler_allocator() = default; + + bool owns(void* p) const { + return std::addressof(storage_) == p; + } + + void* allocate(std::size_t size) { + if (in_use_ || size > alloc_size) { + return 0; + } + in_use_ = true; + return std::addressof(storage_); + } + + void deallocate(void* p) { + if (p) { + in_use_ = false; + } + } + +private: + typename std::aligned_storage::type storage_; + bool in_use_; +}; + +template +class custom_alloc_handler { +private: + typedef custom_alloc_handler this_type; + +public: + typedef void result_type; + + template + custom_alloc_handler(Allocator& allocator, H&& handler): + allocator_(std::addressof(allocator)), handler_(std::forward(handler)) {} + + friend void* asio_handler_allocate(std::size_t size, this_type* context) { + if (void* p = context->allocator_->allocate(size)) { + return p; + } + return allocate(size, context->handler_); + } + + friend void asio_handler_deallocate(void* pointer, std::size_t size, this_type* context) { + if (context->allocator_->owns(pointer)) { + context->allocator_->deallocate(pointer); + } else { + deallocate(pointer, size, context->handler_); + } + } + + template + friend void asio_handler_invoke(Function&& function, this_type* context) { + invoke(std::forward(function), context->handler_); + } + + template + friend void asio_handler_invoke(Function& function, this_type* context) { + invoke(function, context->handler_); + } + + template + friend void asio_handler_invoke(const Function& function, this_type* context) { + invoke(function, context->handler_); + } + + template + void operator()(Arg&&... arg) { + handler_(std::forward(arg)...); + } + + template + void operator()(Arg&&... arg) const { + handler_(std::forward(arg)...); + } + +private: + Allocator* allocator_; + Handler handler_; +}; + +template +custom_alloc_handler::type> +make_custom_alloc_handler(Allocator& allocator, Handler&& handler) { + typedef typename std::decay::type handler_type; + return custom_alloc_handler(allocator, std::forward(handler)); +} + const std::size_t clients_max = 10000; const std::size_t bear_after_mcs = 1000; const std::size_t send_each_mcs = 10000; @@ -23,7 +139,7 @@ const std::size_t buffer_size = 32; std::size_t thread_num; std::vector> services; -std::atomic_size_t services_i(0); +std::atomic_size_t services_index(0); boost::asio::ip::tcp::resolver::iterator connect_to; std::atomic_size_t children(0); @@ -60,42 +176,45 @@ class timer { class connection_handler : public std::enable_shared_from_this { public: explicit connection_handler(const std::mt19937& rand_engine) : - service_id_(++services_i), + service_id_(++services_index), my_service_(*services[service_id_ % thread_num]), socket_(my_service_), bear_timer_(my_service_, boost::posix_time::microseconds(bear_after_mcs)), - exch_timer_(my_service_), + exchange_timer_(my_service_), errored_(false), - send_in_progress_(false), - start_send_when_send_completes_(false), + write_in_progress_(false), + start_write_when_write_completes_(false), exchange_timer_wait_in_progress_(false), start_exchange_timer_wait_when_wait_completes_(false), rand_engine_(rand_engine), data_rand_(0, RAND_MAX), exchange_rand_(0, send_each_mcs) { - std::fill(send_buf_.begin(), send_buf_.end() - 1, data_rand_(rand_engine_)); - *(send_buf_.rbegin()) = '\n'; + std::fill(write_buf_.begin(), write_buf_.end() - 1, data_rand_(rand_engine_)); + *(write_buf_.rbegin()) = '\n'; } void start() { if (service_id_ >= clients_max) { return; } - bear_timer_.async_wait(std::bind( - &connection_handler::new_connection_handler, shared_from_this(), std::placeholders::_1)); + bear_timer_.async_wait(make_custom_alloc_handler(bear_timer_allocator_, + std::bind(&connection_handler::start_new_connection, shared_from_this(), + std::placeholders::_1))); connect_timer_.reset(); ++connection_active; - boost::asio::async_connect(socket_, connect_to, std::bind( - &connection_handler::connect, shared_from_this(), std::placeholders::_1)); + boost::asio::async_connect(socket_, connect_to, make_custom_alloc_handler( + write_allocator_, std::bind(&connection_handler::handle_connect, + shared_from_this(), std::placeholders::_1))); } private: - void new_connection_handler(const boost::system::error_code& e) { - assert(!e); - std::make_shared(rand_engine_)->start(); + void start_new_connection(const boost::system::error_code& e) { + if (!e) { + std::make_shared(rand_engine_)->start(); + } } - void connect(const boost::system::error_code& e) { + void handle_connect(const boost::system::error_code& e) { ++children; --connection_active; if (e) { @@ -114,24 +233,27 @@ class connection_handler : public std::enable_shared_from_this send_buf_; + boost::asio::deadline_timer exchange_timer_; + std::array write_buf_; std::array read_buf_; timer connect_timer_; bool errored_; - bool send_in_progress_; - bool start_send_when_send_completes_; + bool write_in_progress_; + bool start_write_when_write_completes_; bool exchange_timer_wait_in_progress_; bool start_exchange_timer_wait_when_wait_completes_; std::queue timer_queue_; std::mt19937 rand_engine_; std::uniform_int_distribution data_rand_; std::uniform_int_distribution exchange_rand_; + handler_allocator<256> bear_timer_allocator_; + handler_allocator<256> write_allocator_; + handler_allocator<256> read_allocator_; + handler_allocator<256> exchange_timer_allocator_; }; void print_stat(bool final = false) { From 2108da2cd6888ff0fb5f7784e569d3b4e4be618a Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Sun, 24 Feb 2019 22:12:40 +0300 Subject: [PATCH 28/48] Re-worked stopping of test client to terminate stats immideately when time is out --- client-c++-virtan/client.cc | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index 4063139..9fe1671 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -142,6 +142,7 @@ std::vector> services; std::atomic_size_t services_index(0); boost::asio::ip::tcp::resolver::iterator connect_to; +std::atomic_bool stopped(false); std::atomic_size_t children(0); std::atomic_size_t connection_active(0); std::atomic_size_t connection_errors(0); @@ -198,7 +199,7 @@ class connection_handler : public std::enable_shared_from_this(rand_engine_)->start(); } } void handle_connect(const boost::system::error_code& e) { + if (stopped) { + return; + } ++children; --connection_active; if (e) { @@ -254,6 +261,9 @@ class connection_handler : public std::enable_shared_from_this& s) { s->stop(); }); From 09039711bd86b4f70de12b9cbf7f751228739b7b Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Sun, 24 Feb 2019 22:14:15 +0300 Subject: [PATCH 29/48] Changed output of test client to make it easier to find final stats line --- client-c++-virtan/client.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index 9fe1671..480b2a0 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -380,7 +380,7 @@ class connection_handler : public std::enable_shared_from_this Date: Sun, 24 Feb 2019 22:24:25 +0300 Subject: [PATCH 30/48] Redirected usage output of test client to stderr --- client-c++-virtan/client.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index 480b2a0..0e9f17b 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -391,7 +391,7 @@ void print_stat(bool final = false) { int main(int args, char** argv) { if (args < 2) { - std::cout << "Usage: " << argv[0] << " [port = 32000 [threads = 24]]" << std::endl; + std::cerr << "Usage: " << argv[0] << " [port = 32000 [threads = 24]]" << std::endl; return EXIT_FAILURE; } std::string host = argv[1]; From 2bf091dcdce9cf17f073cb199c9d5558793522a0 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Sun, 24 Feb 2019 23:40:33 +0300 Subject: [PATCH 31/48] Reverted final stats output and added test script to run test multiple times and choose the best result --- client-c++-virtan/client.cc | 2 +- client-c++-virtan/test.sh | 48 +++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 client-c++-virtan/test.sh diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index 0e9f17b..b52c8ec 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -380,7 +380,7 @@ class connection_handler : public std::enable_shared_from_this&2 + exit 1 +fi + +target_host="${1}" +target_port=${2} + +if [[ "$#" -ge 3 ]]; then + work_threads=${3} +else + work_threads=24 +fi + +if [[ "$#" -ge 4 ]]; then + run_iterations=${4} +else + run_iterations=16 +fi + +if [[ "$#" -ge 5 ]]; then + docker_image="${5}" +else + docker_image="abrarov/client-cpp-virtan" +fi + +max_msgs=0 +best_stats="" + +echo "Running container from ${docker_image} image ${run_iterations} times with command: ${target_host} ${target_port} ${work_threads}" + +i=0 +while [[ ${i} -lt ${run_iterations} ]] +do + #echo "Running iteration #${i}..." + stats=$(docker run --rm ${docker_image} ${target_host} ${target_port} ${work_threads} 2>&1 | grep -A 1 "Final statistics" | grep "Chld:") + #echo "Stats: ${stats}" + msgs=$(echo "${stats}" | sed -r 's/.*Msgs: ([0-9]+)/\1/') + if [[ "${msgs}" -gt ${max_msgs} ]]; then + max_msgs=${msgs} + best_stats="${stats}" + fi + i=$(( ${i} + 1 )) +done + +echo "Best stats: ${best_stats}" From 267285a8056b14c626066aaf6ea90dcf0edc43f6 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Mon, 25 Feb 2019 00:55:56 +0300 Subject: [PATCH 32/48] Pre-allocation of client sessions and Asio concurrency hint in test client --- client-c++-virtan/client.cc | 144 ++++++++++++++++++++++++------------ 1 file changed, 96 insertions(+), 48 deletions(-) diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index b52c8ec..8582317 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -12,6 +12,7 @@ #include #include #include +#include namespace { @@ -138,18 +139,21 @@ const std::size_t buffer_size = 32; std::size_t thread_num; +class session; + std::vector> services; -std::atomic_size_t services_index(0); +std::vector> sessions; +std::atomic_size_t session_index(0); boost::asio::ip::tcp::resolver::iterator connect_to; std::atomic_bool stopped(false); -std::atomic_size_t children(0); -std::atomic_size_t connection_active(0); -std::atomic_size_t connection_errors(0); -std::atomic_size_t exchange_errors(0); +std::atomic_size_t child_num(0); +std::atomic_size_t active_connection_num(0); +std::atomic_size_t connection_error_num(0); +std::atomic_size_t exchange_error_num(0); std::atomic_size_t connection_sum_mcs(0); std::atomic_size_t latency_sum_mcs(0); -std::atomic_size_t msgs(0); +std::atomic_size_t msg_num(0); class timer { public: @@ -163,7 +167,9 @@ class timer { std::size_t current() const { auto now = std::chrono::steady_clock::now(); - return std::chrono::duration_cast(now - start_time_).count(); + return boost::numeric_cast( + std::chrono::duration_cast( + now - start_time_).count()); } void reset() { @@ -174,13 +180,12 @@ class timer { std::chrono::steady_clock::time_point start_time_; }; -class connection_handler : public std::enable_shared_from_this { +class session : public std::enable_shared_from_this { public: - explicit connection_handler(const std::mt19937& rand_engine) : - service_id_(++services_index), - my_service_(*services[service_id_ % thread_num]), + session(boost::asio::io_service& service, const std::mt19937& rand_engine) : + my_service_(service), socket_(my_service_), - bear_timer_(my_service_, boost::posix_time::microseconds(bear_after_mcs)), + bear_timer_(my_service_), exchange_timer_(my_service_), errored_(false), write_in_progress_(false), @@ -194,45 +199,55 @@ class connection_handler : public std::enable_shared_from_this= clients_max) { - return; - } + bear_timer_.expires_from_now(boost::posix_time::microseconds(bear_after_mcs)); bear_timer_.async_wait(make_custom_alloc_handler(bear_timer_allocator_, - std::bind(&connection_handler::handle_bear_timer, shared_from_this(), - std::placeholders::_1))); + std::bind(&session::handle_bear_timer, shared_from_this(), std::placeholders::_1))); connect_timer_.reset(); - ++connection_active; + ++active_connection_num; boost::asio::async_connect(socket_, connect_to, make_custom_alloc_handler( - write_allocator_, std::bind(&connection_handler::handle_connect, - shared_from_this(), std::placeholders::_1))); + write_allocator_, std::bind(&session::handle_connect, shared_from_this(), + std::placeholders::_1))); } -private: void handle_bear_timer(const boost::system::error_code& e) { if (stopped) { return; } - if (!e) { - std::make_shared(rand_engine_)->start(); + if (e) { + return; + } + if (session_index >= clients_max) { + return; + } + std::size_t new_session_index = session_index.fetch_add(1); + if (new_session_index >= clients_max) { + return; } + sessions[new_session_index]->async_start(); } void handle_connect(const boost::system::error_code& e) { if (stopped) { return; } - ++children; - --connection_active; + ++child_num; + --active_connection_num; if (e) { errored_ = true; - ++connection_errors; + ++connection_error_num; return; } std::size_t spent_mcs = connect_timer_.current(); if (spent_mcs > client_timeout_mcs) { errored_ = true; - ++connection_errors; + ++connection_error_num; stop_operations(); return; } @@ -254,8 +269,7 @@ class connection_handler : public std::enable_shared_from_this client_timeout_mcs) { errored_ = true; - ++exchange_errors; + ++exchange_error_num; stop_operations(); return; } - ++msgs; + ++msg_num; latency_sum_mcs += spent_mcs; start_read(); } @@ -355,7 +367,6 @@ class connection_handler : public std::enable_shared_from_this(connection_errors)) << " ExchErr: " << exchange_errors << " ConnAvg: " << (((double) conn_avg) / 1000) << "ms LatAvg: " << (((double) lat_avg) / 1000) << "ms Msgs: " << msgs << "\n"; + std::size_t conn_avg = connection_sum_mcs / (std::max)( + child_num - connection_error_num, static_cast(1)); + std::size_t lat_avg = latency_sum_mcs / (std::max)( + static_cast(msg_num), static_cast(1)); + std::cout << "Chld: " << child_num + << " ConnErr: " + << (final ? static_cast(connection_error_num + active_connection_num) + : static_cast(connection_error_num)) + << " ExchErr: " << exchange_error_num + << " ConnAvg: " << static_cast(conn_avg) / 1000 + << "ms LatAvg: " << static_cast(lat_avg) / 1000 + << "ms Msgs: " << msg_num << "\n"; +} + +#if BOOST_VERSION >= 106600 + +typedef int io_context_concurrency_hint; + +io_context_concurrency_hint to_io_context_concurrency_hint(std::size_t hint) { + return 1 == hint ? BOOST_ASIO_CONCURRENCY_HINT_UNSAFE_IO + : boost::numeric_cast(hint); } +#else // BOOST_VERSION >= 106600 + +typedef std::size_t io_context_concurrency_hint; + +io_context_concurrency_hint to_io_context_concurrency_hint(std::size_t hint) { + return hint; +} + +#endif // BOOST_VERSION >= 106600 + } // anonymous namespace int main(int args, char** argv) { @@ -396,25 +435,32 @@ int main(int args, char** argv) { } std::string host = argv[1]; std::string port = args > 2 ? argv[2] : "32000"; - thread_num = static_cast(args > 3 ? std::atoi(argv[3]) : 24); + thread_num = boost::numeric_cast(args > 3 ? std::stoi(argv[3]) : 24); std::random_device random_device; std::mt19937 rand_engine(random_device()); std::vector threads; - threads.reserve(thread_num); services.reserve(thread_num); - for(std::size_t i = 0; i < thread_num; ++i) { - services.emplace_back(std::make_unique(1)); + for (std::size_t i = 0; i < thread_num; ++i) { + services.emplace_back(std::make_unique( + to_io_context_concurrency_hint(1))); + } + sessions.reserve(clients_max); + for (std::size_t i = 0; i < clients_max; ++i) { + sessions.emplace_back(std::make_shared( + *services[i % thread_num], rand_engine)); + } + threads.reserve(thread_num); + for (std::size_t i = 0; i < thread_num; ++i) { threads.emplace_back([i] { - boost::asio::io_service::work worker(*services[i]); + boost::asio::io_service::work work_guard(*services[i]); services[i]->run(); }); } - std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "Starting tests" << std::endl; boost::asio::ip::tcp::resolver resolver(*services[0]); boost::asio::ip::tcp::resolver::query query(host, port); connect_to = resolver.resolve(query); - std::make_shared(rand_engine)->start(); + sessions[session_index.fetch_add(1)]->async_start(); for (std::size_t i = 0; i < 60; i += 5) { print_stat(); std::this_thread::sleep_for(std::chrono::seconds(5)); @@ -424,6 +470,8 @@ int main(int args, char** argv) { s->stop(); }); std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread::join)); + threads.clear(); + sessions.clear(); print_stat(true); return EXIT_SUCCESS; } From e8c3c6a8a435483ab1b9385143e7c2b3812f07a7 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Mon, 25 Feb 2019 02:08:25 +0300 Subject: [PATCH 33/48] Asio concurrency hint and std::array for buffer in c++-virtan --- c++-virtan/server.cc | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/c++-virtan/server.cc b/c++-virtan/server.cc index 4097f92..2f9d4bb 100644 --- a/c++-virtan/server.cc +++ b/c++-virtan/server.cc @@ -1,6 +1,8 @@ #include #include #include +#include +#include #include #include #include @@ -8,6 +10,7 @@ #include #include #include +#include namespace { @@ -136,7 +139,7 @@ class connection { ~connection() = default; void start() { - socket_.async_read_some(boost::asio::buffer(data_, max_length), + socket_.async_read_some(boost::asio::buffer(data_), make_custom_alloc_handler(allocator_, std::bind(&connection::read, this, std::placeholders::_1, std::placeholders::_2))); } @@ -165,8 +168,7 @@ class connection { } handler_allocator<128> allocator_; - enum { max_length = 4096 }; - char data_[max_length]; + std::array data_; boost::asio::ip::tcp::socket socket_; }; @@ -177,7 +179,7 @@ class acceptor { public: acceptor(boost::asio::io_service& service, - boost::asio::ip::tcp::acceptor::native_handle_type native_acceptor) : + const boost::asio::ip::tcp::acceptor::native_handle_type& native_acceptor) : service_(service), acceptor_(service_, boost::asio::ip::tcp::v4(), native_acceptor) { start_accept(); } @@ -205,15 +207,34 @@ class acceptor { handler_allocator<256> allocator_; }; +#if BOOST_VERSION >= 106600 + +typedef int io_context_concurrency_hint; + +io_context_concurrency_hint to_io_context_concurrency_hint(std::size_t hint) { + return 1 == hint ? BOOST_ASIO_CONCURRENCY_HINT_UNSAFE_IO + : boost::numeric_cast(hint); +} + +#else // BOOST_VERSION >= 106600 + +typedef std::size_t io_context_concurrency_hint; + +io_context_concurrency_hint to_io_context_concurrency_hint(std::size_t hint) { + return hint; +} + +#endif // BOOST_VERSION >= 106600 + } // anonymous namespace int main(int args, char** argv) { if (args < 2) { - std::cout << "Usage: " << argv[0] << " [threads = 24]" << std::endl; + std::cerr << "Usage: " << argv[0] << " [threads = 24]" << std::endl; return EXIT_FAILURE; } - unsigned short port = std::atoi(argv[1]); - std::size_t thread_num = args > 2 ? std::atoi(argv[2]) : 24; + unsigned short port = boost::numeric_cast(std::stoi(argv[1])); + std::size_t thread_num = boost::numeric_cast(args > 2 ? std::stoi(argv[2]) : 24); std::vector threads; threads.reserve(thread_num); boost::asio::io_service fake_s; @@ -222,7 +243,7 @@ int main(int args, char** argv) { auto native_handle = fake_a.native_handle(); for (std::size_t i = 0; i < thread_num; ++i) { threads.emplace_back([native_handle]() { - boost::asio::io_service service(1); + boost::asio::io_service service(to_io_context_concurrency_hint(1)); acceptor a(service, native_handle); service.run(); }); From b161d1df9d9d068841be17b0565cf4861d3f8507 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Mon, 25 Feb 2019 02:08:59 +0300 Subject: [PATCH 34/48] Fixed missing include in test client --- client-c++-virtan/client.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index 8582317..10444b7 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include From 46a3176534c9caad9252d59a2abb0a5bd81e9e63 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Mon, 25 Feb 2019 19:16:57 +0300 Subject: [PATCH 35/48] Minor fix of error handling in test script --- client-c++-virtan/test.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/client-c++-virtan/test.sh b/client-c++-virtan/test.sh index 3026bba..295a79f 100644 --- a/client-c++-virtan/test.sh +++ b/client-c++-virtan/test.sh @@ -1,26 +1,28 @@ #!/bin/sh -if [[ "$#" -lt 2 ]]; then - echo "Usage: $0 host port [number-of-threads = 24 [number-of-iteration = 16 [client-docker-image]]]" >&2 +set -e + +if [[ "${#}" -lt 2 ]]; then + echo "Usage: ${0} host port [number-of-threads = 24 [number-of-iteration = 16 [client-docker-image]]]" >&2 exit 1 fi target_host="${1}" target_port=${2} -if [[ "$#" -ge 3 ]]; then +if [[ "${#}" -ge 3 ]]; then work_threads=${3} else work_threads=24 fi -if [[ "$#" -ge 4 ]]; then +if [[ "${#}" -ge 4 ]]; then run_iterations=${4} else run_iterations=16 fi -if [[ "$#" -ge 5 ]]; then +if [[ "${#}" -ge 5 ]]; then docker_image="${5}" else docker_image="abrarov/client-cpp-virtan" From 73c183297eeb4300f658fbb4c10bbfd4ef4998ec Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Sat, 2 Mar 2019 21:51:33 +0300 Subject: [PATCH 36/48] Reduced heap usage and global state in test client --- client-c++-virtan/client.cc | 58 ++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index 10444b7..a3e1a6a 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -137,15 +137,10 @@ const std::size_t send_each_mcs = 10000; const std::size_t client_timeout_mcs = 30 * 1000 * 1000; const std::size_t buffer_size = 32; -std::size_t thread_num; - class session; +typedef std::vector> session_vector; -std::vector> services; -std::vector> sessions; std::atomic_size_t session_index(0); -boost::asio::ip::tcp::resolver::iterator connect_to; - std::atomic_bool stopped(false); std::atomic_size_t child_num(0); std::atomic_size_t active_connection_num(0); @@ -197,18 +192,23 @@ class session : public std::enable_shared_from_this { exchange_rand_(0, send_each_mcs) { std::fill(write_buf_.begin(), write_buf_.end() - 1, data_rand_(rand_engine_)); *(write_buf_.rbegin()) = '\n'; + timer_queue_.reserve(16); } - void async_start() { + void async_start(const boost::asio::ip::tcp::resolver::iterator& connect_to, + const session_vector& sessions) { my_service_.post(make_custom_alloc_handler(bear_timer_allocator_, - std::bind(&session::start, shared_from_this()))); + std::bind(&session::start, shared_from_this(), + connect_to, std::cref(sessions)))); } private: - void start() { + void start(const boost::asio::ip::tcp::resolver::iterator& connect_to, + const session_vector& sessions) { bear_timer_.expires_from_now(boost::posix_time::microseconds(bear_after_mcs)); bear_timer_.async_wait(make_custom_alloc_handler(bear_timer_allocator_, - std::bind(&session::handle_bear_timer, shared_from_this(), std::placeholders::_1))); + std::bind(&session::handle_bear_timer, shared_from_this(), + connect_to, std::cref(sessions), std::placeholders::_1))); connect_timer_.reset(); ++active_connection_num; boost::asio::async_connect(socket_, connect_to, make_custom_alloc_handler( @@ -216,7 +216,8 @@ class session : public std::enable_shared_from_this { std::placeholders::_1))); } - void handle_bear_timer(const boost::system::error_code& e) { + void handle_bear_timer(const boost::asio::ip::tcp::resolver::iterator& connect_to, + const session_vector& sessions, const boost::system::error_code& e) { if (stopped) { return; } @@ -226,11 +227,12 @@ class session : public std::enable_shared_from_this { if (session_index >= clients_max) { return; } - std::size_t new_session_index = session_index.fetch_add(1); + auto new_session_index = session_index.fetch_add(1); if (new_session_index >= clients_max) { return; } - sessions[new_session_index]->async_start(); + auto& new_session = *sessions[new_session_index]; + new_session.async_start(connect_to, sessions); } void handle_connect(const boost::system::error_code& e) { @@ -242,9 +244,10 @@ class session : public std::enable_shared_from_this { if (e) { errored_ = true; ++connection_error_num; + stop_operations(); return; } - std::size_t spent_mcs = connect_timer_.current(); + auto spent_mcs = connect_timer_.current(); if (spent_mcs > client_timeout_mcs) { errored_ = true; ++connection_error_num; @@ -266,7 +269,7 @@ class session : public std::enable_shared_from_this { exchange_timer_.expires_from_now(boost::posix_time::microseconds( exchange_rand_(rand_engine_))); start_exchange_timer_wait(); - timer_queue_.push(timer()); + timer_queue_.emplace_back(timer()); boost::asio::async_write(socket_, boost::asio::buffer(write_buf_), make_custom_alloc_handler(write_allocator_, std::bind( &session::handle_write, shared_from_this(), std::placeholders::_1))); @@ -348,8 +351,8 @@ class session : public std::enable_shared_from_this { stop_operations(); return; } - std::size_t spent_mcs = timer_queue_.front().current(); - timer_queue_.pop(); + auto spent_mcs = timer_queue_.front().current(); + timer_queue_.erase(timer_queue_.begin()); if (spent_mcs > client_timeout_mcs) { errored_ = true; ++exchange_error_num; @@ -379,7 +382,7 @@ class session : public std::enable_shared_from_this { bool start_write_when_write_completes_; bool exchange_timer_wait_in_progress_; bool start_exchange_timer_wait_when_wait_completes_; - std::queue timer_queue_; + std::vector timer_queue_; std::mt19937 rand_engine_; std::uniform_int_distribution data_rand_; std::uniform_int_distribution exchange_rand_; @@ -435,32 +438,35 @@ int main(int args, char** argv) { } std::string host = argv[1]; std::string port = args > 2 ? argv[2] : "32000"; - thread_num = boost::numeric_cast(args > 3 ? std::stoi(argv[3]) : 24); + auto thread_num = boost::numeric_cast(args > 3 ? std::stoi(argv[3]) : 24); std::random_device random_device; std::mt19937 rand_engine(random_device()); - std::vector threads; + std::vector> services; services.reserve(thread_num); for (std::size_t i = 0; i < thread_num; ++i) { services.emplace_back(std::make_unique( to_io_context_concurrency_hint(1))); } + session_vector sessions; sessions.reserve(clients_max); for (std::size_t i = 0; i < clients_max; ++i) { sessions.emplace_back(std::make_shared( *services[i % thread_num], rand_engine)); } + std::vector threads; threads.reserve(thread_num); for (std::size_t i = 0; i < thread_num; ++i) { - threads.emplace_back([i] { - boost::asio::io_service::work work_guard(*services[i]); - services[i]->run(); + boost::asio::io_service& service = *services[i]; + threads.emplace_back([&service] { + boost::asio::io_service::work work_guard(service); + service.run(); }); } std::cout << "Starting tests" << std::endl; boost::asio::ip::tcp::resolver resolver(*services[0]); boost::asio::ip::tcp::resolver::query query(host, port); - connect_to = resolver.resolve(query); - sessions[session_index.fetch_add(1)]->async_start(); + auto& session = *sessions[session_index.fetch_add(1)]; + session.async_start(resolver.resolve(query), sessions); for (std::size_t i = 0; i < 60; i += 5) { print_stat(); std::this_thread::sleep_for(std::chrono::seconds(5)); @@ -470,8 +476,6 @@ int main(int args, char** argv) { s->stop(); }); std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread::join)); - threads.clear(); - sessions.clear(); print_stat(true); return EXIT_SUCCESS; } From 261c1498420e6b633ea310507c123d497dbc4856 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Sat, 2 Mar 2019 23:04:50 +0300 Subject: [PATCH 37/48] Code style fixes in c++-virtan, removed usage of ref counting in test client --- c++-virtan/server.cc | 26 ++++----- client-c++-virtan/client.cc | 102 ++++++++++++++++++++---------------- 2 files changed, 66 insertions(+), 62 deletions(-) diff --git a/c++-virtan/server.cc b/c++-virtan/server.cc index 2f9d4bb..01ef179 100644 --- a/c++-virtan/server.cc +++ b/c++-virtan/server.cc @@ -34,13 +34,11 @@ void invoke(Function&& function, Context& context) { template class handler_allocator { -private: - handler_allocator(const handler_allocator&) = delete; - handler_allocator& operator=(const handler_allocator&) = delete; - public: handler_allocator() : in_use_(false) {} ~handler_allocator() = default; + handler_allocator(const handler_allocator&) = delete; + handler_allocator& operator=(const handler_allocator&) = delete; bool owns(void* p) const { return std::addressof(storage_) == p; @@ -71,8 +69,6 @@ class custom_alloc_handler { typedef custom_alloc_handler this_type; public: - typedef void result_type; - template custom_alloc_handler(Allocator& allocator, H&& handler): allocator_(std::addressof(allocator)), handler_(std::forward(handler)) {} @@ -130,13 +126,11 @@ make_custom_alloc_handler(Allocator& allocator, Handler&& handler) { } class connection { -private: - connection(const connection&) = delete; - connection& operator=(const connection&) = delete; - public: explicit connection(boost::asio::io_service& service) : socket_(service) {} ~connection() = default; + connection(const connection&) = delete; + connection& operator=(const connection&) = delete; void start() { socket_.async_read_some(boost::asio::buffer(data_), @@ -173,10 +167,6 @@ class connection { }; class acceptor { -private: - acceptor(const acceptor&) = delete; - acceptor& operator=(const acceptor&) = delete; - public: acceptor(boost::asio::io_service& service, const boost::asio::ip::tcp::acceptor::native_handle_type& native_acceptor) : @@ -185,10 +175,12 @@ class acceptor { } ~acceptor() = default; + acceptor(const acceptor&) = delete; + acceptor& operator=(const acceptor&) = delete; private: void start_accept() { - connection* c = new connection(service_); + auto c = new connection(service_); acceptor_.async_accept(c->socket(), make_custom_alloc_handler(allocator_, std::bind(&acceptor::accept, this, c, std::placeholders::_1))); } @@ -233,8 +225,8 @@ int main(int args, char** argv) { std::cerr << "Usage: " << argv[0] << " [threads = 24]" << std::endl; return EXIT_FAILURE; } - unsigned short port = boost::numeric_cast(std::stoi(argv[1])); - std::size_t thread_num = boost::numeric_cast(args > 2 ? std::stoi(argv[2]) : 24); + auto port = boost::numeric_cast(std::stoi(argv[1])); + auto thread_num = boost::numeric_cast(args > 2 ? std::stoi(argv[2]) : 24); std::vector threads; threads.reserve(thread_num); boost::asio::io_service fake_s; diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index a3e1a6a..7fda55c 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -36,13 +36,11 @@ void invoke(Function&& function, Context& context) { template class handler_allocator { -private: - handler_allocator(const handler_allocator&) = delete; - handler_allocator& operator=(const handler_allocator&) = delete; - public: handler_allocator() : in_use_(false) {} ~handler_allocator() = default; + handler_allocator(const handler_allocator&) = delete; + handler_allocator& operator=(const handler_allocator&) = delete; bool owns(void* p) const { return std::addressof(storage_) == p; @@ -73,8 +71,6 @@ class custom_alloc_handler { typedef custom_alloc_handler this_type; public: - typedef void result_type; - template custom_alloc_handler(Allocator& allocator, H&& handler): allocator_(std::addressof(allocator)), handler_(std::forward(handler)) {} @@ -137,11 +133,11 @@ const std::size_t send_each_mcs = 10000; const std::size_t client_timeout_mcs = 30 * 1000 * 1000; const std::size_t buffer_size = 32; -class session; -typedef std::vector> session_vector; +class connection; +typedef std::vector> connection_vector; -std::atomic_size_t session_index(0); -std::atomic_bool stopped(false); +std::atomic_bool stopped(false); +std::atomic_size_t connection_index(0); std::atomic_size_t child_num(0); std::atomic_size_t active_connection_num(0); std::atomic_size_t connection_error_num(0); @@ -175,9 +171,23 @@ class timer { std::chrono::steady_clock::time_point start_time_; }; -class session : public std::enable_shared_from_this { +struct connection_handler_storage { + connection_handler_storage() = default; + ~connection_handler_storage() = default; + connection_handler_storage(const connection_handler_storage&) = delete; + connection_handler_storage& operator=(const connection_handler_storage&) = delete; + + handler_allocator<256> bear_timer_allocator; + handler_allocator<256> write_allocator; + handler_allocator<256> read_allocator; + handler_allocator<256> exchange_timer_allocator; +}; + +class connection { public: - session(boost::asio::io_service& service, const std::mt19937& rand_engine) : + connection(boost::asio::io_service& service, + connection_handler_storage& handler_storage, + const std::mt19937& rand_engine) : my_service_(service), socket_(my_service_), bear_timer_(my_service_), @@ -189,50 +199,50 @@ class session : public std::enable_shared_from_this { start_exchange_timer_wait_when_wait_completes_(false), rand_engine_(rand_engine), data_rand_(0, RAND_MAX), - exchange_rand_(0, send_each_mcs) { + exchange_rand_(0, send_each_mcs), + handler_storage_(handler_storage) { std::fill(write_buf_.begin(), write_buf_.end() - 1, data_rand_(rand_engine_)); *(write_buf_.rbegin()) = '\n'; timer_queue_.reserve(16); } void async_start(const boost::asio::ip::tcp::resolver::iterator& connect_to, - const session_vector& sessions) { - my_service_.post(make_custom_alloc_handler(bear_timer_allocator_, - std::bind(&session::start, shared_from_this(), - connect_to, std::cref(sessions)))); + const connection_vector& connections) { + my_service_.post(make_custom_alloc_handler(handler_storage_.bear_timer_allocator, + std::bind(&connection::start, this, connect_to, std::cref(connections)))); } private: void start(const boost::asio::ip::tcp::resolver::iterator& connect_to, - const session_vector& sessions) { + const connection_vector& connections) { bear_timer_.expires_from_now(boost::posix_time::microseconds(bear_after_mcs)); - bear_timer_.async_wait(make_custom_alloc_handler(bear_timer_allocator_, - std::bind(&session::handle_bear_timer, shared_from_this(), - connect_to, std::cref(sessions), std::placeholders::_1))); + bear_timer_.async_wait(make_custom_alloc_handler(handler_storage_.bear_timer_allocator, + std::bind(&connection::handle_bear_timer, this, + connect_to, std::cref(connections), std::placeholders::_1))); connect_timer_.reset(); ++active_connection_num; boost::asio::async_connect(socket_, connect_to, make_custom_alloc_handler( - write_allocator_, std::bind(&session::handle_connect, shared_from_this(), - std::placeholders::_1))); + handler_storage_.write_allocator, std::bind(&connection::handle_connect, + this, std::placeholders::_1))); } void handle_bear_timer(const boost::asio::ip::tcp::resolver::iterator& connect_to, - const session_vector& sessions, const boost::system::error_code& e) { + const connection_vector& connections, const boost::system::error_code& e) { if (stopped) { return; } if (e) { return; } - if (session_index >= clients_max) { + if (connection_index >= clients_max) { return; } - auto new_session_index = session_index.fetch_add(1); - if (new_session_index >= clients_max) { + auto new_connection_index = connection_index.fetch_add(1); + if (new_connection_index >= clients_max) { return; } - auto& new_session = *sessions[new_session_index]; - new_session.async_start(connect_to, sessions); + auto& new_connection = *connections[new_connection_index]; + new_connection.async_start(connect_to, connections); } void handle_connect(const boost::system::error_code& e) { @@ -271,8 +281,8 @@ class session : public std::enable_shared_from_this { start_exchange_timer_wait(); timer_queue_.emplace_back(timer()); boost::asio::async_write(socket_, boost::asio::buffer(write_buf_), - make_custom_alloc_handler(write_allocator_, std::bind( - &session::handle_write, shared_from_this(), std::placeholders::_1))); + make_custom_alloc_handler(handler_storage_.write_allocator, std::bind( + &connection::handle_write, this, std::placeholders::_1))); write_in_progress_ = true; } @@ -324,16 +334,16 @@ class session : public std::enable_shared_from_this { start_exchange_timer_wait_when_wait_completes_ = true; return; } - exchange_timer_.async_wait(make_custom_alloc_handler(exchange_timer_allocator_, - std::bind(&session::handle_exchange_timer, shared_from_this(), std::placeholders::_1))); + exchange_timer_.async_wait(make_custom_alloc_handler(handler_storage_.exchange_timer_allocator, + std::bind(&connection::handle_exchange_timer, this, std::placeholders::_1))); exchange_timer_wait_in_progress_ = true; } void start_read() { std::fill(read_buf_.begin(), read_buf_.end(), 0); boost::asio::async_read(socket_, boost::asio::buffer(read_buf_), - make_custom_alloc_handler(read_allocator_, std::bind( - &session::handle_read, shared_from_this(), std::placeholders::_1, std::placeholders::_2))); + make_custom_alloc_handler(handler_storage_.read_allocator, std::bind( + &connection::handle_read, this, std::placeholders::_1, std::placeholders::_2))); } void handle_read(const boost::system::error_code& e, std::size_t transferred) { @@ -386,10 +396,7 @@ class session : public std::enable_shared_from_this { std::mt19937 rand_engine_; std::uniform_int_distribution data_rand_; std::uniform_int_distribution exchange_rand_; - handler_allocator<256> bear_timer_allocator_; - handler_allocator<256> write_allocator_; - handler_allocator<256> read_allocator_; - handler_allocator<256> exchange_timer_allocator_; + connection_handler_storage& handler_storage_; }; void print_stat(bool final = false) { @@ -441,17 +448,22 @@ int main(int args, char** argv) { auto thread_num = boost::numeric_cast(args > 3 ? std::stoi(argv[3]) : 24); std::random_device random_device; std::mt19937 rand_engine(random_device()); + std::vector> handler_storage; + handler_storage.reserve(clients_max); + for (std::size_t i = 0; i < clients_max; ++i) { + handler_storage.emplace_back(std::make_unique()); + } std::vector> services; services.reserve(thread_num); for (std::size_t i = 0; i < thread_num; ++i) { services.emplace_back(std::make_unique( to_io_context_concurrency_hint(1))); } - session_vector sessions; - sessions.reserve(clients_max); + connection_vector connections; + connections.reserve(clients_max); for (std::size_t i = 0; i < clients_max; ++i) { - sessions.emplace_back(std::make_shared( - *services[i % thread_num], rand_engine)); + connections.emplace_back(std::make_unique( + *services[i % thread_num], *handler_storage[i], rand_engine)); } std::vector threads; threads.reserve(thread_num); @@ -465,8 +477,8 @@ int main(int args, char** argv) { std::cout << "Starting tests" << std::endl; boost::asio::ip::tcp::resolver resolver(*services[0]); boost::asio::ip::tcp::resolver::query query(host, port); - auto& session = *sessions[session_index.fetch_add(1)]; - session.async_start(resolver.resolve(query), sessions); + auto& connection = *connections[connection_index.fetch_add(1)]; + connection.async_start(resolver.resolve(query), connections); for (std::size_t i = 0; i < 60; i += 5) { print_stat(); std::this_thread::sleep_for(std::chrono::seconds(5)); From d698b90180d3b7a2f2fc4db93e2386dc78b06c15 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Sat, 2 Mar 2019 23:42:23 +0300 Subject: [PATCH 38/48] Fixed lifetime of read / write buffers in test client (should live till all related asynchronous operations complete) --- client-c++-virtan/client.cc | 64 ++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index 7fda55c..66e972d 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -133,9 +133,6 @@ const std::size_t send_each_mcs = 10000; const std::size_t client_timeout_mcs = 30 * 1000 * 1000; const std::size_t buffer_size = 32; -class connection; -typedef std::vector> connection_vector; - std::atomic_bool stopped(false); std::atomic_size_t connection_index(0); std::atomic_size_t child_num(0); @@ -171,22 +168,27 @@ class timer { std::chrono::steady_clock::time_point start_time_; }; -struct connection_handler_storage { - connection_handler_storage() = default; - ~connection_handler_storage() = default; - connection_handler_storage(const connection_handler_storage&) = delete; - connection_handler_storage& operator=(const connection_handler_storage&) = delete; +struct connection_data_storage { + connection_data_storage() = default; + ~connection_data_storage() = default; + connection_data_storage(const connection_data_storage&) = delete; + connection_data_storage& operator=(const connection_data_storage&) = delete; handler_allocator<256> bear_timer_allocator; handler_allocator<256> write_allocator; handler_allocator<256> read_allocator; handler_allocator<256> exchange_timer_allocator; + std::array write_buf; + std::array read_buf; }; +class connection; +typedef std::vector> connection_vector; + class connection { public: connection(boost::asio::io_service& service, - connection_handler_storage& handler_storage, + connection_data_storage& connection_data, const std::mt19937& rand_engine) : my_service_(service), socket_(my_service_), @@ -200,15 +202,15 @@ class connection { rand_engine_(rand_engine), data_rand_(0, RAND_MAX), exchange_rand_(0, send_each_mcs), - handler_storage_(handler_storage) { - std::fill(write_buf_.begin(), write_buf_.end() - 1, data_rand_(rand_engine_)); - *(write_buf_.rbegin()) = '\n'; + storage_(connection_data) { + std::fill(storage_.write_buf.begin(), storage_.write_buf.end() - 1, data_rand_(rand_engine_)); + *(storage_.write_buf.rbegin()) = '\n'; timer_queue_.reserve(16); } void async_start(const boost::asio::ip::tcp::resolver::iterator& connect_to, const connection_vector& connections) { - my_service_.post(make_custom_alloc_handler(handler_storage_.bear_timer_allocator, + my_service_.post(make_custom_alloc_handler(storage_.bear_timer_allocator, std::bind(&connection::start, this, connect_to, std::cref(connections)))); } @@ -216,13 +218,13 @@ class connection { void start(const boost::asio::ip::tcp::resolver::iterator& connect_to, const connection_vector& connections) { bear_timer_.expires_from_now(boost::posix_time::microseconds(bear_after_mcs)); - bear_timer_.async_wait(make_custom_alloc_handler(handler_storage_.bear_timer_allocator, - std::bind(&connection::handle_bear_timer, this, - connect_to, std::cref(connections), std::placeholders::_1))); + bear_timer_.async_wait(make_custom_alloc_handler(storage_.bear_timer_allocator, + std::bind(&connection::handle_bear_timer, this, connect_to, + std::cref(connections), std::placeholders::_1))); connect_timer_.reset(); ++active_connection_num; boost::asio::async_connect(socket_, connect_to, make_custom_alloc_handler( - handler_storage_.write_allocator, std::bind(&connection::handle_connect, + storage_.write_allocator, std::bind(&connection::handle_connect, this, std::placeholders::_1))); } @@ -280,8 +282,8 @@ class connection { exchange_rand_(rand_engine_))); start_exchange_timer_wait(); timer_queue_.emplace_back(timer()); - boost::asio::async_write(socket_, boost::asio::buffer(write_buf_), - make_custom_alloc_handler(handler_storage_.write_allocator, std::bind( + boost::asio::async_write(socket_, boost::asio::buffer(storage_.write_buf), + make_custom_alloc_handler(storage_.write_allocator, std::bind( &connection::handle_write, this, std::placeholders::_1))); write_in_progress_ = true; } @@ -334,15 +336,15 @@ class connection { start_exchange_timer_wait_when_wait_completes_ = true; return; } - exchange_timer_.async_wait(make_custom_alloc_handler(handler_storage_.exchange_timer_allocator, + exchange_timer_.async_wait(make_custom_alloc_handler(storage_.exchange_timer_allocator, std::bind(&connection::handle_exchange_timer, this, std::placeholders::_1))); exchange_timer_wait_in_progress_ = true; } void start_read() { - std::fill(read_buf_.begin(), read_buf_.end(), 0); - boost::asio::async_read(socket_, boost::asio::buffer(read_buf_), - make_custom_alloc_handler(handler_storage_.read_allocator, std::bind( + std::fill(storage_.read_buf.begin(), storage_.read_buf.end(), 0); + boost::asio::async_read(socket_, boost::asio::buffer(storage_.read_buf), + make_custom_alloc_handler(storage_.read_allocator, std::bind( &connection::handle_read, this, std::placeholders::_1, std::placeholders::_2))); } @@ -354,8 +356,8 @@ class connection { return; } if (e || transferred != 32 || !std::equal( - write_buf_.begin(), write_buf_.end(), - read_buf_.begin(), read_buf_.end())) { + storage_.write_buf.begin(), storage_.write_buf.end(), + storage_.read_buf.begin(), storage_.read_buf.end())) { errored_ = true; ++exchange_error_num; stop_operations(); @@ -384,8 +386,6 @@ class connection { boost::asio::ip::tcp::socket socket_; boost::asio::deadline_timer bear_timer_; boost::asio::deadline_timer exchange_timer_; - std::array write_buf_; - std::array read_buf_; timer connect_timer_; bool errored_; bool write_in_progress_; @@ -396,7 +396,7 @@ class connection { std::mt19937 rand_engine_; std::uniform_int_distribution data_rand_; std::uniform_int_distribution exchange_rand_; - connection_handler_storage& handler_storage_; + connection_data_storage& storage_; }; void print_stat(bool final = false) { @@ -448,10 +448,10 @@ int main(int args, char** argv) { auto thread_num = boost::numeric_cast(args > 3 ? std::stoi(argv[3]) : 24); std::random_device random_device; std::mt19937 rand_engine(random_device()); - std::vector> handler_storage; - handler_storage.reserve(clients_max); + std::vector> connection_data; + connection_data.reserve(clients_max); for (std::size_t i = 0; i < clients_max; ++i) { - handler_storage.emplace_back(std::make_unique()); + connection_data.emplace_back(std::make_unique()); } std::vector> services; services.reserve(thread_num); @@ -463,7 +463,7 @@ int main(int args, char** argv) { connections.reserve(clients_max); for (std::size_t i = 0; i < clients_max; ++i) { connections.emplace_back(std::make_unique( - *services[i % thread_num], *handler_storage[i], rand_engine)); + *services[i % thread_num], *connection_data[i], rand_engine)); } std::vector threads; threads.reserve(thread_num); From 0aa4e9a15f8712ead0c52ec87ff9ba2365470909 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Sat, 23 Mar 2019 00:27:26 +0300 Subject: [PATCH 39/48] README for c++-virtan implementation and renaming docker images for c++-virtan to match original name / author --- c++-virtan/Dockerfile | 2 +- c++-virtan/README.md | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 c++-virtan/README.md diff --git a/c++-virtan/Dockerfile b/c++-virtan/Dockerfile index fb4844b..9486995 100644 --- a/c++-virtan/Dockerfile +++ b/c++-virtan/Dockerfile @@ -1,6 +1,6 @@ FROM alpine:3.9 -LABEL name="abrarov/cpp-virtan" +LABEL name="cpp-virtan" ADD ["CMakeLists.txt", "/tmp/cpp-virtan/"] ADD ["server.cc", "/tmp/cpp-virtan/"] diff --git a/c++-virtan/README.md b/c++-virtan/README.md new file mode 100644 index 0000000..49209a0 --- /dev/null +++ b/c++-virtan/README.md @@ -0,0 +1,35 @@ +# Building + +## Requirements + +1. C++ toolchain (compiler, linker, standard C++ library) +1. Make +1. [CMake](https://cmake.org/) +1. [Boost](https://www.boost.org/) + +## Steps to build + +Assuming current directory is directory where this file is located + +```bash +cmake -D CMAKE_SKIP_BUILD_RPATH=ON -D CMAKE_BUILD_TYPE=Release . +cmake --build . +``` + +# Docker image + +## Building + +Assuming current directory is directory where this file is located + +```bash +docker build -t cpp-virtan . +``` + +## Running with Docker + +```bash +docker run --rm cpp-virtan $params +``` + +where `$params` are parameters passed to application \ No newline at end of file From 129fe7a504a4f4f0e119c1f16c5f9e2fb44e5ab3 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Sat, 23 Mar 2019 00:29:02 +0300 Subject: [PATCH 40/48] README for test client and renamed docker image for test client to match original name / author --- client-c++-virtan/Dockerfile | 2 +- client-c++-virtan/README.md | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 client-c++-virtan/README.md diff --git a/client-c++-virtan/Dockerfile b/client-c++-virtan/Dockerfile index 6c4a176..806c2a9 100644 --- a/client-c++-virtan/Dockerfile +++ b/client-c++-virtan/Dockerfile @@ -1,6 +1,6 @@ FROM alpine:3.9 -LABEL name="abrarov/client-cpp-virtan" +LABEL name="client-cpp-virtan" ADD ["CMakeLists.txt", "/tmp/client-cpp-virtan/"] ADD ["client.cc", "/tmp/client-cpp-virtan/"] diff --git a/client-c++-virtan/README.md b/client-c++-virtan/README.md new file mode 100644 index 0000000..923fb97 --- /dev/null +++ b/client-c++-virtan/README.md @@ -0,0 +1,35 @@ +# Building + +## Requirements + +1. C++ toolchain (compiler, linker, standard C++ library) +1. Make +1. [CMake](https://cmake.org/) +1. [Boost](https://www.boost.org/) + +## Steps to build + +Assuming current directory is directory where this file is located + +```bash +cmake -D CMAKE_SKIP_BUILD_RPATH=ON -D CMAKE_BUILD_TYPE=Release . +cmake --build . +``` + +# Docker image + +## Building + +Assuming current directory is directory where this file is located + +```bash +docker build -t client-cpp-virtan . +``` + +## Running with Docker + +```bash +docker run --rm client-cpp-virtan $params +``` + +where `$params` are parameters passed to application \ No newline at end of file From 98f5884f7dc33bd8705c9288c851313e1f072a58 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Sun, 24 Mar 2019 13:30:08 +0300 Subject: [PATCH 41/48] Configurable duration of test in test client --- client-c++-virtan/client.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index 66e972d..a40cb07 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -436,16 +436,19 @@ io_context_concurrency_hint to_io_context_concurrency_hint(std::size_t hint) { #endif // BOOST_VERSION >= 106600 +const std::size_t print_stats_interval = 5; + } // anonymous namespace int main(int args, char** argv) { if (args < 2) { - std::cerr << "Usage: " << argv[0] << " [port = 32000 [threads = 24]]" << std::endl; + std::cerr << "Usage: " << argv[0] << " [port = 32000 [threads = 24 [duration_seconds = 60]]]" << std::endl; return EXIT_FAILURE; } std::string host = argv[1]; std::string port = args > 2 ? argv[2] : "32000"; auto thread_num = boost::numeric_cast(args > 3 ? std::stoi(argv[3]) : 24); + auto duration = boost::numeric_cast(args > 4 ? std::stoi(argv[4]) : 60); std::random_device random_device; std::mt19937 rand_engine(random_device()); std::vector> connection_data; @@ -479,9 +482,9 @@ int main(int args, char** argv) { boost::asio::ip::tcp::resolver::query query(host, port); auto& connection = *connections[connection_index.fetch_add(1)]; connection.async_start(resolver.resolve(query), connections); - for (std::size_t i = 0; i < 60; i += 5) { + for (std::size_t i = 0; i < duration; i += print_stats_interval) { print_stat(); - std::this_thread::sleep_for(std::chrono::seconds(5)); + std::this_thread::sleep_for(std::chrono::seconds(print_stats_interval)); } stopped = true; std::for_each(services.begin(), services.end(), [](std::unique_ptr& s) { From c2559e300dfcf0093d2fa26b17d6fc8454e1b0f4 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Wed, 27 Mar 2019 01:12:49 +0300 Subject: [PATCH 42/48] Renamed default docker image with test client in test script to match project name --- client-c++-virtan/test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client-c++-virtan/test.sh b/client-c++-virtan/test.sh index 295a79f..23af0f8 100644 --- a/client-c++-virtan/test.sh +++ b/client-c++-virtan/test.sh @@ -25,7 +25,7 @@ fi if [[ "${#}" -ge 5 ]]; then docker_image="${5}" else - docker_image="abrarov/client-cpp-virtan" + docker_image="client-cpp-virtan" fi max_msgs=0 From 2a5ef8bc510148572e8572a4395cb944ae33e05a Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Sun, 27 Sep 2020 05:27:11 +0300 Subject: [PATCH 43/48] Windows support in test client and initial Windows support in c++-virtan, named parameters of test client, Distroless (https://github.com/GoogleContainerTools/distroless) docker images with test client and c++-virtan --- c++-virtan/CMakeLists.txt | 37 ++++- c++-virtan/Dockerfile | 75 ++++++++--- c++-virtan/README.md | 9 +- c++-virtan/server.cc | 158 ++++++++++++++++++---- client-c++-virtan/CMakeLists.txt | 37 ++++- client-c++-virtan/Dockerfile | 75 ++++++++--- client-c++-virtan/README.md | 9 +- client-c++-virtan/client.cc | 223 +++++++++++++++++++++++-------- client-c++-virtan/test.sh | 4 +- 9 files changed, 490 insertions(+), 137 deletions(-) diff --git a/c++-virtan/CMakeLists.txt b/c++-virtan/CMakeLists.txt index 081e6e1..82eba5d 100644 --- a/c++-virtan/CMakeLists.txt +++ b/c++-virtan/CMakeLists.txt @@ -3,15 +3,38 @@ project(server) set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_MULTITHREADED ON) -find_package(Boost REQUIRED system) +find_package(Boost REQUIRED COMPONENTS system date_time chrono regex program_options) find_package(Threads REQUIRED) -set(cxx_libraries ${Boost_LIBRARIES} Threads::Threads) +set(libraries ) +list(APPEND libraries ${Boost_LIBRARIES}) +list(APPEND libraries ${Boost_LIBRARIES} Threads::Threads) if(WIN32) - list(APPEND cxx_libraries "ws2_32" "mswsock") + list(APPEND libraries "ws2_32" "mswsock") endif() -add_executable(${PROJECT_NAME} "server.cc") -target_compile_options(${PROJECT_NAME} PRIVATE "-std=c++17" "-Wall") -target_include_directories(${PROJECT_NAME} PRIVATE ${Boost_INCLUDE_DIRS}) -target_link_libraries(${PROJECT_NAME} PRIVATE ${cxx_libraries}) +if(NOT Boost_USE_STATIC_LIBS) + list(APPEND compile_definitions "BOOST_ALL_DYN_LINK") +endif() +if(WIN32) + list(APPEND compile_definitions + WIN32 + WIN32_LEAN_AND_MEAN + WINVER=0x0501 + _WIN32_WINNT=0x0501 + _WIN32_WINDOWS=0x0501 + _WIN32_IE=0x0600 + _UNICODE + UNICODE + _WINSOCK_DEPRECATED_NO_WARNINGS) +endif() + +if(MINGW) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -municode") +endif() + +add_executable(server "server.cc") +target_compile_features(server PRIVATE cxx_std_11) +target_compile_definitions(server PRIVATE ${compile_definitions}) +target_include_directories(server PRIVATE ${Boost_INCLUDE_DIRS}) +target_link_libraries(server PRIVATE ${libraries}) diff --git a/c++-virtan/Dockerfile b/c++-virtan/Dockerfile index 9486995..edfaed4 100644 --- a/c++-virtan/Dockerfile +++ b/c++-virtan/Dockerfile @@ -1,20 +1,63 @@ -FROM alpine:3.9 +FROM ubuntu:20.04 AS build -LABEL name="cpp-virtan" +ARG CMAKE_VERSION="3.18.3" +ARG BOOST_VERSION="1.74.0" +ARG BOOST_URL="https://dl.bintray.com/mabrarov/generic/boost" + +ENV TZ="Europe/Moscow" \ + PATH="/opt/cmake/bin:${PATH}" \ + DEPENDENCIES_DIR="/dependency" + +RUN ln -snf "/usr/share/zoneinfo/${TZ}" /etc/localtime && \ + echo "${TZ}" > /etc/timezone && \ + apt-get update && \ + apt-get install -y \ + software-properties-common \ + curl \ + git \ + g++ \ + make \ + libstdc++-9-dev + +RUN mkdir -p /opt && \ + cmake_url="https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz" && \ + echo "Downloading CMake ${CMAKE_VERSION} from ${cmake_url}" && \ + curl -jksSL "${cmake_url}" | tar -xzf - -C /opt && \ + mv -f "/opt/cmake-${CMAKE_VERSION}-Linux-x86_64" /opt/cmake + +RUN mkdir -p "${DEPENDENCIES_DIR}" && \ + boost_archive_url="${BOOST_URL}/${BOOST_VERSION}/boost-${BOOST_VERSION}-x64-gcc$(gcc -dumpversion | sed -r 's/^([[:digit:]]+)(\..*)?$/\1/;t;d').tar.gz" && \ + echo "Downloading Boost from ${boost_archive_url}" && \ + curl --connect-timeout 300 \ + --max-time 1800 \ + --retry 10 \ + --retry-delay 10 \ + -jksSL \ + "${boost_archive_url}" | tar -xz -C "${DEPENDENCIES_DIR}" + +ADD ["CMakeLists.txt", "/cpp-virtan/"] +ADD ["server.cc", "/cpp-virtan/"] -ADD ["CMakeLists.txt", "/tmp/cpp-virtan/"] -ADD ["server.cc", "/tmp/cpp-virtan/"] - -RUN apk add --no-cache libstdc++ &&\ - apk add --no-cache --virtual build-dependencies g++ make cmake boost-dev boost-static libstdc++ &&\ - cd /tmp/cpp-virtan &&\ - mkdir build && cd build &&\ - cmake -D CMAKE_SKIP_BUILD_RPATH=ON -D CMAKE_BUILD_TYPE=Release .. &&\ - cmake --build . &&\ - mkdir -p /opt/cpp-virtan &&\ - mv -f server /opt/cpp-virtan/server &&\ - cd /tmp &&\ - rm -rf cpp-virtan &&\ - apk del build-dependencies +RUN mkdir -p "/cpp-virtan/build" && \ + cd "/cpp-virtan/build" && \ + boost_dir="${DEPENDENCIES_DIR}/boost-${BOOST_VERSION}-x64-gcc$(gcc -dumpversion | sed -r 's/^([[:digit:]]+)(\..*)?$/\1/;t;d')" && \ + cmake \ + -D CMAKE_SKIP_BUILD_RPATH=ON \ + -D CMAKE_BUILD_TYPE=Release \ + -D Boost_USE_MULTITHREADED=ON \ + -D Boost_USE_STATIC_LIBS=ON \ + -D Boost_NO_SYSTEM_PATHS=ON \ + -D BOOST_INCLUDEDIR="${boost_dir}/include" \ + -D BOOST_LIBRARYDIR="${boost_dir}/lib" \ + .. && \ + cmake --build . + +FROM gcr.io/distroless/cc-debian10 + +COPY --from=build /cpp-virtan/build/server /opt/cpp-virtan/server ENTRYPOINT ["/opt/cpp-virtan/server"] + +CMD ["--help"] + +LABEL name="cpp-virtan" diff --git a/c++-virtan/README.md b/c++-virtan/README.md index 49209a0..d7aded2 100644 --- a/c++-virtan/README.md +++ b/c++-virtan/README.md @@ -20,7 +20,12 @@ cmake --build . ## Building -Assuming current directory is directory where this file is located +### Requirements + +1. Docker 17.06+ for building +1. Current directory is directory where this file is located + +### Steps to build ```bash docker build -t cpp-virtan . @@ -32,4 +37,4 @@ docker build -t cpp-virtan . docker run --rm cpp-virtan $params ``` -where `$params` are parameters passed to application \ No newline at end of file +where `$params` are parameters passed to application diff --git a/c++-virtan/server.cc b/c++-virtan/server.cc index 01ef179..d8e9ad4 100644 --- a/c++-virtan/server.cc +++ b/c++-virtan/server.cc @@ -1,3 +1,8 @@ +#if defined(WIN32) +#include +#endif + +#include #include #include #include @@ -11,8 +16,9 @@ #include #include #include +#include -namespace { +namespace asio_helpers { template void* allocate(std::size_t size, Context& context) { @@ -32,6 +38,27 @@ void invoke(Function&& function, Context& context) { asio_handler_invoke(std::forward(function), std::addressof(context)); } +#if BOOST_VERSION >= 105400 + +template +bool is_continuation(Context& context) { + using namespace boost::asio; + return asio_handler_is_continuation(std::addressof(context)); +} + +#else // BOOST_VERSION >= 105400 + +template +bool is_continuation(Context& /*context*/) { + return false; +} + +#endif // BOOST_VERSION >= 105400 + +} + +namespace { + template class handler_allocator { public: @@ -77,30 +104,34 @@ class custom_alloc_handler { if (void* p = context->allocator_->allocate(size)) { return p; } - return allocate(size, context->handler_); + return asio_helpers::allocate(size, context->handler_); } friend void asio_handler_deallocate(void* pointer, std::size_t size, this_type* context) { if (context->allocator_->owns(pointer)) { context->allocator_->deallocate(pointer); } else { - deallocate(pointer, size, context->handler_); + asio_helpers::deallocate(pointer, size, context->handler_); } } template friend void asio_handler_invoke(Function&& function, this_type* context) { - invoke(std::forward(function), context->handler_); + asio_helpers::invoke(std::forward(function), context->handler_); } template friend void asio_handler_invoke(Function& function, this_type* context) { - invoke(function, context->handler_); + asio_helpers::invoke(function, context->handler_); } template friend void asio_handler_invoke(const Function& function, this_type* context) { - invoke(function, context->handler_); + asio_helpers::invoke(function, context->handler_); + } + + friend bool asio_handler_is_continuation(this_type* context) { + return asio_helpers::is_continuation(context->handler_); } template @@ -218,28 +249,99 @@ io_context_concurrency_hint to_io_context_concurrency_hint(std::size_t hint) { #endif // BOOST_VERSION >= 106600 +const char* help_option_name = "help"; +const char* host_option_name = "host"; +const char* port_option_name = "port"; +const char* threads_option_name = "threads"; + +boost::program_options::options_description build_program_options_description() { + boost::program_options::options_description description("Usage"); + description.add_options() + ( + help_option_name, + "produce help message" + ) + ( + host_option_name, + boost::program_options::value()->default_value( + boost::asio::ip::address_v4::any().to_string()), + "address to listen" + ) + ( + port_option_name, + boost::program_options::value(), + "port to listen" + ) + ( + threads_option_name, + boost::program_options::value()->default_value(24), + "number of threads" + ); + return std::move(description); +} + +#if defined(WIN32) +boost::program_options::variables_map parse_program_options( + const boost::program_options::options_description& options_description, + int argc, _TCHAR* argv[]) { +#else +boost::program_options::variables_map parse_program_options( + const boost::program_options::options_description& options_description, + int argc, char* argv[]) { +#endif + boost::program_options::variables_map values; + boost::program_options::store( + boost::program_options::parse_command_line(argc, argv, options_description), + values); + boost::program_options::notify(values); + return std::move(values); +} + } // anonymous namespace -int main(int args, char** argv) { - if (args < 2) { - std::cerr << "Usage: " << argv[0] << " [threads = 24]" << std::endl; - return EXIT_FAILURE; - } - auto port = boost::numeric_cast(std::stoi(argv[1])); - auto thread_num = boost::numeric_cast(args > 2 ? std::stoi(argv[2]) : 24); - std::vector threads; - threads.reserve(thread_num); - boost::asio::io_service fake_s; - boost::asio::ip::tcp::acceptor fake_a(fake_s, boost::asio::ip::tcp::endpoint( - boost::asio::ip::tcp::v4(), port)); - auto native_handle = fake_a.native_handle(); - for (std::size_t i = 0; i < thread_num; ++i) { - threads.emplace_back([native_handle]() { - boost::asio::io_service service(to_io_context_concurrency_hint(1)); - acceptor a(service, native_handle); - service.run(); - }); - } - std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread::join)); - return EXIT_SUCCESS; +#if defined(WIN32) +int _tmain(int argc, _TCHAR** argv) { +#else +int main(int argc, char* argv[]) { +#endif + try { + auto po_description = build_program_options_description(); + auto po_values = parse_program_options(po_description, argc, argv); + if (po_values.count(help_option_name)) { + std::cout << po_description; + return EXIT_SUCCESS; + } + if (!po_values.count(port_option_name)) { + std::cerr << "port is required\n" << po_description; + return EXIT_FAILURE; + } + auto host = po_values[host_option_name].as(); + auto port = po_values[port_option_name].as(); + auto thread_num = po_values[threads_option_name].as(); + std::vector threads; + threads.reserve(thread_num); + boost::asio::io_service fake_s; + boost::asio::ip::tcp::acceptor fake_a(fake_s, boost::asio::ip::tcp::endpoint( + boost::asio::ip::address::from_string(host), port)); + auto native_handle = fake_a.native_handle(); + for (std::size_t i = 0; i < thread_num; ++i) { + threads.emplace_back([native_handle]() { + boost::asio::io_service service(to_io_context_concurrency_hint(1)); + acceptor a(service, native_handle); + service.run(); + }); + } + std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread::join)); + return EXIT_SUCCESS; + } + catch (const boost::program_options::error& e) { + std::cerr << "Error reading options: " << e.what() << std::endl; + } + catch (const std::exception& e) { + std::cerr << "Unexpected error: " << e.what() << std::endl; + } + catch (...) { + std::cerr << "Unknown error" << std::endl; + } + return EXIT_FAILURE; } diff --git a/client-c++-virtan/CMakeLists.txt b/client-c++-virtan/CMakeLists.txt index e1eb0e9..fef0c11 100644 --- a/client-c++-virtan/CMakeLists.txt +++ b/client-c++-virtan/CMakeLists.txt @@ -3,15 +3,38 @@ project(client) set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_MULTITHREADED ON) -find_package(Boost REQUIRED date_time system) +find_package(Boost REQUIRED COMPONENTS system date_time chrono regex program_options) find_package(Threads REQUIRED) -set(cxx_libraries ${Boost_LIBRARIES} Threads::Threads) +set(libraries ) +list(APPEND libraries ${Boost_LIBRARIES}) +list(APPEND libraries ${Boost_LIBRARIES} Threads::Threads) if(WIN32) - list(APPEND cxx_libraries "ws2_32" "mswsock") + list(APPEND libraries "ws2_32" "mswsock") endif() -add_executable(${PROJECT_NAME} "client.cc") -target_compile_options(${PROJECT_NAME} PRIVATE "-std=c++17" "-Wall") -target_include_directories(${PROJECT_NAME} PRIVATE ${Boost_INCLUDE_DIRS}) -target_link_libraries(${PROJECT_NAME} PRIVATE ${cxx_libraries}) +if(NOT Boost_USE_STATIC_LIBS) + list(APPEND compile_definitions "BOOST_ALL_DYN_LINK") +endif() +if(WIN32) + list(APPEND compile_definitions + WIN32 + WIN32_LEAN_AND_MEAN + WINVER=0x0501 + _WIN32_WINNT=0x0501 + _WIN32_WINDOWS=0x0501 + _WIN32_IE=0x0600 + _UNICODE + UNICODE + _WINSOCK_DEPRECATED_NO_WARNINGS) +endif() + +if(MINGW) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -municode") +endif() + +add_executable(client "client.cc") +target_compile_features(client PRIVATE cxx_std_11) +target_compile_definitions(client PRIVATE ${compile_definitions}) +target_include_directories(client PRIVATE ${Boost_INCLUDE_DIRS}) +target_link_libraries(client PRIVATE ${libraries}) diff --git a/client-c++-virtan/Dockerfile b/client-c++-virtan/Dockerfile index 806c2a9..314cba7 100644 --- a/client-c++-virtan/Dockerfile +++ b/client-c++-virtan/Dockerfile @@ -1,20 +1,63 @@ -FROM alpine:3.9 +FROM ubuntu:20.04 AS build -LABEL name="client-cpp-virtan" +ARG CMAKE_VERSION="3.18.3" +ARG BOOST_VERSION="1.74.0" +ARG BOOST_URL="https://dl.bintray.com/mabrarov/generic/boost" + +ENV TZ="Europe/Moscow" \ + PATH="/opt/cmake/bin:${PATH}" \ + DEPENDENCIES_DIR="/dependency" + +RUN ln -snf "/usr/share/zoneinfo/${TZ}" /etc/localtime && \ + echo "${TZ}" > /etc/timezone && \ + apt-get update && \ + apt-get install -y \ + software-properties-common \ + curl \ + git \ + g++ \ + make \ + libstdc++-9-dev + +RUN mkdir -p /opt && \ + cmake_url="https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz" && \ + echo "Downloading CMake ${CMAKE_VERSION} from ${cmake_url}" && \ + curl -jksSL "${cmake_url}" | tar -xzf - -C /opt && \ + mv -f "/opt/cmake-${CMAKE_VERSION}-Linux-x86_64" /opt/cmake + +RUN mkdir -p "${DEPENDENCIES_DIR}" && \ + boost_archive_url="${BOOST_URL}/${BOOST_VERSION}/boost-${BOOST_VERSION}-x64-gcc$(gcc -dumpversion | sed -r 's/^([[:digit:]]+)(\..*)?$/\1/;t;d').tar.gz" && \ + echo "Downloading Boost from ${boost_archive_url}" && \ + curl --connect-timeout 300 \ + --max-time 1800 \ + --retry 10 \ + --retry-delay 10 \ + -jksSL \ + "${boost_archive_url}" | tar -xz -C "${DEPENDENCIES_DIR}" + +ADD ["CMakeLists.txt", "/client-cpp-virtan/"] +ADD ["client.cc", "/client-cpp-virtan/"] -ADD ["CMakeLists.txt", "/tmp/client-cpp-virtan/"] -ADD ["client.cc", "/tmp/client-cpp-virtan/"] - -RUN apk add --no-cache libstdc++ &&\ - apk add --no-cache --virtual build-dependencies g++ make cmake boost-dev boost-static libstdc++ &&\ - cd /tmp/client-cpp-virtan &&\ - mkdir build && cd build &&\ - cmake -D CMAKE_SKIP_BUILD_RPATH=ON -D CMAKE_BUILD_TYPE=Release .. &&\ - cmake --build . &&\ - mkdir -p /opt/client-cpp-virtan &&\ - mv -f client /opt/client-cpp-virtan/client &&\ - cd /tmp &&\ - rm -rf client-cpp-virtan &&\ - apk del build-dependencies +RUN mkdir -p "/client-cpp-virtan/build" && \ + cd "/client-cpp-virtan/build" && \ + boost_dir="${DEPENDENCIES_DIR}/boost-${BOOST_VERSION}-x64-gcc$(gcc -dumpversion | sed -r 's/^([[:digit:]]+)(\..*)?$/\1/;t;d')" && \ + cmake \ + -D CMAKE_SKIP_BUILD_RPATH=ON \ + -D CMAKE_BUILD_TYPE=Release \ + -D Boost_USE_MULTITHREADED=ON \ + -D Boost_USE_STATIC_LIBS=ON \ + -D Boost_NO_SYSTEM_PATHS=ON \ + -D BOOST_INCLUDEDIR="${boost_dir}/include" \ + -D BOOST_LIBRARYDIR="${boost_dir}/lib" \ + .. && \ + cmake --build . + +FROM gcr.io/distroless/cc-debian10 + +COPY --from=build /client-cpp-virtan/build/client /opt/client-cpp-virtan/client ENTRYPOINT ["/opt/client-cpp-virtan/client"] + +CMD ["--help"] + +LABEL name="client-cpp-virtan" diff --git a/client-c++-virtan/README.md b/client-c++-virtan/README.md index 923fb97..781878b 100644 --- a/client-c++-virtan/README.md +++ b/client-c++-virtan/README.md @@ -20,7 +20,12 @@ cmake --build . ## Building -Assuming current directory is directory where this file is located +### Requirements + +1. Docker 17.06+ for building +1. Current directory is directory where this file is located + +### Steps to build ```bash docker build -t client-cpp-virtan . @@ -32,4 +37,4 @@ docker build -t client-cpp-virtan . docker run --rm client-cpp-virtan $params ``` -where `$params` are parameters passed to application \ No newline at end of file +where `$params` are parameters passed to application diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index a40cb07..e49f1c4 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -1,3 +1,9 @@ +#if defined(WIN32) +#include +#endif + +#include +#include #include #include #include @@ -11,10 +17,12 @@ #include #include #include +#include #include #include +#include -namespace { +namespace asio_helpers { template void* allocate(std::size_t size, Context& context) { @@ -34,6 +42,27 @@ void invoke(Function&& function, Context& context) { asio_handler_invoke(std::forward(function), std::addressof(context)); } +#if BOOST_VERSION >= 105400 + +template +bool is_continuation(Context& context) { + using namespace boost::asio; + return asio_handler_is_continuation(std::addressof(context)); +} + +#else // BOOST_VERSION >= 105400 + +template +bool is_continuation(Context& /*context*/) { + return false; +} + +#endif // BOOST_VERSION >= 105400 + +} + +namespace { + template class handler_allocator { public: @@ -79,30 +108,34 @@ class custom_alloc_handler { if (void* p = context->allocator_->allocate(size)) { return p; } - return allocate(size, context->handler_); + return asio_helpers::allocate(size, context->handler_); } friend void asio_handler_deallocate(void* pointer, std::size_t size, this_type* context) { if (context->allocator_->owns(pointer)) { context->allocator_->deallocate(pointer); } else { - deallocate(pointer, size, context->handler_); + asio_helpers::deallocate(pointer, size, context->handler_); } } template friend void asio_handler_invoke(Function&& function, this_type* context) { - invoke(std::forward(function), context->handler_); + asio_helpers::invoke(std::forward(function), context->handler_); } template friend void asio_handler_invoke(Function& function, this_type* context) { - invoke(function, context->handler_); + asio_helpers::invoke(function, context->handler_); } template friend void asio_handler_invoke(const Function& function, this_type* context) { - invoke(function, context->handler_); + asio_helpers::invoke(function, context->handler_); + } + + friend bool asio_handler_is_continuation(this_type* context) { + return asio_helpers::is_continuation(context->handler_); } template @@ -438,59 +471,135 @@ io_context_concurrency_hint to_io_context_concurrency_hint(std::size_t hint) { const std::size_t print_stats_interval = 5; +const char* help_option_name = "help"; +const char* host_option_name = "host"; +const char* port_option_name = "port"; +const char* threads_option_name = "threads"; +const char* duration_option_name = "duration"; + +boost::program_options::options_description build_program_options_description() { + boost::program_options::options_description description("Usage"); + description.add_options() + ( + help_option_name, + "produce help message" + ) + ( + host_option_name, + boost::program_options::value(), + "target host" + ) + ( + port_option_name, + boost::program_options::value()->default_value(32000), + "target port" + ) + ( + threads_option_name, + boost::program_options::value()->default_value(24), + "number of threads" + ) + ( + duration_option_name, + boost::program_options::value()->default_value(60), + "duration, seconds" + ); + return std::move(description); +} + +#if defined(WIN32) +boost::program_options::variables_map parse_program_options( + const boost::program_options::options_description& options_description, + int argc, _TCHAR* argv[]) { +#else +boost::program_options::variables_map parse_program_options( + const boost::program_options::options_description& options_description, + int argc, char* argv[]) { +#endif + boost::program_options::variables_map values; + boost::program_options::store( + boost::program_options::parse_command_line(argc, argv, options_description), + values); + boost::program_options::notify(values); + return std::move(values); +} + } // anonymous namespace -int main(int args, char** argv) { - if (args < 2) { - std::cerr << "Usage: " << argv[0] << " [port = 32000 [threads = 24 [duration_seconds = 60]]]" << std::endl; - return EXIT_FAILURE; - } - std::string host = argv[1]; - std::string port = args > 2 ? argv[2] : "32000"; - auto thread_num = boost::numeric_cast(args > 3 ? std::stoi(argv[3]) : 24); - auto duration = boost::numeric_cast(args > 4 ? std::stoi(argv[4]) : 60); - std::random_device random_device; - std::mt19937 rand_engine(random_device()); - std::vector> connection_data; - connection_data.reserve(clients_max); - for (std::size_t i = 0; i < clients_max; ++i) { - connection_data.emplace_back(std::make_unique()); - } - std::vector> services; - services.reserve(thread_num); - for (std::size_t i = 0; i < thread_num; ++i) { - services.emplace_back(std::make_unique( - to_io_context_concurrency_hint(1))); - } - connection_vector connections; - connections.reserve(clients_max); - for (std::size_t i = 0; i < clients_max; ++i) { - connections.emplace_back(std::make_unique( - *services[i % thread_num], *connection_data[i], rand_engine)); - } - std::vector threads; - threads.reserve(thread_num); - for (std::size_t i = 0; i < thread_num; ++i) { - boost::asio::io_service& service = *services[i]; - threads.emplace_back([&service] { - boost::asio::io_service::work work_guard(service); - service.run(); +#if defined(WIN32) +int _tmain(int argc, _TCHAR** argv) { +#else +int main(int argc, char* argv[]) { +#endif + try { + auto po_description = build_program_options_description(); + auto po_values = parse_program_options(po_description, argc, argv); + if (po_values.count(help_option_name)) { + std::cout << po_description; + return EXIT_SUCCESS; + } + if (!po_values.count(host_option_name)) { + std::cerr << "host is required\n" << po_description; + return EXIT_FAILURE; + } + auto host = po_values[host_option_name].as(); + auto port = po_values[port_option_name].as(); + auto thread_num = po_values[threads_option_name].as(); + auto duration = po_values[duration_option_name].as(); + + std::random_device random_device; + std::mt19937 rand_engine(random_device()); + std::vector> connection_data; + connection_data.reserve(clients_max); + for (std::size_t i = 0; i < clients_max; ++i) { + connection_data.emplace_back(std::make_unique()); + } + std::vector> services; + services.reserve(thread_num); + for (std::size_t i = 0; i < thread_num; ++i) { + services.emplace_back(std::make_unique( + to_io_context_concurrency_hint(1))); + } + connection_vector connections; + connections.reserve(clients_max); + for (std::size_t i = 0; i < clients_max; ++i) { + connections.emplace_back(std::make_unique( + *services[i % thread_num], *connection_data[i], rand_engine)); + } + std::vector threads; + threads.reserve(thread_num); + for (std::size_t i = 0; i < thread_num; ++i) { + boost::asio::io_service& service = *services[i]; + threads.emplace_back([&service] { + boost::asio::io_service::work work_guard(service); + service.run(); + }); + } + std::cout << "Starting tests" << std::endl; + boost::asio::ip::tcp::resolver resolver(*services[0]); + boost::asio::ip::tcp::resolver::query query(host, std::to_string(port)); + auto& connection = *connections[connection_index.fetch_add(1)]; + connection.async_start(resolver.resolve(query), connections); + for (std::size_t i = 0; i < duration; i += print_stats_interval) { + print_stat(); + std::this_thread::sleep_for(std::chrono::seconds(print_stats_interval)); + } + stopped = true; + std::for_each(services.begin(), services.end(), [](std::unique_ptr& s) { + s->stop(); }); + std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread::join)); + print_stat(true); + return EXIT_SUCCESS; + } + catch (const boost::program_options::error& e) { + std::cerr << "Error reading options: " << e.what() << std::endl; + } + catch (const std::exception& e) { + std::cerr << "Unexpected error: " << e.what() << std::endl; + } + catch (...) { + std::cerr << "Unknown error" << std::endl; } - std::cout << "Starting tests" << std::endl; - boost::asio::ip::tcp::resolver resolver(*services[0]); - boost::asio::ip::tcp::resolver::query query(host, port); - auto& connection = *connections[connection_index.fetch_add(1)]; - connection.async_start(resolver.resolve(query), connections); - for (std::size_t i = 0; i < duration; i += print_stats_interval) { - print_stat(); - std::this_thread::sleep_for(std::chrono::seconds(print_stats_interval)); - } - stopped = true; - std::for_each(services.begin(), services.end(), [](std::unique_ptr& s) { - s->stop(); - }); - std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread::join)); - print_stat(true); - return EXIT_SUCCESS; + return EXIT_FAILURE; } diff --git a/client-c++-virtan/test.sh b/client-c++-virtan/test.sh index 23af0f8..8f506bd 100644 --- a/client-c++-virtan/test.sh +++ b/client-c++-virtan/test.sh @@ -31,13 +31,13 @@ fi max_msgs=0 best_stats="" -echo "Running container from ${docker_image} image ${run_iterations} times with command: ${target_host} ${target_port} ${work_threads}" +echo "Running container from ${docker_image} image ${run_iterations} times with command: --host \"${target_host}\" --port \"${target_port}\" --threads \"${work_threads}\"" i=0 while [[ ${i} -lt ${run_iterations} ]] do #echo "Running iteration #${i}..." - stats=$(docker run --rm ${docker_image} ${target_host} ${target_port} ${work_threads} 2>&1 | grep -A 1 "Final statistics" | grep "Chld:") + stats=$(docker run --rm "${docker_image}" --host "${target_host}" --port "${target_port}" --threads "${work_threads}" 2>&1 | grep -A 1 "Final statistics" | grep "Chld:") #echo "Stats: ${stats}" msgs=$(echo "${stats}" | sed -r 's/.*Msgs: ([0-9]+)/\1/') if [[ "${msgs}" -gt ${max_msgs} ]]; then From 644bdc438abaadc0b33d7733fa7899829204f03b Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Sun, 27 Sep 2020 09:25:43 +0300 Subject: [PATCH 44/48] Implemented stop in c++-virtan --- c++-virtan/server.cc | 69 ++++++++++++++++++++----------------- client-c++-virtan/client.cc | 2 +- 2 files changed, 39 insertions(+), 32 deletions(-) diff --git a/c++-virtan/server.cc b/c++-virtan/server.cc index d8e9ad4..fdd2bce 100644 --- a/c++-virtan/server.cc +++ b/c++-virtan/server.cc @@ -73,7 +73,7 @@ class handler_allocator { void* allocate(std::size_t size) { if (in_use_ || size > alloc_size) { - return 0; + return nullptr; } in_use_ = true; return std::addressof(storage_); @@ -156,7 +156,7 @@ make_custom_alloc_handler(Allocator& allocator, Handler&& handler) { return custom_alloc_handler(allocator, std::forward(handler)); } -class connection { +class connection : public std::enable_shared_from_this { public: explicit connection(boost::asio::io_service& service) : socket_(service) {} ~connection() = default; @@ -165,7 +165,7 @@ class connection { void start() { socket_.async_read_some(boost::asio::buffer(data_), - make_custom_alloc_handler(allocator_, std::bind(&connection::read, this, + make_custom_alloc_handler(allocator_, std::bind(&connection::read, shared_from_this(), std::placeholders::_1, std::placeholders::_2))); } @@ -175,19 +175,15 @@ class connection { private: void read(const boost::system::error_code& e, std::size_t bytes_transferred) { - if (e) { - delete this; - } else { + if (!e) { boost::asio::async_write(socket_, boost::asio::buffer(data_, bytes_transferred), - make_custom_alloc_handler(allocator_, std::bind(&connection::write, this, + make_custom_alloc_handler(allocator_, std::bind(&connection::write, shared_from_this(), std::placeholders::_1))); } } void write(const boost::system::error_code& e) { - if (e) { - delete this; - } else { + if (!e) { start(); } } @@ -197,32 +193,28 @@ class connection { boost::asio::ip::tcp::socket socket_; }; -class acceptor { +class acceptor : public std::enable_shared_from_this { public: acceptor(boost::asio::io_service& service, const boost::asio::ip::tcp::acceptor::native_handle_type& native_acceptor) : - service_(service), acceptor_(service_, boost::asio::ip::tcp::v4(), native_acceptor) { - start_accept(); - } + service_(service), acceptor_(service_, boost::asio::ip::tcp::v4(), native_acceptor) {} ~acceptor() = default; acceptor(const acceptor&) = delete; acceptor& operator=(const acceptor&) = delete; -private: - void start_accept() { - auto c = new connection(service_); + void start() { + auto c = std::make_shared(service_); acceptor_.async_accept(c->socket(), make_custom_alloc_handler(allocator_, - std::bind(&acceptor::accept, this, c, std::placeholders::_1))); + std::bind(&acceptor::accept, shared_from_this(), c, std::placeholders::_1))); } - void accept(connection* c, const boost::system::error_code& e) { - if (e) { - delete c; - } else { +private: + void accept(const std::shared_ptr& c, const boost::system::error_code& e) { + if (!e) { c->start(); } - start_accept(); + start(); } boost::asio::io_service& service_; @@ -318,20 +310,35 @@ int main(int argc, char* argv[]) { auto host = po_values[host_option_name].as(); auto port = po_values[port_option_name].as(); auto thread_num = po_values[threads_option_name].as(); + boost::asio::io_service io_service; + boost::asio::ip::tcp::acceptor fake_a(io_service, + boost::asio::ip::tcp::endpoint( + boost::asio::ip::address::from_string(host), + port)); + auto native_handle = fake_a.native_handle(); + std::vector> io_services; + io_services.reserve(thread_num); std::vector threads; threads.reserve(thread_num); - boost::asio::io_service fake_s; - boost::asio::ip::tcp::acceptor fake_a(fake_s, boost::asio::ip::tcp::endpoint( - boost::asio::ip::address::from_string(host), port)); - auto native_handle = fake_a.native_handle(); for (std::size_t i = 0; i < thread_num; ++i) { - threads.emplace_back([native_handle]() { - boost::asio::io_service service(to_io_context_concurrency_hint(1)); - acceptor a(service, native_handle); + io_services.push_back(std::make_unique( + to_io_context_concurrency_hint(1))); + auto& service = **io_services.rbegin(); + threads.emplace_back([native_handle, &service]() { + std::make_shared(service, native_handle)->start(); service.run(); }); } - std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread::join)); + boost::asio::signal_set signal_set(io_service, SIGINT, SIGTERM); + signal_set.async_wait([&io_services](const boost::system::error_code& /*error*/, int /*signal*/) { + for (const auto& service : io_services) { + service->stop(); + } + }); + io_service.run(); + for (auto& thread : threads) { + thread.join(); + } return EXIT_SUCCESS; } catch (const boost::program_options::error& e) { diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index e49f1c4..6bd40e8 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -77,7 +77,7 @@ class handler_allocator { void* allocate(std::size_t size) { if (in_use_ || size > alloc_size) { - return 0; + return nullptr; } in_use_ = true; return std::addressof(storage_); From da7899d8dff0e9ab9f061a17abdff299e2d99860 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Sun, 27 Sep 2020 10:06:23 +0300 Subject: [PATCH 45/48] Reformatting --- c++-virtan/server.cc | 9 +++------ client-c++-virtan/client.cc | 9 +++------ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/c++-virtan/server.cc b/c++-virtan/server.cc index fdd2bce..586f21d 100644 --- a/c++-virtan/server.cc +++ b/c++-virtan/server.cc @@ -340,14 +340,11 @@ int main(int argc, char* argv[]) { thread.join(); } return EXIT_SUCCESS; - } - catch (const boost::program_options::error& e) { + } catch (const boost::program_options::error& e) { std::cerr << "Error reading options: " << e.what() << std::endl; - } - catch (const std::exception& e) { + } catch (const std::exception& e) { std::cerr << "Unexpected error: " << e.what() << std::endl; - } - catch (...) { + } catch (...) { std::cerr << "Unknown error" << std::endl; } return EXIT_FAILURE; diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index 6bd40e8..11cb6a9 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -591,14 +591,11 @@ int main(int argc, char* argv[]) { std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread::join)); print_stat(true); return EXIT_SUCCESS; - } - catch (const boost::program_options::error& e) { + } catch (const boost::program_options::error& e) { std::cerr << "Error reading options: " << e.what() << std::endl; - } - catch (const std::exception& e) { + } catch (const std::exception& e) { std::cerr << "Unexpected error: " << e.what() << std::endl; - } - catch (...) { + } catch (...) { std::cerr << "Unknown error" << std::endl; } return EXIT_FAILURE; From 6102bdb07ae09b0f4a860b1d98e73d99dc719cfe Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Sun, 27 Sep 2020 16:43:38 +0300 Subject: [PATCH 46/48] Refactoring and renaming --- c++-virtan/CMakeLists.txt | 2 +- c++-virtan/server.cc | 64 +++++++++++++++++++++----------- client-c++-virtan/CMakeLists.txt | 2 +- client-c++-virtan/client.cc | 29 ++++++++------- 4 files changed, 59 insertions(+), 38 deletions(-) diff --git a/c++-virtan/CMakeLists.txt b/c++-virtan/CMakeLists.txt index 82eba5d..c2991e8 100644 --- a/c++-virtan/CMakeLists.txt +++ b/c++-virtan/CMakeLists.txt @@ -34,7 +34,7 @@ if(MINGW) endif() add_executable(server "server.cc") -target_compile_features(server PRIVATE cxx_std_11) +target_compile_features(server PRIVATE cxx_std_11 cxx_auto_type) target_compile_definitions(server PRIVATE ${compile_definitions}) target_include_directories(server PRIVATE ${Boost_INCLUDE_DIRS}) target_link_libraries(server PRIVATE ${libraries}) diff --git a/c++-virtan/server.cc b/c++-virtan/server.cc index 586f21d..88a1386 100644 --- a/c++-virtan/server.cc +++ b/c++-virtan/server.cc @@ -3,17 +3,17 @@ #endif #include -#include -#include +#include +#include #include +#include #include #include -#include #include #include #include -#include -#include +#include +#include #include #include #include @@ -46,16 +46,16 @@ bool is_continuation(Context& context) { return asio_handler_is_continuation(std::addressof(context)); } -#else // BOOST_VERSION >= 105400 +#else template bool is_continuation(Context& /*context*/) { return false; } -#endif // BOOST_VERSION >= 105400 +#endif -} +} // namespace asio_helpers namespace { @@ -156,10 +156,20 @@ make_custom_alloc_handler(Allocator& allocator, Handler&& handler) { return custom_alloc_handler(allocator, std::forward(handler)); } +template +struct shared_ptr_factory_helper : T { + + template + explicit shared_ptr_factory_helper(Arg&&... arg) : T(std::forward(arg)...) {} + +}; + class connection : public std::enable_shared_from_this { public: - explicit connection(boost::asio::io_service& service) : socket_(service) {} - ~connection() = default; + static std::shared_ptr create(boost::asio::io_service& service) { + return std::make_shared>(service); + } + connection(const connection&) = delete; connection& operator=(const connection&) = delete; @@ -173,6 +183,10 @@ class connection : public std::enable_shared_from_this { return socket_; } +protected: + explicit connection(boost::asio::io_service& service) : socket_(service) {} + ~connection() = default; + private: void read(const boost::system::error_code& e, std::size_t bytes_transferred) { if (!e) { @@ -195,20 +209,26 @@ class connection : public std::enable_shared_from_this { class acceptor : public std::enable_shared_from_this { public: - acceptor(boost::asio::io_service& service, - const boost::asio::ip::tcp::acceptor::native_handle_type& native_acceptor) : - service_(service), acceptor_(service_, boost::asio::ip::tcp::v4(), native_acceptor) {} + static std::shared_ptr create(boost::asio::io_service& service, + const boost::asio::ip::tcp::acceptor::native_handle_type& native_acceptor) { + return std::make_shared>(service, native_acceptor); + } - ~acceptor() = default; acceptor(const acceptor&) = delete; acceptor& operator=(const acceptor&) = delete; void start() { - auto c = std::make_shared(service_); + auto c = connection::create(service_); acceptor_.async_accept(c->socket(), make_custom_alloc_handler(allocator_, std::bind(&acceptor::accept, shared_from_this(), c, std::placeholders::_1))); } +protected: + acceptor(boost::asio::io_service& service, + const boost::asio::ip::tcp::acceptor::native_handle_type& native_acceptor) : + service_(service), acceptor_(service_, boost::asio::ip::tcp::v4(), native_acceptor) {} + ~acceptor() = default; + private: void accept(const std::shared_ptr& c, const boost::system::error_code& e) { if (!e) { @@ -231,7 +251,7 @@ io_context_concurrency_hint to_io_context_concurrency_hint(std::size_t hint) { : boost::numeric_cast(hint); } -#else // BOOST_VERSION >= 106600 +#else typedef std::size_t io_context_concurrency_hint; @@ -239,12 +259,12 @@ io_context_concurrency_hint to_io_context_concurrency_hint(std::size_t hint) { return hint; } -#endif // BOOST_VERSION >= 106600 +#endif -const char* help_option_name = "help"; -const char* host_option_name = "host"; -const char* port_option_name = "port"; -const char* threads_option_name = "threads"; +const char* help_option_name = "help"; +const char* host_option_name = "host"; +const char* port_option_name = "port"; +const char* threads_option_name = "threads"; boost::program_options::options_description build_program_options_description() { boost::program_options::options_description description("Usage"); @@ -325,7 +345,7 @@ int main(int argc, char* argv[]) { to_io_context_concurrency_hint(1))); auto& service = **io_services.rbegin(); threads.emplace_back([native_handle, &service]() { - std::make_shared(service, native_handle)->start(); + acceptor::create(service, native_handle)->start(); service.run(); }); } diff --git a/client-c++-virtan/CMakeLists.txt b/client-c++-virtan/CMakeLists.txt index fef0c11..eedf5e9 100644 --- a/client-c++-virtan/CMakeLists.txt +++ b/client-c++-virtan/CMakeLists.txt @@ -34,7 +34,7 @@ if(MINGW) endif() add_executable(client "client.cc") -target_compile_features(client PRIVATE cxx_std_11) +target_compile_features(client PRIVATE cxx_std_11 cxx_auto_type) target_compile_definitions(client PRIVATE ${compile_definitions}) target_include_directories(client PRIVATE ${Boost_INCLUDE_DIRS}) target_link_libraries(client PRIVATE ${libraries}) diff --git a/client-c++-virtan/client.cc b/client-c++-virtan/client.cc index 11cb6a9..23a1667 100644 --- a/client-c++-virtan/client.cc +++ b/client-c++-virtan/client.cc @@ -3,21 +3,20 @@ #endif #include +#include #include -#include -#include +#include #include +#include #include #include #include #include #include -#include #include -#include #include -#include #include +#include #include #include #include @@ -50,16 +49,16 @@ bool is_continuation(Context& context) { return asio_handler_is_continuation(std::addressof(context)); } -#else // BOOST_VERSION >= 105400 +#else template bool is_continuation(Context& /*context*/) { return false; } -#endif // BOOST_VERSION >= 105400 +#endif -} +} // namespace asio_helpers namespace { @@ -557,13 +556,13 @@ int main(int argc, char* argv[]) { std::vector> services; services.reserve(thread_num); for (std::size_t i = 0; i < thread_num; ++i) { - services.emplace_back(std::make_unique( + services.push_back(std::make_unique( to_io_context_concurrency_hint(1))); } connection_vector connections; connections.reserve(clients_max); for (std::size_t i = 0; i < clients_max; ++i) { - connections.emplace_back(std::make_unique( + connections.push_back(std::make_unique( *services[i % thread_num], *connection_data[i], rand_engine)); } std::vector threads; @@ -585,10 +584,12 @@ int main(int argc, char* argv[]) { std::this_thread::sleep_for(std::chrono::seconds(print_stats_interval)); } stopped = true; - std::for_each(services.begin(), services.end(), [](std::unique_ptr& s) { - s->stop(); - }); - std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread::join)); + for (auto& service : services) { + service->stop(); + } + for (auto& thread : threads) { + thread.join(); + } print_stat(true); return EXIT_SUCCESS; } catch (const boost::program_options::error& e) { From 51b8ee77195d52ef98cd0ac7e293979bb8c38ad6 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Sun, 27 Sep 2020 16:45:56 +0300 Subject: [PATCH 47/48] Enhanced README --- c++-virtan/README.md | 4 ++-- client-c++-virtan/README.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/c++-virtan/README.md b/c++-virtan/README.md index d7aded2..92bd061 100644 --- a/c++-virtan/README.md +++ b/c++-virtan/README.md @@ -23,12 +23,12 @@ cmake --build . ### Requirements 1. Docker 17.06+ for building -1. Current directory is directory where this file is located +1. Current directory is directory where this repository is cloned ### Steps to build ```bash -docker build -t cpp-virtan . +docker build -t cpp-virtan c++-virtan ``` ## Running with Docker diff --git a/client-c++-virtan/README.md b/client-c++-virtan/README.md index 781878b..164207e 100644 --- a/client-c++-virtan/README.md +++ b/client-c++-virtan/README.md @@ -23,12 +23,12 @@ cmake --build . ### Requirements 1. Docker 17.06+ for building -1. Current directory is directory where this file is located +1. Current directory is directory where this repository is cloned ### Steps to build ```bash -docker build -t client-cpp-virtan . +docker build -t client-cpp-virtan client-c++-virtan ``` ## Running with Docker From e993dc5b07f5864be7bc3090ee1f726f48bff4e4 Mon Sep 17 00:00:00 2001 From: Marat Abrarov Date: Mon, 2 Nov 2020 08:22:01 +0300 Subject: [PATCH 48/48] Optimization of docker images --- c++-virtan/Dockerfile | 73 +++++++++++++++++------------------- client-c++-virtan/Dockerfile | 73 +++++++++++++++++------------------- 2 files changed, 70 insertions(+), 76 deletions(-) diff --git a/c++-virtan/Dockerfile b/c++-virtan/Dockerfile index edfaed4..e10e790 100644 --- a/c++-virtan/Dockerfile +++ b/c++-virtan/Dockerfile @@ -1,60 +1,57 @@ FROM ubuntu:20.04 AS build -ARG CMAKE_VERSION="3.18.3" -ARG BOOST_VERSION="1.74.0" -ARG BOOST_URL="https://dl.bintray.com/mabrarov/generic/boost" - -ENV TZ="Europe/Moscow" \ - PATH="/opt/cmake/bin:${PATH}" \ - DEPENDENCIES_DIR="/dependency" - -RUN ln -snf "/usr/share/zoneinfo/${TZ}" /etc/localtime && \ - echo "${TZ}" > /etc/timezone && \ - apt-get update && \ - apt-get install -y \ - software-properties-common \ +RUN apt-get update && \ + apt-get install -y --no-install-suggests --no-install-recommends \ + ca-certificates \ curl \ git \ g++ \ make \ - libstdc++-9-dev + libstdc++-9-dev && \ + rm -rf /var/lib/apt/lists/* + +ENV CMAKE_HOME="/opt/cmake" + +ARG CMAKE_VERSION="3.18.4" -RUN mkdir -p /opt && \ +RUN mkdir -p "${CMAKE_HOME}" && \ cmake_url="https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz" && \ - echo "Downloading CMake ${CMAKE_VERSION} from ${cmake_url}" && \ - curl -jksSL "${cmake_url}" | tar -xzf - -C /opt && \ - mv -f "/opt/cmake-${CMAKE_VERSION}-Linux-x86_64" /opt/cmake + echo "Downloading CMake ${CMAKE_VERSION} from ${cmake_url} to ${CMAKE_HOME}" && \ + curl -jksSL "${cmake_url}" | tar -xzf - -C "${CMAKE_HOME}" --strip-components 1 -RUN mkdir -p "${DEPENDENCIES_DIR}" && \ +ENV PATH="${CMAKE_HOME}/bin:${PATH}" \ + BOOST_HOME="/usr/lib/boost" + +ARG BOOST_VERSION="1.74.0" +ARG BOOST_URL="https://dl.bintray.com/mabrarov/generic/boost" + +RUN mkdir -p "${BOOST_HOME}" && \ boost_archive_url="${BOOST_URL}/${BOOST_VERSION}/boost-${BOOST_VERSION}-x64-gcc$(gcc -dumpversion | sed -r 's/^([[:digit:]]+)(\..*)?$/\1/;t;d').tar.gz" && \ - echo "Downloading Boost from ${boost_archive_url}" && \ - curl --connect-timeout 300 \ - --max-time 1800 \ - --retry 10 \ - --retry-delay 10 \ - -jksSL \ - "${boost_archive_url}" | tar -xz -C "${DEPENDENCIES_DIR}" - -ADD ["CMakeLists.txt", "/cpp-virtan/"] -ADD ["server.cc", "/cpp-virtan/"] - -RUN mkdir -p "/cpp-virtan/build" && \ - cd "/cpp-virtan/build" && \ - boost_dir="${DEPENDENCIES_DIR}/boost-${BOOST_VERSION}-x64-gcc$(gcc -dumpversion | sed -r 's/^([[:digit:]]+)(\..*)?$/\1/;t;d')" && \ + echo "Downloading Boost from ${boost_archive_url} to ${BOOST_HOME}" && \ + curl --connect-timeout 300 --max-time 1800 --retry 10 --retry-delay 10 \ + -jksSL "${boost_archive_url}" \ + | tar -xz -C "${BOOST_HOME}" --strip-components 1 + +ADD ["CMakeLists.txt", "server.cc", "/usr/src/cpp-virtan/"] + +RUN source_dir="/usr/src/cpp-virtan" && \ + build_dir="${source_dir}/build" && \ + mkdir -p "${build_dir}" && \ cmake \ -D CMAKE_SKIP_BUILD_RPATH=ON \ -D CMAKE_BUILD_TYPE=Release \ -D Boost_USE_MULTITHREADED=ON \ -D Boost_USE_STATIC_LIBS=ON \ -D Boost_NO_SYSTEM_PATHS=ON \ - -D BOOST_INCLUDEDIR="${boost_dir}/include" \ - -D BOOST_LIBRARYDIR="${boost_dir}/lib" \ - .. && \ - cmake --build . + -D BOOST_INCLUDEDIR="${BOOST_HOME}/include" \ + -D BOOST_LIBRARYDIR="${BOOST_HOME}/lib" \ + -S "${source_dir}" \ + -B "${build_dir}" && \ + cmake --build "${build_dir}" FROM gcr.io/distroless/cc-debian10 -COPY --from=build /cpp-virtan/build/server /opt/cpp-virtan/server +COPY --from=build ["/usr/src/cpp-virtan/build/server", "/opt/cpp-virtan/server"] ENTRYPOINT ["/opt/cpp-virtan/server"] diff --git a/client-c++-virtan/Dockerfile b/client-c++-virtan/Dockerfile index 314cba7..98be139 100644 --- a/client-c++-virtan/Dockerfile +++ b/client-c++-virtan/Dockerfile @@ -1,60 +1,57 @@ FROM ubuntu:20.04 AS build -ARG CMAKE_VERSION="3.18.3" -ARG BOOST_VERSION="1.74.0" -ARG BOOST_URL="https://dl.bintray.com/mabrarov/generic/boost" - -ENV TZ="Europe/Moscow" \ - PATH="/opt/cmake/bin:${PATH}" \ - DEPENDENCIES_DIR="/dependency" - -RUN ln -snf "/usr/share/zoneinfo/${TZ}" /etc/localtime && \ - echo "${TZ}" > /etc/timezone && \ - apt-get update && \ - apt-get install -y \ - software-properties-common \ +RUN apt-get update && \ + apt-get install -y --no-install-suggests --no-install-recommends \ + ca-certificates \ curl \ git \ g++ \ make \ - libstdc++-9-dev + libstdc++-9-dev && \ + rm -rf /var/lib/apt/lists/* + +ENV CMAKE_HOME="/opt/cmake" + +ARG CMAKE_VERSION="3.18.4" -RUN mkdir -p /opt && \ +RUN mkdir -p "${CMAKE_HOME}" && \ cmake_url="https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz" && \ - echo "Downloading CMake ${CMAKE_VERSION} from ${cmake_url}" && \ - curl -jksSL "${cmake_url}" | tar -xzf - -C /opt && \ - mv -f "/opt/cmake-${CMAKE_VERSION}-Linux-x86_64" /opt/cmake + echo "Downloading CMake ${CMAKE_VERSION} from ${cmake_url} to ${CMAKE_HOME}" && \ + curl -jksSL "${cmake_url}" | tar -xzf - -C "${CMAKE_HOME}" --strip-components 1 -RUN mkdir -p "${DEPENDENCIES_DIR}" && \ +ENV PATH="${CMAKE_HOME}/bin:${PATH}" \ + BOOST_HOME="/usr/lib/boost" + +ARG BOOST_VERSION="1.74.0" +ARG BOOST_URL="https://dl.bintray.com/mabrarov/generic/boost" + +RUN mkdir -p "${BOOST_HOME}" && \ boost_archive_url="${BOOST_URL}/${BOOST_VERSION}/boost-${BOOST_VERSION}-x64-gcc$(gcc -dumpversion | sed -r 's/^([[:digit:]]+)(\..*)?$/\1/;t;d').tar.gz" && \ - echo "Downloading Boost from ${boost_archive_url}" && \ - curl --connect-timeout 300 \ - --max-time 1800 \ - --retry 10 \ - --retry-delay 10 \ - -jksSL \ - "${boost_archive_url}" | tar -xz -C "${DEPENDENCIES_DIR}" - -ADD ["CMakeLists.txt", "/client-cpp-virtan/"] -ADD ["client.cc", "/client-cpp-virtan/"] - -RUN mkdir -p "/client-cpp-virtan/build" && \ - cd "/client-cpp-virtan/build" && \ - boost_dir="${DEPENDENCIES_DIR}/boost-${BOOST_VERSION}-x64-gcc$(gcc -dumpversion | sed -r 's/^([[:digit:]]+)(\..*)?$/\1/;t;d')" && \ + echo "Downloading Boost from ${boost_archive_url} to ${BOOST_HOME}" && \ + curl --connect-timeout 300 --max-time 1800 --retry 10 --retry-delay 10 \ + -jksSL "${boost_archive_url}" \ + | tar -xz -C "${BOOST_HOME}" --strip-components 1 + +ADD ["CMakeLists.txt", "client.cc", "/usr/src/client-cpp-virtan/"] + +RUN source_dir="/usr/src/client-cpp-virtan" && \ + build_dir="${source_dir}/build" && \ + mkdir -p "${build_dir}" && \ cmake \ -D CMAKE_SKIP_BUILD_RPATH=ON \ -D CMAKE_BUILD_TYPE=Release \ -D Boost_USE_MULTITHREADED=ON \ -D Boost_USE_STATIC_LIBS=ON \ -D Boost_NO_SYSTEM_PATHS=ON \ - -D BOOST_INCLUDEDIR="${boost_dir}/include" \ - -D BOOST_LIBRARYDIR="${boost_dir}/lib" \ - .. && \ - cmake --build . + -D BOOST_INCLUDEDIR="${BOOST_HOME}/include" \ + -D BOOST_LIBRARYDIR="${BOOST_HOME}/lib" \ + -S "${source_dir}" \ + -B "${build_dir}" && \ + cmake --build "${build_dir}" FROM gcr.io/distroless/cc-debian10 -COPY --from=build /client-cpp-virtan/build/client /opt/client-cpp-virtan/client +COPY --from=build ["/usr/src/client-cpp-virtan/build/client", "/opt/client-cpp-virtan/client"] ENTRYPOINT ["/opt/client-cpp-virtan/client"]