|
6 | 6 | #include "gtest/gtest.h" |
7 | 7 |
|
8 | 8 | #ifdef ENABLE_OTLP_RETRY_PREVIEW |
| 9 | +# include <ratio> |
9 | 10 | # include "gmock/gmock.h" |
| 11 | +# ifdef _WIN32 |
| 12 | +# include <windows.h> |
| 13 | +# else |
| 14 | +# include <ctime> |
| 15 | +# endif |
10 | 16 | #endif // ENABLE_OTLP_RETRY_PREVIEW |
11 | 17 |
|
12 | 18 | #ifdef ENABLE_OTLP_COMPRESSION_PREVIEW |
@@ -770,6 +776,64 @@ TEST_F(BasicCurlHttpTests, RetryAfterBeyondMaxBackoffDoesNotDelayShutdown) |
770 | 776 | return received.uri == "/retry-after/"; |
771 | 777 | })); |
772 | 778 | } |
| 779 | + |
| 780 | +// CPU time of the whole process. std::clock() measures wall time on Windows. |
| 781 | +std::chrono::microseconds ProcessCpuTime() |
| 782 | +{ |
| 783 | +# ifdef _WIN32 |
| 784 | + FILETIME created{}, exited{}, kernel{}, user{}; |
| 785 | + GetProcessTimes(GetCurrentProcess(), &created, &exited, &kernel, &user); |
| 786 | + // FILETIME counts 100 ns ticks. |
| 787 | + const auto ticks = [](const FILETIME &time) { |
| 788 | + return (static_cast<std::uint64_t>(time.dwHighDateTime) << 32) | time.dwLowDateTime; |
| 789 | + }; |
| 790 | + return std::chrono::microseconds{static_cast<std::int64_t>((ticks(kernel) + ticks(user)) / 10)}; |
| 791 | +# else |
| 792 | + return std::chrono::microseconds{ |
| 793 | + static_cast<std::int64_t>(static_cast<double>(std::clock()) * 1000000 / CLOCKS_PER_SEC)}; |
| 794 | +# endif |
| 795 | +} |
| 796 | + |
| 797 | +// During shutdown the IO loop used to skip its poll while a retry waited out its backoff. |
| 798 | +TEST_F(BasicCurlHttpTests, ShutdownDoesNotSpinWhileARetryIsQueued) |
| 799 | +{ |
| 800 | + received_requests_.clear(); |
| 801 | + curl::HttpClient http_client; |
| 802 | + // Three waits: the first can still sleep on a poll the loop asked for before the join. |
| 803 | + const http_client::RetryPolicy retry_policy = {4, std::chrono::duration<float>{0.25f}, |
| 804 | + std::chrono::duration<float>{5.0f}, 2.0f}; |
| 805 | + |
| 806 | + auto session = http_client.CreateSession("http://127.0.0.1:19000"); |
| 807 | + auto request = session->CreateRequest(); |
| 808 | + request->SetMethod(http_client::Method::Post); |
| 809 | + request->SetUri("retry/"); |
| 810 | + request->SetRetryPolicy(retry_policy); |
| 811 | + auto handler = std::make_shared<RetryEventHandler>(); |
| 812 | + session->SendRequest(handler); |
| 813 | + ASSERT_TRUE(waitForRequests(30, 1)); |
| 814 | + |
| 815 | + const auto cpu_before = ProcessCpuTime(); |
| 816 | + const auto started_at = std::chrono::steady_clock::now(); |
| 817 | + http_client.WaitBackgroundThreadExit(); |
| 818 | + const auto joined_in = std::chrono::steady_clock::now() - started_at; |
| 819 | + const auto cpu_used = ProcessCpuTime() - cpu_before; |
| 820 | + |
| 821 | + session->FinishSession(); |
| 822 | + ASSERT_TRUE(handler->got_response_.load(std::memory_order_acquire)); |
| 823 | + |
| 824 | + std::unique_lock<std::mutex> lock_requests(mtx_requests); |
| 825 | + EXPECT_EQ(4, std::count_if(received_requests_.begin(), received_requests_.end(), |
| 826 | + [](const HTTP_SERVER_NS::HttpRequest &received) { |
| 827 | + return received.uri == "/retry/"; |
| 828 | + })); |
| 829 | + |
| 830 | +# ifdef __APPLE__ |
| 831 | + GTEST_SKIP() << "The test server's kqueue reactor spins while a connection is open"; |
| 832 | +# endif |
| 833 | + EXPECT_TRUE(cpu_used * 2 < joined_in) |
| 834 | + << "join ms: " << std::chrono::duration_cast<std::chrono::milliseconds>(joined_in).count() |
| 835 | + << ", CPU ms: " << std::chrono::duration_cast<std::chrono::milliseconds>(cpu_used).count(); |
| 836 | +} |
773 | 837 | #endif // ENABLE_OTLP_RETRY_PREVIEW |
774 | 838 |
|
775 | 839 | // A cancel that arrives once the server has answered used to deliver Cancelled and the response, |
|
0 commit comments