Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
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// Qt-Security score:significant reason:default
5
6#include "qplatformdefs.h"
7
9#include "qhash.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 <cstdio>
19
20#include <errno.h>
21#include <stdio.h>
22#include <stdlib.h>
23
24#if __has_include(<sys/eventfd.h>)
25# include <sys/eventfd.h>
26static constexpr bool UsingEventfd = true;
27#else
28static constexpr bool UsingEventfd = false;
29#endif
30
31#if defined(Q_OS_VXWORKS)
32# include <taskLib.h>
33#if QT_CONFIG(vxpipedrv)
34# include "qbytearray.h"
35# include "qdatetime.h"
36# include "qdir.h" // to get application name
37# include "qrandom.h"
38# include <pipeDrv.h>
39# include <selectLib.h>
40# include <rtpLib.h>
41# include <sysLib.h>
42#endif
43#endif
44
45using namespace std::chrono;
46using namespace std::chrono_literals;
47
49
50static const char *socketType(QSocketNotifier::Type type)
51{
52 switch (type) {
53 case QSocketNotifier::Read:
54 return "Read";
55 case QSocketNotifier::Write:
56 return "Write";
57 case QSocketNotifier::Exception:
58 return "Exception";
59 }
60
61 Q_UNREACHABLE();
62}
63
67
69{
70 if (fds[0] >= 0)
71 close(fds[0]);
72
73 if (!UsingEventfd && fds[1] >= 0)
74 close(fds[1]);
75
76#if defined(Q_OS_VXWORKS) && QT_CONFIG(vxpipedrv)
77 pipeDevDelete(name, true);
78#endif
79}
80
81#if defined(Q_OS_VXWORKS) && QT_CONFIG(vxpipedrv)
82static void initThreadPipeFD(int fd)
83{
84 int ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
85 if (ret == -1)
86 perror("QEventDispatcherUNIXPrivate: Unable to init thread pipe");
87
88 int flags = fcntl(fd, F_GETFL);
89 if (flags == -1)
90 perror("QEventDispatcherUNIXPrivate: Unable to get flags on thread pipe");
91
92 ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
93 if (ret == -1)
94 perror("QEventDispatcherUNIXPrivate: Unable to set flags on thread pipe");
95}
96#endif
97
99{
100#if defined(Q_OS_WASM)
101 // do nothing.
102#elif defined(Q_OS_VXWORKS) && QT_CONFIG(vxpipedrv)
103 RTP_DESC rtpStruct;
104 rtpInfoGet((RTP_ID)NULL, &rtpStruct);
105
106 QByteArray pipeName("/pipe/qevloop_");
107 QByteArray path(rtpStruct.pathName);
108 pipeName.append(path.mid(path.lastIndexOf(QDir::separator().toLatin1())+1, path.size()));
109 pipeName.append("_");
110 pipeName.append(QByteArray::number((uint)rtpStruct.entrAddr, 16));
111 pipeName.append("_");
112 pipeName.append(QByteArray::number((uint)QThread::currentThreadId(), 16));
113 pipeName.append("_");
114 QRandomGenerator rg(QTime::currentTime().msecsSinceStartOfDay());
115 pipeName.append(QByteArray::number(rg.generate()));
116
117 // make sure there is no pipe with this name
118 pipeDevDelete(pipeName, true);
119
120 // create the pipe
121 if (pipeDevCreate(pipeName, 128 /*maxMsg*/, 1 /*maxLength*/) != OK) {
122 qCritical("QThreadPipe: Unable to create thread pipe device %s : %s", name, std::strerror(errno));
123 return false;
124 }
125
126 if ((fds[0] = open(pipeName, O_RDWR, 0)) < 0) {
127 qCritical("QThreadPipe: Unable to open pipe device %s : %s", name, std::strerror(errno));
128 return false;
129 }
130
131 initThreadPipeFD(fds[0]);
132 fds[1] = fds[0];
133#else
134 int ret;
135# ifdef EFD_CLOEXEC
136 ret = fds[0] = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
137# endif
138 if (!UsingEventfd)
139 ret = qt_safe_pipe(fds, O_NONBLOCK);
140 if (ret == -1) {
141 perror("QThreadPipe: Unable to create pipe");
142 return false;
143 }
144#endif
145
146 return true;
147}
148
150{
151 return qt_make_pollfd(fds[0], POLLIN);
152}
153
155{
156 if ((wakeUps.fetchAndOrAcquire(1) & 1) == 0) {
157# ifdef EFD_CLOEXEC
158 eventfd_write(fds[0], 1);
159 return;
160#endif
161 char c = 0;
162 qt_safe_write(fds[1], &c, 1);
163 }
164}
165
166int QThreadPipe::check(const pollfd &pfd)
167{
168 Q_ASSERT(pfd.fd == fds[0]);
169
170 char c[16];
171 const int readyread = pfd.revents & POLLIN;
172
173 if (readyread) {
174 // consume the data on the thread pipe so that
175 // poll doesn't immediately return next time
176#if defined(Q_OS_VXWORKS) && QT_CONFIG(vxpipedrv)
177 ::read(fds[0], c, sizeof(c));
178 ::ioctl(fds[0], FIOFLUSH, 0);
179#else
180# ifdef EFD_CLOEXEC
181 eventfd_t value;
182 eventfd_read(fds[0], &value);
183# endif
184 if (!UsingEventfd) {
185 while (::read(fds[0], c, sizeof(c)) > 0) {}
186 }
187#endif
188
189 if (!wakeUps.testAndSetRelease(1, 0)) {
190 // hopefully, this is dead code
191 qWarning("QThreadPipe: internal error, wakeUps.testAndSetRelease(1, 0) failed!");
192 }
193 }
194
195 return readyread;
196}
197
198QEventDispatcherUNIXPrivate::QEventDispatcherUNIXPrivate()
199{
200 if (Q_UNLIKELY(threadPipe.init() == false))
201 qFatal("QEventDispatcherUNIXPrivate(): Cannot continue without a thread pipe");
202}
203
204QEventDispatcherUNIXPrivate::~QEventDispatcherUNIXPrivate()
205{
206 // cleanup timers
207 timerList.clearTimers();
208}
209
210void QEventDispatcherUNIXPrivate::setSocketNotifierPending(QSocketNotifier *notifier)
211{
212 Q_ASSERT(notifier);
213
214 if (pendingNotifiers.contains(notifier))
215 return;
216
217 pendingNotifiers << notifier;
218}
219
220int QEventDispatcherUNIXPrivate::activateTimers()
221{
222 return timerList.activateTimers();
223}
224
225void QEventDispatcherUNIXPrivate::markPendingSocketNotifiers()
226{
227 for (const pollfd &pfd : std::as_const(pollfds)) {
228 if (pfd.fd < 0 || pfd.revents == 0)
229 continue;
230
231 auto it = socketNotifiers.find(pfd.fd);
232 Q_ASSERT(it != socketNotifiers.end());
233
234 const QSocketNotifierSetUNIX &sn_set = it.value();
235
236 static const struct {
237 QSocketNotifier::Type type;
238 short flags;
239 } notifiers[] = {
240 { QSocketNotifier::Read, POLLIN | POLLHUP | POLLERR },
241 { QSocketNotifier::Write, POLLOUT | POLLHUP | POLLERR },
242 { QSocketNotifier::Exception, POLLPRI | POLLHUP | POLLERR }
243 };
244
245 for (const auto &n : notifiers) {
246 QSocketNotifier *notifier = sn_set.notifiers[n.type];
247
248 if (!notifier)
249 continue;
250
251 if (pfd.revents & POLLNVAL) {
252 qWarning("QSocketNotifier: Invalid socket %d with type %s, disabling...",
253 it.key(), socketType(n.type));
254 notifier->setEnabled(false);
255 }
256
257 if (pfd.revents & n.flags)
258 setSocketNotifierPending(notifier);
259 }
260 }
261
262 pollfds.clear();
263}
264
265int QEventDispatcherUNIXPrivate::activateSocketNotifiers()
266{
267 markPendingSocketNotifiers();
268
269 if (pendingNotifiers.isEmpty())
270 return 0;
271
272 int n_activated = 0;
273 QEvent event(QEvent::SockAct);
274
275 while (!pendingNotifiers.isEmpty()) {
276 QSocketNotifier *notifier = pendingNotifiers.takeFirst();
277 QCoreApplication::sendEvent(notifier, &event);
278 ++n_activated;
279 }
280
281 return n_activated;
282}
283
284QEventDispatcherUNIX::QEventDispatcherUNIX(QObject *parent)
285 : QAbstractEventDispatcherV2(*new QEventDispatcherUNIXPrivate, parent)
286{ }
287
288QEventDispatcherUNIX::QEventDispatcherUNIX(QEventDispatcherUNIXPrivate &dd, QObject *parent)
289 : QAbstractEventDispatcherV2(dd, parent)
290{ }
291
292QEventDispatcherUNIX::~QEventDispatcherUNIX()
293{ }
294
295/*!
296 \internal
297*/
298void QEventDispatcherUNIX::registerTimer(Qt::TimerId timerId, Duration interval, Qt::TimerType timerType, QObject *obj)
299{
300#ifndef QT_NO_DEBUG
301 if (qToUnderlying(timerId) < 1 || interval.count() < 0 || !obj) {
302 qWarning("QEventDispatcherUNIX::registerTimer: invalid arguments");
303 return;
304 } else if (obj->thread() != thread() || thread() != QThread::currentThread()) {
305 qWarning("QEventDispatcherUNIX::registerTimer: timers cannot be started from another thread");
306 return;
307 }
308#endif
309
310 Q_D(QEventDispatcherUNIX);
311 d->timerList.registerTimer(timerId, interval, timerType, obj);
312}
313
314/*!
315 \internal
316*/
317bool QEventDispatcherUNIX::unregisterTimer(Qt::TimerId timerId)
318{
319#ifndef QT_NO_DEBUG
320 if (qToUnderlying(timerId) < 1) {
321 qWarning("QEventDispatcherUNIX::unregisterTimer: invalid argument");
322 return false;
323 } else if (thread() != QThread::currentThread()) {
324 qWarning("QEventDispatcherUNIX::unregisterTimer: timers cannot be stopped from another thread");
325 return false;
326 }
327#endif
328
329 Q_D(QEventDispatcherUNIX);
330 return d->timerList.unregisterTimer(timerId);
331}
332
333/*!
334 \internal
335*/
336bool QEventDispatcherUNIX::unregisterTimers(QObject *object)
337{
338#ifndef QT_NO_DEBUG
339 if (!object) {
340 qWarning("QEventDispatcherUNIX::unregisterTimers: invalid argument");
341 return false;
342 } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
343 qWarning("QEventDispatcherUNIX::unregisterTimers: timers cannot be stopped from another thread");
344 return false;
345 }
346#endif
347
348 Q_D(QEventDispatcherUNIX);
349 return d->timerList.unregisterTimers(object);
350}
351
352QList<QEventDispatcherUNIX::TimerInfoV2>
353QEventDispatcherUNIX::timersForObject(QObject *object) const
354{
355 if (!object) {
356 qWarning("QEventDispatcherUNIX:registeredTimers: invalid argument");
357 return QList<TimerInfoV2>();
358 }
359
360 Q_D(const QEventDispatcherUNIX);
361 return d->timerList.registeredTimers(object);
362}
363
364/*****************************************************************************
365 QEventDispatcher implementations for UNIX
366 *****************************************************************************/
367
368void QEventDispatcherUNIX::registerSocketNotifier(QSocketNotifier *notifier)
369{
370 Q_ASSERT(notifier);
371 int sockfd = notifier->socket();
372 QSocketNotifier::Type type = notifier->type();
373#ifndef QT_NO_DEBUG
374 if (notifier->thread() != thread() || thread() != QThread::currentThread()) {
375 qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread");
376 return;
377 }
378#endif
379
380 Q_D(QEventDispatcherUNIX);
381 QSocketNotifierSetUNIX &sn_set = d->socketNotifiers[sockfd];
382
383 if (sn_set.notifiers[type] && sn_set.notifiers[type] != notifier)
384 qWarning("%s: Multiple socket notifiers for same socket %d and type %s",
385 Q_FUNC_INFO, sockfd, socketType(type));
386
387 sn_set.notifiers[type] = notifier;
388}
389
390void QEventDispatcherUNIX::unregisterSocketNotifier(QSocketNotifier *notifier)
391{
392 Q_ASSERT(notifier);
393 int sockfd = notifier->socket();
394 QSocketNotifier::Type type = notifier->type();
395#ifndef QT_NO_DEBUG
396 if (notifier->thread() != thread() || thread() != QThread::currentThread()) {
397 qWarning("QSocketNotifier: socket notifier (fd %d) cannot be disabled from another thread.\n"
398 "(Notifier's thread is %s(%p), event dispatcher's thread is %s(%p), current thread is %s(%p))",
399 sockfd,
400 notifier->thread() ? notifier->thread()->metaObject()->className() : "QThread", notifier->thread(),
401 thread() ? thread()->metaObject()->className() : "QThread", thread(),
402 QThread::currentThread() ? QThread::currentThread()->metaObject()->className() : "QThread", QThread::currentThread());
403 return;
404 }
405#endif
406
407 Q_D(QEventDispatcherUNIX);
408
409 d->pendingNotifiers.removeOne(notifier);
410
411 auto i = d->socketNotifiers.find(sockfd);
412 if (i == d->socketNotifiers.end())
413 return;
414
415 QSocketNotifierSetUNIX &sn_set = i.value();
416
417 if (sn_set.notifiers[type] == nullptr)
418 return;
419
420 if (sn_set.notifiers[type] != notifier) {
421 qWarning("%s: Multiple socket notifiers for same socket %d and type %s",
422 Q_FUNC_INFO, sockfd, socketType(type));
423 return;
424 }
425
426 sn_set.notifiers[type] = nullptr;
427
428 if (sn_set.isEmpty())
429 d->socketNotifiers.erase(i);
430}
431
432bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags)
433{
434 Q_D(QEventDispatcherUNIX);
435 d->interrupt.storeRelaxed(0);
436
437 // we are awake, broadcast it
438 emit awake();
439
440 auto threadData = d->threadData.loadRelaxed();
441 QCoreApplicationPrivate::sendPostedEvents(nullptr, 0, threadData);
442
443 const bool include_timers = (flags & QEventLoop::X11ExcludeTimers) == 0;
444 const bool include_notifiers = (flags & QEventLoop::ExcludeSocketNotifiers) == 0;
445 const bool wait_for_events = (flags & QEventLoop::WaitForMoreEvents) != 0;
446
447 const bool canWait = (threadData->canWaitLocked()
448 && !d->interrupt.loadRelaxed()
449 && wait_for_events);
450
451 if (canWait)
452 emit aboutToBlock();
453
454 if (d->interrupt.loadRelaxed())
455 return false;
456
457 QDeadlineTimer deadline;
458 if (canWait) {
459 if (include_timers) {
460 std::optional<nanoseconds> remaining = d->timerList.timerWait();
461 deadline = remaining ? QDeadlineTimer{*remaining}
462 : QDeadlineTimer(QDeadlineTimer::Forever);
463 } else {
464 deadline = QDeadlineTimer(QDeadlineTimer::Forever);
465 }
466 } else {
467 // Using the default-constructed `deadline`, which is already expired,
468 // ensures the code in the do-while loop in qt_safe_poll runs at least once.
469 }
470
471 d->pollfds.clear();
472 d->pollfds.reserve(1 + (include_notifiers ? d->socketNotifiers.size() : 0));
473
474 if (include_notifiers)
475 for (auto it = d->socketNotifiers.cbegin(); it != d->socketNotifiers.cend(); ++it)
476 d->pollfds.append(qt_make_pollfd(it.key(), it.value().events()));
477
478 // This must be last, as it's popped off the end below
479 d->pollfds.append(d->threadPipe.prepare());
480
481 int nevents = 0;
482 switch (qt_safe_poll(d->pollfds.data(), d->pollfds.size(), deadline)) {
483 case -1:
484 qErrnoWarning("qt_safe_poll");
485#if defined(Q_OS_VXWORKS) && defined(EDOOM)
486 if (errno == EDOOM) {
487 // being deleted, stop here and wait for the thread to go away
488 taskSuspend(0);
489 }
490#endif
491 if (QT_CONFIG(poll_exit_on_error))
492 abort();
493 break;
494 case 0:
495 break;
496 default:
497 nevents += d->threadPipe.check(d->pollfds.takeLast());
498 if (include_notifiers)
499 nevents += d->activateSocketNotifiers();
500 break;
501 }
502
503 if (include_timers)
504 nevents += d->activateTimers();
505
506 // return true if we handled events, false otherwise
507 return (nevents > 0);
508}
509
510auto QEventDispatcherUNIX::remainingTime(Qt::TimerId timerId) const -> Duration
511{
512#ifndef QT_NO_DEBUG
513 if (int(timerId) < 1) {
514 qWarning("QEventDispatcherUNIX::remainingTime: invalid argument");
515 return Duration::min();
516 }
517#endif
518
519 Q_D(const QEventDispatcherUNIX);
520 return d->timerList.remainingDuration(timerId);
521}
522
523void QEventDispatcherUNIX::wakeUp()
524{
525 Q_D(QEventDispatcherUNIX);
526 d->threadPipe.wakeUp();
527}
528
529void QEventDispatcherUNIX::interrupt()
530{
531 Q_D(QEventDispatcherUNIX);
532 d->interrupt.storeRelaxed(1);
533 wakeUp();
534}
535
536QT_END_NAMESPACE
537
538#include "moc_qeventdispatcher_unix_p.cpp"
#define __has_include(x)
static QT_BEGIN_NAMESPACE const char * socketType(QSocketNotifier::Type type)
int check(const pollfd &pfd)
pollfd prepare() const