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