QAbstractSocketEngine: port to QDeadlineTimer

qnativesocketengine_win.cpp: don't check if timeout is < 0, because
remainingTimeAsDuration() doesn't return negative values.

All the changes done in one go, not function by function, as that causes
the least churn. You can think of them as a couple of very similar
changes repeated various times.

Drive-by change: replace `forever {` with `for (;;)`

Task-number: QTBUG-113518
Change-Id: Ie9f20031bf0d4ff19e5b2da5034822ba61f9cbc3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Ahmad Samir 2023-07-31 20:05:05 +03:00
parent 51c812af07
commit 032ffb70a8
16 changed files with 146 additions and 130 deletions

View File

@ -439,7 +439,7 @@
#include <qmetaobject.h>
#include <qpointer.h>
#include <qtimer.h>
#include <qelapsedtimer.h>
#include <qdeadlinetimer.h>
#include <qscopedvaluerollback.h>
#include <qvarlengtharray.h>
@ -465,11 +465,12 @@
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
using namespace std::chrono_literals;
QT_IMPL_METATYPE_EXTERN_TAGGED(QAbstractSocket::SocketState, QAbstractSocket__SocketState)
QT_IMPL_METATYPE_EXTERN_TAGGED(QAbstractSocket::SocketError, QAbstractSocket__SocketError)
static const int DefaultConnectTimeout = 30000;
static constexpr auto DefaultConnectTimeout = 30s;
static bool isProxyError(QAbstractSocket::SocketError error)
{
@ -2051,8 +2052,7 @@ bool QAbstractSocket::waitForConnected(int msecs)
bool wasPendingClose = d->pendingClose;
d->pendingClose = false;
QElapsedTimer stopWatch;
stopWatch.start();
QDeadlineTimer deadline{msecs};
if (d->state == HostLookupState) {
#if defined (QABSTRACTSOCKET_DEBUG)
@ -2076,17 +2076,17 @@ bool QAbstractSocket::waitForConnected(int msecs)
#if defined (QABSTRACTSOCKET_DEBUG)
int attempt = 1;
#endif
while (state() == ConnectingState && (msecs == -1 || stopWatch.elapsed() < msecs)) {
int timeout = qt_subtract_from_timeout(msecs, stopWatch.elapsed());
if (msecs != -1 && timeout > DefaultConnectTimeout)
timeout = DefaultConnectTimeout;
while (state() == ConnectingState && !deadline.hasExpired()) {
QDeadlineTimer timer = deadline;
if (!deadline.isForever() && deadline.remainingTimeAsDuration() > DefaultConnectTimeout)
timer = QDeadlineTimer(DefaultConnectTimeout);
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::waitForConnected(%i) waiting %.2f secs for connection attempt #%i",
msecs, timeout / 1000.0, attempt++);
msecs, timer.remainingTime() / 1000.0, attempt++);
#endif
timedOut = false;
if (d->socketEngine && d->socketEngine->waitForWrite(timeout, &timedOut) && !timedOut) {
if (d->socketEngine && d->socketEngine->waitForWrite(timer, &timedOut) && !timedOut) {
d->_q_testConnection();
} else {
d->_q_connectToNextAddress();
@ -2141,8 +2141,7 @@ bool QAbstractSocket::waitForReadyRead(int msecs)
return false;
}
QElapsedTimer stopWatch;
stopWatch.start();
QDeadlineTimer deadline{msecs};
// handle a socket in connecting state
if (state() == HostLookupState || state() == ConnectingState) {
@ -2158,7 +2157,7 @@ bool QAbstractSocket::waitForReadyRead(int msecs)
bool readyToRead = false;
bool readyToWrite = false;
if (!d->socketEngine->waitForReadOrWrite(&readyToRead, &readyToWrite, true, !d->writeBuffer.isEmpty(),
qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
deadline)) {
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::waitForReadyRead(%i) failed (%i, %s)",
msecs, d->socketEngine->error(), d->socketEngine->errorString().toLatin1().constData());
@ -2176,7 +2175,7 @@ bool QAbstractSocket::waitForReadyRead(int msecs)
if (readyToWrite)
d->canWriteNotification();
} while (msecs == -1 || qt_subtract_from_timeout(msecs, stopWatch.elapsed()) > 0);
} while (!deadline.hasExpired());
return false;
}
@ -2212,8 +2211,7 @@ bool QAbstractSocket::waitForBytesWritten(int msecs)
if (d->writeBuffer.isEmpty())
return false;
QElapsedTimer stopWatch;
stopWatch.start();
QDeadlineTimer deadline{msecs};
// handle a socket in connecting state
if (state() == HostLookupState || state() == ConnectingState) {
@ -2221,13 +2219,13 @@ bool QAbstractSocket::waitForBytesWritten(int msecs)
return false;
}
forever {
for (;;) {
bool readyToRead = false;
bool readyToWrite = false;
if (!d->socketEngine->waitForReadOrWrite(&readyToRead, &readyToWrite,
!d->readBufferMaxSize || d->buffer.size() < d->readBufferMaxSize,
!d->writeBuffer.isEmpty(),
qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
deadline)) {
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::waitForBytesWritten(%i) failed (%i, %s)",
msecs, d->socketEngine->error(), d->socketEngine->errorString().toLatin1().constData());
@ -2291,8 +2289,7 @@ bool QAbstractSocket::waitForDisconnected(int msecs)
return false;
}
QElapsedTimer stopWatch;
stopWatch.start();
QDeadlineTimer deadline{msecs};
// handle a socket in connecting state
if (state() == HostLookupState || state() == ConnectingState) {
@ -2302,12 +2299,12 @@ bool QAbstractSocket::waitForDisconnected(int msecs)
return true;
}
forever {
for (;;) {
bool readyToRead = false;
bool readyToWrite = false;
if (!d->socketEngine->waitForReadOrWrite(&readyToRead, &readyToWrite, state() == ConnectedState,
!d->writeBuffer.isEmpty(),
qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
deadline)) {
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::waitForReadyRead(%i) failed (%i, %s)",
msecs, d->socketEngine->error(), d->socketEngine->errorString().toLatin1().constData());

View File

@ -19,8 +19,9 @@
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "QtNetwork/qhostaddress.h"
#include "QtNetwork/qabstractsocket.h"
#include "private/qobject_p.h"
#include <QtCore/qdeadlinetimer.h>
#include "private/qnetworkdatagram_p.h"
#include "private/qobject_p.h"
QT_BEGIN_NAMESPACE
@ -44,6 +45,8 @@ public:
#endif
};
static constexpr std::chrono::seconds DefaultTimeout{30};
class Q_AUTOTEST_EXPORT QAbstractSocketEngine : public QObject
{
Q_OBJECT
@ -128,11 +131,14 @@ public:
virtual int option(SocketOption option) const = 0;
virtual bool setOption(SocketOption option, int value) = 0;
virtual bool waitForRead(int msecs = 30000, bool *timedOut = nullptr) = 0;
virtual bool waitForWrite(int msecs = 30000, bool *timedOut = nullptr) = 0;
virtual bool waitForRead(QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
bool *timedOut = nullptr) = 0;
virtual bool waitForWrite(QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
bool *timedOut = nullptr) = 0;
virtual bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
bool checkRead, bool checkWrite,
int msecs = 30000, bool *timedOut = nullptr) = 0;
QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
bool *timedOut = nullptr) = 0;
QAbstractSocket::SocketError error() const;
QString errorString() const;

View File

@ -7,7 +7,7 @@
#include "qurl.h"
#include "private/qhttpnetworkreply_p.h"
#include "private/qiodevice_p.h"
#include "qelapsedtimer.h"
#include "qdeadlinetimer.h"
#include "qnetworkinterface.h"
#if !defined(QT_NO_NETWORKPROXY)
@ -310,19 +310,16 @@ bool QHttpSocketEngine::setOption(SocketOption option, int value)
return false;
}
bool QHttpSocketEngine::waitForRead(int msecs, bool *timedOut)
bool QHttpSocketEngine::waitForRead(QDeadlineTimer deadline, bool *timedOut)
{
Q_D(const QHttpSocketEngine);
if (!d->socket || d->socket->state() == QAbstractSocket::UnconnectedState)
return false;
QElapsedTimer stopWatch;
stopWatch.start();
// Wait for more data if nothing is available.
if (!d->socket->bytesAvailable()) {
if (!d->socket->waitForReadyRead(qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
if (!d->socket->waitForReadyRead(deadline.remainingTime())) {
if (d->socket->state() == QAbstractSocket::UnconnectedState)
return true;
setError(d->socket->error(), d->socket->errorString());
@ -334,7 +331,7 @@ bool QHttpSocketEngine::waitForRead(int msecs, bool *timedOut)
// If we're not connected yet, wait until we are, or until an error
// occurs.
while (d->state != Connected && d->socket->waitForReadyRead(qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
while (d->state != Connected && d->socket->waitForReadyRead(deadline.remainingTime())) {
// Loop while the protocol handshake is taking place.
}
@ -348,14 +345,14 @@ bool QHttpSocketEngine::waitForRead(int msecs, bool *timedOut)
return true;
}
bool QHttpSocketEngine::waitForWrite(int msecs, bool *timedOut)
bool QHttpSocketEngine::waitForWrite(QDeadlineTimer deadline, bool *timedOut)
{
Q_D(const QHttpSocketEngine);
// If we're connected, just forward the call.
if (d->state == Connected) {
if (d->socket->bytesToWrite()) {
if (!d->socket->waitForBytesWritten(msecs)) {
if (!d->socket->waitForBytesWritten(deadline.remainingTime())) {
if (d->socket->error() == QAbstractSocket::SocketTimeoutError && timedOut)
*timedOut = true;
return false;
@ -364,13 +361,10 @@ bool QHttpSocketEngine::waitForWrite(int msecs, bool *timedOut)
return true;
}
QElapsedTimer stopWatch;
stopWatch.start();
// If we're not connected yet, wait until we are, and until bytes have
// been received (i.e., the socket has connected, we have sent the
// greeting, and then received the response).
while (d->state != Connected && d->socket->waitForReadyRead(qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
while (d->state != Connected && d->socket->waitForReadyRead(deadline.remainingTime())) {
// Loop while the protocol handshake is taking place.
}
@ -386,20 +380,20 @@ bool QHttpSocketEngine::waitForWrite(int msecs, bool *timedOut)
bool QHttpSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
bool checkRead, bool checkWrite,
int msecs, bool *timedOut)
QDeadlineTimer deadline, bool *timedOut)
{
Q_UNUSED(checkRead);
if (!checkWrite) {
// Not interested in writing? Then we wait for read notifications.
bool canRead = waitForRead(msecs, timedOut);
bool canRead = waitForRead(deadline, timedOut);
if (readyToRead)
*readyToRead = canRead;
return canRead;
}
// Interested in writing? Then we wait for write notifications.
bool canWrite = waitForWrite(msecs, timedOut);
bool canWrite = waitForWrite(deadline, timedOut);
if (readyToWrite)
*readyToWrite = canWrite;
return canWrite;

View File

@ -92,11 +92,14 @@ public:
int option(SocketOption option) const override;
bool setOption(SocketOption option, int value) override;
bool waitForRead(int msecs = 30000, bool *timedOut = nullptr) override;
bool waitForWrite(int msecs = 30000, bool *timedOut = nullptr) override;
bool waitForRead(QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
bool *timedOut = nullptr) override;
bool waitForWrite(QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
bool *timedOut = nullptr) override;
bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
bool checkRead, bool checkWrite,
int msecs = 30000, bool *timedOut = nullptr) override;
QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
bool *timedOut = nullptr) override;
bool isReadNotificationEnabled() const override;
void setReadNotificationEnabled(bool enable) override;

View File

@ -962,9 +962,9 @@ void QNativeSocketEngine::close()
}
/*!
Waits for \a msecs milliseconds or until the socket is ready for
reading. If \a timedOut is not \nullptr and \a msecs milliseconds
have passed, the value of \a timedOut is set to true.
Waits until \a deadline has expired or until the socket is ready for
reading. If \a timedOut is not \nullptr and \a deadline has expired,
the value of \a timedOut is set to true.
Returns \c true if data is available for reading; otherwise returns
false.
@ -976,7 +976,7 @@ void QNativeSocketEngine::close()
is to create a QSocketNotifier, passing the socket descriptor
returned by socketDescriptor() to its constructor.
*/
bool QNativeSocketEngine::waitForRead(int msecs, bool *timedOut)
bool QNativeSocketEngine::waitForRead(QDeadlineTimer deadline, bool *timedOut)
{
Q_D(const QNativeSocketEngine);
Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForRead(), false);
@ -986,7 +986,7 @@ bool QNativeSocketEngine::waitForRead(int msecs, bool *timedOut)
if (timedOut)
*timedOut = false;
int ret = d->nativeSelect(msecs, true);
int ret = d->nativeSelect(deadline, true);
if (ret == 0) {
if (timedOut)
*timedOut = true;
@ -1002,9 +1002,9 @@ bool QNativeSocketEngine::waitForRead(int msecs, bool *timedOut)
}
/*!
Waits for \a msecs milliseconds or until the socket is ready for
writing. If \a timedOut is not \nullptr and \a msecs milliseconds
have passed, the value of \a timedOut is set to true.
Waits until \a deadline has expired or until the socket is ready for
writing. If \a timedOut is not \nullptr and \a deadline has expired,
the value of \a timedOut is set to true.
Returns \c true if data is available for writing; otherwise returns
false.
@ -1016,7 +1016,7 @@ bool QNativeSocketEngine::waitForRead(int msecs, bool *timedOut)
is to create a QSocketNotifier, passing the socket descriptor
returned by socketDescriptor() to its constructor.
*/
bool QNativeSocketEngine::waitForWrite(int msecs, bool *timedOut)
bool QNativeSocketEngine::waitForWrite(QDeadlineTimer deadline, bool *timedOut)
{
Q_D(QNativeSocketEngine);
Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForWrite(), false);
@ -1026,7 +1026,7 @@ bool QNativeSocketEngine::waitForWrite(int msecs, bool *timedOut)
if (timedOut)
*timedOut = false;
int ret = d->nativeSelect(msecs, false);
int ret = d->nativeSelect(deadline, false);
// On Windows, the socket is in connected state if a call to
// select(writable) is successful. In this case we should not
// issue a second call to WSAConnect()
@ -1074,14 +1074,14 @@ bool QNativeSocketEngine::waitForWrite(int msecs, bool *timedOut)
bool QNativeSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
bool checkRead, bool checkWrite,
int msecs, bool *timedOut)
QDeadlineTimer deadline, bool *timedOut)
{
Q_D(QNativeSocketEngine);
Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForReadOrWrite(), false);
Q_CHECK_NOT_STATE(QNativeSocketEngine::waitForReadOrWrite(),
QAbstractSocket::UnconnectedState, false);
int ret = d->nativeSelect(msecs, checkRead, checkWrite, readyToRead, readyToWrite);
int ret = d->nativeSelect(deadline, checkRead, checkWrite, readyToRead, readyToWrite);
// On Windows, the socket is in connected state if a call to
// select(writable) is successful. In this case we should not
// issue a second call to WSAConnect()

View File

@ -154,11 +154,14 @@ public:
int option(SocketOption option) const override;
bool setOption(SocketOption option, int value) override;
bool waitForRead(int msecs = 30000, bool *timedOut = nullptr) override;
bool waitForWrite(int msecs = 30000, bool *timedOut = nullptr) override;
bool waitForRead(QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
bool *timedOut = nullptr) override;
bool waitForWrite(QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
bool *timedOut = nullptr) override;
bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
bool checkRead, bool checkWrite,
int msecs = 30000, bool *timedOut = nullptr) override;
QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
bool *timedOut = nullptr) override;
bool isReadNotificationEnabled() const override;
void setReadNotificationEnabled(bool enable) override;

View File

@ -146,8 +146,8 @@ public:
qint64 nativeSendDatagram(const char *data, qint64 length, const QIpPacketHeader &header);
qint64 nativeRead(char *data, qint64 maxLength);
qint64 nativeWrite(const char *data, qint64 length);
int nativeSelect(int timeout, bool selectForRead) const;
int nativeSelect(int timeout, bool checkRead, bool checkWrite,
int nativeSelect(QDeadlineTimer deadline, bool selectForRead) const;
int nativeSelect(QDeadlineTimer deadline, bool checkRead, bool checkWrite,
bool *selectForRead, bool *selectForWrite) const;
void nativeClose();

View File

@ -5,9 +5,9 @@
//#define QNATIVESOCKETENGINE_DEBUG
#include "qnativesocketengine_p_p.h"
#include "private/qnet_unix_p.h"
#include "qdeadlinetimer.h"
#include "qiodevice.h"
#include "qhostaddress.h"
#include "qelapsedtimer.h"
#include "qvarlengtharray.h"
#include "qnetworkinterface.h"
#include "qendian.h"
@ -1344,16 +1344,17 @@ qint64 QNativeSocketEnginePrivate::nativeRead(char *data, qint64 maxSize)
return qint64(r);
}
int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) const
int QNativeSocketEnginePrivate::nativeSelect(QDeadlineTimer deadline, bool selectForRead) const
{
bool dummy;
return nativeSelect(timeout, selectForRead, !selectForRead, &dummy, &dummy);
return nativeSelect(deadline, selectForRead, !selectForRead, &dummy, &dummy);
}
#ifndef Q_OS_WASM
int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool checkRead, bool checkWrite,
bool *selectForRead, bool *selectForWrite) const
int QNativeSocketEnginePrivate::nativeSelect(QDeadlineTimer deadline, bool checkRead,
bool checkWrite, bool *selectForRead,
bool *selectForWrite) const
{
pollfd pfd = qt_make_pollfd(socketDescriptor, 0);
@ -1363,7 +1364,7 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool checkRead, bool c
if (checkWrite)
pfd.events |= POLLOUT;
const int ret = qt_poll_msecs(&pfd, 1, timeout);
const int ret = qt_poll_msecs(&pfd, 1, deadline.remainingTime());
if (ret <= 0)
return ret;
@ -1384,13 +1385,16 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool checkRead, bool c
#else
int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool checkRead, bool checkWrite,
bool *selectForRead, bool *selectForWrite) const
int QNativeSocketEnginePrivate::nativeSelect(QDeadlineTimer deadline, bool checkRead,
bool checkWrite, bool *selectForRead,
bool *selectForWrite) const
{
*selectForRead = checkRead;
*selectForWrite = checkWrite;
bool socketDisconnect = false;
QEventDispatcherWasm::socketSelect(timeout, socketDescriptor, checkRead, checkWrite,selectForRead, selectForWrite, &socketDisconnect);
QEventDispatcherWasm::socketSelect(deadline.remainingTime(), socketDescriptor, checkRead,
checkWrite, selectForRead, selectForWrite,
&socketDisconnect);
// The disconnect/close handling code in QAbstractsScket::canReadNotification()
// does not detect remote disconnect properly; do that here as a workardound.

View File

@ -16,6 +16,7 @@
#include <qvarlengtharray.h>
#include <algorithm>
#include <chrono>
//#define QNATIVESOCKETENGINE_DEBUG
#if defined(QNATIVESOCKETENGINE_DEBUG)
@ -1428,7 +1429,18 @@ qint64 QNativeSocketEnginePrivate::nativeRead(char *data, qint64 maxLength)
return ret;
}
int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) const
inline timeval durationToTimeval(std::chrono::nanoseconds dur) noexcept
{
using namespace std::chrono;
const auto secs = duration_cast<seconds>(dur);
const auto frac = duration_cast<microseconds>(dur - secs);
struct timeval tval;
tval.tv_sec = secs.count();
tval.tv_usec = frac.count();
return tval;
}
int QNativeSocketEnginePrivate::nativeSelect(QDeadlineTimer deadline, bool selectForRead) const
{
bool readEnabled = selectForRead && readNotifier && readNotifier->isEnabled();
if (readEnabled)
@ -1442,12 +1454,10 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) co
fds.fd_count = 1;
fds.fd_array[0] = (SOCKET)socketDescriptor;
struct timeval tv;
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
struct timeval tv = durationToTimeval(deadline.remainingTimeAsDuration());
if (selectForRead) {
ret = select(0, &fds, 0, 0, timeout < 0 ? 0 : &tv);
ret = select(0, &fds, 0, 0, &tv);
} else {
// select for write
@ -1456,7 +1466,7 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) co
FD_ZERO(&fdexception);
FD_SET((SOCKET)socketDescriptor, &fdexception);
ret = select(0, 0, &fds, &fdexception, timeout < 0 ? 0 : &tv);
ret = select(0, 0, &fds, &fdexception, &tv);
// ... but if it is actually set, pretend it did not happen
if (ret > 0 && FD_ISSET((SOCKET)socketDescriptor, &fdexception))
@ -1469,7 +1479,7 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) co
return ret;
}
int QNativeSocketEnginePrivate::nativeSelect(int timeout,
int QNativeSocketEnginePrivate::nativeSelect(QDeadlineTimer deadline,
bool checkRead, bool checkWrite,
bool *selectForRead, bool *selectForWrite) const
{
@ -1498,11 +1508,9 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout,
FD_SET((SOCKET)socketDescriptor, &fdexception);
}
struct timeval tv;
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
struct timeval tv = durationToTimeval(deadline.remainingTimeAsDuration());
ret = select(socketDescriptor + 1, &fdread, &fdwrite, &fdexception, timeout < 0 ? 0 : &tv);
ret = select(socketDescriptor + 1, &fdread, &fdwrite, &fdexception, &tv);
//... but if it is actually set, pretend it did not happen
if (ret > 0 && FD_ISSET((SOCKET)socketDescriptor, &fdexception))

View File

@ -9,6 +9,7 @@
#include "qdebug.h"
#include "qhash.h"
#include "qqueue.h"
#include "qdeadlinetimer.h"
#include "qelapsedtimer.h"
#include "qmutex.h"
#include "qthread.h"
@ -25,13 +26,14 @@
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
using namespace std::chrono_literals;
static const int MaxWriteBufferSize = 128*1024;
//#define QSOCKS5SOCKETLAYER_DEBUG
#define MAX_DATA_DUMP 256
#define SOCKS5_BLOCKING_BIND_TIMEOUT 5000
static constexpr auto Socks5BlockingBindTimeout = 5s;
#define Q_INIT_CHECK(returnValue) do { \
if (!d->data) { \
@ -318,7 +320,6 @@ void QSocks5BindStore::add(qintptr socketDescriptor, QSocks5BindData *bindData)
bindData->timeStamp.start();
store.insert(socketDescriptor, bindData);
using namespace std::chrono_literals;
// start sweep timer if not started
if (sweepTimerId == -1)
sweepTimerId = startTimer(1min);
@ -1327,11 +1328,8 @@ bool QSocks5SocketEngine::bind(const QHostAddress &addr, quint16 port)
return false;
}
int msecs = SOCKS5_BLOCKING_BIND_TIMEOUT;
QElapsedTimer stopWatch;
stopWatch.start();
d->data->controlSocket->connectToHost(d->proxyInfo.hostName(), d->proxyInfo.port());
if (!d->waitForConnected(msecs, nullptr) ||
if (!d->waitForConnected(QDeadlineTimer{Socks5BlockingBindTimeout}, nullptr) ||
d->data->controlSocket->state() == QAbstractSocket::UnconnectedState) {
// waitForConnected sets the error state and closes the socket
QSOCKS5_Q_DEBUG << "waitForConnected to proxy server" << d->data->controlSocket->errorString();
@ -1422,11 +1420,9 @@ void QSocks5SocketEngine::close()
Q_D(QSocks5SocketEngine);
if (d->data && d->data->controlSocket) {
if (d->data->controlSocket->state() == QAbstractSocket::ConnectedState) {
int msecs = 100;
QElapsedTimer stopWatch;
stopWatch.start();
QDeadlineTimer deadline(100ms);
while (!d->data->controlSocket->bytesToWrite()) {
if (!d->data->controlSocket->waitForBytesWritten(qt_subtract_from_timeout(msecs, stopWatch.elapsed())))
if (!d->data->controlSocket->waitForBytesWritten(deadline.remainingTime()))
break;
}
}
@ -1679,7 +1675,7 @@ bool QSocks5SocketEngine::setOption(SocketOption option, int value)
return false;
}
bool QSocks5SocketEnginePrivate::waitForConnected(int msecs, bool *timedOut)
bool QSocks5SocketEnginePrivate::waitForConnected(QDeadlineTimer deadline, bool *timedOut)
{
if (data->controlSocket->state() == QAbstractSocket::UnconnectedState)
return false;
@ -1689,11 +1685,8 @@ bool QSocks5SocketEnginePrivate::waitForConnected(int msecs, bool *timedOut)
mode == BindMode ? BindSuccess :
UdpAssociateSuccess;
QElapsedTimer stopWatch;
stopWatch.start();
while (socks5State != wantedState) {
if (!data->controlSocket->waitForReadyRead(qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
if (!data->controlSocket->waitForReadyRead(deadline.remainingTime())) {
if (data->controlSocket->state() == QAbstractSocket::UnconnectedState)
return true;
@ -1707,18 +1700,15 @@ bool QSocks5SocketEnginePrivate::waitForConnected(int msecs, bool *timedOut)
return true;
}
bool QSocks5SocketEngine::waitForRead(int msecs, bool *timedOut)
bool QSocks5SocketEngine::waitForRead(QDeadlineTimer deadline, bool *timedOut)
{
Q_D(QSocks5SocketEngine);
QSOCKS5_DEBUG << "waitForRead" << msecs;
QSOCKS5_DEBUG << "waitForRead" << deadline.remainingTimeAsDuration();
d->readNotificationActivated = false;
QElapsedTimer stopWatch;
stopWatch.start();
// are we connected yet?
if (!d->waitForConnected(msecs, timedOut))
if (!d->waitForConnected(deadline, timedOut))
return false;
if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
return true;
@ -1732,7 +1722,7 @@ bool QSocks5SocketEngine::waitForRead(int msecs, bool *timedOut)
if (d->mode == QSocks5SocketEnginePrivate::ConnectMode ||
d->mode == QSocks5SocketEnginePrivate::BindMode) {
while (!d->readNotificationActivated) {
if (!d->data->controlSocket->waitForReadyRead(qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
if (!d->data->controlSocket->waitForReadyRead(deadline.remainingTime())) {
if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
return true;
@ -1745,7 +1735,7 @@ bool QSocks5SocketEngine::waitForRead(int msecs, bool *timedOut)
#ifndef QT_NO_UDPSOCKET
} else {
while (!d->readNotificationActivated) {
if (!d->udpData->udpSocket->waitForReadyRead(qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
if (!d->udpData->udpSocket->waitForReadyRead(deadline.remainingTime())) {
setError(d->udpData->udpSocket->error(), d->udpData->udpSocket->errorString());
if (timedOut && d->udpData->udpSocket->error() == QAbstractSocket::SocketTimeoutError)
*timedOut = true;
@ -1764,16 +1754,13 @@ bool QSocks5SocketEngine::waitForRead(int msecs, bool *timedOut)
}
bool QSocks5SocketEngine::waitForWrite(int msecs, bool *timedOut)
bool QSocks5SocketEngine::waitForWrite(QDeadlineTimer deadline, bool *timedOut)
{
Q_D(QSocks5SocketEngine);
QSOCKS5_DEBUG << "waitForWrite" << msecs;
QElapsedTimer stopWatch;
stopWatch.start();
QSOCKS5_DEBUG << "waitForWrite" << deadline.remainingTimeAsDuration();
// are we connected yet?
if (!d->waitForConnected(msecs, timedOut))
if (!d->waitForConnected(deadline, timedOut))
return false;
if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
return true;
@ -1782,27 +1769,32 @@ bool QSocks5SocketEngine::waitForWrite(int msecs, bool *timedOut)
// flush any bytes we may still have buffered in the time that we have left
if (d->data->controlSocket->bytesToWrite())
d->data->controlSocket->waitForBytesWritten(qt_subtract_from_timeout(msecs, stopWatch.elapsed()));
while ((msecs == -1 || stopWatch.elapsed() < msecs)
&& d->data->controlSocket->state() == QAbstractSocket::ConnectedState
&& d->data->controlSocket->bytesToWrite() >= MaxWriteBufferSize)
d->data->controlSocket->waitForBytesWritten(qt_subtract_from_timeout(msecs, stopWatch.elapsed()));
d->data->controlSocket->waitForBytesWritten(deadline.remainingTime());
auto shouldWriteBytes = [&]() {
return d->data->controlSocket->state() == QAbstractSocket::ConnectedState
&& d->data->controlSocket->bytesToWrite() >= MaxWriteBufferSize;
};
qint64 remainingTime = deadline.remainingTime();
for (; remainingTime > 0 && shouldWriteBytes(); remainingTime = deadline.remainingTime())
d->data->controlSocket->waitForBytesWritten(remainingTime);
return d->data->controlSocket->bytesToWrite() < MaxWriteBufferSize;
}
bool QSocks5SocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
bool checkRead, bool checkWrite,
int msecs, bool *timedOut)
QDeadlineTimer deadline, bool *timedOut)
{
Q_UNUSED(checkRead);
if (!checkWrite) {
bool canRead = waitForRead(msecs, timedOut);
bool canRead = waitForRead(deadline, timedOut);
if (readyToRead)
*readyToRead = canRead;
return canRead;
}
bool canWrite = waitForWrite(msecs, timedOut);
bool canWrite = waitForWrite(deadline, timedOut);
if (readyToWrite)
*readyToWrite = canWrite;
return canWrite;

View File

@ -78,11 +78,14 @@ public:
int option(SocketOption option) const override;
bool setOption(SocketOption option, int value) override;
bool waitForRead(int msecs = 30000, bool *timedOut = nullptr) override;
bool waitForWrite(int msecs = 30000, bool *timedOut = nullptr) override;
bool waitForRead(QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
bool *timedOut = nullptr) override;
bool waitForWrite(QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
bool *timedOut = nullptr) override;
bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
bool checkRead, bool checkWrite,
int msecs = 30000, bool *timedOut = nullptr) override;
QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
bool *timedOut = nullptr) override;
bool isReadNotificationEnabled() const override;
void setReadNotificationEnabled(bool enable) override;
@ -208,7 +211,7 @@ public:
void parseRequestMethodReply();
void parseNewConnection();
bool waitForConnected(int msecs, bool *timedOut);
bool waitForConnected(QDeadlineTimer deadline, bool *timedOut);
void _q_controlSocketConnected();
void _q_controlSocketReadNotification();

View File

@ -498,7 +498,7 @@ bool QTcpServer::waitForNewConnection(int msec, bool *timedOut)
if (d->state != QAbstractSocket::ListeningState)
return false;
if (!d->socketEngine->waitForRead(msec, timedOut)) {
if (!d->socketEngine->waitForRead(QDeadlineTimer(msec), timedOut)) {
d->serverSocketError = d->socketEngine->error();
d->serverSocketErrorString = d->socketEngine->errorString();
return false;

View File

@ -189,8 +189,8 @@ void tst_QSocketNotifier::unexpectedDisconnection()
writeEnd2->waitForBytesWritten();
// ensure both read ends are ready for reading, before the event loop
QVERIFY(readEnd1.waitForRead(5000));
QVERIFY(readEnd2.waitForRead(5000));
QVERIFY(readEnd1.waitForRead(5s));
QVERIFY(readEnd2.waitForRead(5s));
UnexpectedDisconnectTester tester(&readEnd1, &readEnd2);

View File

@ -18,6 +18,8 @@
#include "../../../network-settings.h"
using namespace std::chrono_literals;
class tst_QHttpSocketEngine : public QObject
{
Q_OBJECT
@ -323,7 +325,7 @@ void tst_QHttpSocketEngine::simpleErrorsAndStates()
QCOMPARE(socketDevice.state(), QAbstractSocket::UnconnectedState);
QVERIFY(!socketDevice.connectToHost(QHostAddress(QtNetworkSettings::socksProxyServerName()), 8088));
QCOMPARE(socketDevice.state(), QAbstractSocket::ConnectingState);
if (socketDevice.waitForWrite(30000)) {
if (socketDevice.waitForWrite(30s)) {
QVERIFY(socketDevice.state() == QAbstractSocket::ConnectedState ||
socketDevice.state() == QAbstractSocket::UnconnectedState);
} else {

View File

@ -23,6 +23,8 @@
#include "../../../network-settings.h"
using namespace std::chrono_literals;
class tst_QSocks5SocketEngine : public QObject, public QAbstractSocketEngineReceiver
{
Q_OBJECT
@ -341,7 +343,7 @@ void tst_QSocks5SocketEngine::simpleErrorsAndStates()
QCOMPARE(socketDevice.state(), QAbstractSocket::UnconnectedState);
QVERIFY(!socketDevice.connectToHost(QHostInfo::fromName(QtNetworkSettings::socksProxyServerName()).addresses().first(), 8088));
QCOMPARE(socketDevice.state(), QAbstractSocket::ConnectingState);
if (socketDevice.waitForWrite(15000)) {
if (socketDevice.waitForWrite(15s)) {
QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState ||
socketDevice.state() == QAbstractSocket::ConnectedState);
} else {

View File

@ -13,6 +13,8 @@
#include <cstdio>
#include <QCoreApplication>
using namespace std::chrono_literals;
const int bufsize = 16*1024;
char buf[bufsize];
@ -39,7 +41,7 @@ int main(int argc, char**argv)
int r = socketEngine->connectToHost(QHostAddress("74.125.77.99"), 80); // google
bool readyToRead = false;
bool readyToWrite = false;
socketEngine->waitForReadOrWrite(&readyToRead, &readyToWrite, true, true, 10*1000);
socketEngine->waitForReadOrWrite(&readyToRead, &readyToWrite, true, true, 10s);
if (r <= 0) //timeout or error
exit(1);
if (readyToWrite) {
@ -49,7 +51,7 @@ int main(int argc, char**argv)
if (ret == request.length()) {
// read the response in a loop
do {
bool waitReadResult = socketEngine->waitForRead(10*1000);
bool waitReadResult = socketEngine->waitForRead(10s);
int available = socketEngine->bytesAvailable();
if (waitReadResult == true && available == 0) {
// disconnected