Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qeventdispatcher_unix.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qplatformdefs.h"
6
7#include "qcoreapplication.h"
8#include "qhash.h"
9#include "qsocketnotifier.h"
10#include "qthread.h"
11
13#include <private/qthread_p.h>
14#include <private/qcoreapplication_p.h>
15#include <private/qcore_unix_p.h>
16
17#include <errno.h>
18#include <stdio.h>
19#include <stdlib.h>
20
21#if __has_include(<sys/eventfd.h>)
22# include <sys/eventfd.h>
23static constexpr bool UsingEventfd = true;
24#else
25static constexpr bool UsingEventfd = false;
26#endif
27
28#if defined(Q_OS_VXWORKS)
29# include <pipeDrv.h>
30#endif
31
32using namespace std::chrono;
33using namespace std::chrono_literals;
34
36
38{
39 switch (type) {
41 return "Read";
43 return "Write";
45 return "Exception";
46 }
47
48 Q_UNREACHABLE();
49}
50
54
56{
57 if (fds[0] >= 0)
58 close(fds[0]);
59
60 if (!UsingEventfd && fds[1] >= 0)
61 close(fds[1]);
62
63#if defined(Q_OS_VXWORKS)
64 pipeDevDelete(name, true);
65#endif
66}
67
68#if defined(Q_OS_VXWORKS)
69static void initThreadPipeFD(int fd)
70{
71 int ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
72 if (ret == -1)
73 perror("QEventDispatcherUNIXPrivate: Unable to init thread pipe");
74
75 int flags = fcntl(fd, F_GETFL);
76 if (flags == -1)
77 perror("QEventDispatcherUNIXPrivate: Unable to get flags on thread pipe");
78
79 ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
80 if (ret == -1)
81 perror("QEventDispatcherUNIXPrivate: Unable to set flags on thread pipe");
82}
83#endif
84
86{
87#if defined(Q_OS_WASM)
88 // do nothing.
89#elif defined(Q_OS_VXWORKS)
90 qsnprintf(name, sizeof(name), "/pipe/qt_%08x", int(taskIdSelf()));
91
92 // make sure there is no pipe with this name
93 pipeDevDelete(name, true);
94
95 // create the pipe
96 if (pipeDevCreate(name, 128 /*maxMsg*/, 1 /*maxLength*/) != OK) {
97 perror("QThreadPipe: Unable to create thread pipe device");
98 return false;
99 }
100
101 if ((fds[0] = open(name, O_RDWR, 0)) < 0) {
102 perror("QThreadPipe: Unable to open pipe device");
103 return false;
104 }
105
106 initThreadPipeFD(fds[0]);
107 fds[1] = fds[0];
108#else
109 int ret;
110# ifdef EFD_CLOEXEC
111 ret = fds[0] = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
112# endif
113 if (!UsingEventfd)
114 ret = qt_safe_pipe(fds, O_NONBLOCK);
115 if (ret == -1) {
116 perror("QThreadPipe: Unable to create pipe");
117 return false;
118 }
119#endif
120
121 return true;
122}
123
125{
126 return qt_make_pollfd(fds[0], POLLIN);
127}
128
130{
131 if ((wakeUps.fetchAndOrAcquire(1) & 1) == 0) {
132# ifdef EFD_CLOEXEC
133 eventfd_write(fds[0], 1);
134 return;
135#endif
136 char c = 0;
137 qt_safe_write(fds[1], &c, 1);
138 }
139}
140
141int QThreadPipe::check(const pollfd &pfd)
142{
143 Q_ASSERT(pfd.fd == fds[0]);
144
145 char c[16];
146 const int readyread = pfd.revents & POLLIN;
147
148 if (readyread) {
149 // consume the data on the thread pipe so that
150 // poll doesn't immediately return next time
151#if defined(Q_OS_VXWORKS)
152 ::read(fds[0], c, sizeof(c));
153 ::ioctl(fds[0], FIOFLUSH, 0);
154#else
155# ifdef EFD_CLOEXEC
156 eventfd_t value;
157 eventfd_read(fds[0], &value);
158# endif
159 if (!UsingEventfd) {
160 while (::read(fds[0], c, sizeof(c)) > 0) {}
161 }
162#endif
163
164 if (!wakeUps.testAndSetRelease(1, 0)) {
165 // hopefully, this is dead code
166 qWarning("QThreadPipe: internal error, wakeUps.testAndSetRelease(1, 0) failed!");
167 }
168 }
169
170 return readyread;
171}
172
174{
175 if (Q_UNLIKELY(threadPipe.init() == false))
176 qFatal("QEventDispatcherUNIXPrivate(): Cannot continue without a thread pipe");
177}
178
184
194
199
201{
202 for (const pollfd &pfd : std::as_const(pollfds)) {
203 if (pfd.fd < 0 || pfd.revents == 0)
204 continue;
205
206 auto it = socketNotifiers.find(pfd.fd);
208
209 const QSocketNotifierSetUNIX &sn_set = it.value();
210
211 static const struct {
213 short flags;
214 } notifiers[] = {
215 { QSocketNotifier::Read, POLLIN | POLLHUP | POLLERR },
216 { QSocketNotifier::Write, POLLOUT | POLLHUP | POLLERR },
217 { QSocketNotifier::Exception, POLLPRI | POLLHUP | POLLERR }
218 };
219
220 for (const auto &n : notifiers) {
221 QSocketNotifier *notifier = sn_set.notifiers[n.type];
222
223 if (!notifier)
224 continue;
225
226 if (pfd.revents & POLLNVAL) {
227 qWarning("QSocketNotifier: Invalid socket %d with type %s, disabling...",
228 it.key(), socketType(n.type));
229 notifier->setEnabled(false);
230 }
231
232 if (pfd.revents & n.flags)
234 }
235 }
236
237 pollfds.clear();
238}
239
241{
243
245 return 0;
246
247 int n_activated = 0;
249
250 while (!pendingNotifiers.isEmpty()) {
253 ++n_activated;
254 }
255
256 return n_activated;
257}
258
262
266
269
274{
275#ifndef QT_NO_DEBUG
276 if (qToUnderlying(timerId) < 1 || interval.count() < 0 || !obj) {
277 qWarning("QEventDispatcherUNIX::registerTimer: invalid arguments");
278 return;
279 } else if (obj->thread() != thread() || thread() != QThread::currentThread()) {
280 qWarning("QEventDispatcherUNIX::registerTimer: timers cannot be started from another thread");
281 return;
282 }
283#endif
284
286 d->timerList.registerTimer(timerId, interval, timerType, obj);
287}
288
293{
294#ifndef QT_NO_DEBUG
295 if (qToUnderlying(timerId) < 1) {
296 qWarning("QEventDispatcherUNIX::unregisterTimer: invalid argument");
297 return false;
298 } else if (thread() != QThread::currentThread()) {
299 qWarning("QEventDispatcherUNIX::unregisterTimer: timers cannot be stopped from another thread");
300 return false;
301 }
302#endif
303
305 return d->timerList.unregisterTimer(timerId);
306}
307
312{
313#ifndef QT_NO_DEBUG
314 if (!object) {
315 qWarning("QEventDispatcherUNIX::unregisterTimers: invalid argument");
316 return false;
317 } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
318 qWarning("QEventDispatcherUNIX::unregisterTimers: timers cannot be stopped from another thread");
319 return false;
320 }
321#endif
322
324 return d->timerList.unregisterTimers(object);
325}
326
327QList<QEventDispatcherUNIX::TimerInfoV2>
329{
330 if (!object) {
331 qWarning("QEventDispatcherUNIX:registeredTimers: invalid argument");
332 return QList<TimerInfoV2>();
333 }
334
335 Q_D(const QEventDispatcherUNIX);
336 return d->timerList.registeredTimers(object);
337}
338
339/*****************************************************************************
340 QEventDispatcher implementations for UNIX
341 *****************************************************************************/
342
344{
346 int sockfd = notifier->socket();
348#ifndef QT_NO_DEBUG
349 if (notifier->thread() != thread() || thread() != QThread::currentThread()) {
350 qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread");
351 return;
352 }
353#endif
354
356 QSocketNotifierSetUNIX &sn_set = d->socketNotifiers[sockfd];
357
358 if (sn_set.notifiers[type] && sn_set.notifiers[type] != notifier)
359 qWarning("%s: Multiple socket notifiers for same socket %d and type %s",
360 Q_FUNC_INFO, sockfd, socketType(type));
361
362 sn_set.notifiers[type] = notifier;
363}
364
366{
368 int sockfd = notifier->socket();
370#ifndef QT_NO_DEBUG
371 if (notifier->thread() != thread() || thread() != QThread::currentThread()) {
372 qWarning("QSocketNotifier: socket notifier (fd %d) cannot be disabled from another thread.\n"
373 "(Notifier's thread is %s(%p), event dispatcher's thread is %s(%p), current thread is %s(%p))",
374 sockfd,
375 notifier->thread() ? notifier->thread()->metaObject()->className() : "QThread", notifier->thread(),
376 thread() ? thread()->metaObject()->className() : "QThread", thread(),
377 QThread::currentThread() ? QThread::currentThread()->metaObject()->className() : "QThread", QThread::currentThread());
378 return;
379 }
380#endif
381
383
384 d->pendingNotifiers.removeOne(notifier);
385
386 auto i = d->socketNotifiers.find(sockfd);
387 if (i == d->socketNotifiers.end())
388 return;
389
390 QSocketNotifierSetUNIX &sn_set = i.value();
391
392 if (sn_set.notifiers[type] == nullptr)
393 return;
394
395 if (sn_set.notifiers[type] != notifier) {
396 qWarning("%s: Multiple socket notifiers for same socket %d and type %s",
397 Q_FUNC_INFO, sockfd, socketType(type));
398 return;
399 }
400
401 sn_set.notifiers[type] = nullptr;
402
403 if (sn_set.isEmpty())
404 d->socketNotifiers.erase(i);
405}
406
407bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags)
408{
410 d->interrupt.storeRelaxed(0);
411
412 // we are awake, broadcast it
413 emit awake();
414
415 auto threadData = d->threadData.loadRelaxed();
416 QCoreApplicationPrivate::sendPostedEvents(nullptr, 0, threadData);
417
418 const bool include_timers = (flags & QEventLoop::X11ExcludeTimers) == 0;
419 const bool include_notifiers = (flags & QEventLoop::ExcludeSocketNotifiers) == 0;
420 const bool wait_for_events = (flags & QEventLoop::WaitForMoreEvents) != 0;
421
422 const bool canWait = (threadData->canWaitLocked()
423 && !d->interrupt.loadRelaxed()
424 && wait_for_events);
425
426 if (canWait)
428
429 if (d->interrupt.loadRelaxed())
430 return false;
431
433 if (canWait) {
434 if (include_timers) {
435 std::optional<nanoseconds> remaining = d->timerList.timerWait();
436 deadline = remaining ? QDeadlineTimer{*remaining}
438 } else {
440 }
441 } else {
442 // Using the default-constructed `deadline`, which is already expired,
443 // ensures the code in the do-while loop in qt_safe_poll runs at least once.
444 }
445
446 d->pollfds.clear();
447 d->pollfds.reserve(1 + (include_notifiers ? d->socketNotifiers.size() : 0));
448
449 if (include_notifiers)
450 for (auto it = d->socketNotifiers.cbegin(); it != d->socketNotifiers.cend(); ++it)
451 d->pollfds.append(qt_make_pollfd(it.key(), it.value().events()));
452
453 // This must be last, as it's popped off the end below
454 d->pollfds.append(d->threadPipe.prepare());
455
456 int nevents = 0;
457 switch (qt_safe_poll(d->pollfds.data(), d->pollfds.size(), deadline)) {
458 case -1:
459 qErrnoWarning("qt_safe_poll");
460 if (QT_CONFIG(poll_exit_on_error))
461 abort();
462 break;
463 case 0:
464 break;
465 default:
466 nevents += d->threadPipe.check(d->pollfds.takeLast());
467 if (include_notifiers)
468 nevents += d->activateSocketNotifiers();
469 break;
470 }
471
472 if (include_timers)
473 nevents += d->activateTimers();
474
475 // return true if we handled events, false otherwise
476 return (nevents > 0);
477}
478
480{
481#ifndef QT_NO_DEBUG
482 if (int(timerId) < 1) {
483 qWarning("QEventDispatcherUNIX::remainingTime: invalid argument");
484 return Duration::min();
485 }
486#endif
487
488 Q_D(const QEventDispatcherUNIX);
489 return d->timerList.remainingDuration(timerId);
490}
491
493{
495 d->threadPipe.wakeUp();
496}
497
499{
501 d->interrupt.storeRelaxed(1);
502 wakeUp();
503}
504
506
507#include "moc_qeventdispatcher_unix_p.cpp"
DarwinBluetooth::LECBManagerNotifier * notifier
void aboutToBlock()
This signal is emitted before the event loop calls a function that could block.
std::chrono::nanoseconds Duration
A {std::chrono::duration} type that is used in various API in this class.
void awake()
This signal is emitted after the event loop returns from a function that could block.
T fetchAndOrAcquire(T valueToAdd) noexcept
bool testAndSetRelease(T expectedValue, T newValue) noexcept
static void sendPostedEvents(QObject *receiver, int event_type, QThreadData *data)
static bool sendEvent(QObject *receiver, QEvent *event)
Sends event event directly to receiver receiver, using the notify() function.
\inmodule QtCore
static constexpr ForeverConstant Forever
QHash< int, QSocketNotifierSetUNIX > socketNotifiers
QList< QSocketNotifier * > pendingNotifiers
void setSocketNotifierPending(QSocketNotifier *notifier)
QEventDispatcherUNIX(QObject *parent=nullptr)
Duration remainingTime(Qt::TimerId timerId) const override final
Returns the remaining time of the timer with the given timerId.
void registerTimer(Qt::TimerId timerId, Duration interval, Qt::TimerType timerType, QObject *object) override final
bool processEvents(QEventLoop::ProcessEventsFlags flags) override
Processes pending events that match flags until there are no more events to process.
void interrupt() final
Interrupts event dispatching.
void wakeUp() override
\threadsafe
void registerSocketNotifier(QSocketNotifier *notifier) final
Registers notifier with the event loop.
void unregisterSocketNotifier(QSocketNotifier *notifier) final
Unregisters notifier from the event dispatcher.
bool unregisterTimer(Qt::TimerId timerId) override final
QList< TimerInfoV2 > timersForObject(QObject *object) const override final
bool unregisterTimers(QObject *object) override final
@ ExcludeSocketNotifiers
Definition qeventloop.h:28
@ X11ExcludeTimers
Definition qeventloop.h:30
@ WaitForMoreEvents
Definition qeventloop.h:29
\inmodule QtCore
Definition qcoreevent.h:45
@ SockAct
Definition qcoreevent.h:98
iterator find(const Key &key)
Returns an iterator pointing to the item with the key in the hash.
Definition qhash.h:1291
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1216
bool isEmpty() const noexcept
Definition qlist.h:401
value_type takeFirst()
Definition qlist.h:566
void clear()
Definition qlist.h:434
\inmodule QtCore
Definition qobject.h:103
QThread * thread() const
Returns the thread in which the object lives.
Definition qobject.cpp:1598
const_iterator cend() const noexcept
Definition qset.h:142
const_iterator cbegin() const noexcept
Definition qset.h:138
\inmodule QtCore
Type
This enum describes the various types of events that a socket notifier can recognize.
static QThread * currentThread()
Definition qthread.cpp:1039
EGLint EGLint EGLint EGLint int int int int * fds
QSet< QString >::iterator it
void qErrnoWarning(const char *msg,...)
Combined button and popup list for selecting options.
TimerType
Q_CORE_EXPORT int qsnprintf(char *str, size_t n, const char *fmt,...)
#define Q_UNLIKELY(x)
#define Q_FUNC_INFO
int qt_safe_poll(struct pollfd *fds, nfds_t nfds, QDeadlineTimer deadline)
static qint64 qt_safe_write(int fd, const void *data, qint64 len)
static struct pollfd qt_make_pollfd(int fd, short events)
static int qt_safe_pipe(int pipefd[2], int flags=0)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
static constexpr bool UsingEventfd
static QT_BEGIN_NAMESPACE const char * socketType(QSocketNotifier::Type type)
#define qWarning
Definition qlogging.h:166
#define qFatal
Definition qlogging.h:168
return ret
GLuint object
[3]
GLenum type
GLbitfield flags
GLuint64 GLenum GLint fd
GLuint name
GLfloat n
struct _cl_event * event
GLhandleARB obj
[2]
const GLubyte * c
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QT_CONFIG(feature)
#define emit
QT_BEGIN_NAMESPACE constexpr std::underlying_type_t< Enum > qToUnderlying(Enum e) noexcept
ReturnedValue read(const char *data)
file open(QIODevice::ReadOnly)
QDeadlineTimer deadline(30s)
bool contains(const AT &t) const noexcept
Definition qlist.h:45
int check(const pollfd &pfd)
pollfd prepare() const