1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #define BUILDING_QSOCKETNOTIFIER |
5 | #include "qsocketnotifier.h" |
6 | #undef BUILDING_QSOCKETNOTIFIER |
7 | |
8 | #include "qplatformdefs.h" |
9 | |
10 | #include "qabstracteventdispatcher.h" |
11 | #include "qcoreapplication.h" |
12 | |
13 | #include "qmetatype.h" |
14 | |
15 | #include "qobject_p.h" |
16 | #include <private/qthread_p.h> |
17 | |
18 | #include <QtCore/QLoggingCategory> |
19 | |
20 | QT_BEGIN_NAMESPACE |
21 | |
22 | Q_DECLARE_LOGGING_CATEGORY(lcSocketNotifierDeprecation) |
23 | Q_LOGGING_CATEGORY(lcSocketNotifierDeprecation, "qt.core.socketnotifier_deprecation" ); |
24 | |
25 | QT_IMPL_METATYPE_EXTERN_TAGGED(QSocketNotifier::Type, QSocketNotifier_Type) |
26 | QT_IMPL_METATYPE_EXTERN(QSocketDescriptor) |
27 | |
28 | class QSocketNotifierPrivate : public QObjectPrivate |
29 | { |
30 | Q_DECLARE_PUBLIC(QSocketNotifier) |
31 | public: |
32 | QSocketDescriptor sockfd; |
33 | QSocketNotifier::Type sntype; |
34 | bool snenabled = false; |
35 | }; |
36 | |
37 | /*! |
38 | \class QSocketNotifier |
39 | \inmodule QtCore |
40 | \brief The QSocketNotifier class provides support for monitoring |
41 | activity on a file descriptor. |
42 | |
43 | \ingroup network |
44 | \ingroup io |
45 | |
46 | The QSocketNotifier makes it possible to integrate Qt's event |
47 | loop with other event loops based on file descriptors. File |
48 | descriptor action is detected in Qt's main event |
49 | loop (QCoreApplication::exec()). |
50 | |
51 | \target write notifiers |
52 | |
53 | Once you have opened a device using a low-level (usually |
54 | platform-specific) API, you can create a socket notifier to |
55 | monitor the file descriptor. If the descriptor is passed to the |
56 | notifier's constructor, the socket notifier is enabled by default, |
57 | i.e. it emits the activated() signal whenever a socket event |
58 | corresponding to its type occurs. Connect the activated() signal |
59 | to the slot you want to be called when an event corresponding to |
60 | your socket notifier's type occurs. |
61 | |
62 | You can create a socket notifier with no descriptor assigned. In |
63 | this case, you should call the setSocket() function after the |
64 | descriptor has been obtained. |
65 | |
66 | There are three types of socket notifiers: read, write, and |
67 | exception. The type is described by the \l Type enum, and must be |
68 | specified when constructing the socket notifier. After |
69 | construction it can be determined using the type() function. Note |
70 | that if you need to monitor both reads and writes for the same |
71 | file descriptor, you must create two socket notifiers. Note also |
72 | that it is not possible to install two socket notifiers of the |
73 | same type (\l Read, \l Write, \l Exception) on the same socket. |
74 | |
75 | The setEnabled() function allows you to disable as well as enable |
76 | the socket notifier. It is generally advisable to explicitly |
77 | enable or disable the socket notifier, especially for write |
78 | notifiers. A disabled notifier ignores socket events (the same |
79 | effect as not creating the socket notifier). Use the isEnabled() |
80 | function to determine the notifier's current status. |
81 | |
82 | Finally, you can use the socket() function to retrieve the |
83 | socket identifier. Although the class is called QSocketNotifier, |
84 | it is normally used for other types of devices than sockets. |
85 | QTcpSocket and QUdpSocket provide notification through signals, so |
86 | there is normally no need to use a QSocketNotifier on them. |
87 | |
88 | \sa QFile, QProcess, QTcpSocket, QUdpSocket |
89 | */ |
90 | |
91 | /*! |
92 | \enum QSocketNotifier::Type |
93 | |
94 | This enum describes the various types of events that a socket |
95 | notifier can recognize. The type must be specified when |
96 | constructing the socket notifier. |
97 | |
98 | Note that if you need to monitor both reads and writes for the |
99 | same file descriptor, you must create two socket notifiers. Note |
100 | also that it is not possible to install two socket notifiers of |
101 | the same type (Read, Write, Exception) on the same socket. |
102 | |
103 | \value Read There is data to be read. |
104 | \value Write Data can be written. |
105 | \value Exception An exception has occurred. We recommend against using this. |
106 | |
107 | \sa QSocketNotifier(), type() |
108 | */ |
109 | |
110 | /*! |
111 | \since 6.1 |
112 | |
113 | Constructs a socket notifier with the given \a type that has no |
114 | descriptor assigned. The \a parent argument is passed to QObject's |
115 | constructor. |
116 | |
117 | Call the setSocket() function to set the descriptor for monitoring. |
118 | |
119 | \sa setSocket(), isValid(), isEnabled() |
120 | */ |
121 | |
122 | QSocketNotifier::QSocketNotifier(Type type, QObject *parent) |
123 | : QObject(*new QSocketNotifierPrivate, parent) |
124 | { |
125 | Q_D(QSocketNotifier); |
126 | |
127 | qRegisterMetaType<QSocketDescriptor>(); |
128 | qRegisterMetaType<QSocketNotifier::Type>(); |
129 | |
130 | d->sntype = type; |
131 | } |
132 | |
133 | /*! |
134 | Constructs a socket notifier with the given \a parent. It enables |
135 | the \a socket, and watches for events of the given \a type. |
136 | |
137 | It is generally advisable to explicitly enable or disable the |
138 | socket notifier, especially for write notifiers. |
139 | |
140 | \b{Note for Windows users:} The socket passed to QSocketNotifier |
141 | will become non-blocking, even if it was created as a blocking socket. |
142 | |
143 | \sa setEnabled(), isEnabled() |
144 | */ |
145 | |
146 | QSocketNotifier::QSocketNotifier(qintptr socket, Type type, QObject *parent) |
147 | : QSocketNotifier(type, parent) |
148 | { |
149 | Q_D(QSocketNotifier); |
150 | |
151 | d->sockfd = socket; |
152 | d->snenabled = true; |
153 | |
154 | auto thisThreadData = d->threadData.loadRelaxed(); |
155 | |
156 | if (!d->sockfd.isValid()) |
157 | qWarning(msg: "QSocketNotifier: Invalid socket specified" ); |
158 | else if (!thisThreadData->hasEventDispatcher()) |
159 | qWarning(msg: "QSocketNotifier: Can only be used with threads started with QThread" ); |
160 | else |
161 | thisThreadData->eventDispatcher.loadRelaxed()->registerSocketNotifier(notifier: this); |
162 | } |
163 | |
164 | /*! |
165 | Destroys this socket notifier. |
166 | */ |
167 | |
168 | QSocketNotifier::~QSocketNotifier() |
169 | { |
170 | setEnabled(false); |
171 | } |
172 | |
173 | |
174 | /*! |
175 | \fn void QSocketNotifier::activated(int socket) |
176 | \deprecated To avoid unintended truncation of the descriptor, use |
177 | the QSocketDescriptor overload of this function. If you need |
178 | compatibility with versions older than 5.15 you need to change |
179 | the slot to accept qintptr if it currently accepts an int, and |
180 | then connect using Functor-Based Connection. |
181 | |
182 | This signal is emitted whenever the socket notifier is enabled and |
183 | a socket event corresponding to its \l {Type}{type} occurs. |
184 | |
185 | The socket identifier is passed in the \a socket parameter. |
186 | |
187 | \sa type(), socket() |
188 | */ |
189 | |
190 | /*! |
191 | \fn void QSocketNotifier::activated(QSocketDescriptor socket, QSocketNotifier::Type type) |
192 | \since 5.15 |
193 | |
194 | This signal is emitted whenever the socket notifier is enabled and |
195 | a socket event corresponding to its \a type occurs. |
196 | |
197 | The socket identifier is passed in the \a socket parameter. |
198 | |
199 | \sa type(), socket() |
200 | */ |
201 | |
202 | /*! |
203 | \since 6.1 |
204 | |
205 | Assigns the \a socket to this notifier. |
206 | |
207 | \note The notifier will be disabled as a side effect and needs |
208 | to be re-enabled. |
209 | |
210 | \sa setEnabled(), isValid() |
211 | */ |
212 | void QSocketNotifier::setSocket(qintptr socket) |
213 | { |
214 | Q_D(QSocketNotifier); |
215 | |
216 | setEnabled(false); |
217 | d->sockfd = socket; |
218 | } |
219 | |
220 | /*! |
221 | Returns the socket identifier assigned to this object. |
222 | |
223 | \sa isValid(), type() |
224 | */ |
225 | qintptr QSocketNotifier::socket() const |
226 | { |
227 | Q_D(const QSocketNotifier); |
228 | return qintptr(d->sockfd); |
229 | } |
230 | |
231 | /*! |
232 | Returns the socket event type specified to the constructor. |
233 | |
234 | \sa socket() |
235 | */ |
236 | QSocketNotifier::Type QSocketNotifier::type() const |
237 | { |
238 | Q_D(const QSocketNotifier); |
239 | return d->sntype; |
240 | } |
241 | |
242 | /*! |
243 | \since 6.1 |
244 | |
245 | Returns \c true if the notifier is valid (that is, it has |
246 | a descriptor assigned); otherwise returns \c false. |
247 | |
248 | \sa setSocket() |
249 | */ |
250 | bool QSocketNotifier::isValid() const |
251 | { |
252 | Q_D(const QSocketNotifier); |
253 | return d->sockfd.isValid(); |
254 | } |
255 | |
256 | /*! |
257 | Returns \c true if the notifier is enabled; otherwise returns \c false. |
258 | |
259 | \sa setEnabled() |
260 | */ |
261 | bool QSocketNotifier::isEnabled() const |
262 | { |
263 | Q_D(const QSocketNotifier); |
264 | return d->snenabled; |
265 | } |
266 | |
267 | /*! |
268 | If \a enable is true, the notifier is enabled; otherwise the notifier |
269 | is disabled. |
270 | |
271 | When the notifier is enabled, it emits the activated() signal whenever |
272 | a socket event corresponding to its \l{type()}{type} occurs. When it is |
273 | disabled, it ignores socket events (the same effect as not creating |
274 | the socket notifier). |
275 | |
276 | Write notifiers should normally be disabled immediately after the |
277 | activated() signal has been emitted |
278 | |
279 | \sa isEnabled(), activated() |
280 | */ |
281 | |
282 | void QSocketNotifier::setEnabled(bool enable) |
283 | { |
284 | Q_D(QSocketNotifier); |
285 | if (!d->sockfd.isValid()) |
286 | return; |
287 | if (d->snenabled == enable) // no change |
288 | return; |
289 | d->snenabled = enable; |
290 | |
291 | |
292 | auto thisThreadData = d->threadData.loadRelaxed(); |
293 | |
294 | if (!thisThreadData->hasEventDispatcher()) // perhaps application/thread is shutting down |
295 | return; |
296 | if (Q_UNLIKELY(thread() != QThread::currentThread())) { |
297 | qWarning(msg: "QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread" ); |
298 | return; |
299 | } |
300 | if (d->snenabled) |
301 | thisThreadData->eventDispatcher.loadRelaxed()->registerSocketNotifier(notifier: this); |
302 | else |
303 | thisThreadData->eventDispatcher.loadRelaxed()->unregisterSocketNotifier(notifier: this); |
304 | } |
305 | |
306 | |
307 | /*!\reimp |
308 | */ |
309 | bool QSocketNotifier::event(QEvent *e) |
310 | { |
311 | Q_D(QSocketNotifier); |
312 | // Emits the activated() signal when a QEvent::SockAct or QEvent::SockClose is |
313 | // received. |
314 | switch (e->type()) { |
315 | case QEvent::ThreadChange: |
316 | if (d->snenabled) { |
317 | QMetaObject::invokeMethod(obj: this, member: "setEnabled" , c: Qt::QueuedConnection, |
318 | Q_ARG(bool, d->snenabled)); |
319 | setEnabled(false); |
320 | } |
321 | break; |
322 | case QEvent::SockAct: |
323 | case QEvent::SockClose: |
324 | { |
325 | QPointer<QSocketNotifier> alive(this); |
326 | emit activated(socket: d->sockfd, activationEvent: d->sntype, QPrivateSignal()); |
327 | // ### Qt7: Remove emission if the activated(int) signal is removed |
328 | if (alive) |
329 | emit activated(socket: int(qintptr(d->sockfd)), QPrivateSignal()); |
330 | } |
331 | return true; |
332 | default: |
333 | break; |
334 | } |
335 | return QObject::event(event: e); |
336 | } |
337 | |
338 | /*! |
339 | \class QSocketDescriptor |
340 | \inmodule QtCore |
341 | \brief A class which holds a native socket descriptor. |
342 | \internal |
343 | |
344 | \ingroup network |
345 | \ingroup io |
346 | |
347 | \since 5.15 |
348 | |
349 | QSocketDescriptor makes it easier to handle native socket |
350 | descriptors in cross-platform code. |
351 | |
352 | On Windows it holds a \c {Qt::HANDLE} and on Unix it holds an \c int. |
353 | The class will implicitly convert between the class and the |
354 | native descriptor type. |
355 | */ |
356 | |
357 | /*! |
358 | \fn QSocketDescriptor::QSocketDescriptor(DescriptorType descriptor) |
359 | \internal |
360 | |
361 | Construct a QSocketDescriptor from a native socket \a descriptor. |
362 | */ |
363 | |
364 | /*! |
365 | \fn QSocketDescriptor::QSocketDescriptor(qintptr descriptor) |
366 | \internal |
367 | |
368 | Construct a QSocketDescriptor from a native socket \a descriptor. |
369 | |
370 | \note This constructor is only available on Windows. |
371 | */ |
372 | |
373 | /*! |
374 | \fn Qt::HANDLE QSocketDescriptor::winHandle() const noexcept |
375 | \internal |
376 | |
377 | Returns the internal handle. |
378 | |
379 | \note This function is only available on Windows. |
380 | */ |
381 | |
382 | QT_END_NAMESPACE |
383 | |
384 | #include "moc_qsocketnotifier.cpp" |
385 | |