Qt 6.x
The Qt SDK
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 "qpair.h"
9#include "qhash.h"
10#include "qsocketnotifier.h"
11#include "qthread.h"
12
14#include <private/qthread_p.h>
15#include <private/qcoreapplication_p.h>
16#include <private/qcore_unix_p.h>
17
18#include <errno.h>
19#include <stdio.h>
20#include <stdlib.h>
21
22#ifndef QT_NO_EVENTFD
23# include <sys/eventfd.h>
24#endif
25
26// VxWorks doesn't correctly set the _POSIX_... options
27#if defined(Q_OS_VXWORKS)
28# if defined(_POSIX_MONOTONIC_CLOCK) && (_POSIX_MONOTONIC_CLOCK <= 0)
29# undef _POSIX_MONOTONIC_CLOCK
30# define _POSIX_MONOTONIC_CLOCK 1
31# endif
32# include <pipeDrv.h>
33# include <sys/time.h>
34#endif
35
36#if (_POSIX_MONOTONIC_CLOCK-0 <= 0) || defined(QT_BOOTSTRAPPED)
37# include <sys/times.h>
38#endif
39
41
43{
44 switch (type) {
46 return "Read";
48 return "Write";
50 return "Exception";
51 }
52
53 Q_UNREACHABLE();
54}
55
57{
58 fds[0] = -1;
59 fds[1] = -1;
60#if defined(Q_OS_VXWORKS)
61 name[0] = '\0';
62#endif
63}
64
66{
67 if (fds[0] >= 0)
68 close(fds[0]);
69
70 if (fds[1] >= 0)
71 close(fds[1]);
72
73#if defined(Q_OS_VXWORKS)
74 pipeDevDelete(name, true);
75#endif
76}
77
78#if defined(Q_OS_VXWORKS)
79static void initThreadPipeFD(int fd)
80{
81 int ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
82 if (ret == -1)
83 perror("QEventDispatcherUNIXPrivate: Unable to init thread pipe");
84
85 int flags = fcntl(fd, F_GETFL);
86 if (flags == -1)
87 perror("QEventDispatcherUNIXPrivate: Unable to get flags on thread pipe");
88
89 ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
90 if (ret == -1)
91 perror("QEventDispatcherUNIXPrivate: Unable to set flags on thread pipe");
92}
93#endif
94
96{
97#if defined(Q_OS_WASM)
98 // do nothing.
99#elif defined(Q_OS_VXWORKS)
100 qsnprintf(name, sizeof(name), "/pipe/qt_%08x", int(taskIdSelf()));
101
102 // make sure there is no pipe with this name
103 pipeDevDelete(name, true);
104
105 // create the pipe
106 if (pipeDevCreate(name, 128 /*maxMsg*/, 1 /*maxLength*/) != OK) {
107 perror("QThreadPipe: Unable to create thread pipe device %s", name);
108 return false;
109 }
110
111 if ((fds[0] = open(name, O_RDWR, 0)) < 0) {
112 perror("QThreadPipe: Unable to open pipe device %s", name);
113 return false;
114 }
115
116 initThreadPipeFD(fds[0]);
117 fds[1] = fds[0];
118#else
119# ifndef QT_NO_EVENTFD
120 if ((fds[0] = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC)) >= 0)
121 return true;
122# endif
123 if (qt_safe_pipe(fds, O_NONBLOCK) == -1) {
124 perror("QThreadPipe: Unable to create pipe");
125 return false;
126 }
127#endif
128
129 return true;
130}
131
133{
134 return qt_make_pollfd(fds[0], POLLIN);
135}
136
138{
139 if (wakeUps.testAndSetAcquire(0, 1)) {
140#ifndef QT_NO_EVENTFD
141 if (fds[1] == -1) {
142 // eventfd
143 eventfd_t value = 1;
144 int ret;
145 EINTR_LOOP(ret, eventfd_write(fds[0], value));
146 return;
147 }
148#endif
149 char c = 0;
150 qt_safe_write(fds[1], &c, 1);
151 }
152}
153
154int QThreadPipe::check(const pollfd &pfd)
155{
156 Q_ASSERT(pfd.fd == fds[0]);
157
158 char c[16];
159 const int readyread = pfd.revents & POLLIN;
160
161 if (readyread) {
162 // consume the data on the thread pipe so that
163 // poll doesn't immediately return next time
164#if defined(Q_OS_VXWORKS)
165 ::read(fds[0], c, sizeof(c));
166 ::ioctl(fds[0], FIOFLUSH, 0);
167#else
168# ifndef QT_NO_EVENTFD
169 if (fds[1] == -1) {
170 // eventfd
171 eventfd_t value;
172 eventfd_read(fds[0], &value);
173 } else
174# endif
175 {
176 while (::read(fds[0], c, sizeof(c)) > 0) {}
177 }
178#endif
179
180 if (!wakeUps.testAndSetRelease(1, 0)) {
181 // hopefully, this is dead code
182 qWarning("QThreadPipe: internal error, wakeUps.testAndSetRelease(1, 0) failed!");
183 }
184 }
185
186 return readyread;
187}
188
190{
191 if (Q_UNLIKELY(threadPipe.init() == false))
192 qFatal("QEventDispatcherUNIXPrivate(): Cannot continue without a thread pipe");
193}
194
196{
197 // cleanup timers
199}
200
202{
204
206 return;
207
209}
210
212{
213 return timerList.activateTimers();
214}
215
217{
218 for (const pollfd &pfd : std::as_const(pollfds)) {
219 if (pfd.fd < 0 || pfd.revents == 0)
220 continue;
221
222 auto it = socketNotifiers.find(pfd.fd);
224
225 const QSocketNotifierSetUNIX &sn_set = it.value();
226
227 static const struct {
229 short flags;
230 } notifiers[] = {
231 { QSocketNotifier::Read, POLLIN | POLLHUP | POLLERR },
232 { QSocketNotifier::Write, POLLOUT | POLLHUP | POLLERR },
233 { QSocketNotifier::Exception, POLLPRI | POLLHUP | POLLERR }
234 };
235
236 for (const auto &n : notifiers) {
237 QSocketNotifier *notifier = sn_set.notifiers[n.type];
238
239 if (!notifier)
240 continue;
241
242 if (pfd.revents & POLLNVAL) {
243 qWarning("QSocketNotifier: Invalid socket %d with type %s, disabling...",
244 it.key(), socketType(n.type));
245 notifier->setEnabled(false);
246 }
247
248 if (pfd.revents & n.flags)
250 }
251 }
252
253 pollfds.clear();
254}
255
257{
259
261 return 0;
262
263 int n_activated = 0;
265
266 while (!pendingNotifiers.isEmpty()) {
269 ++n_activated;
270 }
271
272 return n_activated;
273}
274
277{ }
278
281{ }
282
284{ }
285
290{
291#ifndef QT_NO_DEBUG
292 if (timerId < 1 || interval < 0 || !obj) {
293 qWarning("QEventDispatcherUNIX::registerTimer: invalid arguments");
294 return;
295 } else if (obj->thread() != thread() || thread() != QThread::currentThread()) {
296 qWarning("QEventDispatcherUNIX::registerTimer: timers cannot be started from another thread");
297 return;
298 }
299#endif
300
302 d->timerList.registerTimer(timerId, std::chrono::milliseconds{ interval }, timerType, obj);
303}
304
309{
310#ifndef QT_NO_DEBUG
311 if (timerId < 1) {
312 qWarning("QEventDispatcherUNIX::unregisterTimer: invalid argument");
313 return false;
314 } else if (thread() != QThread::currentThread()) {
315 qWarning("QEventDispatcherUNIX::unregisterTimer: timers cannot be stopped from another thread");
316 return false;
317 }
318#endif
319
321 return d->timerList.unregisterTimer(timerId);
322}
323
328{
329#ifndef QT_NO_DEBUG
330 if (!object) {
331 qWarning("QEventDispatcherUNIX::unregisterTimers: invalid argument");
332 return false;
333 } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
334 qWarning("QEventDispatcherUNIX::unregisterTimers: timers cannot be stopped from another thread");
335 return false;
336 }
337#endif
338
340 return d->timerList.unregisterTimers(object);
341}
342
345{
346 if (!object) {
347 qWarning("QEventDispatcherUNIX:registeredTimers: invalid argument");
348 return QList<TimerInfo>();
349 }
350
351 Q_D(const QEventDispatcherUNIX);
352 return d->timerList.registeredTimers(object);
353}
354
355/*****************************************************************************
356 QEventDispatcher implementations for UNIX
357 *****************************************************************************/
358
360{
362 int sockfd = notifier->socket();
364#ifndef QT_NO_DEBUG
365 if (notifier->thread() != thread() || thread() != QThread::currentThread()) {
366 qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread");
367 return;
368 }
369#endif
370
372 QSocketNotifierSetUNIX &sn_set = d->socketNotifiers[sockfd];
373
374 if (sn_set.notifiers[type] && sn_set.notifiers[type] != notifier)
375 qWarning("%s: Multiple socket notifiers for same socket %d and type %s",
376 Q_FUNC_INFO, sockfd, socketType(type));
377
378 sn_set.notifiers[type] = notifier;
379}
380
382{
384 int sockfd = notifier->socket();
386#ifndef QT_NO_DEBUG
387 if (notifier->thread() != thread() || thread() != QThread::currentThread()) {
388 qWarning("QSocketNotifier: socket notifier (fd %d) cannot be disabled from another thread.\n"
389 "(Notifier's thread is %s(%p), event dispatcher's thread is %s(%p), current thread is %s(%p))",
390 sockfd,
391 notifier->thread() ? notifier->thread()->metaObject()->className() : "QThread", notifier->thread(),
392 thread() ? thread()->metaObject()->className() : "QThread", thread(),
393 QThread::currentThread() ? QThread::currentThread()->metaObject()->className() : "QThread", QThread::currentThread());
394 return;
395 }
396#endif
397
399
400 d->pendingNotifiers.removeOne(notifier);
401
402 auto i = d->socketNotifiers.find(sockfd);
403 if (i == d->socketNotifiers.end())
404 return;
405
406 QSocketNotifierSetUNIX &sn_set = i.value();
407
408 if (sn_set.notifiers[type] == nullptr)
409 return;
410
411 if (sn_set.notifiers[type] != notifier) {
412 qWarning("%s: Multiple socket notifiers for same socket %d and type %s",
413 Q_FUNC_INFO, sockfd, socketType(type));
414 return;
415 }
416
417 sn_set.notifiers[type] = nullptr;
418
419 if (sn_set.isEmpty())
420 d->socketNotifiers.erase(i);
421}
422
423bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags)
424{
426 d->interrupt.storeRelaxed(0);
427
428 // we are awake, broadcast it
429 emit awake();
430
431 auto threadData = d->threadData.loadRelaxed();
432 QCoreApplicationPrivate::sendPostedEvents(nullptr, 0, threadData);
433
434 const bool include_timers = (flags & QEventLoop::X11ExcludeTimers) == 0;
435 const bool include_notifiers = (flags & QEventLoop::ExcludeSocketNotifiers) == 0;
436 const bool wait_for_events = (flags & QEventLoop::WaitForMoreEvents) != 0;
437
438 const bool canWait = (threadData->canWaitLocked()
439 && !d->interrupt.loadRelaxed()
440 && wait_for_events);
441
442 if (canWait)
444
445 if (d->interrupt.loadRelaxed())
446 return false;
447
448 timespec *tm = nullptr;
449 timespec wait_tm = { 0, 0 };
450
451 if (!canWait || (include_timers && d->timerList.timerWait(wait_tm)))
452 tm = &wait_tm;
453
454 d->pollfds.clear();
455 d->pollfds.reserve(1 + (include_notifiers ? d->socketNotifiers.size() : 0));
456
457 if (include_notifiers)
458 for (auto it = d->socketNotifiers.cbegin(); it != d->socketNotifiers.cend(); ++it)
459 d->pollfds.append(qt_make_pollfd(it.key(), it.value().events()));
460
461 // This must be last, as it's popped off the end below
462 d->pollfds.append(d->threadPipe.prepare());
463
464 int nevents = 0;
465
466 switch (qt_safe_poll(d->pollfds.data(), d->pollfds.size(), tm)) {
467 case -1:
468 qErrnoWarning("qt_safe_poll");
469 if (QT_CONFIG(poll_exit_on_error))
470 abort();
471 break;
472 case 0:
473 break;
474 default:
475 nevents += d->threadPipe.check(d->pollfds.takeLast());
476 if (include_notifiers)
477 nevents += d->activateSocketNotifiers();
478 break;
479 }
480
481 if (include_timers)
482 nevents += d->activateTimers();
483
484 // return true if we handled events, false otherwise
485 return (nevents > 0);
486}
487
489{
490#ifndef QT_NO_DEBUG
491 if (timerId < 1) {
492 qWarning("QEventDispatcherUNIX::remainingTime: invalid argument");
493 return -1;
494 }
495#endif
496
498 return d->timerList.timerRemainingTime(timerId);
499}
500
502{
504 d->threadPipe.wakeUp();
505}
506
508{
510 d->interrupt.storeRelaxed(1);
511 wakeUp();
512}
513
515
516#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.
void awake()
This signal is emitted after the event loop returns from a function that could block.
bool testAndSetAcquire(T expectedValue, T newValue) 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.
QHash< int, QSocketNotifierSetUNIX > socketNotifiers
QList< QSocketNotifier * > pendingNotifiers
void setSocketNotifierPending(QSocketNotifier *notifier)
QEventDispatcherUNIX(QObject *parent=nullptr)
bool unregisterTimers(QObject *object) final
bool processEvents(QEventLoop::ProcessEventsFlags flags) override
Processes pending events that match flags until there are no more events to process.
void registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object) final
bool unregisterTimer(int timerId) final
void interrupt() final
Interrupts event dispatching.
QList< TimerInfo > registeredTimers(QObject *object) const final
Returns a list of registered timers for object.
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.
int remainingTime(int timerId) final
Returns the remaining time in milliseconds with the given timerId.
@ 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:1258
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1206
Definition qlist.h:74
bool isEmpty() const noexcept
Definition qlist.h:390
value_type takeFirst()
Definition qlist.h:549
void clear()
Definition qlist.h:417
\inmodule QtCore
Definition qobject.h:90
QThread * thread() const
Returns the thread in which the object lives.
Definition qobject.cpp:1561
\inmodule QtCore
Type
This enum describes the various types of events that a socket notifier can recognize.
static QThread * currentThread()
Definition qthread.cpp:966
EGLint EGLint EGLint EGLint int int int int * fds
qDeleteAll(list.begin(), list.end())
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, const struct timespec *timeout_ts)
static qint64 qt_safe_write(int fd, const void *data, qint64 len)
static struct pollfd qt_make_pollfd(int fd, short events)
#define EINTR_LOOP(var, cmd)
static int qt_safe_pipe(int pipefd[2], int flags=0)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
static QT_BEGIN_NAMESPACE const char * socketType(QSocketNotifier::Type type)
#define qWarning
Definition qlogging.h:162
#define qFatal
Definition qlogging.h:164
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
long long qint64
Definition qtypes.h:55
ReturnedValue read(const char *data)
file open(QIODevice::ReadOnly)
bool contains(const AT &t) const noexcept
Definition qlist.h:44
QSocketNotifier * notifiers[3]
bool isEmpty() const noexcept
int check(const pollfd &pfd)
pollfd prepare() const
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent