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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtbase/src/corelib/kernel/qeventdispatcher_unix.cpp