Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions src/detail/posix_transport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,23 @@ std::size_t PosixTransport::receive(std::uint8_t *data, const std::size_t capaci
}

pollfd descriptor{socket, POLLIN, 0};
const auto deadline = std::chrono::steady_clock::now() + timeout;
auto remaining = timeout;
int poll_result{};
do {
poll_result = ::poll(&descriptor, 1, timeout_milliseconds(timeout));
} while (poll_result < 0 && errno == EINTR);
if (poll_result < 0) {
throw socket_error("failed to wait for UDP record");
while (true) {
poll_result = ::poll(&descriptor, 1, timeout_milliseconds(remaining));
if (poll_result >= 0) {
break;
}
if (errno != EINTR) {
throw socket_error("failed to wait for UDP record");
}

const auto now = std::chrono::steady_clock::now();
if (now >= deadline) {
return 0;
}
remaining = deadline - now;
}
if (poll_result == 0) {
return 0;
Expand Down
15 changes: 15 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,21 @@ target_link_libraries(netft_fault_latch PRIVATE netft::netft ${NETFT_GTEST_MAIN_

add_test(NAME netft_fault_latch COMMAND netft_fault_latch)

add_executable(netft_posix_transport
test_posix_transport.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../src/detail/posix_transport.cpp
)
target_compile_features(netft_posix_transport PRIVATE cxx_std_17)
target_include_directories(netft_posix_transport PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/../src
)
target_link_options(netft_posix_transport PRIVATE
"-Wl,--wrap=poll"
)
target_link_libraries(netft_posix_transport PRIVATE ${NETFT_GTEST_MAIN_TARGET})

add_test(NAME netft_posix_transport COMMAND netft_posix_transport)

add_executable(netft_discovery
test_discovery.cpp
support/fake_http_server.cpp
Expand Down
72 changes: 72 additions & 0 deletions test/test_posix_transport.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#include "detail/posix_transport.hpp"

#include <gtest/gtest.h>

#include <poll.h>

#include <array>
#include <cerrno>
#include <chrono>
#include <cstdint>
#include <stdexcept>
#include <thread>
#include <vector>

namespace {

using namespace std::chrono_literals;

enum class PollBehavior { RepeatedEintrThenTimeout, Error };

constexpr int kInterruptCount = 3;
std::vector<int> poll_timeouts;
int poll_calls{};
PollBehavior poll_behavior{PollBehavior::RepeatedEintrThenTimeout};

} // namespace

extern "C" int __wrap_poll(pollfd *, nfds_t, const int timeout) {
poll_timeouts.push_back(timeout);
if (poll_behavior == PollBehavior::Error) {
errno = EBADF;
return -1;
}
if (poll_calls++ < kInterruptCount) {
std::this_thread::sleep_for(10ms);
errno = EINTR;
return -1;
}

std::this_thread::sleep_for(std::chrono::milliseconds{timeout});
return 0;
}

TEST(PosixTransportTest, RepeatedEintrPreservesOriginalTimeout) {
poll_timeouts.clear();
poll_calls = 0;
poll_behavior = PollBehavior::RepeatedEintrThenTimeout;

netft::detail::PosixTransport transport;
transport.connect("127.0.0.1", 49152);
std::array<std::uint8_t, 36> buffer{};

EXPECT_EQ(transport.receive(buffer.data(), buffer.size(), 100ms), 0U);

ASSERT_EQ(poll_timeouts.size(), static_cast<std::size_t>(kInterruptCount + 1));
for (std::size_t index = 1; index < poll_timeouts.size(); ++index) {
EXPECT_LT(poll_timeouts[index], poll_timeouts[index - 1]);
}
}

TEST(PosixTransportTest, NonEintrPollErrorStillThrows) {
poll_timeouts.clear();
poll_calls = 0;
poll_behavior = PollBehavior::Error;

netft::detail::PosixTransport transport;
transport.connect("127.0.0.1", 49152);
std::array<std::uint8_t, 36> buffer{};

EXPECT_THROW(transport.receive(buffer.data(), buffer.size(), 100ms), std::runtime_error);
EXPECT_EQ(poll_timeouts.size(), 1U);
}
Loading