From 06e7ce83582bdeb6508554664a4a30b33a2d3714 Mon Sep 17 00:00:00 2001 From: mrabine Date: Sat, 29 Aug 2026 18:56:21 +0200 Subject: [PATCH 1/9] Use chrono in syncron socket API --- core/include/join/socket.hpp | 18 ++++++---- core/include/join/socket_stream.hpp | 27 +++++++------- core/include/join/stream_socket.hpp | 27 +++++++------- core/include/join/utils.hpp | 17 +++++++++ core/tests/hybrid_proactor_test.cpp | 4 +-- .../tests/icmp_async_datagram_socket_test.cpp | 4 +-- core/tests/icmp_socket_test.cpp | 4 +-- core/tests/proactor_test.cpp | 4 +-- core/tests/raw_async_socket_test.cpp | 4 +-- core/tests/raw_socket_test.cpp | 4 +-- core/tests/reactor_test.cpp | 4 +-- core/tests/sqpoll_proactor_test.cpp | 4 +-- core/tests/tcp_async_acceptor_test.cpp | 4 +-- core/tests/tcp_async_stream_socket_test.cpp | 4 +-- core/tests/tcp_socket_stream_test.cpp | 4 +-- core/tests/tcp_socket_test.cpp | 4 +-- core/tests/udp_async_datagram_socket_test.cpp | 4 +-- core/tests/udp_socket_test.cpp | 4 +-- core/tests/unix_async_acceptor_test.cpp | 4 +-- .../tests/unix_async_datagram_socket_test.cpp | 4 +-- core/tests/unix_async_stream_socket_test.cpp | 4 +-- core/tests/unix_datagram_socket_test.cpp | 4 +-- core/tests/unix_stream_socket_test.cpp | 4 +-- core/tests/utils_test.cpp | 16 +++++++++ crypto/include/join/tls.hpp | 35 ++++++++++--------- crypto/include/join/tls_wrapper.hpp | 12 +++---- crypto/tests/dtls_wrapper_test.cpp | 4 +-- crypto/tests/tls_stream_test.cpp | 4 +-- crypto/tests/tls_wrapper_test.cpp | 4 +-- fabric/include/join/resolver.hpp | 8 ++--- fabric/tests/netlink_socket_test.cpp | 4 +-- services/include/join/http_server.hpp | 2 +- services/samples/webbench.cpp | 3 +- services/tests/smtp_test.cpp | 4 +-- services/tests/smtps_test.cpp | 4 +-- 35 files changed, 154 insertions(+), 111 deletions(-) diff --git a/core/include/join/socket.hpp b/core/include/join/socket.hpp index f488781e..626497ba 100644 --- a/core/include/join/socket.hpp +++ b/core/include/join/socket.hpp @@ -32,6 +32,7 @@ #include // C++. +#include #include #include @@ -318,10 +319,10 @@ namespace join /** * @brief block until new data is available for reading. - * @param timeout timeout in milliseconds. + * @param timeout timeout duration (zero: infinite). * @return true if there is new data available for reading, false otherwise. */ - bool waitReadyRead (int timeout = 0) const noexcept + bool waitReadyRead (std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero ()) const noexcept { return (wait (true, false, timeout) == 0); } @@ -364,10 +365,10 @@ namespace join /** * @brief block until at least one byte can be written. - * @param timeout timeout in milliseconds. + * @param timeout timeout duration (zero: infinite). * @return true if data can be written, false otherwise. */ - bool waitReadyWrite (int timeout = 0) const noexcept + bool waitReadyWrite (std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero ()) const noexcept { return (wait (false, true, timeout) == 0); } @@ -625,10 +626,10 @@ namespace join * @brief wait for the socket handle to become ready. * @param wantRead set to true if want read * @param wantWrite set to true if want write. - * @param timeout timeout in milliseconds. + * @param timeout timeout duration (zero: infinite). * @return 0 on success, -1 on failure. */ - int wait (bool wantRead, bool wantWrite, int timeout) const noexcept + int wait (bool wantRead, bool wantWrite, std::chrono::nanoseconds timeout) const noexcept { struct pollfd handle; handle.fd = _handle; @@ -645,7 +646,10 @@ namespace join handle.events |= POLLOUT; } - int nset = (handle.fd > -1) ? ::poll (&handle, 1, timeout == 0 ? -1 : timeout) : -1; + struct timespec ts = toTimespec (timeout); + const struct timespec* deadline = (timeout > std::chrono::nanoseconds::zero ()) ? &ts : nullptr; + + int nset = (handle.fd > -1) ? ::ppoll (&handle, 1, deadline, nullptr) : -1; if (nset != 1) { if (nset == -1) diff --git a/core/include/join/socket_stream.hpp b/core/include/join/socket_stream.hpp index 891de8f6..c8920c9f 100644 --- a/core/include/join/socket_stream.hpp +++ b/core/include/join/socket_stream.hpp @@ -31,6 +31,7 @@ // C++. #include #include +#include #include namespace join @@ -194,18 +195,18 @@ namespace join /** * @brief set the socket timeout. - * @param ms timeout in milliseconds. + * @param timeout timeout duration (zero: infinite). */ - void timeout (int ms) + void timeout (std::chrono::nanoseconds timeout) { - _timeout = ms; + _timeout = timeout; } /** - * @brief get the current timeout in milliseconds. - * @return the current timeout. + * @brief get the current timeout duration. + * @return the current timeout duration. */ - int timeout () const + std::chrono::nanoseconds timeout () const { return _timeout; } @@ -324,7 +325,7 @@ namespace join std::unique_ptr _buf; /// timeout. - int _timeout = 30000; + std::chrono::nanoseconds _timeout = std::chrono::seconds (30); /// internal socket. Socket _socket; @@ -485,18 +486,18 @@ namespace join /** * @brief set the socket timeout. - * @param ms timeout in milliseconds. + * @param timeout timeout duration (zero: infinite). */ - void timeout (int ms) + void timeout (std::chrono::nanoseconds timeout) { - _sockbuf.timeout (ms); + _sockbuf.timeout (timeout); } /** - * @brief get the current timeout in milliseconds. - * @return the current timeout. + * @brief get the current timeout duration. + * @return the current timeout duration. */ - int timeout () const + std::chrono::nanoseconds timeout () const { return _sockbuf.timeout (); } diff --git a/core/include/join/stream_socket.hpp b/core/include/join/stream_socket.hpp index 6a31709f..a09f10f8 100644 --- a/core/include/join/stream_socket.hpp +++ b/core/include/join/stream_socket.hpp @@ -172,10 +172,10 @@ namespace join /** * @brief block until connected. - * @param timeout timeout in milliseconds. + * @param timeout timeout duration (zero: infinite). * @return true if connected, false otherwise. */ - bool waitConnected (int timeout = 0) + bool waitConnected (std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero ()) { if (this->_state != State::Connected) { @@ -237,10 +237,10 @@ namespace join /** * @brief wait until the connection as been shut down. - * @param timeout timeout in milliseconds. + * @param timeout timeout duration (zero: infinite). * return true if the connection as been shut down, false otherwise. */ - bool waitDisconnected (int timeout = 0) + bool waitDisconnected (std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero ()) { if ((this->_state != State::Disconnected) && (this->_state != State::Closed)) { @@ -251,7 +251,7 @@ namespace join } auto start = std::chrono::steady_clock::now (); - int elapsed = 0; + auto elapsed = std::chrono::nanoseconds::zero (); while ((lastError == Errc::TemporaryError) && (elapsed <= timeout)) { @@ -265,11 +265,10 @@ namespace join return true; } - if (timeout) + if (timeout != std::chrono::nanoseconds::zero ()) { - elapsed = std::chrono::duration_cast ( - std::chrono::steady_clock::now () - start) - .count (); + elapsed = std::chrono::duration_cast ( + std::chrono::steady_clock::now () - start); } } @@ -310,10 +309,11 @@ namespace join * @brief read data until size is reached or an error occurred. * @param data buffer used to store the data received. * @param size number of bytes to read. - * @param timeout timeout in milliseconds. + * @param timeout timeout duration (zero: infinite). * @return 0 on success, -1 on failure. */ - int readExactly (char* data, size_t size, int timeout = 0) noexcept + int readExactly (char* data, size_t size, + std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero ()) noexcept { size_t numRead = 0; @@ -343,10 +343,11 @@ namespace join * @brief write data until size is reached or an error occurred. * @param data data buffer to send. * @param size number of bytes to write. - * @param timeout timeout in milliseconds. + * @param timeout timeout duration (zero: infinite). * @return 0 on success, -1 on failure. */ - int writeExactly (const char* data, size_t size, int timeout = 0) noexcept + int writeExactly (const char* data, size_t size, + std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero ()) noexcept { size_t numWrite = 0; diff --git a/core/include/join/utils.hpp b/core/include/join/utils.hpp index 5e4bbe72..f4828cea 100644 --- a/core/include/join/utils.hpp +++ b/core/include/join/utils.hpp @@ -470,6 +470,23 @@ namespace join return {.tv_sec = scount, .tv_nsec = ncount}; } + + /** + * @brief converts duration to timespec. + * @param duration the duration to convert. + * @return timespec structure. + */ + template + struct timespec toTimespec (std::chrono::duration duration) + { + auto secs = std::chrono::duration_cast (duration); + auto ns = std::chrono::duration_cast (duration - secs); + + auto scount = secs.count (); + auto ncount = ns.count (); + + return {.tv_sec = scount, .tv_nsec = ncount}; + } } #endif diff --git a/core/tests/hybrid_proactor_test.cpp b/core/tests/hybrid_proactor_test.cpp index 1df8981a..b047f712 100644 --- a/core/tests/hybrid_proactor_test.cpp +++ b/core/tests/hybrid_proactor_test.cpp @@ -131,7 +131,7 @@ class HybridProactorTest : public CompletionHandler, public ::testing::Test static uint16_t _port; /// timeout. - static const int _timeout; + static const std::chrono::milliseconds _timeout; /// condition variable. static Condition _cond; @@ -166,7 +166,7 @@ Tcp::Socket HybridProactorTest::_client (Tcp::Socket::Blocking); Tcp::Socket HybridProactorTest::_server; std::string HybridProactorTest::_host = "127.0.0.1"; uint16_t HybridProactorTest::_port = 5001; -const int HybridProactorTest::_timeout = 1000; +const std::chrono::milliseconds HybridProactorTest::_timeout{1000}; Condition HybridProactorTest::_cond; Mutex HybridProactorTest::_mut; IoOperation* HybridProactorTest::_op = nullptr; diff --git a/core/tests/icmp_async_datagram_socket_test.cpp b/core/tests/icmp_async_datagram_socket_test.cpp index 950f5f12..e3f446af 100644 --- a/core/tests/icmp_async_datagram_socket_test.cpp +++ b/core/tests/icmp_async_datagram_socket_test.cpp @@ -113,7 +113,7 @@ class IcmpAsyncDatagramSocket : public ::testing::Test static const std::string _host; /// timeout. - static const int _timeout; + static const std::chrono::milliseconds _timeout; }; Mutex IcmpAsyncDatagramSocket::_mut; @@ -125,7 +125,7 @@ char IcmpAsyncDatagramSocket::_buf[1024] = {}; Icmp::Endpoint IcmpAsyncDatagramSocket::_from; char IcmpAsyncDatagramSocket::_data[sizeof (struct icmphdr)] = {}; const std::string IcmpAsyncDatagramSocket::_host = "127.0.0.1"; -const int IcmpAsyncDatagramSocket::_timeout = 1000; +const std::chrono::milliseconds IcmpAsyncDatagramSocket::_timeout{1000}; /** * @brief Test open method. diff --git a/core/tests/icmp_socket_test.cpp b/core/tests/icmp_socket_test.cpp index 1aa6d3de..3d25c9d2 100644 --- a/core/tests/icmp_socket_test.cpp +++ b/core/tests/icmp_socket_test.cpp @@ -60,14 +60,14 @@ class IcmpSocket : public ::testing::Test static const std::string _host; /// timeout. - static const int _timeout; + static const std::chrono::milliseconds _timeout; /// data. static std::unique_ptr _data; }; const std::string IcmpSocket::_host = "127.0.0.1"; -const int IcmpSocket::_timeout = 1000; +const std::chrono::milliseconds IcmpSocket::_timeout{1000}; std::unique_ptr IcmpSocket::_data; /** diff --git a/core/tests/proactor_test.cpp b/core/tests/proactor_test.cpp index 5f6610e5..16283ce4 100644 --- a/core/tests/proactor_test.cpp +++ b/core/tests/proactor_test.cpp @@ -135,7 +135,7 @@ class ProactorTest : public CompletionHandler, public ::testing::Test static uint16_t _port; /// timeout. - static const int _timeout; + static const std::chrono::milliseconds _timeout; /// condition variable. static Condition _cond; @@ -170,7 +170,7 @@ Tcp::Socket ProactorTest::_client (Tcp::Socket::Blocking); Tcp::Socket ProactorTest::_server; std::string ProactorTest::_host = "127.0.0.1"; uint16_t ProactorTest::_port = 5001; -const int ProactorTest::_timeout = 1000; +const std::chrono::milliseconds ProactorTest::_timeout{1000}; Condition ProactorTest::_cond; Mutex ProactorTest::_mut; IoOperation* ProactorTest::_op = nullptr; diff --git a/core/tests/raw_async_socket_test.cpp b/core/tests/raw_async_socket_test.cpp index f0769563..43e4fc7a 100644 --- a/core/tests/raw_async_socket_test.cpp +++ b/core/tests/raw_async_socket_test.cpp @@ -161,7 +161,7 @@ class RawAsyncSocket : public ::testing::Test static const std::string _interface; /// timeout. - static const int _timeout; + static const std::chrono::milliseconds _timeout; }; Mutex RawAsyncSocket::_mut; @@ -172,7 +172,7 @@ size_t RawAsyncSocket::_transferred = 0; RawAsyncSocket::Packet RawAsyncSocket::_packet; char RawAsyncSocket::_buf[2048] = {}; const std::string RawAsyncSocket::_interface = "lo"; -const int RawAsyncSocket::_timeout = 1000; +const std::chrono::milliseconds RawAsyncSocket::_timeout{1000}; /** * @brief Test open method. diff --git a/core/tests/raw_socket_test.cpp b/core/tests/raw_socket_test.cpp index 62682fad..402b1a7b 100644 --- a/core/tests/raw_socket_test.cpp +++ b/core/tests/raw_socket_test.cpp @@ -144,12 +144,12 @@ class RawSocket : public Raw::Socket, public EventHandler, public ::testing::Tes static const std::string _interface; /// timeout. - static const int _timeout; + static const std::chrono::milliseconds _timeout; }; RawSocket::Packet RawSocket::_packet; const std::string RawSocket::_interface = "lo"; -const int RawSocket::_timeout = 1000; +const std::chrono::milliseconds RawSocket::_timeout{1000}; /** * @brief Test open method. diff --git a/core/tests/reactor_test.cpp b/core/tests/reactor_test.cpp index 768a254b..64821617 100644 --- a/core/tests/reactor_test.cpp +++ b/core/tests/reactor_test.cpp @@ -159,7 +159,7 @@ class ReactorTest : public join::EventHandler, public ::testing::Test static uint16_t _port; /// timeout. - static const int _timeout; + static const std::chrono::milliseconds _timeout; /// condition variable. static Condition _cond; @@ -176,7 +176,7 @@ Tcp::Socket ReactorTest::_client (Tcp::Socket::Blocking); Tcp::Socket ReactorTest::_server; std::string ReactorTest::_host = "127.0.0.1"; uint16_t ReactorTest::_port = 5000; -const int ReactorTest::_timeout = 1000; +const std::chrono::milliseconds ReactorTest::_timeout{1000}; Condition ReactorTest::_cond; Mutex ReactorTest::_mut; std::string ReactorTest::_event; diff --git a/core/tests/sqpoll_proactor_test.cpp b/core/tests/sqpoll_proactor_test.cpp index 4e5c98c0..297d0671 100644 --- a/core/tests/sqpoll_proactor_test.cpp +++ b/core/tests/sqpoll_proactor_test.cpp @@ -131,7 +131,7 @@ class SqpollProactorTest : public CompletionHandler, public ::testing::Test static uint16_t _port; /// timeout. - static const int _timeout; + static const std::chrono::milliseconds _timeout; /// condition variable. static Condition _cond; @@ -166,7 +166,7 @@ Tcp::Socket SqpollProactorTest::_client (Tcp::Socket::Blocking); Tcp::Socket SqpollProactorTest::_server; std::string SqpollProactorTest::_host = "127.0.0.1"; uint16_t SqpollProactorTest::_port = 5001; -const int SqpollProactorTest::_timeout = 1000; +const std::chrono::milliseconds SqpollProactorTest::_timeout{1000}; Condition SqpollProactorTest::_cond; Mutex SqpollProactorTest::_mut; IoOperation* SqpollProactorTest::_op = nullptr; diff --git a/core/tests/tcp_async_acceptor_test.cpp b/core/tests/tcp_async_acceptor_test.cpp index 15e90eda..dc0fce0b 100644 --- a/core/tests/tcp_async_acceptor_test.cpp +++ b/core/tests/tcp_async_acceptor_test.cpp @@ -124,7 +124,7 @@ class TcpAsyncAcceptor : public ::testing::Test static const uint16_t _port; /// completion timeout. - static const int _timeout; + static const std::chrono::milliseconds _timeout; /// condition mutex. static Mutex _mut; @@ -144,7 +144,7 @@ class TcpAsyncAcceptor : public ::testing::Test const IpAddress TcpAsyncAcceptor::_address = "::1"; const uint16_t TcpAsyncAcceptor::_port = 5033; -const int TcpAsyncAcceptor::_timeout = 1000; +const std::chrono::milliseconds TcpAsyncAcceptor::_timeout{1000}; Mutex TcpAsyncAcceptor::_mut; Condition TcpAsyncAcceptor::_cond; std::error_code TcpAsyncAcceptor::_code; diff --git a/core/tests/tcp_async_stream_socket_test.cpp b/core/tests/tcp_async_stream_socket_test.cpp index d80e249a..cca077ae 100644 --- a/core/tests/tcp_async_stream_socket_test.cpp +++ b/core/tests/tcp_async_stream_socket_test.cpp @@ -217,7 +217,7 @@ class TcpAsyncStreamSocket : public ::testing::Test static const uint16_t _stallport; /// timeout. - static const int _timeout; + static const std::chrono::milliseconds _timeout; }; Mutex TcpAsyncStreamSocket::_mut; @@ -232,7 +232,7 @@ int TcpAsyncStreamSocket::_rearms = 0; const IpAddress TcpAsyncStreamSocket::_host = "::1"; const uint16_t TcpAsyncStreamSocket::_port = 5034; const uint16_t TcpAsyncStreamSocket::_stallport = 5035; -const int TcpAsyncStreamSocket::_timeout = 1000; +const std::chrono::milliseconds TcpAsyncStreamSocket::_timeout{1000}; /** * @brief Test open method. diff --git a/core/tests/tcp_socket_stream_test.cpp b/core/tests/tcp_socket_stream_test.cpp index 4eee1e8f..0271d1ed 100644 --- a/core/tests/tcp_socket_stream_test.cpp +++ b/core/tests/tcp_socket_stream_test.cpp @@ -93,7 +93,7 @@ class TcpSocketStream : public EventHandler, public ::testing::Test Tcp::Acceptor _server; /// timeout. - static const int _timeout; + static const std::chrono::milliseconds _timeout; /// host. static const std::string _host; @@ -103,7 +103,7 @@ class TcpSocketStream : public EventHandler, public ::testing::Test static const uint16_t _invalid_port; }; -const int TcpSocketStream::_timeout = 1000; +const std::chrono::milliseconds TcpSocketStream::_timeout{1000}; const std::string TcpSocketStream::_host = "127.0.0.1"; const uint16_t TcpSocketStream::_port = 5000; const uint16_t TcpSocketStream::_invalid_port = 5032; diff --git a/core/tests/tcp_socket_test.cpp b/core/tests/tcp_socket_test.cpp index 0d2dd25d..e3cd2c5f 100644 --- a/core/tests/tcp_socket_test.cpp +++ b/core/tests/tcp_socket_test.cpp @@ -99,13 +99,13 @@ class TcpSocket : public EventHandler, public ::testing::Test static const uint16_t _port; /// timeout. - static const int _timeout; + static const std::chrono::milliseconds _timeout; }; const std::string TcpSocket::_hostv4 = "127.0.0.1"; const std::string TcpSocket::_hostv6 = "::1"; const uint16_t TcpSocket::_port = 5000; -const int TcpSocket::_timeout = 1000; +const std::chrono::milliseconds TcpSocket::_timeout{1000}; /** * @brief Test move. diff --git a/core/tests/udp_async_datagram_socket_test.cpp b/core/tests/udp_async_datagram_socket_test.cpp index 1554eb7c..cd72ea51 100644 --- a/core/tests/udp_async_datagram_socket_test.cpp +++ b/core/tests/udp_async_datagram_socket_test.cpp @@ -205,7 +205,7 @@ class UdpAsyncDatagramSocket : public ::testing::Test static const uint16_t _port; /// timeout. - static const int _timeout; + static const std::chrono::milliseconds _timeout; }; Mutex UdpAsyncDatagramSocket::_mut; @@ -222,7 +222,7 @@ int UdpAsyncDatagramSocket::_rearms = 0; Udp::Endpoint UdpAsyncDatagramSocket::_dest; const std::string UdpAsyncDatagramSocket::_host = "127.0.0.1"; const uint16_t UdpAsyncDatagramSocket::_port = 5036; -const int UdpAsyncDatagramSocket::_timeout = 1000; +const std::chrono::milliseconds UdpAsyncDatagramSocket::_timeout{1000}; /** * @brief Test open method. diff --git a/core/tests/udp_socket_test.cpp b/core/tests/udp_socket_test.cpp index c9390aba..31c52c09 100644 --- a/core/tests/udp_socket_test.cpp +++ b/core/tests/udp_socket_test.cpp @@ -88,12 +88,12 @@ class UdpSocket : public EventHandler, public ::testing::Test static const uint16_t _port; /// timeout. - static const int _timeout; + static const std::chrono::milliseconds _timeout; }; const std::string UdpSocket::_host = "127.0.0.1"; const uint16_t UdpSocket::_port = 5000; -const int UdpSocket::_timeout = 1000; +const std::chrono::milliseconds UdpSocket::_timeout{1000}; /** * @brief Test open method. diff --git a/core/tests/unix_async_acceptor_test.cpp b/core/tests/unix_async_acceptor_test.cpp index bf219ba2..55f409db 100644 --- a/core/tests/unix_async_acceptor_test.cpp +++ b/core/tests/unix_async_acceptor_test.cpp @@ -128,7 +128,7 @@ class UnixAsyncAcceptor : public ::testing::Test static const std::string _path; /// completion timeout. - static const int _timeout; + static const std::chrono::milliseconds _timeout; /// condition mutex. static Mutex _mut; @@ -147,7 +147,7 @@ class UnixAsyncAcceptor : public ::testing::Test }; const std::string UnixAsyncAcceptor::_path = "/tmp/unixasyncacceptor_test.sock"; -const int UnixAsyncAcceptor::_timeout = 1000; +const std::chrono::milliseconds UnixAsyncAcceptor::_timeout{1000}; Mutex UnixAsyncAcceptor::_mut; Condition UnixAsyncAcceptor::_cond; std::error_code UnixAsyncAcceptor::_code; diff --git a/core/tests/unix_async_datagram_socket_test.cpp b/core/tests/unix_async_datagram_socket_test.cpp index 748de15a..9ec844e5 100644 --- a/core/tests/unix_async_datagram_socket_test.cpp +++ b/core/tests/unix_async_datagram_socket_test.cpp @@ -218,7 +218,7 @@ class UnixAsyncDatagramSocket : public ::testing::Test static const std::string _senderpath; /// timeout. - static const int _timeout; + static const std::chrono::milliseconds _timeout; }; Mutex UnixAsyncDatagramSocket::_mut; @@ -236,7 +236,7 @@ UnixDgram::Endpoint UnixAsyncDatagramSocket::_dest; const std::string UnixAsyncDatagramSocket::_serverpath = "/tmp/unixasyncdgramserver_test.sock"; const std::string UnixAsyncDatagramSocket::_clientpath = "/tmp/unixasyncdgramclient_test.sock"; const std::string UnixAsyncDatagramSocket::_senderpath = "/tmp/unixasyncdgramsender_test.sock"; -const int UnixAsyncDatagramSocket::_timeout = 1000; +const std::chrono::milliseconds UnixAsyncDatagramSocket::_timeout{1000}; /** * @brief Test open method. diff --git a/core/tests/unix_async_stream_socket_test.cpp b/core/tests/unix_async_stream_socket_test.cpp index 22f96bc2..d8077950 100644 --- a/core/tests/unix_async_stream_socket_test.cpp +++ b/core/tests/unix_async_stream_socket_test.cpp @@ -224,7 +224,7 @@ class UnixAsyncStreamSocket : public ::testing::Test static const std::string _stallpath; /// timeout. - static const int _timeout; + static const std::chrono::milliseconds _timeout; }; Mutex UnixAsyncStreamSocket::_mut; @@ -239,7 +239,7 @@ int UnixAsyncStreamSocket::_rearms = 0; const std::string UnixAsyncStreamSocket::_serverpath = "/tmp/unixasyncserver_test.sock"; const std::string UnixAsyncStreamSocket::_clientpath = "/tmp/unixasyncclient_test.sock"; const std::string UnixAsyncStreamSocket::_stallpath = "/tmp/unixasyncstall_test.sock"; -const int UnixAsyncStreamSocket::_timeout = 1000; +const std::chrono::milliseconds UnixAsyncStreamSocket::_timeout{1000}; /** * @brief Test open method. diff --git a/core/tests/unix_datagram_socket_test.cpp b/core/tests/unix_datagram_socket_test.cpp index 9e16a67c..25e0ed94 100644 --- a/core/tests/unix_datagram_socket_test.cpp +++ b/core/tests/unix_datagram_socket_test.cpp @@ -96,12 +96,12 @@ class UnixDgramSocket : public EventHandler, public ::testing::Test static const std::string _clientpath; /// timeout. - static const int _timeout; + static const std::chrono::milliseconds _timeout; }; const std::string UnixDgramSocket::_serverpath = "/tmp/unixserver_test.sock"; const std::string UnixDgramSocket::_clientpath = "/tmp/unixclient_test.sock"; -const int UnixDgramSocket::_timeout = 1000; +const std::chrono::milliseconds UnixDgramSocket::_timeout{1000}; /** * @brief Test open method. diff --git a/core/tests/unix_stream_socket_test.cpp b/core/tests/unix_stream_socket_test.cpp index 861dc0a8..feef1e97 100644 --- a/core/tests/unix_stream_socket_test.cpp +++ b/core/tests/unix_stream_socket_test.cpp @@ -107,12 +107,12 @@ class UnixStreamSocket : public EventHandler, public ::testing::Test static const std::string _clientpath; /// timeout. - static const int _timeout; + static const std::chrono::milliseconds _timeout; }; const std::string UnixStreamSocket::_serverpath = "/tmp/unixserver_test.sock"; const std::string UnixStreamSocket::_clientpath = "/tmp/unixclient_test.sock"; -const int UnixStreamSocket::_timeout = 1000; +const std::chrono::milliseconds UnixStreamSocket::_timeout{1000}; /** * @brief Test open method. diff --git a/core/tests/utils_test.cpp b/core/tests/utils_test.cpp index 36f8e408..54fa005f 100644 --- a/core/tests/utils_test.cpp +++ b/core/tests/utils_test.cpp @@ -256,6 +256,22 @@ TEST (Utils, toTimespec) ts = join::toTimespec (tp); EXPECT_EQ (ts.tv_sec, 0); EXPECT_EQ (ts.tv_nsec, 0); + + ts = join::toTimespec (std::chrono::seconds (1234)); + EXPECT_EQ (ts.tv_sec, 1234); + EXPECT_EQ (ts.tv_nsec, 0); + + ts = join::toTimespec (std::chrono::seconds (5) + std::chrono::nanoseconds (123456789)); + EXPECT_EQ (ts.tv_sec, 5); + EXPECT_EQ (ts.tv_nsec, 123456789); + + ts = join::toTimespec (std::chrono::milliseconds (1500)); + EXPECT_EQ (ts.tv_sec, 1); + EXPECT_EQ (ts.tv_nsec, 500000000); + + ts = join::toTimespec (std::chrono::nanoseconds::zero ()); + EXPECT_EQ (ts.tv_sec, 0); + EXPECT_EQ (ts.tv_nsec, 0); } /** diff --git a/crypto/include/join/tls.hpp b/crypto/include/join/tls.hpp index 0abcc4b6..5979b7e5 100644 --- a/crypto/include/join/tls.hpp +++ b/crypto/include/join/tls.hpp @@ -36,6 +36,7 @@ // C++. #include #include +#include #include // C. @@ -342,10 +343,10 @@ namespace join /** * @brief block until TLS handshake is finished. - * @param timeout timeout in milliseconds (0: infinite). + * @param timeout timeout duration (zero: infinite). * @return true if TLS handshake is finished. */ - virtual bool waitHandshake (int timeout) + virtual bool waitHandshake (std::chrono::nanoseconds timeout) { if (handshake () == 0) { @@ -364,17 +365,18 @@ namespace join break; // LCOV_EXCL_LINE } - int activeTimeout = timeout; + std::chrono::nanoseconds activeTimeout = timeout; if (isDtls) { struct timeval dtlsTimeout; if (DTLSv1_get_timeout (_ssl.get (), &dtlsTimeout)) { - int dtlsTimeoutMs = (dtlsTimeout.tv_sec * 1000) + (dtlsTimeout.tv_usec / 1000); - if (timeout <= 0 || dtlsTimeoutMs < timeout) + auto dtlsDuration = std::chrono::duration_cast ( + std::chrono::seconds (dtlsTimeout.tv_sec) + std::chrono::microseconds (dtlsTimeout.tv_usec)); + if ((timeout <= std::chrono::nanoseconds::zero ()) || (dtlsDuration < timeout)) { - activeTimeout = dtlsTimeoutMs; + activeTimeout = dtlsDuration; } } } @@ -467,10 +469,10 @@ namespace join /** * @brief block until TLS shutdown is finished. - * @param timeout timeout in milliseconds (0: infinite). + * @param timeout timeout duration (zero: infinite). * @return true if TLS shutdown is finished. */ - bool waitShutdown (int timeout) noexcept + bool waitShutdown (std::chrono::nanoseconds timeout) noexcept { if (!_ssl) { @@ -526,10 +528,10 @@ namespace join /** * @brief block until new data is available for reading. - * @param timeout timeout in milliseconds (0: infinite). + * @param timeout timeout duration (zero: infinite). * @return true if there is new data available for reading, false otherwise. */ - bool waitReadyRead (int timeout = 0) const noexcept + bool waitReadyRead (std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero ()) const noexcept { if (_ssl) { @@ -571,10 +573,10 @@ namespace join * @brief read data until size is reached or an error occurred. * @param data buffer used to store the data received. * @param size number of bytes to read. - * @param timeout timeout in milliseconds. + * @param timeout timeout duration (zero: infinite). * @return 0 on success, -1 on failure. */ - int readExactly (char* data, size_t size, int timeout = 0) + int readExactly (char* data, size_t size, std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero ()) { size_t numRead = 0; @@ -602,10 +604,10 @@ namespace join /** * @brief block until at least one byte can be written on the socket. - * @param timeout timeout in milliseconds (0: infinite). + * @param timeout timeout duration (zero: infinite). * @return true if data can be written on the socket, false otherwise. */ - bool waitReadyWrite (int timeout = 0) const noexcept + bool waitReadyWrite (std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero ()) const noexcept { if (_ssl) { @@ -647,10 +649,11 @@ namespace join * @brief write data until size is reached or an error occurred. * @param data data buffer to send. * @param size number of bytes to write. - * @param timeout timeout in milliseconds. + * @param timeout timeout duration (zero: infinite). * @return 0 on success, -1 on failure. */ - int writeExactly (const char* data, size_t size, int timeout = 0) + int writeExactly (const char* data, size_t size, + std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero ()) { size_t numWrite = 0; diff --git a/crypto/include/join/tls_wrapper.hpp b/crypto/include/join/tls_wrapper.hpp index 47679aa0..d46ad7a6 100644 --- a/crypto/include/join/tls_wrapper.hpp +++ b/crypto/include/join/tls_wrapper.hpp @@ -53,31 +53,31 @@ namespace join /** * @brief block until the underlying socket is connected. - * @param timeout timeout in milliseconds. + * @param timeout timeout duration (zero: infinite). * @return true if connected, false otherwise. */ - bool waitConnected (int timeout = 0) + bool waitConnected (std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero ()) { return this->_socket.waitConnected (timeout); } /** * @brief block until the underlying socket is disconnected. - * @param timeout timeout in milliseconds. + * @param timeout timeout duration (zero: infinite). * @return true if disconnected, false otherwise. */ - bool waitDisconnected (int timeout = 0) + bool waitDisconnected (std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero ()) { return this->_socket.waitDisconnected (timeout); } /** * @brief block until TLS handshake is finished. - * @param timeout timeout in milliseconds (0: infinite). + * @param timeout timeout duration (zero: infinite). * @return true if TLS handshake is finished. * @note waits for the transport connection first, then runs the common handshake. */ - bool waitHandshake (int timeout) override + bool waitHandshake (std::chrono::nanoseconds timeout) override { if (!this->_socket.waitConnected (timeout)) { diff --git a/crypto/tests/dtls_wrapper_test.cpp b/crypto/tests/dtls_wrapper_test.cpp index 98d05e77..11fdc989 100644 --- a/crypto/tests/dtls_wrapper_test.cpp +++ b/crypto/tests/dtls_wrapper_test.cpp @@ -239,7 +239,7 @@ class DtlsSocket : public EventHandler, public ::testing::Test static const uint16_t _port; /// timeout. - static const int _timeout; + static const std::chrono::milliseconds _timeout; /// root certificate. static const std::string _rootcert; @@ -260,7 +260,7 @@ class DtlsSocket : public EventHandler, public ::testing::Test const std::string DtlsSocket::_hostv4 = "127.0.0.1"; const std::string DtlsSocket::_hostv6 = "::1"; const uint16_t DtlsSocket::_port = 5000; -const int DtlsSocket::_timeout = 1000; +const std::chrono::milliseconds DtlsSocket::_timeout{1000}; const std::string DtlsSocket::_rootcert = "/tmp/tlssocket_test_root.cert"; const std::string DtlsSocket::_certPath = "/tmp/certs"; const std::string DtlsSocket::_certFile = _certPath + "/tlssocket_test.cert"; diff --git a/crypto/tests/tls_stream_test.cpp b/crypto/tests/tls_stream_test.cpp index 07737b45..bed422da 100644 --- a/crypto/tests/tls_stream_test.cpp +++ b/crypto/tests/tls_stream_test.cpp @@ -252,7 +252,7 @@ class TlsSocketStream : public EventHandler, public ::testing::Test static const uint16_t _invalid_port; /// timeout. - static const int _timeout; + static const std::chrono::milliseconds _timeout; /// root certificate. static const std::string _rootcert; @@ -273,7 +273,7 @@ class TlsSocketStream : public EventHandler, public ::testing::Test const std::string TlsSocketStream::_host = "127.0.0.1"; const uint16_t TlsSocketStream::_port = 5000; const uint16_t TlsSocketStream::_invalid_port = 5032; -const int TlsSocketStream::_timeout = 1000; +const std::chrono::milliseconds TlsSocketStream::_timeout{1000}; const std::string TlsSocketStream::_rootcert = "/tmp/tlssocket_test_root.cert"; const std::string TlsSocketStream::_certPath = "/tmp/certs"; const std::string TlsSocketStream::_certFile = _certPath + "/tlssocket_test.cert"; diff --git a/crypto/tests/tls_wrapper_test.cpp b/crypto/tests/tls_wrapper_test.cpp index 192a49dd..a9d8618b 100644 --- a/crypto/tests/tls_wrapper_test.cpp +++ b/crypto/tests/tls_wrapper_test.cpp @@ -257,7 +257,7 @@ class TlsSocket : public EventHandler, public ::testing::Test static const uint16_t _invalid_port; /// timeout. - static const int _timeout; + static const std::chrono::milliseconds _timeout; /// root certificate. static const std::string _rootcert; @@ -279,7 +279,7 @@ const std::string TlsSocket::_hostv4 = "127.0.0.1"; const std::string TlsSocket::_hostv6 = "::1"; const uint16_t TlsSocket::_port = 5000; const uint16_t TlsSocket::_invalid_port = 5032; -const int TlsSocket::_timeout = 1000; +const std::chrono::milliseconds TlsSocket::_timeout{1000}; const std::string TlsSocket::_rootcert = "/tmp/tlssocket_test_root.cert"; const std::string TlsSocket::_certPath = "/tmp/certs"; const std::string TlsSocket::_certFile = _certPath + "/tlssocket_test.cert"; diff --git a/fabric/include/join/resolver.hpp b/fabric/include/join/resolver.hpp index 3105f756..ac1c3830 100644 --- a/fabric/include/join/resolver.hpp +++ b/fabric/include/join/resolver.hpp @@ -1219,7 +1219,7 @@ namespace join return -1; } - if (!this->_socket.waitConnected (timeout.count ())) + if (!this->_socket.waitConnected (timeout)) { close (); return -1; @@ -1237,7 +1237,7 @@ namespace join return -1; } - if (!this->_socket.waitHandshake (timeout.count ())) + if (!this->_socket.waitHandshake (timeout)) { close (); return -1; @@ -1270,14 +1270,14 @@ namespace join return -1; } - if (!this->_socket.waitShutdown (timeout.count ())) + if (!this->_socket.waitShutdown (timeout)) { close (); return -1; } } - if (!this->_socket.waitDisconnected (timeout.count ())) + if (!this->_socket.waitDisconnected (timeout)) { close (); return -1; diff --git a/fabric/tests/netlink_socket_test.cpp b/fabric/tests/netlink_socket_test.cpp index 58360400..312c7472 100644 --- a/fabric/tests/netlink_socket_test.cpp +++ b/fabric/tests/netlink_socket_test.cpp @@ -64,14 +64,14 @@ class NetlinkSocket : public ::testing::Test static const uint32_t _groups; /// timeout. - static const int _timeout; + static const std::chrono::milliseconds _timeout; /// data. static std::unique_ptr _data; }; const uint32_t NetlinkSocket::_groups = RTMGRP_LINK; -const int NetlinkSocket::_timeout = 1000; +const std::chrono::milliseconds NetlinkSocket::_timeout{1000}; std::unique_ptr NetlinkSocket::_data; /** diff --git a/services/include/join/http_server.hpp b/services/include/join/http_server.hpp index b5303320..34678f5a 100644 --- a/services/include/join/http_server.hpp +++ b/services/include/join/http_server.hpp @@ -396,7 +396,7 @@ namespace join if (FD_ISSET (this->_server->_acceptor.handle (), &fdset)) { this->_sockbuf.socket () = this->_server->accept (); - this->_sockbuf.timeout (this->_server->keepAliveTimeout ().count () * 1000); + this->_sockbuf.timeout (this->_server->keepAliveTimeout ()); } } } diff --git a/services/samples/webbench.cpp b/services/samples/webbench.cpp index a7293996..ef5f7410 100644 --- a/services/samples/webbench.cpp +++ b/services/samples/webbench.cpp @@ -31,6 +31,7 @@ #include // C++. +#include #include #include @@ -89,7 +90,7 @@ void benchmark (Client client, join::HttpRequest request, const std::string& fil void* addr = nullptr; struct stat sbuf; - client.timeout (timeout * 1000); + client.timeout (std::chrono::seconds (timeout)); if (!file.empty ()) { diff --git a/services/tests/smtp_test.cpp b/services/tests/smtp_test.cpp index ec70e85f..51b45bdc 100644 --- a/services/tests/smtp_test.cpp +++ b/services/tests/smtp_test.cpp @@ -285,7 +285,7 @@ class SmtpClient : public EventHandler, public ::testing::Test static const uint16_t _port; /// timeout. - static const int _timeout; + static const std::chrono::milliseconds _timeout; /// root certificate. static const std::string _rootcert; @@ -317,7 +317,7 @@ class SmtpClient : public EventHandler, public ::testing::Test const std::string SmtpClient::_host = "localhost"; const uint16_t SmtpClient::_port = 5000; -const int SmtpClient::_timeout = 1000; +const std::chrono::milliseconds SmtpClient::_timeout{1000}; const std::string SmtpClient::_rootcert = "/tmp/tlssocket_test_root.cert"; const std::string SmtpClient::_certPath = "/tmp/certs"; const std::string SmtpClient::_certFile = _certPath + "/tlssocket_test.cert"; diff --git a/services/tests/smtps_test.cpp b/services/tests/smtps_test.cpp index 6a805982..e5725aec 100644 --- a/services/tests/smtps_test.cpp +++ b/services/tests/smtps_test.cpp @@ -284,7 +284,7 @@ class SmtpsClient : public EventHandler, public ::testing::Test static const uint16_t _port; /// timeout. - static const int _timeout; + static const std::chrono::milliseconds _timeout; /// root certificate. static const std::string _rootcert; @@ -316,7 +316,7 @@ class SmtpsClient : public EventHandler, public ::testing::Test const std::string SmtpsClient::_host = "localhost"; const uint16_t SmtpsClient::_port = 5000; -const int SmtpsClient::_timeout = 1000; +const std::chrono::milliseconds SmtpsClient::_timeout{1000}; const std::string SmtpsClient::_rootcert = "/tmp/tlssocket_test_root.cert"; const std::string SmtpsClient::_certPath = "/tmp/certs"; const std::string SmtpsClient::_certFile = _certPath + "/tlssocket_test.cert"; From d2aa6b528657fa3e003f70cddb15a0c0a4f724f8 Mon Sep 17 00:00:00 2001 From: mrabine Date: Sat, 29 Aug 2026 18:58:51 +0200 Subject: [PATCH 2/9] invalid doxygen --- fabric/include/join/nameserver.hpp | 12 ++++++------ fabric/include/join/resolver.hpp | 28 ++++++++++++++-------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/fabric/include/join/nameserver.hpp b/fabric/include/join/nameserver.hpp index be87d0a1..fbb129fe 100644 --- a/fabric/include/join/nameserver.hpp +++ b/fabric/include/join/nameserver.hpp @@ -517,7 +517,7 @@ namespace join * @brief resolve host name and return all IP addresses found. * @param host host name to resolve. * @param family address family. - * @param timeout timeout in milliseconds (default: 5000). + * @param timeout timeout duration (default: 5 s). * @return the resolved IP address list. */ IpAddressList resolveAllAddress (const std::string& host, int family, @@ -559,7 +559,7 @@ namespace join /** * @brief resolve host name and return all IP addresses found. * @param host host name to resolve. - * @param timeout timeout in milliseconds (default: 5000). + * @param timeout timeout duration (default: 5 s). * @return the resolved IP address list. */ IpAddressList resolveAllAddress (const std::string& host, @@ -580,7 +580,7 @@ namespace join * @brief resolve host name using address family. * @param host host name to resolve. * @param family address family. - * @param timeout timeout in milliseconds (default: 5000). + * @param timeout timeout duration (default: 5 s). * @return the first resolved IP address found matching address family. */ IpAddress resolveAddress (const std::string& host, int family, @@ -597,7 +597,7 @@ namespace join /** * @brief resolve host name. * @param host host name to resolve. - * @param timeout timeout in milliseconds (default: 5000). + * @param timeout timeout duration (default: 5 s). * @return the first resolved IP address found. */ IpAddress resolveAddress (const std::string& host, std::chrono::milliseconds timeout = std::chrono::seconds (5)) @@ -613,7 +613,7 @@ namespace join /** * @brief resolve all host address. * @param address host address to resolve. - * @param timeout timeout in milliseconds (default: 5000). + * @param timeout timeout duration (default: 5 s). * @return the resolved alias list. */ AliasList resolveAllName (const IpAddress& address, @@ -655,7 +655,7 @@ namespace join /** * @brief resolve host address. * @param address host address to resolve. - * @param timeout timeout in milliseconds (default: 5000). + * @param timeout timeout duration (default: 5 s). * @return the first resolved alias. */ std::string resolveName (const IpAddress& address, std::chrono::milliseconds timeout = std::chrono::seconds (5)) diff --git a/fabric/include/join/resolver.hpp b/fabric/include/join/resolver.hpp index ac1c3830..204f9917 100644 --- a/fabric/include/join/resolver.hpp +++ b/fabric/include/join/resolver.hpp @@ -134,7 +134,7 @@ namespace join * @brief resolve host name and return all IP addresses found. * @param host host name to resolve. * @param family address family. - * @param timeout timeout in milliseconds (default: 5000). + * @param timeout timeout duration (default: 5 s). * @return the resolved IP address list. */ IpAddressList resolveAllAddress (const std::string& host, int family, @@ -197,7 +197,7 @@ namespace join /** * @brief resolve host name and return all IP addresses found. * @param host host name to resolve. - * @param timeout timeout in milliseconds (default: 5000). + * @param timeout timeout duration (default: 5 s). * @return the resolved IP address list. */ IpAddressList resolveAllAddress (const std::string& host, @@ -237,7 +237,7 @@ namespace join * @brief resolve host name using address family. * @param host host name to resolve. * @param family address family. - * @param timeout timeout in milliseconds (default: 5000). + * @param timeout timeout duration (default: 5 s). * @return the first resolved IP address found matching address family. */ IpAddress resolveAddress (const std::string& host, int family, @@ -270,7 +270,7 @@ namespace join /** * @brief resolve host name. * @param host host name to resolve. - * @param timeout timeout in milliseconds (default: 5000). + * @param timeout timeout duration (default: 5 s). * @return the first resolved IP address found. */ IpAddress resolveAddress (const std::string& host, std::chrono::milliseconds timeout = std::chrono::seconds (5)) @@ -301,7 +301,7 @@ namespace join /** * @brief resolve all host address. * @param address host address to resolve. - * @param timeout timeout in milliseconds (default: 5000). + * @param timeout timeout duration (default: 5 s). * @return the resolved alias list. */ AliasList resolveAllName (const IpAddress& address, @@ -362,7 +362,7 @@ namespace join /** * @brief resolve host address. * @param address host address to resolve. - * @param timeout timeout in milliseconds (default: 5000). + * @param timeout timeout duration (default: 5 s). * @return the first resolved alias. */ std::string resolveName (const IpAddress& address, std::chrono::milliseconds timeout = std::chrono::seconds (5)) @@ -393,7 +393,7 @@ namespace join /** * @brief resolve all host name server. * @param host host name to resolve. - * @param timeout timeout in milliseconds (default: 5000). + * @param timeout timeout duration (default: 5 s). * @return the resolved name server list. */ ServerList resolveAllNameServer (const std::string& host, @@ -454,7 +454,7 @@ namespace join /** * @brief resolve host name server. * @param host host name to resolve. - * @param timeout timeout in milliseconds (default: 5000). + * @param timeout timeout duration (default: 5 s). * @return the first resolved name server. */ std::string resolveNameServer (const std::string& host, @@ -486,7 +486,7 @@ namespace join /** * @brief resolve host start of authority name server. * @param host host name to resolve. - * @param timeout timeout in milliseconds (default: 5000). + * @param timeout timeout duration (default: 5 s). * @return the start of authority name server. */ std::string resolveAuthority (const std::string& host, @@ -545,7 +545,7 @@ namespace join /** * @brief resolve all host mail exchanger. * @param host host name to resolve. - * @param timeout timeout in milliseconds (default: 5000). + * @param timeout timeout duration (default: 5 s). * @return the resolved mail exchanger list. */ ExchangerList resolveAllMailExchanger (const std::string& host, @@ -606,7 +606,7 @@ namespace join /** * @brief resolve host mail exchanger. * @param host host name to resolve. - * @param timeout timeout in milliseconds (default: 5000). + * @param timeout timeout duration (default: 5 s). * @return the first resolved mail exchanger. */ std::string resolveMailExchanger (const std::string& host, @@ -688,7 +688,7 @@ namespace join /** * @brief make a connection to the given endpoint. * @param endpoint endpoint to connect to. - * @param timeout timeout in milliseconds. + * @param timeout timeout duration. * @return 0 on success, -1 on failure. */ virtual int connect (const Endpoint& endpoint, @@ -742,7 +742,7 @@ namespace join /** * @brief reconnect to the remote DNS server. * @param endpoint endpoint to connect to. - * @param timeout timeout in milliseconds. + * @param timeout timeout duration. * @return 0 on success, -1 on failure. */ int reconnect (const Endpoint& endpoint, std::chrono::milliseconds timeout = std::chrono::seconds (5)) @@ -1205,7 +1205,7 @@ namespace join /** * @brief make an encrypted connection to the given endpoint. * @param endpoint endpoint to connect to. - * @param timeout timeout in milliseconds. + * @param timeout timeout duration. * @return 0 on success, -1 on failure. */ int connect (const Endpoint& endpoint, From d53beef3737e4fa8eae1605bc7c446af900ebe0e Mon Sep 17 00:00:00 2001 From: mrabine Date: Sat, 29 Aug 2026 19:20:52 +0200 Subject: [PATCH 3/9] format --- crypto/include/join/tls.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crypto/include/join/tls.hpp b/crypto/include/join/tls.hpp index 5979b7e5..7e95a494 100644 --- a/crypto/include/join/tls.hpp +++ b/crypto/include/join/tls.hpp @@ -373,7 +373,8 @@ namespace join if (DTLSv1_get_timeout (_ssl.get (), &dtlsTimeout)) { auto dtlsDuration = std::chrono::duration_cast ( - std::chrono::seconds (dtlsTimeout.tv_sec) + std::chrono::microseconds (dtlsTimeout.tv_usec)); + std::chrono::seconds (dtlsTimeout.tv_sec) + + std::chrono::microseconds (dtlsTimeout.tv_usec)); if ((timeout <= std::chrono::nanoseconds::zero ()) || (dtlsDuration < timeout)) { activeTimeout = dtlsDuration; From 9c9c91936532c6177f0f7ebabaa7835137b55e88 Mon Sep 17 00:00:00 2001 From: mrabine Date: Sat, 29 Aug 2026 21:49:28 +0200 Subject: [PATCH 4/9] cleanup wait --- core/include/join/socket.hpp | 91 ++++++++++++++--- core/include/join/socket_stream.hpp | 24 +++-- core/include/join/stream_socket.hpp | 122 ++++++++++++++++++----- core/tests/tcp_socket_test.cpp | 147 ++++++++++++++++++++++++++++ 4 files changed, 342 insertions(+), 42 deletions(-) diff --git a/core/include/join/socket.hpp b/core/include/join/socket.hpp index 626497ba..ba8a578e 100644 --- a/core/include/join/socket.hpp +++ b/core/include/join/socket.hpp @@ -32,6 +32,7 @@ #include // C++. +#include #include #include #include @@ -54,6 +55,7 @@ namespace join public: using Ptr = std::unique_ptr>; using Endpoint = typename Protocol::Endpoint; + using TimePoint = std::chrono::steady_clock::time_point; /** * @brief socket modes. @@ -319,12 +321,31 @@ namespace join /** * @brief block until new data is available for reading. - * @param timeout timeout duration (zero: infinite). * @return true if there is new data available for reading, false otherwise. */ - bool waitReadyRead (std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero ()) const noexcept + bool waitReadyRead () const noexcept { - return (wait (true, false, timeout) == 0); + return (waitUntil (true, false, TimePoint::max ()) == 0); + } + + /** + * @brief block until new data is available for reading, giving up after the given duration. + * @param timeout maximum time to wait. + * @return true if there is new data available for reading, false otherwise. + */ + bool waitReadyRead (std::chrono::nanoseconds timeout) const noexcept + { + return (waitUntil (true, false, std::chrono::steady_clock::now () + timeout) == 0); + } + + /** + * @brief block until new data is available for reading, giving up at the given time point. + * @param deadline time point at which to give up, max to wait indefinitely. + * @return true if there is new data available for reading, false otherwise. + */ + bool waitReadyRead (TimePoint deadline) const noexcept + { + return (waitUntil (true, false, deadline) == 0); } /** @@ -365,12 +386,31 @@ namespace join /** * @brief block until at least one byte can be written. - * @param timeout timeout duration (zero: infinite). * @return true if data can be written, false otherwise. */ - bool waitReadyWrite (std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero ()) const noexcept + bool waitReadyWrite () const noexcept { - return (wait (false, true, timeout) == 0); + return (waitUntil (false, true, TimePoint::max ()) == 0); + } + + /** + * @brief block until at least one byte can be written, giving up after the given duration. + * @param timeout maximum time to wait. + * @return true if data can be written, false otherwise. + */ + bool waitReadyWrite (std::chrono::nanoseconds timeout) const noexcept + { + return (waitUntil (false, true, std::chrono::steady_clock::now () + timeout) == 0); + } + + /** + * @brief block until at least one byte can be written, giving up at the given time point. + * @param deadline time point at which to give up, max to wait indefinitely. + * @return true if data can be written, false otherwise. + */ + bool waitReadyWrite (TimePoint deadline) const noexcept + { + return (waitUntil (false, true, deadline) == 0); } /** @@ -626,10 +666,33 @@ namespace join * @brief wait for the socket handle to become ready. * @param wantRead set to true if want read * @param wantWrite set to true if want write. - * @param timeout timeout duration (zero: infinite). * @return 0 on success, -1 on failure. */ - int wait (bool wantRead, bool wantWrite, std::chrono::nanoseconds timeout) const noexcept + int wait (bool wantRead, bool wantWrite) const noexcept + { + return waitUntil (wantRead, wantWrite, TimePoint::max ()); + } + + /** + * @brief wait for the socket handle to become ready, giving up after the given duration. + * @param wantRead set to true if want read + * @param wantWrite set to true if want write. + * @param timeout maximum time to wait. + * @return 0 on success, -1 on failure. + */ + int waitFor (bool wantRead, bool wantWrite, std::chrono::nanoseconds timeout) const noexcept + { + return waitUntil (wantRead, wantWrite, std::chrono::steady_clock::now () + timeout); + } + + /** + * @brief wait for the socket handle to become ready, giving up at the given time point. + * @param wantRead set to true if want read + * @param wantWrite set to true if want write. + * @param deadline time point at which to give up, max to wait indefinitely. + * @return 0 on success, -1 on failure. + */ + int waitUntil (bool wantRead, bool wantWrite, TimePoint deadline) const noexcept { struct pollfd handle; handle.fd = _handle; @@ -646,10 +709,16 @@ namespace join handle.events |= POLLOUT; } - struct timespec ts = toTimespec (timeout); - const struct timespec* deadline = (timeout > std::chrono::nanoseconds::zero ()) ? &ts : nullptr; + struct timespec ts = {}; + const struct timespec* remaining = nullptr; + + if (deadline != TimePoint::max ()) + { + ts = toTimespec (std::max (deadline - std::chrono::steady_clock::now (), TimePoint::duration::zero ())); + remaining = &ts; + } - int nset = (handle.fd > -1) ? ::ppoll (&handle, 1, deadline, nullptr) : -1; + int nset = (handle.fd > -1) ? ::ppoll (&handle, 1, remaining, nullptr) : -1; if (nset != 1) { if (nset == -1) diff --git a/core/include/join/socket_stream.hpp b/core/include/join/socket_stream.hpp index c8920c9f..7594fc67 100644 --- a/core/include/join/socket_stream.hpp +++ b/core/include/join/socket_stream.hpp @@ -147,7 +147,7 @@ namespace join return nullptr; } - if (!_socket.waitConnected (_timeout)) + if (!(hasTimeout () ? _socket.waitConnected (_timeout) : _socket.waitConnected ())) { _socket.close (); return nullptr; @@ -175,7 +175,7 @@ namespace join return nullptr; } - if (!_socket.waitDisconnected (_timeout)) + if (!(hasTimeout () ? _socket.waitDisconnected (_timeout) : _socket.waitDisconnected ())) { return nullptr; } @@ -195,7 +195,7 @@ namespace join /** * @brief set the socket timeout. - * @param timeout timeout duration (zero: infinite). + * @param timeout maximum time granted to each stream operation, zero to remove the limit. */ void timeout (std::chrono::nanoseconds timeout) { @@ -247,7 +247,7 @@ namespace join { if (lastError == Errc::TemporaryError) { - if (_socket.waitReadyRead (_timeout)) + if (hasTimeout () ? _socket.waitReadyRead (_timeout) : _socket.waitReadyRead ()) { continue; } @@ -287,7 +287,8 @@ namespace join std::streamsize pending = pptr () - pbase (); if (pending) { - if (_socket.writeExactly (pbase (), pending, _timeout) == -1) + if ((hasTimeout () ? _socket.writeExactly (pbase (), pending, _timeout) + : _socket.writeExactly (pbase (), pending)) == -1) { _socket.close (); return traits_type::eof (); @@ -324,7 +325,16 @@ namespace join /// internal buffer. std::unique_ptr _buf; - /// timeout. + /** + * @brief check if a timeout is configured. + * @return true if a timeout is configured. + */ + bool hasTimeout () const noexcept + { + return _timeout != std::chrono::nanoseconds::zero (); + } + + /// timeout, zero when the stream operations are not time bounded. std::chrono::nanoseconds _timeout = std::chrono::seconds (30); /// internal socket. @@ -486,7 +496,7 @@ namespace join /** * @brief set the socket timeout. - * @param timeout timeout duration (zero: infinite). + * @param timeout maximum time granted to each stream operation, zero to remove the limit. */ void timeout (std::chrono::nanoseconds timeout) { diff --git a/core/include/join/stream_socket.hpp b/core/include/join/stream_socket.hpp index a09f10f8..7a6b3a4c 100644 --- a/core/include/join/stream_socket.hpp +++ b/core/include/join/stream_socket.hpp @@ -52,6 +52,7 @@ namespace join using Option = typename BasicSocket::Option; using State = typename BasicSocket::State; using Endpoint = typename Protocol::Endpoint; + using TimePoint = typename BasicSocket::TimePoint; /** * @brief default constructor. @@ -172,10 +173,29 @@ namespace join /** * @brief block until connected. - * @param timeout timeout duration (zero: infinite). * @return true if connected, false otherwise. */ - bool waitConnected (std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero ()) + bool waitConnected () + { + return waitConnected (TimePoint::max ()); + } + + /** + * @brief block until connected, giving up after the given duration. + * @param timeout maximum time to wait. + * @return true if connected, false otherwise. + */ + bool waitConnected (std::chrono::nanoseconds timeout) + { + return waitConnected (std::chrono::steady_clock::now () + timeout); + } + + /** + * @brief block until connected, giving up at the given time point. + * @param deadline time point at which to give up, max to wait indefinitely. + * @return true if connected, false otherwise. + */ + bool waitConnected (TimePoint deadline) { if (this->_state != State::Connected) { @@ -185,7 +205,7 @@ namespace join return false; } - if (!this->waitReadyWrite (timeout)) + if (this->waitUntil (false, true, deadline) == -1) { return false; } @@ -237,10 +257,29 @@ namespace join /** * @brief wait until the connection as been shut down. - * @param timeout timeout duration (zero: infinite). * return true if the connection as been shut down, false otherwise. */ - bool waitDisconnected (std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero ()) + bool waitDisconnected () + { + return waitDisconnected (TimePoint::max ()); + } + + /** + * @brief wait until the connection as been shut down, giving up after the given duration. + * @param timeout maximum time to wait. + * return true if the connection as been shut down, false otherwise. + */ + bool waitDisconnected (std::chrono::nanoseconds timeout) + { + return waitDisconnected (std::chrono::steady_clock::now () + timeout); + } + + /** + * @brief wait until the connection as been shut down, giving up at the given time point. + * @param deadline time point at which to give up, max to wait indefinitely. + * return true if the connection as been shut down, false otherwise. + */ + bool waitDisconnected (TimePoint deadline) { if ((this->_state != State::Disconnected) && (this->_state != State::Closed)) { @@ -250,12 +289,9 @@ namespace join return false; } - auto start = std::chrono::steady_clock::now (); - auto elapsed = std::chrono::nanoseconds::zero (); - - while ((lastError == Errc::TemporaryError) && (elapsed <= timeout)) + while (lastError == Errc::TemporaryError) { - if (!this->waitReadyRead (timeout - elapsed)) + if (this->waitUntil (true, false, deadline) == -1) { return false; } @@ -264,12 +300,6 @@ namespace join { return true; } - - if (timeout != std::chrono::nanoseconds::zero ()) - { - elapsed = std::chrono::duration_cast ( - std::chrono::steady_clock::now () - start); - } } return false; @@ -309,11 +339,33 @@ namespace join * @brief read data until size is reached or an error occurred. * @param data buffer used to store the data received. * @param size number of bytes to read. - * @param timeout timeout duration (zero: infinite). * @return 0 on success, -1 on failure. */ - int readExactly (char* data, size_t size, - std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero ()) noexcept + int readExactly (char* data, size_t size) noexcept + { + return readExactly (data, size, TimePoint::max ()); + } + + /** + * @brief read data until size is reached, an error occurred or the given duration elapsed. + * @param data buffer used to store the data received. + * @param size number of bytes to read. + * @param timeout maximum time granted to the whole read. + * @return 0 on success, -1 on failure. + */ + int readExactly (char* data, size_t size, std::chrono::nanoseconds timeout) noexcept + { + return readExactly (data, size, std::chrono::steady_clock::now () + timeout); + } + + /** + * @brief read data until size is reached, an error occurred or the deadline expired. + * @param data buffer used to store the data received. + * @param size number of bytes to read. + * @param deadline time point at which to give up, max to wait indefinitely. + * @return 0 on success, -1 on failure. + */ + int readExactly (char* data, size_t size, TimePoint deadline) noexcept { size_t numRead = 0; @@ -324,7 +376,7 @@ namespace join { if (lastError == Errc::TemporaryError) { - if (this->waitReadyRead (timeout)) + if (this->waitUntil (true, false, deadline) == 0) { continue; } @@ -343,11 +395,33 @@ namespace join * @brief write data until size is reached or an error occurred. * @param data data buffer to send. * @param size number of bytes to write. - * @param timeout timeout duration (zero: infinite). * @return 0 on success, -1 on failure. */ - int writeExactly (const char* data, size_t size, - std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero ()) noexcept + int writeExactly (const char* data, size_t size) noexcept + { + return writeExactly (data, size, TimePoint::max ()); + } + + /** + * @brief write data until size is reached, an error occurred or the given duration elapsed. + * @param data data buffer to send. + * @param size number of bytes to write. + * @param timeout maximum time granted to the whole write. + * @return 0 on success, -1 on failure. + */ + int writeExactly (const char* data, size_t size, std::chrono::nanoseconds timeout) noexcept + { + return writeExactly (data, size, std::chrono::steady_clock::now () + timeout); + } + + /** + * @brief write data until size is reached, an error occurred or the deadline expired. + * @param data data buffer to send. + * @param size number of bytes to write. + * @param deadline time point at which to give up, max to wait indefinitely. + * @return 0 on success, -1 on failure. + */ + int writeExactly (const char* data, size_t size, TimePoint deadline) noexcept { size_t numWrite = 0; @@ -358,7 +432,7 @@ namespace join { if (lastError == Errc::TemporaryError) { - if (this->waitReadyWrite (timeout)) + if (this->waitUntil (false, true, deadline) == 0) { continue; } diff --git a/core/tests/tcp_socket_test.cpp b/core/tests/tcp_socket_test.cpp index e3cd2c5f..6d20a768 100644 --- a/core/tests/tcp_socket_test.cpp +++ b/core/tests/tcp_socket_test.cpp @@ -29,6 +29,10 @@ // Libraries. #include +// C++. +#include +#include + using join::Errc; using join::IpAddress; using join::ReactorThread; @@ -98,6 +102,10 @@ class TcpSocket : public EventHandler, public ::testing::Test /// port. static const uint16_t _port; + /// ports of the acceptors used to test the operation deadlines. + static const uint16_t _stallport; + static const uint16_t _stallport2; + /// timeout. static const std::chrono::milliseconds _timeout; }; @@ -105,6 +113,8 @@ class TcpSocket : public EventHandler, public ::testing::Test const std::string TcpSocket::_hostv4 = "127.0.0.1"; const std::string TcpSocket::_hostv6 = "::1"; const uint16_t TcpSocket::_port = 5000; +const uint16_t TcpSocket::_stallport = 5002; +const uint16_t TcpSocket::_stallport2 = 5003; const std::chrono::milliseconds TcpSocket::_timeout{1000}; /** @@ -224,6 +234,8 @@ TEST_F (TcpSocket, waitConnected) ASSERT_TRUE (tcpSocket.connecting ()); } ASSERT_TRUE (tcpSocket.waitConnected (_timeout)) << join::lastError.message (); + ASSERT_TRUE (tcpSocket.waitConnected ()) << join::lastError.message (); + ASSERT_TRUE (tcpSocket.waitConnected (std::chrono::steady_clock::now () + _timeout)) << join::lastError.message (); if (tcpSocket.disconnect () == -1) { ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); @@ -256,6 +268,9 @@ TEST_F (TcpSocket, waitDisconnected) Tcp::Socket tcpSocket; ASSERT_TRUE (tcpSocket.waitDisconnected (_timeout)) << join::lastError.message (); + ASSERT_TRUE (tcpSocket.waitDisconnected ()) << join::lastError.message (); + ASSERT_TRUE (tcpSocket.waitDisconnected (std::chrono::steady_clock::now () + _timeout)) + << join::lastError.message (); if (tcpSocket.connect ({_hostv4, _port}) == -1) { ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); @@ -308,6 +323,8 @@ TEST_F (TcpSocket, waitReadyRead) ASSERT_TRUE (tcpSocket.waitReadyWrite (_timeout)) << join::lastError.message (); ASSERT_EQ (tcpSocket.writeExactly (data, sizeof (data)), 0) << join::lastError.message (); ASSERT_TRUE (tcpSocket.waitReadyRead (_timeout)) << join::lastError.message (); + ASSERT_TRUE (tcpSocket.waitReadyRead ()) << join::lastError.message (); + ASSERT_TRUE (tcpSocket.waitReadyRead (std::chrono::steady_clock::now () + _timeout)) << join::lastError.message (); if (tcpSocket.disconnect () == -1) { ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); @@ -351,6 +368,106 @@ TEST_F (TcpSocket, readExactly) ASSERT_EQ (tcpSocket.readExactly (data, sizeof (data)), 0) << join::lastError.message (); ASSERT_EQ (tcpSocket.disconnect (), 0) << join::lastError.message (); tcpSocket.close (); + + Tcp::Acceptor stall; + Tcp::Socket dribbler; + + ASSERT_EQ (stall.create ({IpAddress (_hostv4), _stallport}), 0) << join::lastError.message (); + + if (dribbler.connect ({_hostv4, _stallport}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError); + ASSERT_TRUE (dribbler.waitConnected (_timeout)) << join::lastError.message (); + } + + Tcp::Socket peer = stall.accept (); + ASSERT_TRUE (peer.connected ()); + + std::thread sender ([&peer] () { + for (int i = 0; i < 8; ++i) + { + std::this_thread::sleep_for (std::chrono::milliseconds (100)); + peer.writeExactly ("x", 1); + } + }); + + char slow[8] = {}; + auto beg = std::chrono::steady_clock::now (); + + int result = dribbler.readExactly (slow, sizeof (slow), std::chrono::milliseconds (250)); + std::error_code error = join::lastError; + auto elapsed = std::chrono::steady_clock::now () - beg; + + sender.join (); + dribbler.close (); + peer.close (); + stall.close (); + + ASSERT_EQ (result, -1); + ASSERT_EQ (error, Errc::TimedOut); + ASSERT_LT (elapsed, std::chrono::milliseconds (700)); +} + +/** + * @brief Test wait method. + */ +TEST_F (TcpSocket, wait) +{ + Tcp::Socket tcpSocket (Tcp::Socket::Blocking); + + ASSERT_EQ (tcpSocket.wait (true, false), -1); + ASSERT_EQ (join::lastError, std::errc::bad_file_descriptor); + + ASSERT_EQ (tcpSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (tcpSocket.wait (false, true), 0) << join::lastError.message (); + ASSERT_EQ (tcpSocket.disconnect (), 0) << join::lastError.message (); + tcpSocket.close (); +} + +/** + * @brief Test waitFor method. + */ +TEST_F (TcpSocket, waitFor) +{ + Tcp::Socket tcpSocket (Tcp::Socket::Blocking); + + ASSERT_EQ (tcpSocket.waitFor (true, false, _timeout), -1); + ASSERT_EQ (join::lastError, std::errc::bad_file_descriptor); + + ASSERT_EQ (tcpSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (tcpSocket.waitFor (false, true, _timeout), 0) << join::lastError.message (); + + auto beg = std::chrono::steady_clock::now (); + + ASSERT_EQ (tcpSocket.waitFor (true, false, std::chrono::milliseconds (100)), -1); + ASSERT_EQ (join::lastError, Errc::TimedOut); + ASSERT_GE (std::chrono::steady_clock::now () - beg, std::chrono::milliseconds (100)); + + ASSERT_EQ (tcpSocket.waitFor (true, false, std::chrono::nanoseconds::zero ()), -1); + ASSERT_EQ (join::lastError, Errc::TimedOut); + + ASSERT_EQ (tcpSocket.disconnect (), 0) << join::lastError.message (); + tcpSocket.close (); +} + +/** + * @brief Test waitUntil method. + */ +TEST_F (TcpSocket, waitUntil) +{ + Tcp::Socket tcpSocket (Tcp::Socket::Blocking); + + ASSERT_EQ (tcpSocket.waitUntil (true, false, Tcp::Socket::TimePoint::max ()), -1); + ASSERT_EQ (join::lastError, std::errc::bad_file_descriptor); + + ASSERT_EQ (tcpSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (tcpSocket.waitUntil (false, true, Tcp::Socket::TimePoint::max ()), 0) << join::lastError.message (); + + ASSERT_EQ (tcpSocket.waitUntil (true, false, std::chrono::steady_clock::now ()), -1); + ASSERT_EQ (join::lastError, Errc::TimedOut); + + ASSERT_EQ (tcpSocket.disconnect (), 0) << join::lastError.message (); + tcpSocket.close (); } /** @@ -368,6 +485,8 @@ TEST_F (TcpSocket, waitReadyWrite) } ASSERT_TRUE (tcpSocket.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE (tcpSocket.waitReadyWrite (_timeout)) << join::lastError.message (); + ASSERT_TRUE (tcpSocket.waitReadyWrite ()) << join::lastError.message (); + ASSERT_TRUE (tcpSocket.waitReadyWrite (std::chrono::steady_clock::now () + _timeout)) << join::lastError.message (); if (tcpSocket.disconnect () == -1) { ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); @@ -409,6 +528,34 @@ TEST_F (TcpSocket, writeExactly) ASSERT_TRUE (tcpSocket.waitReadyRead (_timeout)) << join::lastError.message (); ASSERT_EQ (tcpSocket.disconnect (), 0) << join::lastError.message (); tcpSocket.close (); + + Tcp::Acceptor stall; + Tcp::Socket sender; + + ASSERT_EQ (stall.create ({IpAddress (_hostv4), _stallport2}), 0) << join::lastError.message (); + + if (sender.connect ({_hostv4, _stallport2}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError); + ASSERT_TRUE (sender.waitConnected (_timeout)) << join::lastError.message (); + } + + Tcp::Socket peer = stall.accept (); + ASSERT_TRUE (peer.connected ()); + + ASSERT_EQ (sender.setOption (Tcp::Socket::SndBuffer, 4096), 0) << join::lastError.message (); + ASSERT_EQ (peer.setOption (Tcp::Socket::RcvBuffer, 4096), 0) << join::lastError.message (); + + std::vector bulk (1024 * 1024, 'x'); + auto beg = std::chrono::steady_clock::now (); + + ASSERT_EQ (sender.writeExactly (bulk.data (), bulk.size (), std::chrono::milliseconds (250)), -1); + ASSERT_EQ (join::lastError, Errc::TimedOut); + ASSERT_LT (std::chrono::steady_clock::now () - beg, std::chrono::milliseconds (700)); + + sender.close (); + peer.close (); + stall.close (); } /** From 202320c13bd43aae04801e0683ad8501852177bd Mon Sep 17 00:00:00 2001 From: mrabine Date: Sat, 29 Aug 2026 21:52:58 +0200 Subject: [PATCH 5/9] cleanup wait --- crypto/include/join/tls.hpp | 203 ++++++++++++++++++++++------ crypto/include/join/tls_wrapper.hpp | 60 ++++++-- crypto/tests/tls_wrapper_test.cpp | 25 +++- 3 files changed, 234 insertions(+), 54 deletions(-) diff --git a/crypto/include/join/tls.hpp b/crypto/include/join/tls.hpp index 7e95a494..dcb0e43d 100644 --- a/crypto/include/join/tls.hpp +++ b/crypto/include/join/tls.hpp @@ -34,6 +34,7 @@ #include // C++. +#include #include #include #include @@ -57,6 +58,7 @@ namespace join using Option = typename UnderlyingSocket::Option; using State = typename UnderlyingSocket::State; using Endpoint = typename Protocol::Endpoint; + using TimePoint = typename UnderlyingSocket::TimePoint; /** * @brief create a TLS decorator with an internally created socket. @@ -343,10 +345,29 @@ namespace join /** * @brief block until TLS handshake is finished. - * @param timeout timeout duration (zero: infinite). * @return true if TLS handshake is finished. */ - virtual bool waitHandshake (std::chrono::nanoseconds timeout) + bool waitHandshake () + { + return waitHandshake (TimePoint::max ()); + } + + /** + * @brief block until TLS handshake is finished, giving up after the given duration. + * @param timeout maximum time granted to the whole handshake. + * @return true if TLS handshake is finished. + */ + bool waitHandshake (std::chrono::nanoseconds timeout) + { + return waitHandshake (std::chrono::steady_clock::now () + timeout); + } + + /** + * @brief block until TLS handshake is finished, giving up at the given time point. + * @param deadline time point at which to give up, max to wait indefinitely. + * @return true if TLS handshake is finished. + */ + virtual bool waitHandshake (TimePoint deadline) { if (handshake () == 0) { @@ -365,27 +386,23 @@ namespace join break; // LCOV_EXCL_LINE } - std::chrono::nanoseconds activeTimeout = timeout; + TimePoint activeDeadline = deadline; if (isDtls) { struct timeval dtlsTimeout; if (DTLSv1_get_timeout (_ssl.get (), &dtlsTimeout)) { - auto dtlsDuration = std::chrono::duration_cast ( - std::chrono::seconds (dtlsTimeout.tv_sec) + - std::chrono::microseconds (dtlsTimeout.tv_usec)); - if ((timeout <= std::chrono::nanoseconds::zero ()) || (dtlsDuration < timeout)) - { - activeTimeout = dtlsDuration; - } + activeDeadline = std::min (activeDeadline, std::chrono::steady_clock::now () + + std::chrono::seconds (dtlsTimeout.tv_sec) + + std::chrono::microseconds (dtlsTimeout.tv_usec)); } } - int waitResult = _socket.wait (wantRead, wantWrite, activeTimeout); - if (waitResult == -1) + if (_socket.waitUntil (wantRead, wantWrite, activeDeadline) == -1) { - if (isDtls && (lastError == make_error_code (Errc::TimedOut))) + if (isDtls && (lastError == make_error_code (Errc::TimedOut)) && + (std::chrono::steady_clock::now () < deadline)) { int ret = DTLSv1_handle_timeout (_ssl.get ()); if (ret < 0) @@ -470,10 +487,29 @@ namespace join /** * @brief block until TLS shutdown is finished. - * @param timeout timeout duration (zero: infinite). + * @return true if TLS shutdown is finished. + */ + bool waitShutdown () noexcept + { + return waitShutdown (TimePoint::max ()); + } + + /** + * @brief block until TLS shutdown is finished, giving up after the given duration. + * @param timeout maximum time granted to the whole shutdown. * @return true if TLS shutdown is finished. */ bool waitShutdown (std::chrono::nanoseconds timeout) noexcept + { + return waitShutdown (std::chrono::steady_clock::now () + timeout); + } + + /** + * @brief block until TLS shutdown is finished, giving up at the given time point. + * @param deadline time point at which to give up, max to wait indefinitely. + * @return true if TLS shutdown is finished. + */ + bool waitShutdown (TimePoint deadline) noexcept { if (!_ssl) { @@ -495,7 +531,7 @@ namespace join break; // LCOV_EXCL_LINE } - if (_socket.wait (wantRead, wantWrite, timeout) == -1) + if (_socket.waitUntil (wantRead, wantWrite, deadline) == -1) { return false; } @@ -529,23 +565,40 @@ namespace join /** * @brief block until new data is available for reading. - * @param timeout timeout duration (zero: infinite). * @return true if there is new data available for reading, false otherwise. */ - bool waitReadyRead (std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero ()) const noexcept + bool waitReadyRead () const noexcept { - if (_ssl) - { - bool wantRead = SSL_want_read (_ssl.get ()); - bool wantWrite = SSL_want_write (_ssl.get ()); + return waitReadyRead (TimePoint::max ()); + } - if (wantRead || wantWrite) - { - return (_socket.wait (wantRead, wantWrite, timeout) == 0); - } + /** + * @brief block until new data is available for reading, giving up after the given duration. + * @param timeout maximum time to wait. + * @return true if there is new data available for reading, false otherwise. + */ + bool waitReadyRead (std::chrono::nanoseconds timeout) const noexcept + { + return waitReadyRead (std::chrono::steady_clock::now () + timeout); + } + + /** + * @brief block until new data is available for reading, giving up at the given time point. + * @param deadline time point at which to give up, max to wait indefinitely. + * @return true if there is new data available for reading, false otherwise. + */ + bool waitReadyRead (TimePoint deadline) const noexcept + { + bool wantRead = true; + bool wantWrite = false; + + if (_ssl && (SSL_want_read (_ssl.get ()) || SSL_want_write (_ssl.get ()))) + { + wantRead = SSL_want_read (_ssl.get ()); + wantWrite = SSL_want_write (_ssl.get ()); } - return _socket.waitReadyRead (timeout); + return (_socket.waitUntil (wantRead, wantWrite, deadline) == 0); } /** @@ -574,10 +627,33 @@ namespace join * @brief read data until size is reached or an error occurred. * @param data buffer used to store the data received. * @param size number of bytes to read. - * @param timeout timeout duration (zero: infinite). * @return 0 on success, -1 on failure. */ - int readExactly (char* data, size_t size, std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero ()) + int readExactly (char* data, size_t size) + { + return readExactly (data, size, TimePoint::max ()); + } + + /** + * @brief read data until size is reached, an error occurred or the given duration elapsed. + * @param data buffer used to store the data received. + * @param size number of bytes to read. + * @param timeout maximum time granted to the whole read. + * @return 0 on success, -1 on failure. + */ + int readExactly (char* data, size_t size, std::chrono::nanoseconds timeout) + { + return readExactly (data, size, std::chrono::steady_clock::now () + timeout); + } + + /** + * @brief read data until size is reached, an error occurred or the deadline expired. + * @param data buffer used to store the data received. + * @param size number of bytes to read. + * @param deadline time point at which to give up, max to wait indefinitely. + * @return 0 on success, -1 on failure. + */ + int readExactly (char* data, size_t size, TimePoint deadline) { size_t numRead = 0; @@ -588,7 +664,7 @@ namespace join { if (lastError == Errc::TemporaryError) { - if (waitReadyRead (timeout)) + if (waitReadyRead (deadline)) { continue; } @@ -605,23 +681,40 @@ namespace join /** * @brief block until at least one byte can be written on the socket. - * @param timeout timeout duration (zero: infinite). * @return true if data can be written on the socket, false otherwise. */ - bool waitReadyWrite (std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero ()) const noexcept + bool waitReadyWrite () const noexcept { - if (_ssl) - { - bool wantRead = SSL_want_read (_ssl.get ()); - bool wantWrite = SSL_want_write (_ssl.get ()); + return waitReadyWrite (TimePoint::max ()); + } - if (wantRead || wantWrite) - { - return (_socket.wait (wantRead, wantWrite, timeout) == 0); - } + /** + * @brief block until at least one byte can be written on the socket, giving up after the given duration. + * @param timeout maximum time to wait. + * @return true if data can be written on the socket, false otherwise. + */ + bool waitReadyWrite (std::chrono::nanoseconds timeout) const noexcept + { + return waitReadyWrite (std::chrono::steady_clock::now () + timeout); + } + + /** + * @brief block until at least one byte can be written, giving up at the given time point. + * @param deadline time point at which to give up, max to wait indefinitely. + * @return true if data can be written on the socket, false otherwise. + */ + bool waitReadyWrite (TimePoint deadline) const noexcept + { + bool wantRead = false; + bool wantWrite = true; + + if (_ssl && (SSL_want_read (_ssl.get ()) || SSL_want_write (_ssl.get ()))) + { + wantRead = SSL_want_read (_ssl.get ()); + wantWrite = SSL_want_write (_ssl.get ()); } - return _socket.waitReadyWrite (timeout); + return (_socket.waitUntil (wantRead, wantWrite, deadline) == 0); } /** @@ -650,11 +743,33 @@ namespace join * @brief write data until size is reached or an error occurred. * @param data data buffer to send. * @param size number of bytes to write. - * @param timeout timeout duration (zero: infinite). * @return 0 on success, -1 on failure. */ - int writeExactly (const char* data, size_t size, - std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero ()) + int writeExactly (const char* data, size_t size) + { + return writeExactly (data, size, TimePoint::max ()); + } + + /** + * @brief write data until size is reached, an error occurred or the given duration elapsed. + * @param data data buffer to send. + * @param size number of bytes to write. + * @param timeout maximum time granted to the whole write. + * @return 0 on success, -1 on failure. + */ + int writeExactly (const char* data, size_t size, std::chrono::nanoseconds timeout) + { + return writeExactly (data, size, std::chrono::steady_clock::now () + timeout); + } + + /** + * @brief write data until size is reached, an error occurred or the deadline expired. + * @param data data buffer to send. + * @param size number of bytes to write. + * @param deadline time point at which to give up, max to wait indefinitely. + * @return 0 on success, -1 on failure. + */ + int writeExactly (const char* data, size_t size, TimePoint deadline) { size_t numWrite = 0; @@ -665,7 +780,7 @@ namespace join { if (lastError == Errc::TemporaryError) { - if (waitReadyWrite (timeout)) + if (waitReadyWrite (deadline)) { continue; } diff --git a/crypto/include/join/tls_wrapper.hpp b/crypto/include/join/tls_wrapper.hpp index d46ad7a6..daa1a7b7 100644 --- a/crypto/include/join/tls_wrapper.hpp +++ b/crypto/include/join/tls_wrapper.hpp @@ -38,6 +38,7 @@ namespace join { public: using Endpoint = typename BasicTls::Endpoint; + using TimePoint = typename BasicTls::TimePoint; /// inherit the base constructors. using BasicTls::BasicTls; @@ -53,38 +54,79 @@ namespace join /** * @brief block until the underlying socket is connected. - * @param timeout timeout duration (zero: infinite). * @return true if connected, false otherwise. */ - bool waitConnected (std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero ()) + bool waitConnected () + { + return this->_socket.waitConnected (); + } + + /** + * @brief block until the underlying socket is connected, giving up after the given duration. + * @param timeout maximum time to wait. + * @return true if connected, false otherwise. + */ + bool waitConnected (std::chrono::nanoseconds timeout) { return this->_socket.waitConnected (timeout); } + /** + * @brief block until the underlying socket is connected, giving up at the given time point. + * @param deadline time point at which to give up, max to wait indefinitely. + * @return true if connected, false otherwise. + */ + bool waitConnected (TimePoint deadline) + { + return this->_socket.waitConnected (deadline); + } + /** * @brief block until the underlying socket is disconnected. - * @param timeout timeout duration (zero: infinite). * @return true if disconnected, false otherwise. */ - bool waitDisconnected (std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero ()) + bool waitDisconnected () + { + return this->_socket.waitDisconnected (); + } + + /** + * @brief block until the underlying socket is disconnected, giving up after the given duration. + * @param timeout maximum time to wait. + * @return true if disconnected, false otherwise. + */ + bool waitDisconnected (std::chrono::nanoseconds timeout) { return this->_socket.waitDisconnected (timeout); } /** - * @brief block until TLS handshake is finished. - * @param timeout timeout duration (zero: infinite). + * @brief block until the underlying socket is disconnected, giving up at the given time point. + * @param deadline time point at which to give up, max to wait indefinitely. + * @return true if disconnected, false otherwise. + */ + bool waitDisconnected (TimePoint deadline) + { + return this->_socket.waitDisconnected (deadline); + } + + /// keep the base overloads visible, the override below hides them. + using BasicTls::waitHandshake; + + /** + * @brief block until TLS handshake is finished, giving up at the given time point. + * @param deadline time point at which to give up, max to wait indefinitely. * @return true if TLS handshake is finished. * @note waits for the transport connection first, then runs the common handshake. */ - bool waitHandshake (std::chrono::nanoseconds timeout) override + bool waitHandshake (TimePoint deadline) override { - if (!this->_socket.waitConnected (timeout)) + if (!this->_socket.waitConnected (deadline)) { return false; } - return BasicTls::waitHandshake (timeout); + return BasicTls::waitHandshake (deadline); } /** diff --git a/crypto/tests/tls_wrapper_test.cpp b/crypto/tests/tls_wrapper_test.cpp index a9d8618b..7bfb0f90 100644 --- a/crypto/tests/tls_wrapper_test.cpp +++ b/crypto/tests/tls_wrapper_test.cpp @@ -422,6 +422,8 @@ TEST_F (TlsSocket, waitConnected) ASSERT_TRUE (tls.connecting ()); } ASSERT_TRUE (tls.waitConnected (_timeout)) << join::lastError.message (); + ASSERT_TRUE (tls.waitConnected ()) << join::lastError.message (); + ASSERT_TRUE (tls.waitConnected (std::chrono::steady_clock::now () + _timeout)) << join::lastError.message (); if (tls.disconnect () == -1) { ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); @@ -478,7 +480,8 @@ TEST_F (TlsSocket, waitHandshake) ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); } ASSERT_TRUE (tls.waitHandshake (_timeout)) << join::lastError.message (); - ASSERT_TRUE (tls.waitHandshake (_timeout)) << join::lastError.message (); + ASSERT_TRUE (tls.waitHandshake ()) << join::lastError.message (); + ASSERT_TRUE (tls.waitHandshake (std::chrono::steady_clock::now () + _timeout)) << join::lastError.message (); if (tls.shutdown () == -1) { ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); @@ -518,6 +521,8 @@ TEST_F (TlsSocket, waitDisconnected) Tls::Socket tls (ctx, Tcp::Socket::NonBlocking); ASSERT_TRUE (tls.waitDisconnected (_timeout)) << join::lastError.message (); + ASSERT_TRUE (tls.waitDisconnected ()) << join::lastError.message (); + ASSERT_TRUE (tls.waitDisconnected (std::chrono::steady_clock::now () + _timeout)) << join::lastError.message (); if (tls.connect ({_hostv4, _port}) == -1) { ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); @@ -556,6 +561,8 @@ TEST_F (TlsSocket, waitReadyRead) ASSERT_TRUE (tls.waitReadyWrite (_timeout)) << join::lastError.message (); ASSERT_EQ (tls.writeExactly (data, sizeof (data)), 0) << join::lastError.message (); ASSERT_TRUE (tls.waitReadyRead (_timeout)) << join::lastError.message (); + ASSERT_TRUE (tls.waitReadyRead ()) << join::lastError.message (); + ASSERT_TRUE (tls.waitReadyRead (std::chrono::steady_clock::now () + _timeout)) << join::lastError.message (); if (tls.shutdown () == -1) { ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); @@ -630,6 +637,19 @@ TEST_F (TlsSocket, readExactly) ASSERT_EQ (tls.shutdown (), 0) << join::lastError.message (); ASSERT_EQ (tls.disconnect (), 0) << join::lastError.message (); tls.close (); + + Tls::Socket pending (ctx, Tcp::Socket::NonBlocking); + char over[sizeof (data) * 2] = {}; + + if (pending.connect ({_hostv4, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (pending.waitHandshake (_timeout)) << join::lastError.message (); + ASSERT_EQ (pending.writeExactly (data, sizeof (data), _timeout), 0) << join::lastError.message (); + ASSERT_EQ (pending.readExactly (over, sizeof (over), std::chrono::milliseconds (100)), -1); + ASSERT_EQ (join::lastError, Errc::TimedOut); + pending.close (); } /** @@ -650,9 +670,12 @@ TEST_F (TlsSocket, waitReadyWrite) if (tls.handshake () == -1) { ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + ASSERT_TRUE (tls.waitReadyWrite (_timeout)) << join::lastError.message (); } ASSERT_TRUE (tls.waitHandshake (_timeout)) << join::lastError.message (); ASSERT_TRUE (tls.waitReadyWrite (_timeout)) << join::lastError.message (); + ASSERT_TRUE (tls.waitReadyWrite ()) << join::lastError.message (); + ASSERT_TRUE (tls.waitReadyWrite (std::chrono::steady_clock::now () + _timeout)) << join::lastError.message (); if (tls.shutdown () == -1) { ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); From 2c31b3dc0db79e39b3878119a513c48d1347b378 Mon Sep 17 00:00:00 2001 From: mrabine Date: Sat, 29 Aug 2026 23:20:38 +0200 Subject: [PATCH 6/9] blocking api guard --- core/include/join/socket.hpp | 9 +++++++++ core/include/join/socket_stream.hpp | 29 ++++++++++++++------------- core/include/join/stream_socket.hpp | 18 +++++++++++++++++ core/tests/tcp_socket_stream_test.cpp | 8 ++++++++ core/tests/tcp_socket_test.cpp | 28 ++++++++++++++++++++++++++ crypto/include/join/tls.hpp | 24 ++++++++++++++++++++++ crypto/include/join/tls_stream.hpp | 5 +++-- crypto/tests/dtls_wrapper_test.cpp | 11 ++++++---- crypto/tests/tls_wrapper_test.cpp | 14 +++++++++++++ 9 files changed, 126 insertions(+), 20 deletions(-) diff --git a/core/include/join/socket.hpp b/core/include/join/socket.hpp index ba8a578e..91cbc390 100644 --- a/core/include/join/socket.hpp +++ b/core/include/join/socket.hpp @@ -653,6 +653,15 @@ namespace join return _protocol.protocol (); } + /** + * @brief get the blocking mode of the socket. + * @return the blocking mode of the socket. + */ + Mode mode () const noexcept + { + return _mode; + } + /** * @brief get socket native handle. * @return socket native handle. diff --git a/core/include/join/socket_stream.hpp b/core/include/join/socket_stream.hpp index 7594fc67..4a1e8464 100644 --- a/core/include/join/socket_stream.hpp +++ b/core/include/join/socket_stream.hpp @@ -45,6 +45,7 @@ namespace join public: using Endpoint = typename Protocol::Endpoint; using Socket = typename Protocol::Socket; + using TimePoint = typename Socket::TimePoint; /** * @brief default constructor. @@ -147,7 +148,7 @@ namespace join return nullptr; } - if (!(hasTimeout () ? _socket.waitConnected (_timeout) : _socket.waitConnected ())) + if (!_socket.waitConnected (deadline ())) { _socket.close (); return nullptr; @@ -175,7 +176,7 @@ namespace join return nullptr; } - if (!(hasTimeout () ? _socket.waitDisconnected (_timeout) : _socket.waitDisconnected ())) + if (!_socket.waitDisconnected (deadline ())) { return nullptr; } @@ -211,6 +212,16 @@ namespace join return _timeout; } + /** + * @brief get the deadline of an operation started now. + * @return the deadline, max when the stream operations are not time bounded. + */ + TimePoint deadline () const noexcept + { + return (_timeout == std::chrono::nanoseconds::zero ()) ? TimePoint::max () + : std::chrono::steady_clock::now () + _timeout; + } + /** * @brief get the nested socket. * @return the nested socket. @@ -247,7 +258,7 @@ namespace join { if (lastError == Errc::TemporaryError) { - if (hasTimeout () ? _socket.waitReadyRead (_timeout) : _socket.waitReadyRead ()) + if (_socket.waitReadyRead (deadline ())) { continue; } @@ -287,8 +298,7 @@ namespace join std::streamsize pending = pptr () - pbase (); if (pending) { - if ((hasTimeout () ? _socket.writeExactly (pbase (), pending, _timeout) - : _socket.writeExactly (pbase (), pending)) == -1) + if (_socket.writeExactly (pbase (), pending, deadline ()) == -1) { _socket.close (); return traits_type::eof (); @@ -325,15 +335,6 @@ namespace join /// internal buffer. std::unique_ptr _buf; - /** - * @brief check if a timeout is configured. - * @return true if a timeout is configured. - */ - bool hasTimeout () const noexcept - { - return _timeout != std::chrono::nanoseconds::zero (); - } - /// timeout, zero when the stream operations are not time bounded. std::chrono::nanoseconds _timeout = std::chrono::seconds (30); diff --git a/core/include/join/stream_socket.hpp b/core/include/join/stream_socket.hpp index 7a6b3a4c..9f2bacec 100644 --- a/core/include/join/stream_socket.hpp +++ b/core/include/join/stream_socket.hpp @@ -281,6 +281,12 @@ namespace join */ bool waitDisconnected (TimePoint deadline) { + if (JOIN_UNLIKELY ((deadline != TimePoint::max ()) && (this->_mode == Mode::Blocking))) + { + lastError = make_error_code (Errc::OperationFailed); + return false; + } + if ((this->_state != State::Disconnected) && (this->_state != State::Closed)) { if (this->_state != State::Disconnecting) @@ -367,6 +373,12 @@ namespace join */ int readExactly (char* data, size_t size, TimePoint deadline) noexcept { + if (JOIN_UNLIKELY ((deadline != TimePoint::max ()) && (this->_mode == Mode::Blocking))) + { + lastError = make_error_code (Errc::OperationFailed); + return -1; + } + size_t numRead = 0; while (numRead < size) @@ -423,6 +435,12 @@ namespace join */ int writeExactly (const char* data, size_t size, TimePoint deadline) noexcept { + if (JOIN_UNLIKELY ((deadline != TimePoint::max ()) && (this->_mode == Mode::Blocking))) + { + lastError = make_error_code (Errc::OperationFailed); + return -1; + } + size_t numWrite = 0; while (numWrite < size) diff --git a/core/tests/tcp_socket_stream_test.cpp b/core/tests/tcp_socket_stream_test.cpp index 0271d1ed..94c94cda 100644 --- a/core/tests/tcp_socket_stream_test.cpp +++ b/core/tests/tcp_socket_stream_test.cpp @@ -280,6 +280,14 @@ TEST_F (TcpSocketStream, timeout) ASSERT_NE (tcpStream.timeout (), _timeout); tcpStream.timeout (_timeout); ASSERT_EQ (tcpStream.timeout (), _timeout); + + join::BasicSocketStreambuf sockbuf; + + auto before = std::chrono::steady_clock::now (); + + ASSERT_GE (sockbuf.deadline (), before + sockbuf.timeout ()); + sockbuf.timeout (std::chrono::nanoseconds::zero ()); + ASSERT_EQ (sockbuf.deadline (), Tcp::Socket::TimePoint::max ()); } /** diff --git a/core/tests/tcp_socket_test.cpp b/core/tests/tcp_socket_test.cpp index 6d20a768..29a06903 100644 --- a/core/tests/tcp_socket_test.cpp +++ b/core/tests/tcp_socket_test.cpp @@ -398,6 +398,12 @@ TEST_F (TcpSocket, readExactly) std::error_code error = join::lastError; auto elapsed = std::chrono::steady_clock::now () - beg; + dribbler.setMode (Tcp::Socket::Blocking); + int blocking = dribbler.readExactly (slow, sizeof (slow), std::chrono::milliseconds (250)); + std::error_code blockingError = join::lastError; + bool blockingWait = dribbler.waitDisconnected (std::chrono::milliseconds (250)); + std::error_code blockingWaitError = join::lastError; + sender.join (); dribbler.close (); peer.close (); @@ -406,6 +412,10 @@ TEST_F (TcpSocket, readExactly) ASSERT_EQ (result, -1); ASSERT_EQ (error, Errc::TimedOut); ASSERT_LT (elapsed, std::chrono::milliseconds (700)); + ASSERT_EQ (blocking, -1); + ASSERT_EQ (blockingError, Errc::OperationFailed); + ASSERT_FALSE (blockingWait); + ASSERT_EQ (blockingWaitError, Errc::OperationFailed); } /** @@ -553,6 +563,12 @@ TEST_F (TcpSocket, writeExactly) ASSERT_EQ (join::lastError, Errc::TimedOut); ASSERT_LT (std::chrono::steady_clock::now () - beg, std::chrono::milliseconds (700)); + sender.setMode (Tcp::Socket::Blocking); + ASSERT_EQ (sender.writeExactly (bulk.data (), bulk.size (), std::chrono::milliseconds (250)), -1); + ASSERT_EQ (join::lastError, Errc::OperationFailed); + ASSERT_EQ (sender.writeExactly (bulk.data (), bulk.size (), std::chrono::steady_clock::now ()), -1); + ASSERT_EQ (join::lastError, Errc::OperationFailed); + sender.close (); peer.close (); stall.close (); @@ -581,6 +597,18 @@ TEST_F (TcpSocket, setMode) tcpSocket.close (); } +/** + * @brief Test mode method. + */ +TEST_F (TcpSocket, mode) +{ + Tcp::Socket tcpSocket; + + ASSERT_EQ (tcpSocket.mode (), Tcp::Socket::NonBlocking); + tcpSocket.setMode (Tcp::Socket::Blocking); + ASSERT_EQ (tcpSocket.mode (), Tcp::Socket::Blocking); +} + /** * @brief Test setOption method. */ diff --git a/crypto/include/join/tls.hpp b/crypto/include/join/tls.hpp index dcb0e43d..40d7beb6 100644 --- a/crypto/include/join/tls.hpp +++ b/crypto/include/join/tls.hpp @@ -369,6 +369,12 @@ namespace join */ virtual bool waitHandshake (TimePoint deadline) { + if (JOIN_UNLIKELY ((deadline != TimePoint::max ()) && (_socket.mode () == Mode::Blocking))) + { + lastError = make_error_code (Errc::OperationFailed); + return false; + } + if (handshake () == 0) { return true; @@ -511,6 +517,12 @@ namespace join */ bool waitShutdown (TimePoint deadline) noexcept { + if (JOIN_UNLIKELY ((deadline != TimePoint::max ()) && (_socket.mode () == Mode::Blocking))) + { + lastError = make_error_code (Errc::OperationFailed); + return false; + } + if (!_ssl) { return true; @@ -655,6 +667,12 @@ namespace join */ int readExactly (char* data, size_t size, TimePoint deadline) { + if (JOIN_UNLIKELY ((deadline != TimePoint::max ()) && (_socket.mode () == Mode::Blocking))) + { + lastError = make_error_code (Errc::OperationFailed); + return -1; + } + size_t numRead = 0; while (numRead < size) @@ -771,6 +789,12 @@ namespace join */ int writeExactly (const char* data, size_t size, TimePoint deadline) { + if (JOIN_UNLIKELY ((deadline != TimePoint::max ()) && (_socket.mode () == Mode::Blocking))) + { + lastError = make_error_code (Errc::OperationFailed); + return -1; + } + size_t numWrite = 0; while (numWrite < size) diff --git a/crypto/include/join/tls_stream.hpp b/crypto/include/join/tls_stream.hpp index 2a59e11c..4885d176 100644 --- a/crypto/include/join/tls_stream.hpp +++ b/crypto/include/join/tls_stream.hpp @@ -30,6 +30,7 @@ #include // C++. +#include #include namespace join @@ -131,7 +132,7 @@ namespace join { if (lastError == Errc::TemporaryError) { - if (this->_sockbuf.socket ().waitHandshake (this->timeout ())) + if (this->_sockbuf.socket ().waitHandshake (this->_sockbuf.deadline ())) { return; } @@ -150,7 +151,7 @@ namespace join { if (lastError == Errc::TemporaryError) { - if (this->_sockbuf.socket ().waitShutdown (this->timeout ())) + if (this->_sockbuf.socket ().waitShutdown (this->_sockbuf.deadline ())) { return; } diff --git a/crypto/tests/dtls_wrapper_test.cpp b/crypto/tests/dtls_wrapper_test.cpp index 11fdc989..f845d327 100644 --- a/crypto/tests/dtls_wrapper_test.cpp +++ b/crypto/tests/dtls_wrapper_test.cpp @@ -216,10 +216,13 @@ class DtlsSocket : public EventHandler, public ::testing::Test { char buffer[65536]; Udp::Endpoint from; - ssize_t nread = _socket.readFrom (buffer, sizeof (buffer), &from); - if (nread > 0) + if (_socket.waitReadyRead (_timeout)) { - _socket.writeTo (buffer, nread, from); + ssize_t nread = _socket.readFrom (buffer, sizeof (buffer), &from); + if (nread > 0) + { + _socket.writeTo (buffer, nread, from); + } } _socket.waitShutdown (_timeout); } @@ -229,7 +232,7 @@ class DtlsSocket : public EventHandler, public ::testing::Test TlsContext _tlsContext{TlsContext::DtlsServer}; /// socket. - Dtls::Socket _socket{_tlsContext, Udp::Socket::Blocking}; + Dtls::Socket _socket{_tlsContext, Udp::Socket::NonBlocking}; /// host. static const std::string _hostv4; diff --git a/crypto/tests/tls_wrapper_test.cpp b/crypto/tests/tls_wrapper_test.cpp index 7bfb0f90..ffe86bb5 100644 --- a/crypto/tests/tls_wrapper_test.cpp +++ b/crypto/tests/tls_wrapper_test.cpp @@ -467,6 +467,14 @@ TEST_F (TlsSocket, waitHandshake) { TlsContext ctx (TlsContext::TlsClient); Tls::Socket tls (ctx, Tcp::Socket::NonBlocking); + Tls::Socket blocking (ctx, Tcp::Socket::Blocking); + + ASSERT_EQ (blocking.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_FALSE (blocking.waitHandshake (_timeout)); + ASSERT_EQ (join::lastError, Errc::OperationFailed); + ASSERT_FALSE (blocking.waitShutdown (_timeout)); + ASSERT_EQ (join::lastError, Errc::OperationFailed); + blocking.close (); ASSERT_EQ (tls.open (Tls::v6 ()), 0) << join::lastError.message (); ASSERT_FALSE (tls.waitHandshake (_timeout)); @@ -487,6 +495,8 @@ TEST_F (TlsSocket, waitHandshake) ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); } ASSERT_TRUE (tls.waitShutdown (_timeout)) << join::lastError.message (); + ASSERT_TRUE (tls.waitShutdown ()) << join::lastError.message (); + ASSERT_TRUE (tls.waitShutdown (std::chrono::steady_clock::now () + _timeout)) << join::lastError.message (); if (tls.disconnect () == -1) { ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); @@ -628,6 +638,8 @@ TEST_F (TlsSocket, readExactly) ASSERT_EQ (tls.readExactly (data, sizeof (data)), -1); ASSERT_EQ (join::lastError, Errc::OperationFailed); + ASSERT_EQ (tls.readExactly (data, sizeof (data), _timeout), -1); + ASSERT_EQ (join::lastError, Errc::OperationFailed); ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_EQ (tls.handshake (), 0) << join::lastError.message (); ASSERT_TRUE (tls.waitReadyWrite (_timeout)) << join::lastError.message (); @@ -718,6 +730,8 @@ TEST_F (TlsSocket, writeExactly) ASSERT_EQ (tls.writeExactly (data, sizeof (data)), -1); ASSERT_EQ (join::lastError, Errc::OperationFailed); + ASSERT_EQ (tls.writeExactly (data, sizeof (data), _timeout), -1); + ASSERT_EQ (join::lastError, Errc::OperationFailed); ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_EQ (tls.handshake (), 0) << join::lastError.message (); ASSERT_TRUE (tls.waitReadyWrite (_timeout)) << join::lastError.message (); From f95ad04c983e242a3b94d751859092934c5343d2 Mon Sep 17 00:00:00 2001 From: mrabine Date: Sat, 29 Aug 2026 23:30:14 +0200 Subject: [PATCH 7/9] fix tests --- core/tests/proactor_test.cpp | 107 ++++++++++++++++++++++++++++------- core/tests/reactor_test.cpp | 44 +++++++++++--- 2 files changed, 122 insertions(+), 29 deletions(-) diff --git a/core/tests/proactor_test.cpp b/core/tests/proactor_test.cpp index 16283ce4..fdb38093 100644 --- a/core/tests/proactor_test.cpp +++ b/core/tests/proactor_test.cpp @@ -166,7 +166,7 @@ class ProactorTest : public CompletionHandler, public ::testing::Test }; Tcp::Acceptor ProactorTest::_acceptor; -Tcp::Socket ProactorTest::_client (Tcp::Socket::Blocking); +Tcp::Socket ProactorTest::_client (Tcp::Socket::NonBlocking); Tcp::Socket ProactorTest::_server; std::string ProactorTest::_host = "127.0.0.1"; uint16_t ProactorTest::_port = 5001; @@ -191,7 +191,11 @@ TEST_F (ProactorTest, stop) proactor.run (); }); - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); auto op = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); @@ -273,7 +277,11 @@ TEST_F (ProactorTest, submit) ASSERT_EQ (proactor.submit (&op1, true, true), -1); ASSERT_EQ (join::lastError, std::errc::bad_file_descriptor); - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); op1 = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); @@ -344,7 +352,11 @@ TEST_F (ProactorTest, cancel) ASSERT_EQ (proactor.cancel (&op1, true, true), -1); ASSERT_EQ (join::lastError, std::errc::bad_file_descriptor); - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); op1 = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); @@ -383,7 +395,11 @@ TEST_F (ProactorTest, flush) proactor.run (); }); - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); auto op = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); @@ -416,7 +432,11 @@ TEST_F (ProactorTest, chain) proactor.run (); }); - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); auto writeOp = IoOperation::makeWrite (_server.handle (), "ping", 4, this, true); @@ -552,7 +572,6 @@ TEST_F (ProactorTest, asyncConnect) Tcp::Endpoint endpoint{_host, _port}; ASSERT_EQ (_client.open (Tcp::v4 ()), 0) << join::lastError.message (); - _client.setMode (Tcp::Socket::NonBlocking); auto op = IoOperation::makeConnect (_client.handle (), endpoint.addr (), endpoint.length (), this); ASSERT_EQ (ProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); @@ -567,8 +586,6 @@ TEST_F (ProactorTest, asyncConnect) _op = nullptr; _result = 0; } - - _client.setMode (Tcp::Socket::Blocking); } /** @@ -592,7 +609,11 @@ TEST_F (ProactorTest, asyncAccept) SOCK_NONBLOCK | SOCK_CLOEXEC, this); ASSERT_EQ (ProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); { ScopedLock lock (_mut); @@ -610,7 +631,11 @@ TEST_F (ProactorTest, asyncAccept) */ TEST_F (ProactorTest, asyncWrite) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); ASSERT_EQ (ProactorThread::affinity (0), 0) << join::lastError.message (); @@ -648,7 +673,11 @@ TEST_F (ProactorTest, asyncWrite) */ TEST_F (ProactorTest, asyncRead) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); ASSERT_EQ (ProactorThread::affinity (0), 0) << join::lastError.message (); @@ -683,7 +712,11 @@ TEST_F (ProactorTest, asyncRead) */ TEST_F (ProactorTest, asyncWriteFixed) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); const char* msg = "asyncWriteFixed"; @@ -718,7 +751,11 @@ TEST_F (ProactorTest, asyncWriteFixed) */ TEST_F (ProactorTest, asyncReadFixed) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); std::vector regbuf (sizeof (_buf)); @@ -751,7 +788,11 @@ TEST_F (ProactorTest, asyncReadFixed) */ TEST_F (ProactorTest, asyncSendmsg) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); ASSERT_EQ (ProactorThread::affinity (0), 0) << join::lastError.message (); @@ -793,7 +834,11 @@ TEST_F (ProactorTest, asyncSendmsg) */ TEST_F (ProactorTest, asyncRecvmsg) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); ASSERT_EQ (ProactorThread::affinity (0), 0) << join::lastError.message (); @@ -832,7 +877,11 @@ TEST_F (ProactorTest, asyncRecvmsg) */ TEST_F (ProactorTest, asyncSend) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); const char* msg = "asyncSend"; @@ -860,7 +909,11 @@ TEST_F (ProactorTest, asyncSend) */ TEST_F (ProactorTest, asyncRecv) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); auto op = IoOperation::makeRecv (_server.handle (), _buf, sizeof (_buf), 0, this); @@ -884,7 +937,11 @@ TEST_F (ProactorTest, asyncRecv) */ TEST_F (ProactorTest, onClose) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); ASSERT_EQ (ProactorThread::affinity (0), 0) << join::lastError.message (); @@ -917,7 +974,11 @@ TEST_F (ProactorTest, onClose) */ TEST_F (ProactorTest, onError) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); ASSERT_EQ (ProactorThread::affinity (0), 0) << join::lastError.message (); @@ -954,7 +1015,11 @@ TEST_F (ProactorTest, resubmit) { const char* msg = "resubmit"; - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); _resubmits = 1; diff --git a/core/tests/reactor_test.cpp b/core/tests/reactor_test.cpp index 64821617..3cd03a66 100644 --- a/core/tests/reactor_test.cpp +++ b/core/tests/reactor_test.cpp @@ -172,7 +172,7 @@ class ReactorTest : public join::EventHandler, public ::testing::Test }; Tcp::Acceptor ReactorTest::_acceptor; -Tcp::Socket ReactorTest::_client (Tcp::Socket::Blocking); +Tcp::Socket ReactorTest::_client (Tcp::Socket::NonBlocking); Tcp::Socket ReactorTest::_server; std::string ReactorTest::_host = "127.0.0.1"; uint16_t ReactorTest::_port = 5000; @@ -200,7 +200,11 @@ TEST_F (ReactorTest, addWriteHandler) ASSERT_EQ (join::lastError, std::errc::bad_file_descriptor); // connect sockets. - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); // test invalid event. @@ -239,7 +243,11 @@ TEST_F (ReactorTest, addReadHandler) ASSERT_EQ (join::lastError, std::errc::bad_file_descriptor); // connect socket. - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); // test invalid event. @@ -274,7 +282,11 @@ TEST_F (ReactorTest, delHandler) ASSERT_EQ (join::lastError, std::errc::bad_file_descriptor); // connect socket. - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); // add handler. @@ -379,7 +391,11 @@ TEST_F (ReactorTest, waitStopped) TEST_F (ReactorTest, onReadable) { // connect socket. - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); // tun thread @@ -418,7 +434,11 @@ TEST_F (ReactorTest, onReadable) TEST_F (ReactorTest, onWriteable) { // connect socket. - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); // tun thread @@ -454,7 +474,11 @@ TEST_F (ReactorTest, onWriteable) TEST_F (ReactorTest, onClose) { // connect socket. - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); // tun thread @@ -490,7 +514,11 @@ TEST_F (ReactorTest, onClose) TEST_F (ReactorTest, onError) { // connect socket. - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); // tun thread From b6d89a0cf91e8bfe7dbabd7db5e2d3b253f165b8 Mon Sep 17 00:00:00 2001 From: mrabine Date: Sat, 29 Aug 2026 23:38:47 +0200 Subject: [PATCH 8/9] fix tests --- core/tests/hybrid_proactor_test.cpp | 107 ++++++++++++++++++++++------ core/tests/sqpoll_proactor_test.cpp | 107 ++++++++++++++++++++++------ 2 files changed, 172 insertions(+), 42 deletions(-) diff --git a/core/tests/hybrid_proactor_test.cpp b/core/tests/hybrid_proactor_test.cpp index b047f712..8dd57733 100644 --- a/core/tests/hybrid_proactor_test.cpp +++ b/core/tests/hybrid_proactor_test.cpp @@ -162,7 +162,7 @@ class HybridProactorTest : public CompletionHandler, public ::testing::Test }; Tcp::Acceptor HybridProactorTest::_acceptor; -Tcp::Socket HybridProactorTest::_client (Tcp::Socket::Blocking); +Tcp::Socket HybridProactorTest::_client (Tcp::Socket::NonBlocking); Tcp::Socket HybridProactorTest::_server; std::string HybridProactorTest::_host = "127.0.0.1"; uint16_t HybridProactorTest::_port = 5001; @@ -187,7 +187,11 @@ TEST_F (HybridProactorTest, stop) proactor.run (); }); - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); auto op = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); @@ -269,7 +273,11 @@ TEST_F (HybridProactorTest, submit) ASSERT_EQ (proactor.submit (&op1, true, true), -1); ASSERT_EQ (join::lastError, std::errc::bad_file_descriptor); - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); op1 = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); @@ -340,7 +348,11 @@ TEST_F (HybridProactorTest, cancel) ASSERT_EQ (proactor.cancel (&op1, true, true), -1); ASSERT_EQ (join::lastError, std::errc::bad_file_descriptor); - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); op1 = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); @@ -378,7 +390,11 @@ TEST_F (HybridProactorTest, flush) proactor.run (); }); - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); auto op = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); @@ -411,7 +427,11 @@ TEST_F (HybridProactorTest, chain) proactor.run (); }); - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); auto writeOp = IoOperation::makeWrite (_server.handle (), "ping", 4, this, true); @@ -546,7 +566,6 @@ TEST_F (HybridProactorTest, asyncConnect) Tcp::Endpoint endpoint{_host, _port}; ASSERT_EQ (_client.open (Tcp::v4 ()), 0) << join::lastError.message (); - _client.setMode (Tcp::Socket::NonBlocking); auto op = IoOperation::makeConnect (_client.handle (), endpoint.addr (), endpoint.length (), this); ASSERT_EQ (HybridProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); @@ -561,8 +580,6 @@ TEST_F (HybridProactorTest, asyncConnect) _op = nullptr; _result = 0; } - - _client.setMode (Tcp::Socket::Blocking); } /** @@ -586,7 +603,11 @@ TEST_F (HybridProactorTest, asyncAccept) SOCK_NONBLOCK | SOCK_CLOEXEC, this); ASSERT_EQ (HybridProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); { ScopedLock lock (_mut); @@ -604,7 +625,11 @@ TEST_F (HybridProactorTest, asyncAccept) */ TEST_F (HybridProactorTest, asyncWrite) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); ASSERT_EQ (HybridProactorThread::affinity (0), 0) << join::lastError.message (); @@ -642,7 +667,11 @@ TEST_F (HybridProactorTest, asyncWrite) */ TEST_F (HybridProactorTest, asyncRead) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); ASSERT_EQ (HybridProactorThread::affinity (0), 0) << join::lastError.message (); @@ -676,7 +705,11 @@ TEST_F (HybridProactorTest, asyncRead) */ TEST_F (HybridProactorTest, asyncWriteFixed) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); const char* msg = "asyncWriteFixed"; @@ -711,7 +744,11 @@ TEST_F (HybridProactorTest, asyncWriteFixed) */ TEST_F (HybridProactorTest, asyncReadFixed) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); std::vector regbuf (sizeof (_buf)); @@ -743,7 +780,11 @@ TEST_F (HybridProactorTest, asyncReadFixed) */ TEST_F (HybridProactorTest, asyncSendmsg) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); ASSERT_EQ (HybridProactorThread::affinity (0), 0) << join::lastError.message (); @@ -785,7 +826,11 @@ TEST_F (HybridProactorTest, asyncSendmsg) */ TEST_F (HybridProactorTest, asyncRecvmsg) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); ASSERT_EQ (HybridProactorThread::affinity (0), 0) << join::lastError.message (); @@ -824,7 +869,11 @@ TEST_F (HybridProactorTest, asyncRecvmsg) */ TEST_F (HybridProactorTest, asyncSend) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); const char* msg = "asyncSend"; @@ -852,7 +901,11 @@ TEST_F (HybridProactorTest, asyncSend) */ TEST_F (HybridProactorTest, asyncRecv) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); auto op = IoOperation::makeRecv (_server.handle (), _buf, sizeof (_buf), 0, this); @@ -876,7 +929,11 @@ TEST_F (HybridProactorTest, asyncRecv) */ TEST_F (HybridProactorTest, onClose) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); ASSERT_EQ (HybridProactorThread::affinity (0), 0) << join::lastError.message (); @@ -909,7 +966,11 @@ TEST_F (HybridProactorTest, onClose) */ TEST_F (HybridProactorTest, onError) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); ASSERT_EQ (HybridProactorThread::affinity (0), 0) << join::lastError.message (); @@ -946,7 +1007,11 @@ TEST_F (HybridProactorTest, resubmit) { const char* msg = "resubmit"; - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); _resubmits = 1; diff --git a/core/tests/sqpoll_proactor_test.cpp b/core/tests/sqpoll_proactor_test.cpp index 297d0671..bea64599 100644 --- a/core/tests/sqpoll_proactor_test.cpp +++ b/core/tests/sqpoll_proactor_test.cpp @@ -162,7 +162,7 @@ class SqpollProactorTest : public CompletionHandler, public ::testing::Test }; Tcp::Acceptor SqpollProactorTest::_acceptor; -Tcp::Socket SqpollProactorTest::_client (Tcp::Socket::Blocking); +Tcp::Socket SqpollProactorTest::_client (Tcp::Socket::NonBlocking); Tcp::Socket SqpollProactorTest::_server; std::string SqpollProactorTest::_host = "127.0.0.1"; uint16_t SqpollProactorTest::_port = 5001; @@ -187,7 +187,11 @@ TEST_F (SqpollProactorTest, stop) proactor.run (); }); - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); auto op = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); @@ -269,7 +273,11 @@ TEST_F (SqpollProactorTest, submit) ASSERT_EQ (proactor.submit (&op1, true, true), -1); ASSERT_EQ (join::lastError, std::errc::bad_file_descriptor); - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); op1 = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); @@ -340,7 +348,11 @@ TEST_F (SqpollProactorTest, cancel) ASSERT_EQ (proactor.cancel (&op1, true, true), -1); ASSERT_EQ (join::lastError, std::errc::bad_file_descriptor); - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); op1 = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); @@ -378,7 +390,11 @@ TEST_F (SqpollProactorTest, flush) proactor.run (); }); - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); auto op = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); @@ -411,7 +427,11 @@ TEST_F (SqpollProactorTest, chain) proactor.run (); }); - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); auto writeOp = IoOperation::makeWrite (_server.handle (), "ping", 4, this, true); @@ -546,7 +566,6 @@ TEST_F (SqpollProactorTest, asyncConnect) Tcp::Endpoint endpoint{_host, _port}; ASSERT_EQ (_client.open (Tcp::v4 ()), 0) << join::lastError.message (); - _client.setMode (Tcp::Socket::NonBlocking); auto op = IoOperation::makeConnect (_client.handle (), endpoint.addr (), endpoint.length (), this); ASSERT_EQ (SqpollProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); @@ -561,8 +580,6 @@ TEST_F (SqpollProactorTest, asyncConnect) _op = nullptr; _result = 0; } - - _client.setMode (Tcp::Socket::Blocking); } /** @@ -586,7 +603,11 @@ TEST_F (SqpollProactorTest, asyncAccept) SOCK_NONBLOCK | SOCK_CLOEXEC, this); ASSERT_EQ (SqpollProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); { ScopedLock lock (_mut); @@ -604,7 +625,11 @@ TEST_F (SqpollProactorTest, asyncAccept) */ TEST_F (SqpollProactorTest, asyncWrite) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); ASSERT_EQ (SqpollProactorThread::affinity (0), 0) << join::lastError.message (); @@ -642,7 +667,11 @@ TEST_F (SqpollProactorTest, asyncWrite) */ TEST_F (SqpollProactorTest, asyncRead) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); ASSERT_EQ (SqpollProactorThread::affinity (0), 0) << join::lastError.message (); @@ -676,7 +705,11 @@ TEST_F (SqpollProactorTest, asyncRead) */ TEST_F (SqpollProactorTest, asyncWriteFixed) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); const char* msg = "asyncWriteFixed"; @@ -711,7 +744,11 @@ TEST_F (SqpollProactorTest, asyncWriteFixed) */ TEST_F (SqpollProactorTest, asyncReadFixed) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); std::vector regbuf (sizeof (_buf)); @@ -743,7 +780,11 @@ TEST_F (SqpollProactorTest, asyncReadFixed) */ TEST_F (SqpollProactorTest, asyncSendmsg) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); ASSERT_EQ (SqpollProactorThread::affinity (0), 0) << join::lastError.message (); @@ -785,7 +826,11 @@ TEST_F (SqpollProactorTest, asyncSendmsg) */ TEST_F (SqpollProactorTest, asyncRecvmsg) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); ASSERT_EQ (SqpollProactorThread::affinity (0), 0) << join::lastError.message (); @@ -824,7 +869,11 @@ TEST_F (SqpollProactorTest, asyncRecvmsg) */ TEST_F (SqpollProactorTest, asyncSend) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); const char* msg = "asyncSend"; @@ -852,7 +901,11 @@ TEST_F (SqpollProactorTest, asyncSend) */ TEST_F (SqpollProactorTest, asyncRecv) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); auto op = IoOperation::makeRecv (_server.handle (), _buf, sizeof (_buf), 0, this); @@ -876,7 +929,11 @@ TEST_F (SqpollProactorTest, asyncRecv) */ TEST_F (SqpollProactorTest, onClose) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); ASSERT_EQ (SqpollProactorThread::affinity (0), 0) << join::lastError.message (); @@ -909,7 +966,11 @@ TEST_F (SqpollProactorTest, onClose) */ TEST_F (SqpollProactorTest, onError) { - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); ASSERT_EQ (SqpollProactorThread::affinity (0), 0) << join::lastError.message (); @@ -946,7 +1007,11 @@ TEST_F (SqpollProactorTest, resubmit) { const char* msg = "resubmit"; - ASSERT_EQ (_client.connect ({_host, _port}), 0) << join::lastError.message (); + if (_client.connect ({_host, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); _resubmits = 1; From 4aaa4be76cdba507be4ec5eba2315aff4ea554ef Mon Sep 17 00:00:00 2001 From: mrabine Date: Sun, 30 Aug 2026 00:03:07 +0200 Subject: [PATCH 9/9] coverage --- core/tests/hybrid_proactor_test.cpp | 171 ++++++++++++++++------------ core/tests/proactor_test.cpp | 171 ++++++++++++++++------------ core/tests/sqpoll_proactor_test.cpp | 171 ++++++++++++++++------------ crypto/tests/tls_wrapper_test.cpp | 42 +++++++ 4 files changed, 333 insertions(+), 222 deletions(-) diff --git a/core/tests/hybrid_proactor_test.cpp b/core/tests/hybrid_proactor_test.cpp index 8dd57733..0e847ac8 100644 --- a/core/tests/hybrid_proactor_test.cpp +++ b/core/tests/hybrid_proactor_test.cpp @@ -60,6 +60,13 @@ class HybridProactorTest : public CompletionHandler, public ::testing::Test */ void TearDown () override { + auto& proactor = HybridProactorThread::proactor (); + proactor.cancel (&_readOp, true, true); + proactor.cancel (&_writeOp, true, true); + proactor.cancel (&_spareOp, true, true); + proactor.cancel (&_invalidOp, true, true); + proactor.cancel (&_resubmitOp, true, true); + _server.close (); _client.close (); _acceptor.close (); @@ -142,6 +149,18 @@ class HybridProactorTest : public CompletionHandler, public ::testing::Test /// last completed operation. static IoOperation* _op; + /// read side operation submitted by the tests. + static IoOperation _readOp; + + /// write side operation submitted by the tests. + static IoOperation _writeOp; + + /// spare operation for the tests submitting two operations on the same handle. + static IoOperation _spareOp; + + /// operation on an invalid handle. + static IoOperation _invalidOp; + /// operation resubmitted from a handler. static IoOperation _resubmitOp; @@ -170,6 +189,10 @@ const std::chrono::milliseconds HybridProactorTest::_timeout{1000}; Condition HybridProactorTest::_cond; Mutex HybridProactorTest::_mut; IoOperation* HybridProactorTest::_op = nullptr; +IoOperation HybridProactorTest::_readOp = {}; +IoOperation HybridProactorTest::_writeOp = {}; +IoOperation HybridProactorTest::_spareOp = {}; +IoOperation HybridProactorTest::_invalidOp = {}; IoOperation HybridProactorTest::_resubmitOp = {}; int HybridProactorTest::_resubmits = 0; int HybridProactorTest::_resubmitted = 0; @@ -194,15 +217,15 @@ TEST_F (HybridProactorTest, stop) ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); - auto op = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - ASSERT_EQ (proactor.submit (&op, true, true), 0) << join::lastError.message (); + _readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + ASSERT_EQ (proactor.submit (&_readOp, true, true), 0) << join::lastError.message (); proactor.stop (); th.join (); { ScopedLock lock (_mut); - ASSERT_EQ (_op, &op); + ASSERT_EQ (_op, &_readOp); ASSERT_EQ (_result, -ECANCELED); _op = nullptr; _result = 0; @@ -269,8 +292,8 @@ TEST_F (HybridProactorTest, submit) ASSERT_EQ (proactor.submit (nullptr, true, true), -1); ASSERT_EQ (join::lastError, Errc::InvalidParam); - auto op1 = IoOperation::makeRead (-1, _buf, sizeof (_buf), this); - ASSERT_EQ (proactor.submit (&op1, true, true), -1); + _readOp = IoOperation::makeRead (-1, _buf, sizeof (_buf), this); + ASSERT_EQ (proactor.submit (&_readOp, true, true), -1); ASSERT_EQ (join::lastError, std::errc::bad_file_descriptor); if (_client.connect ({_host, _port}) == -1) @@ -280,48 +303,48 @@ TEST_F (HybridProactorTest, submit) ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); - op1 = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - op1.state = IoOperation::State::Submitted; - ASSERT_EQ (proactor.submit (&op1, true, true), -1); + _readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + _readOp.state = IoOperation::State::Submitted; + ASSERT_EQ (proactor.submit (&_readOp, true, true), -1); ASSERT_EQ (join::lastError, std::errc::device_or_resource_busy); - op1.state = IoOperation::State::Idle; - ASSERT_EQ (proactor.submit (&op1, true, true), 0) << join::lastError.message (); + _readOp.state = IoOperation::State::Idle; + ASSERT_EQ (proactor.submit (&_readOp, true, true), 0) << join::lastError.message (); - auto op3 = IoOperation::makeRead (-1, _buf, sizeof (_buf), this); - ASSERT_EQ (proactor.submit (&op3, true, false), 0) << join::lastError.message (); + _invalidOp = IoOperation::makeRead (-1, _buf, sizeof (_buf), this); + ASSERT_EQ (proactor.submit (&_invalidOp, true, false), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op3 && _result == -EBADF; + return _op == &_invalidOp && _result == -EBADF; })); _op = nullptr; _result = 0; } #ifndef JOIN_HAS_IO_URING - auto op2 = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - ASSERT_EQ (proactor.submit (&op2, true, true), -1); + _spareOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + ASSERT_EQ (proactor.submit (&_spareOp, true, true), -1); ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (proactor.submit (&op2, true, false), 0) << join::lastError.message (); + ASSERT_EQ (proactor.submit (&_spareOp, true, false), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op2 && _result == -EINVAL; + return _op == &_spareOp && _result == -EINVAL; })); _op = nullptr; _result = 0; } #endif - ASSERT_EQ (proactor.cancel (&op1, true, true), 0) << join::lastError.message (); + ASSERT_EQ (proactor.cancel (&_readOp, true, true), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op1 && _result == -ECANCELED; + return _op == &_readOp && _result == -ECANCELED; })); _op = nullptr; _result = 0; @@ -344,8 +367,8 @@ TEST_F (HybridProactorTest, cancel) ASSERT_EQ (proactor.cancel (nullptr, true, true), -1); ASSERT_EQ (join::lastError, Errc::InvalidParam); - auto op1 = IoOperation::makeRead (-1, _buf, sizeof (_buf), this); - ASSERT_EQ (proactor.cancel (&op1, true, true), -1); + _readOp = IoOperation::makeRead (-1, _buf, sizeof (_buf), this); + ASSERT_EQ (proactor.cancel (&_readOp, true, true), -1); ASSERT_EQ (join::lastError, std::errc::bad_file_descriptor); if (_client.connect ({_host, _port}) == -1) @@ -355,22 +378,22 @@ TEST_F (HybridProactorTest, cancel) ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); - op1 = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - ASSERT_EQ (proactor.cancel (&op1, true, true), -1); + _readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + ASSERT_EQ (proactor.cancel (&_readOp, true, true), -1); ASSERT_EQ (join::lastError, Errc::OperationFailed); - ASSERT_EQ (proactor.submit (&op1, true, true), 0) << join::lastError.message (); + ASSERT_EQ (proactor.submit (&_readOp, true, true), 0) << join::lastError.message (); - auto op2 = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - op2.state = IoOperation::State::Submitted; - ASSERT_EQ (proactor.cancel (&op2, true, true), -1); + _spareOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + _spareOp.state = IoOperation::State::Submitted; + ASSERT_EQ (proactor.cancel (&_spareOp, true, true), -1); ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (proactor.cancel (&op1, true, true), 0) << join::lastError.message (); + ASSERT_EQ (proactor.cancel (&_readOp, true, true), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op1 && _result == -ECANCELED; + return _op == &_readOp && _result == -ECANCELED; })); _op = nullptr; _result = 0; @@ -397,8 +420,8 @@ TEST_F (HybridProactorTest, flush) ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); - auto op = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - ASSERT_EQ (proactor.submit (&op, false, true), 0) << join::lastError.message (); + _readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + ASSERT_EQ (proactor.submit (&_readOp, false, true), 0) << join::lastError.message (); ASSERT_EQ (proactor.flush (true), 0) << join::lastError.message (); ASSERT_EQ (_client.writeExactly ("flush", strlen ("flush"), _timeout), 0) << join::lastError.message (); @@ -406,7 +429,7 @@ TEST_F (HybridProactorTest, flush) { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_readOp && _result > 0; })); ASSERT_EQ (std::string (_buf, _result), "flush"); _op = nullptr; @@ -434,16 +457,16 @@ TEST_F (HybridProactorTest, chain) ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); - auto writeOp = IoOperation::makeWrite (_server.handle (), "ping", 4, this, true); - auto readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + _writeOp = IoOperation::makeWrite (_server.handle (), "ping", 4, this, true); + _readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - ASSERT_EQ (proactor.submit (&writeOp, false, true), 0) << join::lastError.message (); - ASSERT_EQ (proactor.submit (&readOp, true, true), 0) << join::lastError.message (); + ASSERT_EQ (proactor.submit (&_writeOp, false, true), 0) << join::lastError.message (); + ASSERT_EQ (proactor.submit (&_readOp, true, true), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &writeOp && _result == 4; + return _op == &_writeOp && _result == 4; })); _op = nullptr; _result = 0; @@ -456,7 +479,7 @@ TEST_F (HybridProactorTest, chain) { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &readOp && _result > 0; + return _op == &_readOp && _result > 0; })); ASSERT_EQ (std::string (_buf, _result), "pong"); _op = nullptr; @@ -567,15 +590,15 @@ TEST_F (HybridProactorTest, asyncConnect) ASSERT_EQ (_client.open (Tcp::v4 ()), 0) << join::lastError.message (); - auto op = IoOperation::makeConnect (_client.handle (), endpoint.addr (), endpoint.length (), this); - ASSERT_EQ (HybridProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + _readOp = IoOperation::makeConnect (_client.handle (), endpoint.addr (), endpoint.length (), this); + ASSERT_EQ (HybridProactorThread::proactor ().submit (&_readOp, true, true), 0) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result == 0; + return _op == &_readOp && _result == 0; })); _op = nullptr; _result = 0; @@ -599,10 +622,10 @@ TEST_F (HybridProactorTest, asyncAccept) sockaddr_storage addr = {}; socklen_t addrlen = sizeof (addr); - auto op = IoOperation::makeAccept (_acceptor.handle (), reinterpret_cast (&addr), &addrlen, + _readOp = IoOperation::makeAccept (_acceptor.handle (), reinterpret_cast (&addr), &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC, this); - ASSERT_EQ (HybridProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (HybridProactorThread::proactor ().submit (&_readOp, true, true), 0) << join::lastError.message (); if (_client.connect ({_host, _port}) == -1) { ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); @@ -612,7 +635,7 @@ TEST_F (HybridProactorTest, asyncAccept) { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result >= 0; + return _op == &_readOp && _result >= 0; })); ::close (_result); _op = nullptr; @@ -643,14 +666,14 @@ TEST_F (HybridProactorTest, asyncWrite) ASSERT_GT (HybridProactorThread::handle (), 0); const char* msg = "asyncWrite"; - auto op = IoOperation::makeWrite (_server.handle (), msg, strlen (msg), this); + _writeOp = IoOperation::makeWrite (_server.handle (), msg, strlen (msg), this); - ASSERT_EQ (HybridProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (HybridProactorThread::proactor ().submit (&_writeOp, true, true), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_writeOp && _result > 0; })); ASSERT_EQ (_result, static_cast (strlen (msg))); _op = nullptr; @@ -684,15 +707,15 @@ TEST_F (HybridProactorTest, asyncRead) ASSERT_EQ (HybridProactorThread::mlock (), 0) << join::lastError.message (); ASSERT_GT (HybridProactorThread::handle (), 0); - auto op = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + _readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - ASSERT_EQ (HybridProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (HybridProactorThread::proactor ().submit (&_readOp, true, true), 0) << join::lastError.message (); ASSERT_EQ (_client.writeExactly ("asyncRead", strlen ("asyncRead"), _timeout), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_readOp && _result > 0; })); ASSERT_EQ (std::string (_buf, _result), "asyncRead"); _op = nullptr; @@ -719,13 +742,13 @@ TEST_F (HybridProactorTest, asyncWriteFixed) ASSERT_EQ (HybridProactorThread::proactor ().registerBuffers (iovecs), 0) << join::lastError.message (); - auto op = IoOperation::makeWriteFixed (_server.handle (), regbuf.data (), regbuf.size (), 0, this); - ASSERT_EQ (HybridProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + _writeOp = IoOperation::makeWriteFixed (_server.handle (), regbuf.data (), regbuf.size (), 0, this); + ASSERT_EQ (HybridProactorThread::proactor ().submit (&_writeOp, true, true), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_writeOp && _result > 0; })); ASSERT_EQ (_result, static_cast (strlen (msg))); _op = nullptr; @@ -757,15 +780,15 @@ TEST_F (HybridProactorTest, asyncReadFixed) ASSERT_EQ (HybridProactorThread::proactor ().registerBuffers (iovecs), 0) << join::lastError.message (); - auto op = IoOperation::makeReadFixed (_server.handle (), regbuf.data (), regbuf.size (), 0, this); - ASSERT_EQ (HybridProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + _readOp = IoOperation::makeReadFixed (_server.handle (), regbuf.data (), regbuf.size (), 0, this); + ASSERT_EQ (HybridProactorThread::proactor ().submit (&_readOp, true, true), 0) << join::lastError.message (); ASSERT_EQ (_client.writeExactly ("asyncReadFixed", strlen ("asyncReadFixed"), _timeout), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_readOp && _result > 0; })); ASSERT_EQ (std::string (regbuf.data (), _result), "asyncReadFixed"); _op = nullptr; @@ -802,14 +825,14 @@ TEST_F (HybridProactorTest, asyncSendmsg) msghdr msg = {}; msg.msg_iov = &iov; msg.msg_iovlen = 1; - auto op = IoOperation::makeSendmsg (_server.handle (), &msg, 0, this); + _writeOp = IoOperation::makeSendmsg (_server.handle (), &msg, 0, this); - ASSERT_EQ (HybridProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (HybridProactorThread::proactor ().submit (&_writeOp, true, true), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_writeOp && _result > 0; })); ASSERT_EQ (_result, static_cast (strlen (payload))); _op = nullptr; @@ -847,16 +870,16 @@ TEST_F (HybridProactorTest, asyncRecvmsg) msghdr msg = {}; msg.msg_iov = &iov; msg.msg_iovlen = 1; - auto op = IoOperation::makeRecvmsg (_server.handle (), &msg, 0, this); + _readOp = IoOperation::makeRecvmsg (_server.handle (), &msg, 0, this); - ASSERT_EQ (HybridProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (HybridProactorThread::proactor ().submit (&_readOp, true, true), 0) << join::lastError.message (); ASSERT_EQ (_client.writeExactly ("asyncRecvmsg", strlen ("asyncRecvmsg"), _timeout), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_readOp && _result > 0; })); ASSERT_EQ (std::string (_buf, _result), "asyncRecvmsg"); _op = nullptr; @@ -877,14 +900,14 @@ TEST_F (HybridProactorTest, asyncSend) ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); const char* msg = "asyncSend"; - auto op = IoOperation::makeSend (_server.handle (), msg, strlen (msg), 0, this); + _writeOp = IoOperation::makeSend (_server.handle (), msg, strlen (msg), 0, this); - ASSERT_EQ (HybridProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (HybridProactorThread::proactor ().submit (&_writeOp, true, true), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_writeOp && _result > 0; })); ASSERT_EQ (_result, static_cast (strlen (msg))); _op = nullptr; @@ -908,15 +931,15 @@ TEST_F (HybridProactorTest, asyncRecv) ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); - auto op = IoOperation::makeRecv (_server.handle (), _buf, sizeof (_buf), 0, this); + _readOp = IoOperation::makeRecv (_server.handle (), _buf, sizeof (_buf), 0, this); - ASSERT_EQ (HybridProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (HybridProactorThread::proactor ().submit (&_readOp, true, true), 0) << join::lastError.message (); ASSERT_EQ (_client.writeExactly ("asyncRecv", strlen ("asyncRecv"), _timeout), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_readOp && _result > 0; })); ASSERT_EQ (std::string (_buf, _result), "asyncRecv"); _op = nullptr; @@ -946,15 +969,15 @@ TEST_F (HybridProactorTest, onClose) ASSERT_EQ (HybridProactorThread::mlock (), 0) << join::lastError.message (); ASSERT_GT (HybridProactorThread::handle (), 0); - auto op = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + _readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - ASSERT_EQ (HybridProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (HybridProactorThread::proactor ().submit (&_readOp, true, true), 0) << join::lastError.message (); _client.close (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result == 0; + return _op == &_readOp && _result == 0; })); _op = nullptr; _result = 0; @@ -983,9 +1006,9 @@ TEST_F (HybridProactorTest, onError) ASSERT_EQ (HybridProactorThread::mlock (), 0) << join::lastError.message (); ASSERT_GT (HybridProactorThread::handle (), 0); - auto op = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + _readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - ASSERT_EQ (HybridProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (HybridProactorThread::proactor ().submit (&_readOp, true, true), 0) << join::lastError.message (); linger sl{.l_onoff = 1, .l_linger = 0}; ASSERT_EQ (setsockopt (_client.handle (), SOL_SOCKET, SO_LINGER, &sl, sizeof (sl)), 0) << strerror (errno); _client.close (); @@ -993,7 +1016,7 @@ TEST_F (HybridProactorTest, onError) { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result == -ECONNRESET; + return _op == &_readOp && _result == -ECONNRESET; })); _op = nullptr; _result = 0; diff --git a/core/tests/proactor_test.cpp b/core/tests/proactor_test.cpp index fdb38093..b745d2d9 100644 --- a/core/tests/proactor_test.cpp +++ b/core/tests/proactor_test.cpp @@ -60,6 +60,13 @@ class ProactorTest : public CompletionHandler, public ::testing::Test */ void TearDown () override { + auto& proactor = ProactorThread::proactor (); + proactor.cancel (&_readOp, true, true); + proactor.cancel (&_writeOp, true, true); + proactor.cancel (&_spareOp, true, true); + proactor.cancel (&_invalidOp, true, true); + proactor.cancel (&_resubmitOp, true, true); + _server.close (); _client.close (); _acceptor.close (); @@ -146,6 +153,18 @@ class ProactorTest : public CompletionHandler, public ::testing::Test /// last completed operation. static IoOperation* _op; + /// read side operation submitted by the tests. + static IoOperation _readOp; + + /// write side operation submitted by the tests. + static IoOperation _writeOp; + + /// spare operation for the tests submitting two operations on the same handle. + static IoOperation _spareOp; + + /// operation on an invalid handle. + static IoOperation _invalidOp; + /// operation resubmitted from a handler. static IoOperation _resubmitOp; @@ -174,6 +193,10 @@ const std::chrono::milliseconds ProactorTest::_timeout{1000}; Condition ProactorTest::_cond; Mutex ProactorTest::_mut; IoOperation* ProactorTest::_op = nullptr; +IoOperation ProactorTest::_readOp = {}; +IoOperation ProactorTest::_writeOp = {}; +IoOperation ProactorTest::_spareOp = {}; +IoOperation ProactorTest::_invalidOp = {}; IoOperation ProactorTest::_resubmitOp = {}; int ProactorTest::_resubmits = 0; int ProactorTest::_resubmitted = 0; @@ -198,15 +221,15 @@ TEST_F (ProactorTest, stop) ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); - auto op = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - ASSERT_EQ (proactor.submit (&op, true, true), 0) << join::lastError.message (); + _readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + ASSERT_EQ (proactor.submit (&_readOp, true, true), 0) << join::lastError.message (); proactor.stop (); th.join (); { ScopedLock lock (_mut); - ASSERT_EQ (_op, &op); + ASSERT_EQ (_op, &_readOp); ASSERT_EQ (_result, -ECANCELED); _op = nullptr; _result = 0; @@ -273,8 +296,8 @@ TEST_F (ProactorTest, submit) ASSERT_EQ (proactor.submit (nullptr, true, true), -1); ASSERT_EQ (join::lastError, Errc::InvalidParam); - auto op1 = IoOperation::makeRead (-1, _buf, sizeof (_buf), this); - ASSERT_EQ (proactor.submit (&op1, true, true), -1); + _readOp = IoOperation::makeRead (-1, _buf, sizeof (_buf), this); + ASSERT_EQ (proactor.submit (&_readOp, true, true), -1); ASSERT_EQ (join::lastError, std::errc::bad_file_descriptor); if (_client.connect ({_host, _port}) == -1) @@ -284,48 +307,48 @@ TEST_F (ProactorTest, submit) ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); - op1 = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - op1.state = IoOperation::State::Submitted; - ASSERT_EQ (proactor.submit (&op1, true, true), -1); + _readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + _readOp.state = IoOperation::State::Submitted; + ASSERT_EQ (proactor.submit (&_readOp, true, true), -1); ASSERT_EQ (join::lastError, std::errc::device_or_resource_busy); - op1.state = IoOperation::State::Idle; - ASSERT_EQ (proactor.submit (&op1, true, true), 0) << join::lastError.message (); + _readOp.state = IoOperation::State::Idle; + ASSERT_EQ (proactor.submit (&_readOp, true, true), 0) << join::lastError.message (); - auto op3 = IoOperation::makeRead (-1, _buf, sizeof (_buf), this); - ASSERT_EQ (proactor.submit (&op3, true, false), 0) << join::lastError.message (); + _invalidOp = IoOperation::makeRead (-1, _buf, sizeof (_buf), this); + ASSERT_EQ (proactor.submit (&_invalidOp, true, false), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op3 && _result == -EBADF; + return _op == &_invalidOp && _result == -EBADF; })); _op = nullptr; _result = 0; } #ifndef JOIN_HAS_IO_URING - auto op2 = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - ASSERT_EQ (proactor.submit (&op2, true, true), -1); + _spareOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + ASSERT_EQ (proactor.submit (&_spareOp, true, true), -1); ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (proactor.submit (&op2, true, false), 0) << join::lastError.message (); + ASSERT_EQ (proactor.submit (&_spareOp, true, false), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op2 && _result == -EINVAL; + return _op == &_spareOp && _result == -EINVAL; })); _op = nullptr; _result = 0; } #endif - ASSERT_EQ (proactor.cancel (&op1, true, true), 0) << join::lastError.message (); + ASSERT_EQ (proactor.cancel (&_readOp, true, true), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op1 && _result == -ECANCELED; + return _op == &_readOp && _result == -ECANCELED; })); _op = nullptr; _result = 0; @@ -348,8 +371,8 @@ TEST_F (ProactorTest, cancel) ASSERT_EQ (proactor.cancel (nullptr, true, true), -1); ASSERT_EQ (join::lastError, Errc::InvalidParam); - auto op1 = IoOperation::makeRead (-1, _buf, sizeof (_buf), this); - ASSERT_EQ (proactor.cancel (&op1, true, true), -1); + _readOp = IoOperation::makeRead (-1, _buf, sizeof (_buf), this); + ASSERT_EQ (proactor.cancel (&_readOp, true, true), -1); ASSERT_EQ (join::lastError, std::errc::bad_file_descriptor); if (_client.connect ({_host, _port}) == -1) @@ -359,22 +382,22 @@ TEST_F (ProactorTest, cancel) ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); - op1 = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - ASSERT_EQ (proactor.cancel (&op1, true, true), -1); + _readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + ASSERT_EQ (proactor.cancel (&_readOp, true, true), -1); ASSERT_EQ (join::lastError, Errc::OperationFailed); - ASSERT_EQ (proactor.submit (&op1, true, true), 0) << join::lastError.message (); + ASSERT_EQ (proactor.submit (&_readOp, true, true), 0) << join::lastError.message (); - auto op2 = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - op2.state = IoOperation::State::Submitted; - ASSERT_EQ (proactor.cancel (&op2, true, true), -1); + _spareOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + _spareOp.state = IoOperation::State::Submitted; + ASSERT_EQ (proactor.cancel (&_spareOp, true, true), -1); ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (proactor.cancel (&op1, true, true), 0) << join::lastError.message (); + ASSERT_EQ (proactor.cancel (&_readOp, true, true), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op1 && _result == -ECANCELED; + return _op == &_readOp && _result == -ECANCELED; })); _op = nullptr; _result = 0; @@ -402,8 +425,8 @@ TEST_F (ProactorTest, flush) ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); - auto op = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - ASSERT_EQ (proactor.submit (&op, false, true), 0) << join::lastError.message (); + _readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + ASSERT_EQ (proactor.submit (&_readOp, false, true), 0) << join::lastError.message (); ASSERT_EQ (proactor.flush (true), 0) << join::lastError.message (); ASSERT_EQ (_client.writeExactly ("flush", strlen ("flush"), _timeout), 0) << join::lastError.message (); @@ -411,7 +434,7 @@ TEST_F (ProactorTest, flush) { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_readOp && _result > 0; })); ASSERT_EQ (std::string (_buf, _result), "flush"); _op = nullptr; @@ -439,16 +462,16 @@ TEST_F (ProactorTest, chain) ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); - auto writeOp = IoOperation::makeWrite (_server.handle (), "ping", 4, this, true); - auto readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + _writeOp = IoOperation::makeWrite (_server.handle (), "ping", 4, this, true); + _readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - ASSERT_EQ (proactor.submit (&writeOp, false, true), 0) << join::lastError.message (); - ASSERT_EQ (proactor.submit (&readOp, true, true), 0) << join::lastError.message (); + ASSERT_EQ (proactor.submit (&_writeOp, false, true), 0) << join::lastError.message (); + ASSERT_EQ (proactor.submit (&_readOp, true, true), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &writeOp && _result == 4; + return _op == &_writeOp && _result == 4; })); _op = nullptr; _result = 0; @@ -461,7 +484,7 @@ TEST_F (ProactorTest, chain) { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &readOp && _result > 0; + return _op == &_readOp && _result > 0; })); ASSERT_EQ (std::string (_buf, _result), "pong"); _op = nullptr; @@ -573,15 +596,15 @@ TEST_F (ProactorTest, asyncConnect) ASSERT_EQ (_client.open (Tcp::v4 ()), 0) << join::lastError.message (); - auto op = IoOperation::makeConnect (_client.handle (), endpoint.addr (), endpoint.length (), this); - ASSERT_EQ (ProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + _readOp = IoOperation::makeConnect (_client.handle (), endpoint.addr (), endpoint.length (), this); + ASSERT_EQ (ProactorThread::proactor ().submit (&_readOp, true, true), 0) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result == 0; + return _op == &_readOp && _result == 0; })); _op = nullptr; _result = 0; @@ -605,10 +628,10 @@ TEST_F (ProactorTest, asyncAccept) sockaddr_storage addr = {}; socklen_t addrlen = sizeof (addr); - auto op = IoOperation::makeAccept (_acceptor.handle (), reinterpret_cast (&addr), &addrlen, + _readOp = IoOperation::makeAccept (_acceptor.handle (), reinterpret_cast (&addr), &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC, this); - ASSERT_EQ (ProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (ProactorThread::proactor ().submit (&_readOp, true, true), 0) << join::lastError.message (); if (_client.connect ({_host, _port}) == -1) { ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); @@ -618,7 +641,7 @@ TEST_F (ProactorTest, asyncAccept) { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result >= 0; + return _op == &_readOp && _result >= 0; })); ::close (_result); _op = nullptr; @@ -649,14 +672,14 @@ TEST_F (ProactorTest, asyncWrite) ASSERT_GT (ProactorThread::handle (), 0); const char* msg = "asyncWrite"; - auto op = IoOperation::makeWrite (_server.handle (), msg, strlen (msg), this); + _writeOp = IoOperation::makeWrite (_server.handle (), msg, strlen (msg), this); - ASSERT_EQ (ProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (ProactorThread::proactor ().submit (&_writeOp, true, true), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_writeOp && _result > 0; })); ASSERT_EQ (_result, static_cast (strlen (msg))); _op = nullptr; @@ -690,15 +713,15 @@ TEST_F (ProactorTest, asyncRead) ASSERT_EQ (ProactorThread::mlock (), 0) << join::lastError.message (); ASSERT_GT (ProactorThread::handle (), 0); - auto op = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + _readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - ASSERT_EQ (ProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (ProactorThread::proactor ().submit (&_readOp, true, true), 0) << join::lastError.message (); ASSERT_EQ (_client.writeExactly ("asyncRead", strlen ("asyncRead"), _timeout), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_readOp && _result > 0; })); ASSERT_EQ (std::string (_buf, _result), "asyncRead"); _op = nullptr; @@ -726,13 +749,13 @@ TEST_F (ProactorTest, asyncWriteFixed) ASSERT_EQ (ProactorThread::proactor ().registerBuffers (iovecs), 0) << join::lastError.message (); - auto op = IoOperation::makeWriteFixed (_server.handle (), regbuf.data (), regbuf.size (), 0, this); - ASSERT_EQ (ProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + _writeOp = IoOperation::makeWriteFixed (_server.handle (), regbuf.data (), regbuf.size (), 0, this); + ASSERT_EQ (ProactorThread::proactor ().submit (&_writeOp, true, true), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_writeOp && _result > 0; })); ASSERT_EQ (_result, static_cast (strlen (msg))); _op = nullptr; @@ -764,15 +787,15 @@ TEST_F (ProactorTest, asyncReadFixed) ASSERT_EQ (ProactorThread::proactor ().registerBuffers (iovecs), 0) << join::lastError.message (); - auto op = IoOperation::makeReadFixed (_server.handle (), regbuf.data (), regbuf.size (), 0, this); - ASSERT_EQ (ProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + _readOp = IoOperation::makeReadFixed (_server.handle (), regbuf.data (), regbuf.size (), 0, this); + ASSERT_EQ (ProactorThread::proactor ().submit (&_readOp, true, true), 0) << join::lastError.message (); ASSERT_EQ (_client.writeExactly ("asyncReadFixed", strlen ("asyncReadFixed"), _timeout), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_readOp && _result > 0; })); ASSERT_EQ (std::string (regbuf.data (), _result), "asyncReadFixed"); _op = nullptr; @@ -810,14 +833,14 @@ TEST_F (ProactorTest, asyncSendmsg) msghdr msg = {}; msg.msg_iov = &iov; msg.msg_iovlen = 1; - auto op = IoOperation::makeSendmsg (_server.handle (), &msg, 0, this); + _writeOp = IoOperation::makeSendmsg (_server.handle (), &msg, 0, this); - ASSERT_EQ (ProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (ProactorThread::proactor ().submit (&_writeOp, true, true), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_writeOp && _result > 0; })); ASSERT_EQ (_result, static_cast (strlen (payload))); _op = nullptr; @@ -855,16 +878,16 @@ TEST_F (ProactorTest, asyncRecvmsg) msghdr msg = {}; msg.msg_iov = &iov; msg.msg_iovlen = 1; - auto op = IoOperation::makeRecvmsg (_server.handle (), &msg, 0, this); + _readOp = IoOperation::makeRecvmsg (_server.handle (), &msg, 0, this); - ASSERT_EQ (ProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (ProactorThread::proactor ().submit (&_readOp, true, true), 0) << join::lastError.message (); ASSERT_EQ (_client.writeExactly ("asyncRecvmsg", strlen ("asyncRecvmsg"), _timeout), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_readOp && _result > 0; })); ASSERT_EQ (std::string (_buf, _result), "asyncRecvmsg"); _op = nullptr; @@ -885,14 +908,14 @@ TEST_F (ProactorTest, asyncSend) ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); const char* msg = "asyncSend"; - auto op = IoOperation::makeSend (_server.handle (), msg, strlen (msg), 0, this); + _writeOp = IoOperation::makeSend (_server.handle (), msg, strlen (msg), 0, this); - ASSERT_EQ (ProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (ProactorThread::proactor ().submit (&_writeOp, true, true), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_writeOp && _result > 0; })); ASSERT_EQ (_result, static_cast (strlen (msg))); _op = nullptr; @@ -916,15 +939,15 @@ TEST_F (ProactorTest, asyncRecv) ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); - auto op = IoOperation::makeRecv (_server.handle (), _buf, sizeof (_buf), 0, this); + _readOp = IoOperation::makeRecv (_server.handle (), _buf, sizeof (_buf), 0, this); - ASSERT_EQ (ProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (ProactorThread::proactor ().submit (&_readOp, true, true), 0) << join::lastError.message (); ASSERT_EQ (_client.writeExactly ("asyncRecv", strlen ("asyncRecv"), _timeout), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_readOp && _result > 0; })); ASSERT_EQ (std::string (_buf, _result), "asyncRecv"); _op = nullptr; @@ -954,15 +977,15 @@ TEST_F (ProactorTest, onClose) ASSERT_EQ (ProactorThread::mlock (), 0) << join::lastError.message (); ASSERT_GT (ProactorThread::handle (), 0); - auto op = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + _readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - ASSERT_EQ (ProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (ProactorThread::proactor ().submit (&_readOp, true, true), 0) << join::lastError.message (); _client.close (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result == 0; + return _op == &_readOp && _result == 0; })); _op = nullptr; _result = 0; @@ -991,9 +1014,9 @@ TEST_F (ProactorTest, onError) ASSERT_EQ (ProactorThread::mlock (), 0) << join::lastError.message (); ASSERT_GT (ProactorThread::handle (), 0); - auto op = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + _readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - ASSERT_EQ (ProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (ProactorThread::proactor ().submit (&_readOp, true, true), 0) << join::lastError.message (); linger sl{.l_onoff = 1, .l_linger = 0}; ASSERT_EQ (setsockopt (_client.handle (), SOL_SOCKET, SO_LINGER, &sl, sizeof (sl)), 0) << strerror (errno); _client.close (); @@ -1001,7 +1024,7 @@ TEST_F (ProactorTest, onError) { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result == -ECONNRESET; + return _op == &_readOp && _result == -ECONNRESET; })); _op = nullptr; _result = 0; diff --git a/core/tests/sqpoll_proactor_test.cpp b/core/tests/sqpoll_proactor_test.cpp index bea64599..3054ec8e 100644 --- a/core/tests/sqpoll_proactor_test.cpp +++ b/core/tests/sqpoll_proactor_test.cpp @@ -60,6 +60,13 @@ class SqpollProactorTest : public CompletionHandler, public ::testing::Test */ void TearDown () override { + auto& proactor = SqpollProactorThread::proactor (); + proactor.cancel (&_readOp, true, true); + proactor.cancel (&_writeOp, true, true); + proactor.cancel (&_spareOp, true, true); + proactor.cancel (&_invalidOp, true, true); + proactor.cancel (&_resubmitOp, true, true); + _server.close (); _client.close (); _acceptor.close (); @@ -142,6 +149,18 @@ class SqpollProactorTest : public CompletionHandler, public ::testing::Test /// last completed operation. static IoOperation* _op; + /// read side operation submitted by the tests. + static IoOperation _readOp; + + /// write side operation submitted by the tests. + static IoOperation _writeOp; + + /// spare operation for the tests submitting two operations on the same handle. + static IoOperation _spareOp; + + /// operation on an invalid handle. + static IoOperation _invalidOp; + /// operation resubmitted from a handler. static IoOperation _resubmitOp; @@ -170,6 +189,10 @@ const std::chrono::milliseconds SqpollProactorTest::_timeout{1000}; Condition SqpollProactorTest::_cond; Mutex SqpollProactorTest::_mut; IoOperation* SqpollProactorTest::_op = nullptr; +IoOperation SqpollProactorTest::_readOp = {}; +IoOperation SqpollProactorTest::_writeOp = {}; +IoOperation SqpollProactorTest::_spareOp = {}; +IoOperation SqpollProactorTest::_invalidOp = {}; IoOperation SqpollProactorTest::_resubmitOp = {}; int SqpollProactorTest::_resubmits = 0; int SqpollProactorTest::_resubmitted = 0; @@ -194,15 +217,15 @@ TEST_F (SqpollProactorTest, stop) ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); - auto op = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - ASSERT_EQ (proactor.submit (&op, true, true), 0) << join::lastError.message (); + _readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + ASSERT_EQ (proactor.submit (&_readOp, true, true), 0) << join::lastError.message (); proactor.stop (); th.join (); { ScopedLock lock (_mut); - ASSERT_EQ (_op, &op); + ASSERT_EQ (_op, &_readOp); ASSERT_EQ (_result, -ECANCELED); _op = nullptr; _result = 0; @@ -269,8 +292,8 @@ TEST_F (SqpollProactorTest, submit) ASSERT_EQ (proactor.submit (nullptr, true, true), -1); ASSERT_EQ (join::lastError, Errc::InvalidParam); - auto op1 = IoOperation::makeRead (-1, _buf, sizeof (_buf), this); - ASSERT_EQ (proactor.submit (&op1, true, true), -1); + _readOp = IoOperation::makeRead (-1, _buf, sizeof (_buf), this); + ASSERT_EQ (proactor.submit (&_readOp, true, true), -1); ASSERT_EQ (join::lastError, std::errc::bad_file_descriptor); if (_client.connect ({_host, _port}) == -1) @@ -280,48 +303,48 @@ TEST_F (SqpollProactorTest, submit) ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); - op1 = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - op1.state = IoOperation::State::Submitted; - ASSERT_EQ (proactor.submit (&op1, true, true), -1); + _readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + _readOp.state = IoOperation::State::Submitted; + ASSERT_EQ (proactor.submit (&_readOp, true, true), -1); ASSERT_EQ (join::lastError, std::errc::device_or_resource_busy); - op1.state = IoOperation::State::Idle; - ASSERT_EQ (proactor.submit (&op1, true, true), 0) << join::lastError.message (); + _readOp.state = IoOperation::State::Idle; + ASSERT_EQ (proactor.submit (&_readOp, true, true), 0) << join::lastError.message (); - auto op3 = IoOperation::makeRead (-1, _buf, sizeof (_buf), this); - ASSERT_EQ (proactor.submit (&op3, true, false), 0) << join::lastError.message (); + _invalidOp = IoOperation::makeRead (-1, _buf, sizeof (_buf), this); + ASSERT_EQ (proactor.submit (&_invalidOp, true, false), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op3 && _result == -EBADF; + return _op == &_invalidOp && _result == -EBADF; })); _op = nullptr; _result = 0; } #ifndef JOIN_HAS_IO_URING - auto op2 = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - ASSERT_EQ (proactor.submit (&op2, true, true), -1); + _spareOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + ASSERT_EQ (proactor.submit (&_spareOp, true, true), -1); ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (proactor.submit (&op2, true, false), 0) << join::lastError.message (); + ASSERT_EQ (proactor.submit (&_spareOp, true, false), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op2 && _result == -EINVAL; + return _op == &_spareOp && _result == -EINVAL; })); _op = nullptr; _result = 0; } #endif - ASSERT_EQ (proactor.cancel (&op1, true, true), 0) << join::lastError.message (); + ASSERT_EQ (proactor.cancel (&_readOp, true, true), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op1 && _result == -ECANCELED; + return _op == &_readOp && _result == -ECANCELED; })); _op = nullptr; _result = 0; @@ -344,8 +367,8 @@ TEST_F (SqpollProactorTest, cancel) ASSERT_EQ (proactor.cancel (nullptr, true, true), -1); ASSERT_EQ (join::lastError, Errc::InvalidParam); - auto op1 = IoOperation::makeRead (-1, _buf, sizeof (_buf), this); - ASSERT_EQ (proactor.cancel (&op1, true, true), -1); + _readOp = IoOperation::makeRead (-1, _buf, sizeof (_buf), this); + ASSERT_EQ (proactor.cancel (&_readOp, true, true), -1); ASSERT_EQ (join::lastError, std::errc::bad_file_descriptor); if (_client.connect ({_host, _port}) == -1) @@ -355,22 +378,22 @@ TEST_F (SqpollProactorTest, cancel) ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); - op1 = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - ASSERT_EQ (proactor.cancel (&op1, true, true), -1); + _readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + ASSERT_EQ (proactor.cancel (&_readOp, true, true), -1); ASSERT_EQ (join::lastError, Errc::OperationFailed); - ASSERT_EQ (proactor.submit (&op1, true, true), 0) << join::lastError.message (); + ASSERT_EQ (proactor.submit (&_readOp, true, true), 0) << join::lastError.message (); - auto op2 = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - op2.state = IoOperation::State::Submitted; - ASSERT_EQ (proactor.cancel (&op2, true, true), -1); + _spareOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + _spareOp.state = IoOperation::State::Submitted; + ASSERT_EQ (proactor.cancel (&_spareOp, true, true), -1); ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (proactor.cancel (&op1, true, true), 0) << join::lastError.message (); + ASSERT_EQ (proactor.cancel (&_readOp, true, true), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op1 && _result == -ECANCELED; + return _op == &_readOp && _result == -ECANCELED; })); _op = nullptr; _result = 0; @@ -397,8 +420,8 @@ TEST_F (SqpollProactorTest, flush) ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); - auto op = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - ASSERT_EQ (proactor.submit (&op, false, true), 0) << join::lastError.message (); + _readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + ASSERT_EQ (proactor.submit (&_readOp, false, true), 0) << join::lastError.message (); ASSERT_EQ (proactor.flush (true), 0) << join::lastError.message (); ASSERT_EQ (_client.writeExactly ("flush", strlen ("flush"), _timeout), 0) << join::lastError.message (); @@ -406,7 +429,7 @@ TEST_F (SqpollProactorTest, flush) { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_readOp && _result > 0; })); ASSERT_EQ (std::string (_buf, _result), "flush"); _op = nullptr; @@ -434,16 +457,16 @@ TEST_F (SqpollProactorTest, chain) ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); - auto writeOp = IoOperation::makeWrite (_server.handle (), "ping", 4, this, true); - auto readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + _writeOp = IoOperation::makeWrite (_server.handle (), "ping", 4, this, true); + _readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - ASSERT_EQ (proactor.submit (&writeOp, false, true), 0) << join::lastError.message (); - ASSERT_EQ (proactor.submit (&readOp, true, true), 0) << join::lastError.message (); + ASSERT_EQ (proactor.submit (&_writeOp, false, true), 0) << join::lastError.message (); + ASSERT_EQ (proactor.submit (&_readOp, true, true), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &writeOp && _result == 4; + return _op == &_writeOp && _result == 4; })); _op = nullptr; _result = 0; @@ -456,7 +479,7 @@ TEST_F (SqpollProactorTest, chain) { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &readOp && _result > 0; + return _op == &_readOp && _result > 0; })); ASSERT_EQ (std::string (_buf, _result), "pong"); _op = nullptr; @@ -567,15 +590,15 @@ TEST_F (SqpollProactorTest, asyncConnect) ASSERT_EQ (_client.open (Tcp::v4 ()), 0) << join::lastError.message (); - auto op = IoOperation::makeConnect (_client.handle (), endpoint.addr (), endpoint.length (), this); - ASSERT_EQ (SqpollProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + _readOp = IoOperation::makeConnect (_client.handle (), endpoint.addr (), endpoint.length (), this); + ASSERT_EQ (SqpollProactorThread::proactor ().submit (&_readOp, true, true), 0) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result == 0; + return _op == &_readOp && _result == 0; })); _op = nullptr; _result = 0; @@ -599,10 +622,10 @@ TEST_F (SqpollProactorTest, asyncAccept) sockaddr_storage addr = {}; socklen_t addrlen = sizeof (addr); - auto op = IoOperation::makeAccept (_acceptor.handle (), reinterpret_cast (&addr), &addrlen, + _readOp = IoOperation::makeAccept (_acceptor.handle (), reinterpret_cast (&addr), &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC, this); - ASSERT_EQ (SqpollProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (SqpollProactorThread::proactor ().submit (&_readOp, true, true), 0) << join::lastError.message (); if (_client.connect ({_host, _port}) == -1) { ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); @@ -612,7 +635,7 @@ TEST_F (SqpollProactorTest, asyncAccept) { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result >= 0; + return _op == &_readOp && _result >= 0; })); ::close (_result); _op = nullptr; @@ -643,14 +666,14 @@ TEST_F (SqpollProactorTest, asyncWrite) ASSERT_GT (SqpollProactorThread::handle (), 0); const char* msg = "asyncWrite"; - auto op = IoOperation::makeWrite (_server.handle (), msg, strlen (msg), this); + _writeOp = IoOperation::makeWrite (_server.handle (), msg, strlen (msg), this); - ASSERT_EQ (SqpollProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (SqpollProactorThread::proactor ().submit (&_writeOp, true, true), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_writeOp && _result > 0; })); ASSERT_EQ (_result, static_cast (strlen (msg))); _op = nullptr; @@ -684,15 +707,15 @@ TEST_F (SqpollProactorTest, asyncRead) ASSERT_EQ (SqpollProactorThread::mlock (), 0) << join::lastError.message (); ASSERT_GT (SqpollProactorThread::handle (), 0); - auto op = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + _readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - ASSERT_EQ (SqpollProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (SqpollProactorThread::proactor ().submit (&_readOp, true, true), 0) << join::lastError.message (); ASSERT_EQ (_client.writeExactly ("asyncRead", strlen ("asyncRead"), _timeout), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_readOp && _result > 0; })); ASSERT_EQ (std::string (_buf, _result), "asyncRead"); _op = nullptr; @@ -719,13 +742,13 @@ TEST_F (SqpollProactorTest, asyncWriteFixed) ASSERT_EQ (SqpollProactorThread::proactor ().registerBuffers (iovecs), 0) << join::lastError.message (); - auto op = IoOperation::makeWriteFixed (_server.handle (), regbuf.data (), regbuf.size (), 0, this); - ASSERT_EQ (SqpollProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + _writeOp = IoOperation::makeWriteFixed (_server.handle (), regbuf.data (), regbuf.size (), 0, this); + ASSERT_EQ (SqpollProactorThread::proactor ().submit (&_writeOp, true, true), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_writeOp && _result > 0; })); ASSERT_EQ (_result, static_cast (strlen (msg))); _op = nullptr; @@ -757,15 +780,15 @@ TEST_F (SqpollProactorTest, asyncReadFixed) ASSERT_EQ (SqpollProactorThread::proactor ().registerBuffers (iovecs), 0) << join::lastError.message (); - auto op = IoOperation::makeReadFixed (_server.handle (), regbuf.data (), regbuf.size (), 0, this); - ASSERT_EQ (SqpollProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + _readOp = IoOperation::makeReadFixed (_server.handle (), regbuf.data (), regbuf.size (), 0, this); + ASSERT_EQ (SqpollProactorThread::proactor ().submit (&_readOp, true, true), 0) << join::lastError.message (); ASSERT_EQ (_client.writeExactly ("asyncReadFixed", strlen ("asyncReadFixed"), _timeout), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_readOp && _result > 0; })); ASSERT_EQ (std::string (regbuf.data (), _result), "asyncReadFixed"); _op = nullptr; @@ -802,14 +825,14 @@ TEST_F (SqpollProactorTest, asyncSendmsg) msghdr msg = {}; msg.msg_iov = &iov; msg.msg_iovlen = 1; - auto op = IoOperation::makeSendmsg (_server.handle (), &msg, 0, this); + _writeOp = IoOperation::makeSendmsg (_server.handle (), &msg, 0, this); - ASSERT_EQ (SqpollProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (SqpollProactorThread::proactor ().submit (&_writeOp, true, true), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_writeOp && _result > 0; })); ASSERT_EQ (_result, static_cast (strlen (payload))); _op = nullptr; @@ -847,16 +870,16 @@ TEST_F (SqpollProactorTest, asyncRecvmsg) msghdr msg = {}; msg.msg_iov = &iov; msg.msg_iovlen = 1; - auto op = IoOperation::makeRecvmsg (_server.handle (), &msg, 0, this); + _readOp = IoOperation::makeRecvmsg (_server.handle (), &msg, 0, this); - ASSERT_EQ (SqpollProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (SqpollProactorThread::proactor ().submit (&_readOp, true, true), 0) << join::lastError.message (); ASSERT_EQ (_client.writeExactly ("asyncRecvmsg", strlen ("asyncRecvmsg"), _timeout), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_readOp && _result > 0; })); ASSERT_EQ (std::string (_buf, _result), "asyncRecvmsg"); _op = nullptr; @@ -877,14 +900,14 @@ TEST_F (SqpollProactorTest, asyncSend) ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); const char* msg = "asyncSend"; - auto op = IoOperation::makeSend (_server.handle (), msg, strlen (msg), 0, this); + _writeOp = IoOperation::makeSend (_server.handle (), msg, strlen (msg), 0, this); - ASSERT_EQ (SqpollProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (SqpollProactorThread::proactor ().submit (&_writeOp, true, true), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_writeOp && _result > 0; })); ASSERT_EQ (_result, static_cast (strlen (msg))); _op = nullptr; @@ -908,15 +931,15 @@ TEST_F (SqpollProactorTest, asyncRecv) ASSERT_TRUE (_client.waitConnected (_timeout)) << join::lastError.message (); ASSERT_TRUE ((_server = _acceptor.accept ()).connected ()) << join::lastError.message (); - auto op = IoOperation::makeRecv (_server.handle (), _buf, sizeof (_buf), 0, this); + _readOp = IoOperation::makeRecv (_server.handle (), _buf, sizeof (_buf), 0, this); - ASSERT_EQ (SqpollProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (SqpollProactorThread::proactor ().submit (&_readOp, true, true), 0) << join::lastError.message (); ASSERT_EQ (_client.writeExactly ("asyncRecv", strlen ("asyncRecv"), _timeout), 0) << join::lastError.message (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result > 0; + return _op == &_readOp && _result > 0; })); ASSERT_EQ (std::string (_buf, _result), "asyncRecv"); _op = nullptr; @@ -946,15 +969,15 @@ TEST_F (SqpollProactorTest, onClose) ASSERT_EQ (SqpollProactorThread::mlock (), 0) << join::lastError.message (); ASSERT_GT (SqpollProactorThread::handle (), 0); - auto op = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + _readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - ASSERT_EQ (SqpollProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (SqpollProactorThread::proactor ().submit (&_readOp, true, true), 0) << join::lastError.message (); _client.close (); { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result == 0; + return _op == &_readOp && _result == 0; })); _op = nullptr; _result = 0; @@ -983,9 +1006,9 @@ TEST_F (SqpollProactorTest, onError) ASSERT_EQ (SqpollProactorThread::mlock (), 0) << join::lastError.message (); ASSERT_GT (SqpollProactorThread::handle (), 0); - auto op = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); + _readOp = IoOperation::makeRead (_server.handle (), _buf, sizeof (_buf), this); - ASSERT_EQ (SqpollProactorThread::proactor ().submit (&op, true, true), 0) << join::lastError.message (); + ASSERT_EQ (SqpollProactorThread::proactor ().submit (&_readOp, true, true), 0) << join::lastError.message (); linger sl{.l_onoff = 1, .l_linger = 0}; ASSERT_EQ (setsockopt (_client.handle (), SOL_SOCKET, SO_LINGER, &sl, sizeof (sl)), 0) << strerror (errno); _client.close (); @@ -993,7 +1016,7 @@ TEST_F (SqpollProactorTest, onError) { ScopedLock lock (_mut); ASSERT_TRUE (_cond.timedWait (lock, std::chrono::milliseconds (_timeout), [&] () { - return _op == &op && _result == -ECONNRESET; + return _op == &_readOp && _result == -ECONNRESET; })); _op = nullptr; _result = 0; diff --git a/crypto/tests/tls_wrapper_test.cpp b/crypto/tests/tls_wrapper_test.cpp index ffe86bb5..fee28b56 100644 --- a/crypto/tests/tls_wrapper_test.cpp +++ b/crypto/tests/tls_wrapper_test.cpp @@ -31,7 +31,10 @@ #include // C++. +#include #include +#include +#include using join::Errc; using join::IpAddress; @@ -254,6 +257,7 @@ class TlsSocket : public EventHandler, public ::testing::Test /// port. static const uint16_t _port; + static const uint16_t _stallport; static const uint16_t _invalid_port; /// timeout. @@ -278,6 +282,7 @@ class TlsSocket : public EventHandler, public ::testing::Test const std::string TlsSocket::_hostv4 = "127.0.0.1"; const std::string TlsSocket::_hostv6 = "::1"; const uint16_t TlsSocket::_port = 5000; +const uint16_t TlsSocket::_stallport = 5004; const uint16_t TlsSocket::_invalid_port = 5032; const std::chrono::milliseconds TlsSocket::_timeout{1000}; const std::string TlsSocket::_rootcert = "/tmp/tlssocket_test_root.cert"; @@ -740,6 +745,43 @@ TEST_F (TlsSocket, writeExactly) ASSERT_EQ (tls.shutdown (), 0) << join::lastError.message (); ASSERT_EQ (tls.disconnect (), 0) << join::lastError.message (); tls.close (); + + Tcp::Acceptor stall; + ASSERT_EQ (stall.create ({IpAddress (_hostv4), _stallport}), 0) << join::lastError.message (); + + std::atomic stop{false}; + std::thread stalled ([this, &stall, &stop] () { + Tls::Socket peer (stall.accept (), _tlsContext); + peer.waitHandshake (_timeout); + while (!stop.load (std::memory_order_acquire)) + { + std::this_thread::sleep_for (std::chrono::milliseconds (10)); + } + peer.close (); + }); + + Tls::Socket sender (ctx, Tcp::Socket::NonBlocking); + + if (sender.connect ({_hostv4, _stallport}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (sender.waitHandshake (_timeout)) << join::lastError.message (); + + ASSERT_EQ (sender.setOption (Tcp::Socket::SndBuffer, 4096), 0) << join::lastError.message (); + ASSERT_EQ (sender.setOption (Tcp::Socket::RcvBuffer, 4096), 0) << join::lastError.message (); + + std::vector bulk (1024 * 1024, 'x'); + auto beg = std::chrono::steady_clock::now (); + + ASSERT_EQ (sender.writeExactly (bulk.data (), bulk.size (), std::chrono::milliseconds (250)), -1); + ASSERT_EQ (join::lastError, Errc::TimedOut); + ASSERT_LT (std::chrono::steady_clock::now () - beg, std::chrono::milliseconds (700)); + + sender.close (); + stop.store (true, std::memory_order_release); + stalled.join (); + stall.close (); } /**