From bdac513da0e35e0dee8ff8d8a16a371b5e16b907 Mon Sep 17 00:00:00 2001 From: eidheim Date: Wed, 5 Jul 2017 12:57:45 +0200 Subject: [PATCH 1/4] Added SimpleWeb::asio namespace when using standalone asio as well --- client_http.hpp | 1 + server_http.hpp | 1 + 2 files changed, 2 insertions(+) diff --git a/client_http.hpp b/client_http.hpp index 96a17f4..1dd551b 100644 --- a/client_http.hpp +++ b/client_http.hpp @@ -9,6 +9,7 @@ #ifdef USE_STANDALONE_ASIO #include namespace SimpleWeb { + namespace asio = asio; using error_code = std::error_code; using errc = std::errc; using system_error = std::system_error; diff --git a/server_http.hpp b/server_http.hpp index 7862835..23ffd1e 100644 --- a/server_http.hpp +++ b/server_http.hpp @@ -11,6 +11,7 @@ #ifdef USE_STANDALONE_ASIO #include namespace SimpleWeb { + namespace asio = asio; using error_code = std::error_code; using errc = std::errc; namespace make_error_code = std; From fa5a3bdc4f6fc208dc69cb55683e6edb730df585 Mon Sep 17 00:00:00 2001 From: eidheim Date: Wed, 5 Jul 2017 13:15:01 +0200 Subject: [PATCH 2/4] Reverted namespace asio = asio for standalone asio because of gcc error --- client_http.hpp | 1 - server_http.hpp | 1 - 2 files changed, 2 deletions(-) diff --git a/client_http.hpp b/client_http.hpp index 1dd551b..96a17f4 100644 --- a/client_http.hpp +++ b/client_http.hpp @@ -9,7 +9,6 @@ #ifdef USE_STANDALONE_ASIO #include namespace SimpleWeb { - namespace asio = asio; using error_code = std::error_code; using errc = std::errc; using system_error = std::system_error; diff --git a/server_http.hpp b/server_http.hpp index 23ffd1e..7862835 100644 --- a/server_http.hpp +++ b/server_http.hpp @@ -11,7 +11,6 @@ #ifdef USE_STANDALONE_ASIO #include namespace SimpleWeb { - namespace asio = asio; using error_code = std::error_code; using errc = std::errc; namespace make_error_code = std; From 28eeef7d65b1df271c2e612744a0b351331a7cf1 Mon Sep 17 00:00:00 2001 From: eidheim Date: Fri, 7 Jul 2017 17:16:53 +0200 Subject: [PATCH 3/4] Synchronous client request calls is now safe to use concurrently --- client_http.hpp | 31 +++++++++++++++++++++++++++---- http_examples.cpp | 1 - https_examples.cpp | 1 - tests/io_test.cpp | 30 +++++++++++++++++++++++++++++- 4 files changed, 56 insertions(+), 7 deletions(-) diff --git a/client_http.hpp b/client_http.hpp index 96a17f4..afa669b 100644 --- a/client_http.hpp +++ b/client_http.hpp @@ -132,7 +132,8 @@ namespace SimpleWeb { virtual ~ClientBase() {} /// Convenience function to perform synchronous request. The io_service is run within this function. - /// If reusing the io_service for other tasks, please use the asynchronous request functions instead. + /// If reusing the io_service for other tasks, use the asynchronous request functions instead. + /// Do not use concurrently with the asynchronous request functions. std::shared_ptr request(const std::string &method, const std::string &path = std::string("/"), string_view content = "", const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) { std::shared_ptr response; @@ -142,14 +143,23 @@ namespace SimpleWeb { throw system_error(ec); }); - io_service->reset(); + { + std::unique_lock lock(concurrent_synchronous_requests_mutex); + ++concurrent_synchronous_requests; + } io_service->run(); + { + std::unique_lock lock(concurrent_synchronous_requests_mutex); + if(--concurrent_synchronous_requests == 0) + io_service->reset(); + } return response; } /// Convenience function to perform synchronous request. The io_service is run within this function. - /// If reusing the io_service for other tasks, please use the asynchronous request functions instead. + /// If reusing the io_service for other tasks, use the asynchronous request functions instead. + /// Do not use concurrently with the asynchronous request functions. std::shared_ptr request(const std::string &method, const std::string &path, std::istream &content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) { std::shared_ptr response; @@ -159,13 +169,22 @@ namespace SimpleWeb { throw system_error(ec); }); - io_service->reset(); + { + std::unique_lock lock(concurrent_synchronous_requests_mutex); + ++concurrent_synchronous_requests; + } io_service->run(); + { + std::unique_lock lock(concurrent_synchronous_requests_mutex); + if(--concurrent_synchronous_requests == 0) + io_service->reset(); + } return response; } /// Asynchronous request where setting and/or running Client's io_service is required. + /// Do not use concurrently with the synchronous request functions. void request(const std::string &method, const std::string &path, string_view content, const CaseInsensitiveMultimap &header, std::function, const error_code &)> &&request_callback_) { auto session = std::make_shared(io_service, get_connection(), create_request_header(method, path, header)); @@ -208,6 +227,7 @@ namespace SimpleWeb { } /// Asynchronous request where setting and/or running Client's io_service is required. + /// Do not use concurrently with the synchronous request functions. void request(const std::string &method, const std::string &path, string_view content, std::function, const error_code &)> &&request_callback) { request(method, path, content, CaseInsensitiveMultimap(), std::move(request_callback)); @@ -283,6 +303,9 @@ namespace SimpleWeb { std::shared_ptr>> connections; std::shared_ptr connections_mutex; + size_t concurrent_synchronous_requests = 0; + std::mutex concurrent_synchronous_requests_mutex; + ClientBase(const std::string &host_port, unsigned short default_port) : io_service(new asio::io_service()), connections(new std::vector>()), connections_mutex(new std::mutex()) { auto parsed_host_port = parse_host_port(host_port, default_port); diff --git a/http_examples.cpp b/http_examples.cpp index 3c98748..f6a1a10 100644 --- a/http_examples.cpp +++ b/http_examples.cpp @@ -221,7 +221,6 @@ int main() { if(!ec) cout << response->content.rdbuf() << endl; }); - client.io_service->reset(); // needed because the io_service has been run already in the synchronous examples client.io_service->run(); server_thread.join(); diff --git a/https_examples.cpp b/https_examples.cpp index a93b423..465e7b1 100644 --- a/https_examples.cpp +++ b/https_examples.cpp @@ -220,7 +220,6 @@ int main() { if(!ec) cout << response->content.rdbuf() << endl; }); - client.io_service->reset(); // needed because the io_service has been run already in the synchronous examples client.io_service->run(); server_thread.join(); diff --git a/tests/io_test.cpp b/tests/io_test.cpp index d0d75ef..c7bf3a0 100644 --- a/tests/io_test.cpp +++ b/tests/io_test.cpp @@ -179,7 +179,7 @@ int main() { assert(call); { - vector calls(100); + vector calls(100, 0); vector threads; for(size_t c = 0; c < 100; ++c) { calls[c] = 0; @@ -204,6 +204,34 @@ int main() { } } + /// Test concurrent synchronous request calls + { + HttpClient client("localhost:8080"); + { + vector calls(100, 0); + vector threads; + for(size_t c = 0; c < 100; ++c) { + calls[c] = 0; + threads.emplace_back([c, &client, &calls] { + try { + auto r = client.request("GET", "/match/123"); + assert(SimpleWeb::status_code(r->status_code) == SimpleWeb::StatusCode::success_ok); + assert(r->content.string() == "123"); + calls[c] = 1; + } + catch(...) { + assert(false); + } + }); + } + for(auto &thread : threads) + thread.join(); + assert(client.connections->size() == 1); + for(auto call : calls) + assert(call); + } + } + { HttpClient client("localhost:8080"); assert(client.connections->size() == 0); From 6c6f30302a471002a81fab8bea1c7c3cf5c15f40 Mon Sep 17 00:00:00 2001 From: eidheim Date: Fri, 7 Jul 2017 17:36:27 +0200 Subject: [PATCH 4/4] Fixed io_test (out of stack?), also fixed concurrent sync requests count on request error. --- client_http.hpp | 24 ++++++++++++++++-------- status_code.hpp | 4 ++-- tests/io_test.cpp | 6 ++---- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/client_http.hpp b/client_http.hpp index afa669b..d7d7087 100644 --- a/client_http.hpp +++ b/client_http.hpp @@ -137,10 +137,10 @@ namespace SimpleWeb { std::shared_ptr request(const std::string &method, const std::string &path = std::string("/"), string_view content = "", const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) { std::shared_ptr response; - request(method, path, content, header, [&response](std::shared_ptr response_, const error_code &ec) { + error_code ec; + request(method, path, content, header, [&response, &ec](std::shared_ptr response_, const error_code &ec_) { response = response_; - if(ec) - throw system_error(ec); + ec = ec_; }); { @@ -150,10 +150,14 @@ namespace SimpleWeb { io_service->run(); { std::unique_lock lock(concurrent_synchronous_requests_mutex); - if(--concurrent_synchronous_requests == 0) + --concurrent_synchronous_requests; + if(!concurrent_synchronous_requests) io_service->reset(); } + if(ec) + throw system_error(ec); + return response; } @@ -163,10 +167,10 @@ namespace SimpleWeb { std::shared_ptr request(const std::string &method, const std::string &path, std::istream &content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) { std::shared_ptr response; - request(method, path, content, header, [&response](std::shared_ptr response_, const error_code &ec) { + error_code ec; + request(method, path, content, header, [&response, &ec](std::shared_ptr response_, const error_code &ec_) { response = response_; - if(ec) - throw system_error(ec); + ec = ec_; }); { @@ -176,10 +180,14 @@ namespace SimpleWeb { io_service->run(); { std::unique_lock lock(concurrent_synchronous_requests_mutex); - if(--concurrent_synchronous_requests == 0) + --concurrent_synchronous_requests; + if(!concurrent_synchronous_requests) io_service->reset(); } + if(ec) + throw system_error(ec); + return response; } diff --git a/status_code.hpp b/status_code.hpp index 3c3f833..2c699f6 100644 --- a/status_code.hpp +++ b/status_code.hpp @@ -70,8 +70,8 @@ namespace SimpleWeb { server_error_network_authentication_required }; - inline const static std::vector> &status_codes() { - static std::vector> status_codes = { + const static std::vector> &status_codes() { + const static std::vector> status_codes = { {StatusCode::unknown, ""}, {StatusCode::information_continue, "100 Continue"}, {StatusCode::information_switching_protocols, "101 Switching Protocols"}, diff --git a/tests/io_test.cpp b/tests/io_test.cpp index c7bf3a0..afd40f7 100644 --- a/tests/io_test.cpp +++ b/tests/io_test.cpp @@ -182,7 +182,6 @@ int main() { vector calls(100, 0); vector threads; for(size_t c = 0; c < 100; ++c) { - calls[c] = 0; threads.emplace_back([c, &client, &calls] { client.request("GET", "/match/123", [c, &calls](shared_ptr response, const SimpleWeb::error_code &ec) { assert(!ec); @@ -208,10 +207,9 @@ int main() { { HttpClient client("localhost:8080"); { - vector calls(100, 0); + vector calls(2, 0); vector threads; - for(size_t c = 0; c < 100; ++c) { - calls[c] = 0; + for(size_t c = 0; c < 2; ++c) { threads.emplace_back([c, &client, &calls] { try { auto r = client.request("GET", "/match/123");