QSocketNotifier: extend API to provide more flexible startup

Technically, having a single constructor limits the use-cases for this
class. We should take into account that:

  - an opened socket descriptor must be available at the moment of
    construction;
  - the constructor unconditionally enables the notifier (the possible
    solution

        notifier = new QSocketNotifier(...);
        notifier->setEnabled(false);

    is suboptimal due to heavy operations inside the event dispatcher);
  - for these reasons, QSocketNotifier most often cannot be a member of
    another class (we have an extra allocation and indirect access).

This patch addresses this shortcoming by making it possible to set the
socket descriptor at a later point:

[ChangeLog][QtCore][QSocketNotifier] Added setSocket() and an additional
constructor which requires no socket.

Change-Id: I2eb2edf33ddafe99e528aac7d3774ade40795e7a
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
bb10
Alex Trotsenko 2020-12-09 20:54:00 +02:00
parent 7715b186c7
commit 6dac45b246
3 changed files with 122 additions and 17 deletions

View File

@ -64,7 +64,7 @@ class QSocketNotifierPrivate : public QObjectPrivate
public:
QSocketDescriptor sockfd;
QSocketNotifier::Type sntype;
bool snenabled;
bool snenabled = false;
};
/*!
@ -85,11 +85,16 @@ public:
Once you have opened a device using a low-level (usually
platform-specific) API, you can create a socket notifier to
monitor the file descriptor. The socket notifier is enabled by
default, i.e. it emits the activated() signal whenever a socket
event corresponding to its type occurs. Connect the activated()
signal to the slot you want to be called when an event
corresponding to your socket notifier's type occurs.
monitor the file descriptor. If the descriptor is passed to the
notifier's constructor, the socket notifier is enabled by default,
i.e. it emits the activated() signal whenever a socket event
corresponding to its type occurs. Connect the activated() signal
to the slot you want to be called when an event corresponding to
your socket notifier's type occurs.
You can create a socket notifier with no descriptor assigned. In
this case, you should call the setSocket() function after the
descriptor has been obtained.
There are three types of socket notifiers: read, write, and
exception. The type is described by the \l Type enum, and must be
@ -135,6 +140,29 @@ public:
\sa QSocketNotifier(), type()
*/
/*!
\since 6.1
Constructs a socket notifier with the given \a type that has no
descriptor assigned. The \a parent argument is passed to QObject's
constructor.
Call the setSocket() function to set the descriptor for monitoring.
\sa setSocket(), isValid(), isEnabled()
*/
QSocketNotifier::QSocketNotifier(Type type, QObject *parent)
: QObject(*new QSocketNotifierPrivate, parent)
{
Q_D(QSocketNotifier);
qRegisterMetaType<QSocketDescriptor>();
qRegisterMetaType<QSocketNotifier::Type>();
d->sntype = type;
}
/*!
Constructs a socket notifier with the given \a parent. It enables
the \a socket, and watches for events of the given \a type.
@ -149,15 +177,11 @@ public:
*/
QSocketNotifier::QSocketNotifier(qintptr socket, Type type, QObject *parent)
: QObject(*new QSocketNotifierPrivate, parent)
: QSocketNotifier(type, parent)
{
Q_D(QSocketNotifier);
qRegisterMetaType<QSocketDescriptor>();
qRegisterMetaType<QSocketNotifier::Type>();
d->sockfd = socket;
d->sntype = type;
d->snenabled = true;
auto thisThreadData = d->threadData.loadRelaxed();
@ -208,11 +232,29 @@ QSocketNotifier::~QSocketNotifier()
\sa type(), socket()
*/
/*!
\since 6.1
Assigns the \a socket to this notifier. The \a enable argument is
passed to the setEnabled() function.
\sa setEnabled(), isValid()
*/
void QSocketNotifier::setSocket(qintptr socket, bool enable)
{
Q_D(QSocketNotifier);
if (d->sockfd != QSocketDescriptor(socket)) {
setEnabled(false);
d->sockfd = socket;
}
setEnabled(enable);
}
/*!
Returns the socket identifier specified to the constructor.
Returns the socket identifier assigned to this object.
\sa type()
\sa isValid(), type()
*/
qintptr QSocketNotifier::socket() const
{
@ -231,6 +273,20 @@ QSocketNotifier::Type QSocketNotifier::type() const
return d->sntype;
}
/*!
\since 6.1
Returns \c true if the notifier is valid (that is, it has
a descriptor assigned); otherwise returns \c false.
\sa setSocket()
*/
bool QSocketNotifier::isValid() const
{
Q_D(const QSocketNotifier);
return d->sockfd.isValid();
}
/*!
Returns \c true if the notifier is enabled; otherwise returns \c false.
@ -246,10 +302,10 @@ bool QSocketNotifier::isEnabled() const
If \a enable is true, the notifier is enabled; otherwise the notifier
is disabled.
The notifier is enabled by default, i.e. it emits the activated()
signal whenever a socket event corresponding to its
\l{type()}{type} occurs. If it is disabled, it ignores socket
events (the same effect as not creating the socket notifier).
When the notifier is enabled, it emits the activated() signal whenever
a socket event corresponding to its \l{type()}{type} occurs. When it is
disabled, it ignores socket events (the same effect as not creating
the socket notifier).
Write notifiers should normally be disabled immediately after the
activated() signal has been emitted

View File

@ -54,12 +54,15 @@ class Q_CORE_EXPORT QSocketNotifier : public QObject
public:
enum Type { Read, Write, Exception };
explicit QSocketNotifier(Type, QObject *parent = nullptr);
QSocketNotifier(qintptr socket, Type, QObject *parent = nullptr);
~QSocketNotifier();
void setSocket(qintptr socket, bool enable = false);
qintptr socket() const;
Type type() const;
bool isValid() const;
bool isEnabled() const;
public Q_SLOTS:

View File

@ -54,6 +54,7 @@ class tst_QSocketNotifier : public QObject
{
Q_OBJECT
private slots:
void constructing();
void unexpectedDisconnection();
void mixingWithTimers();
#ifdef Q_OS_UNIX
@ -85,6 +86,51 @@ static QHostAddress makeNonAny(const QHostAddress &address,
return address;
}
void tst_QSocketNotifier::constructing()
{
const qintptr fd = 15;
// Test constructing with no descriptor assigned.
{
QSocketNotifier notifier(QSocketNotifier::Read);
QVERIFY(!notifier.isValid());
QCOMPARE(notifier.socket(), Q_INT64_C(-1));
QCOMPARE(notifier.type(), QSocketNotifier::Read);
QVERIFY(!notifier.isEnabled());
notifier.setEnabled(true);
QVERIFY(!notifier.isEnabled());
notifier.setSocket(fd, true);
QVERIFY(notifier.isValid());
QCOMPARE(notifier.socket(), fd);
QVERIFY(notifier.isEnabled());
notifier.setEnabled(false);
QVERIFY(!notifier.isEnabled());
}
// Test constructing with the notifications enabled by default.
{
QSocketNotifier notifier(fd, QSocketNotifier::Write);
QVERIFY(notifier.isValid());
QCOMPARE(notifier.socket(), fd);
QCOMPARE(notifier.type(), QSocketNotifier::Write);
QVERIFY(notifier.isEnabled());
notifier.setSocket(fd, false);
QVERIFY(!notifier.isEnabled());
notifier.setEnabled(true);
QVERIFY(notifier.isEnabled());
notifier.setSocket(-1, true);
QVERIFY(!notifier.isValid());
QCOMPARE(notifier.socket(), Q_INT64_C(-1));
QVERIFY(!notifier.isEnabled());
}
}
class UnexpectedDisconnectTester : public QObject
{
Q_OBJECT