diff --git a/core/include/join/socket.hpp b/core/include/join/socket.hpp index f488781e..91cbc390 100644 --- a/core/include/join/socket.hpp +++ b/core/include/join/socket.hpp @@ -32,6 +32,8 @@ #include // C++. +#include +#include #include #include @@ -53,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. @@ -318,12 +321,31 @@ namespace join /** * @brief block until new data is available for reading. - * @param timeout timeout in milliseconds. * @return true if there is new data available for reading, false otherwise. */ - bool waitReadyRead (int timeout = 0) 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); } /** @@ -364,12 +386,31 @@ namespace join /** * @brief block until at least one byte can be written. - * @param timeout timeout in milliseconds. * @return true if data can be written, false otherwise. */ - bool waitReadyWrite (int timeout = 0) 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); } /** @@ -612,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. @@ -625,10 +675,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 in milliseconds. * @return 0 on success, -1 on failure. */ - int wait (bool wantRead, bool wantWrite, int 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; @@ -645,7 +718,16 @@ namespace join handle.events |= POLLOUT; } - int nset = (handle.fd > -1) ? ::poll (&handle, 1, timeout == 0 ? -1 : timeout) : -1; + 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, 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 891de8f6..4a1e8464 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 @@ -44,6 +45,7 @@ namespace join public: using Endpoint = typename Protocol::Endpoint; using Socket = typename Protocol::Socket; + using TimePoint = typename Socket::TimePoint; /** * @brief default constructor. @@ -146,7 +148,7 @@ namespace join return nullptr; } - if (!_socket.waitConnected (_timeout)) + if (!_socket.waitConnected (deadline ())) { _socket.close (); return nullptr; @@ -174,7 +176,7 @@ namespace join return nullptr; } - if (!_socket.waitDisconnected (_timeout)) + if (!_socket.waitDisconnected (deadline ())) { return nullptr; } @@ -194,22 +196,32 @@ namespace join /** * @brief set the socket timeout. - * @param ms timeout in milliseconds. + * @param timeout maximum time granted to each stream operation, zero to remove the limit. */ - 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; } + /** + * @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. @@ -246,7 +258,7 @@ namespace join { if (lastError == Errc::TemporaryError) { - if (_socket.waitReadyRead (_timeout)) + if (_socket.waitReadyRead (deadline ())) { continue; } @@ -286,7 +298,7 @@ namespace join std::streamsize pending = pptr () - pbase (); if (pending) { - if (_socket.writeExactly (pbase (), pending, _timeout) == -1) + if (_socket.writeExactly (pbase (), pending, deadline ()) == -1) { _socket.close (); return traits_type::eof (); @@ -323,8 +335,8 @@ namespace join /// internal buffer. std::unique_ptr _buf; - /// timeout. - int _timeout = 30000; + /// timeout, zero when the stream operations are not time bounded. + std::chrono::nanoseconds _timeout = std::chrono::seconds (30); /// internal socket. Socket _socket; @@ -485,18 +497,18 @@ namespace join /** * @brief set the socket timeout. - * @param ms timeout in milliseconds. + * @param timeout maximum time granted to each stream operation, zero to remove the limit. */ - 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..9f2bacec 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 in milliseconds. * @return true if connected, false otherwise. */ - bool waitConnected (int timeout = 0) + 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,11 +257,36 @@ namespace join /** * @brief wait until the connection as been shut down. - * @param timeout timeout in milliseconds. * return true if the connection as been shut down, false otherwise. */ - bool waitDisconnected (int timeout = 0) + 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 (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) @@ -250,12 +295,9 @@ namespace join return false; } - auto start = std::chrono::steady_clock::now (); - int elapsed = 0; - - 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,13 +306,6 @@ namespace join { return true; } - - if (timeout) - { - elapsed = std::chrono::duration_cast ( - std::chrono::steady_clock::now () - start) - .count (); - } } return false; @@ -310,11 +345,40 @@ 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. * @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) 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 + { + 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) @@ -324,7 +388,7 @@ namespace join { if (lastError == Errc::TemporaryError) { - if (this->waitReadyRead (timeout)) + if (this->waitUntil (true, false, deadline) == 0) { continue; } @@ -343,11 +407,40 @@ 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. * @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) 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 + { + 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) @@ -357,7 +450,7 @@ namespace join { if (lastError == Errc::TemporaryError) { - if (this->waitReadyWrite (timeout)) + if (this->waitUntil (false, true, deadline) == 0) { continue; } 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..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 (); @@ -131,7 +138,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; @@ -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; @@ -162,14 +181,18 @@ 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; -const int HybridProactorTest::_timeout = 1000; +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; @@ -187,18 +210,22 @@ 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); - 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; @@ -265,55 +292,59 @@ 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); - 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); - 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; @@ -336,29 +367,33 @@ 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); - 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); - 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; @@ -378,11 +413,15 @@ 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); - 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 (); @@ -390,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; @@ -411,19 +450,23 @@ 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); - 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; @@ -436,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; @@ -546,23 +589,20 @@ 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 (); + _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; } - - _client.setMode (Tcp::Socket::Blocking); } /** @@ -582,16 +622,20 @@ 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 (_client.connect ({_host, _port}), 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 (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << 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; })); ::close (_result); _op = nullptr; @@ -604,7 +648,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 (); @@ -618,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; @@ -642,7 +690,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 (); @@ -655,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; @@ -676,7 +728,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"; @@ -686,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; @@ -711,7 +767,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)); @@ -720,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; @@ -743,7 +803,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 (); @@ -761,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; @@ -785,7 +849,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 (); @@ -802,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; @@ -824,18 +892,22 @@ 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"; - 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; @@ -852,18 +924,22 @@ 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); + _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; @@ -876,7 +952,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 (); @@ -889,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; @@ -909,7 +989,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 (); @@ -922,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 (); @@ -932,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; @@ -946,7 +1030,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/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..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 (); @@ -135,7 +142,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; @@ -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; @@ -166,14 +185,18 @@ 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; -const int ProactorTest::_timeout = 1000; +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; @@ -191,18 +214,22 @@ 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); - 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,55 +296,59 @@ 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); - 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); - 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; @@ -340,29 +371,33 @@ 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); - 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); - 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; @@ -383,11 +418,15 @@ 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); - 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 (); @@ -395,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; @@ -416,19 +455,23 @@ 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); - 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; @@ -441,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; @@ -552,23 +595,20 @@ 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 (); + _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; } - - _client.setMode (Tcp::Socket::Blocking); } /** @@ -588,16 +628,20 @@ 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 (_client.connect ({_host, _port}), 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 (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << 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; })); ::close (_result); _op = nullptr; @@ -610,7 +654,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 (); @@ -624,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; @@ -648,7 +696,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 (); @@ -661,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; @@ -683,7 +735,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"; @@ -693,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; @@ -718,7 +774,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)); @@ -727,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; @@ -751,7 +811,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 (); @@ -769,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; @@ -793,7 +857,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 (); @@ -810,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; @@ -832,18 +900,22 @@ 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"; - 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; @@ -860,18 +932,22 @@ 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); + _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; @@ -884,7 +960,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 (); @@ -897,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; @@ -917,7 +997,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 (); @@ -930,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 (); @@ -940,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; @@ -954,7 +1038,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/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..3cd03a66 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; @@ -172,11 +172,11 @@ 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; -const int ReactorTest::_timeout = 1000; +const std::chrono::milliseconds ReactorTest::_timeout{1000}; Condition ReactorTest::_cond; Mutex ReactorTest::_mut; std::string ReactorTest::_event; @@ -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 diff --git a/core/tests/sqpoll_proactor_test.cpp b/core/tests/sqpoll_proactor_test.cpp index 4e5c98c0..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 (); @@ -131,7 +138,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; @@ -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; @@ -162,14 +181,18 @@ 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; -const int SqpollProactorTest::_timeout = 1000; +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; @@ -187,18 +210,22 @@ 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); - 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; @@ -265,55 +292,59 @@ 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); - 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); - 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; @@ -336,29 +367,33 @@ 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); - 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); - 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; @@ -378,11 +413,15 @@ 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); - 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 (); @@ -390,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; @@ -411,19 +450,23 @@ 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); - 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; @@ -436,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; @@ -546,23 +589,20 @@ 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 (); + _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; } - - _client.setMode (Tcp::Socket::Blocking); } /** @@ -582,16 +622,20 @@ 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 (_client.connect ({_host, _port}), 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 (); + } + ASSERT_TRUE (_client.waitConnected (_timeout)) << 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; })); ::close (_result); _op = nullptr; @@ -604,7 +648,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 (); @@ -618,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; @@ -642,7 +690,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 (); @@ -655,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; @@ -676,7 +728,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"; @@ -686,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; @@ -711,7 +767,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)); @@ -720,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; @@ -743,7 +803,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 (); @@ -761,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; @@ -785,7 +849,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 (); @@ -802,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; @@ -824,18 +892,22 @@ 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"; - 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; @@ -852,18 +924,22 @@ 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); + _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; @@ -876,7 +952,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 (); @@ -889,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; @@ -909,7 +989,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 (); @@ -922,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 (); @@ -932,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; @@ -946,7 +1030,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; 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..94c94cda 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; @@ -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 0d2dd25d..29a06903 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,14 +102,20 @@ 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 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 uint16_t TcpSocket::_stallport = 5002; +const uint16_t TcpSocket::_stallport2 = 5003; +const std::chrono::milliseconds TcpSocket::_timeout{1000}; /** * @brief Test move. @@ -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,116 @@ 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; + + 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 (); + stall.close (); + + 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); +} + +/** + * @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 +495,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 +538,40 @@ 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.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 (); } /** @@ -434,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/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..40d7beb6 100644 --- a/crypto/include/join/tls.hpp +++ b/crypto/include/join/tls.hpp @@ -34,8 +34,10 @@ #include // C++. +#include #include #include +#include #include // C. @@ -56,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. @@ -342,11 +345,36 @@ namespace join /** * @brief block until TLS handshake is finished. - * @param timeout timeout in milliseconds (0: infinite). * @return true if TLS handshake is finished. */ - virtual bool waitHandshake (int 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 (JOIN_UNLIKELY ((deadline != TimePoint::max ()) && (_socket.mode () == Mode::Blocking))) + { + lastError = make_error_code (Errc::OperationFailed); + return false; + } + if (handshake () == 0) { return true; @@ -364,25 +392,23 @@ namespace join break; // LCOV_EXCL_LINE } - int activeTimeout = timeout; + TimePoint activeDeadline = deadline; 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) - { - activeTimeout = dtlsTimeoutMs; - } + 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) @@ -467,11 +493,36 @@ namespace join /** * @brief block until TLS shutdown is finished. - * @param timeout timeout in milliseconds (0: infinite). * @return true if TLS shutdown is finished. */ - bool waitShutdown (int timeout) noexcept + 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 (JOIN_UNLIKELY ((deadline != TimePoint::max ()) && (_socket.mode () == Mode::Blocking))) + { + lastError = make_error_code (Errc::OperationFailed); + return false; + } + if (!_ssl) { return true; @@ -492,7 +543,7 @@ namespace join break; // LCOV_EXCL_LINE } - if (_socket.wait (wantRead, wantWrite, timeout) == -1) + if (_socket.waitUntil (wantRead, wantWrite, deadline) == -1) { return false; } @@ -526,23 +577,40 @@ namespace join /** * @brief block until new data is available for reading. - * @param timeout timeout in milliseconds (0: infinite). * @return true if there is new data available for reading, false otherwise. */ - bool waitReadyRead (int timeout = 0) 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); } /** @@ -571,11 +639,40 @@ 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. * @return 0 on success, -1 on failure. */ - int readExactly (char* data, size_t size, int timeout = 0) + 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) + { + 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) @@ -585,7 +682,7 @@ namespace join { if (lastError == Errc::TemporaryError) { - if (waitReadyRead (timeout)) + if (waitReadyRead (deadline)) { continue; } @@ -602,23 +699,40 @@ namespace join /** * @brief block until at least one byte can be written on the socket. - * @param timeout timeout in milliseconds (0: infinite). * @return true if data can be written on the socket, false otherwise. */ - bool waitReadyWrite (int timeout = 0) 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); } /** @@ -647,11 +761,40 @@ 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. * @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) + { + 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) { + 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) @@ -661,7 +804,7 @@ namespace join { if (lastError == Errc::TemporaryError) { - if (waitReadyWrite (timeout)) + if (waitReadyWrite (deadline)) { continue; } 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/include/join/tls_wrapper.hpp b/crypto/include/join/tls_wrapper.hpp index 47679aa0..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 in milliseconds. * @return true if connected, false otherwise. */ - bool waitConnected (int timeout = 0) + 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 in milliseconds. * @return true if disconnected, false otherwise. */ - bool waitDisconnected (int timeout = 0) + 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 in milliseconds (0: 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 (int 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/dtls_wrapper_test.cpp b/crypto/tests/dtls_wrapper_test.cpp index 98d05e77..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; @@ -239,7 +242,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 +263,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..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,10 +257,11 @@ 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. - static const int _timeout; + static const std::chrono::milliseconds _timeout; /// root certificate. static const std::string _rootcert; @@ -278,8 +282,9 @@ 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 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"; @@ -422,6 +427,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 (); @@ -465,6 +472,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)); @@ -478,12 +493,15 @@ 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 (); } 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 (); @@ -518,6 +536,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 +576,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 (); @@ -621,6 +643,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 (); @@ -630,6 +654,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 +687,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 (); @@ -695,6 +735,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 (); @@ -703,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 (); } /** 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 3105f756..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, @@ -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";