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
13#include "qeventdispatcher_unix_p.h"
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
40QT_BEGIN_NAMESPACE
41
42static const char *socketType(QSocketNotifier::Type type)
43{
44 switch (type) {
45 case QSocketNotifier::Read:
46 return "Read";
47 case QSocketNotifier::Write:
48 return "Write";
49 case QSocketNotifier::Exception:
50 return "Exception";
51 }
52
53 Q_UNREACHABLE();
54}
55
56QThreadPipe::QThreadPipe()
57{
58 fds[0] = -1;
59 fds[1] = -1;
60#if defined(Q_OS_VXWORKS)
61 name[0] = '\0';
62#endif
63}
64
65QThreadPipe::~QThreadPipe()
66{
67 if (fds[0] >= 0)
68 close(fd: fds[0]);
69
70 if (fds[1] >= 0)
71 close(fd: 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
95bool QThreadPipe::init()
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(count: 0, EFD_NONBLOCK | EFD_CLOEXEC)) >= 0)
121 return true;
122# endif
123 if (qt_safe_pipe(pipefd: fds, O_NONBLOCK) == -1) {
124 perror(s: "QThreadPipe: Unable to create pipe");
125 return false;
126 }
127#endif
128
129 return true;
130}
131
132pollfd QThreadPipe::prepare() const
133{
134 return qt_make_pollfd(fd: fds[0], POLLIN);
135}
136
137void QThreadPipe::wakeUp()
138{
139 if (wakeUps.testAndSetAcquire(expectedValue: 0, newValue: 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(fd: fds[1], data: &c, len: 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(fd: fds[0], value: &value);
173 } else
174# endif
175 {
176 while (::read(fd: fds[0], buf: c, nbytes: sizeof(c)) > 0) {}
177 }
178#endif
179
180 if (!wakeUps.testAndSetRelease(expectedValue: 1, newValue: 0)) {
181 // hopefully, this is dead code
182 qWarning(msg: "QThreadPipe: internal error, wakeUps.testAndSetRelease(1, 0) failed!");
183 }
184 }
185
186 return readyread;
187}
188
189QEventDispatcherUNIXPrivate::QEventDispatcherUNIXPrivate()
190{
191 if (Q_UNLIKELY(threadPipe.init() == false))
192 qFatal(msg: "QEventDispatcherUNIXPrivate(): Cannot continue without a thread pipe");
193}
194
195QEventDispatcherUNIXPrivate::~QEventDispatcherUNIXPrivate()
196{
197 // cleanup timers
198 qDeleteAll(c: timerList);
199}
200
201void QEventDispatcherUNIXPrivate::setSocketNotifierPending(QSocketNotifier *notifier)
202{
203 Q_ASSERT(notifier);
204
205 if (pendingNotifiers.contains(t: notifier))
206 return;
207
208 pendingNotifiers << notifier;
209}
210
211int QEventDispatcherUNIXPrivate::activateTimers()
212{
213 return timerList.activateTimers();
214}
215
216void QEventDispatcherUNIXPrivate::markPendingSocketNotifiers()
217{
218 for (const pollfd &pfd : std::as_const(t&: pollfds)) {
219 if (pfd.fd < 0 || pfd.revents == 0)
220 continue;
221
222 auto it = socketNotifiers.find(key: pfd.fd);
223 Q_ASSERT(it != socketNotifiers.end());
224
225 const QSocketNotifierSetUNIX &sn_set = it.value();
226
227 static const struct {
228 QSocketNotifier::Type type;
229 short flags;
230 } notifiers[] = {
231 { .type: QSocketNotifier::Read, POLLIN | POLLHUP | POLLERR },
232 { .type: QSocketNotifier::Write, POLLOUT | POLLHUP | POLLERR },
233 { .type: 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(msg: "QSocketNotifier: Invalid socket %d with type %s, disabling...",
244 it.key(), socketType(type: n.type));
245 notifier->setEnabled(false);
246 }
247
248 if (pfd.revents & n.flags)
249 setSocketNotifierPending(notifier);
250 }
251 }
252
253 pollfds.clear();
254}
255
256int QEventDispatcherUNIXPrivate::activateSocketNotifiers()
257{
258 markPendingSocketNotifiers();
259
260 if (pendingNotifiers.isEmpty())
261 return 0;
262
263 int n_activated = 0;
264 QEvent event(QEvent::SockAct);
265
266 while (!pendingNotifiers.isEmpty()) {
267 QSocketNotifier *notifier = pendingNotifiers.takeFirst();
268 QCoreApplication::sendEvent(receiver: notifier, event: &event);
269 ++n_activated;
270 }
271
272 return n_activated;
273}
274
275QEventDispatcherUNIX::QEventDispatcherUNIX(QObject *parent)
276 : QAbstractEventDispatcher(*new QEventDispatcherUNIXPrivate, parent)
277{ }
278
279QEventDispatcherUNIX::QEventDispatcherUNIX(QEventDispatcherUNIXPrivate &dd, QObject *parent)
280 : QAbstractEventDispatcher(dd, parent)
281{ }
282
283QEventDispatcherUNIX::~QEventDispatcherUNIX()
284{ }
285
286/*!
287 \internal
288*/
289void QEventDispatcherUNIX::registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *obj)
290{
291#ifndef QT_NO_DEBUG
292 if (timerId < 1 || interval < 0 || !obj) {
293 qWarning(msg: "QEventDispatcherUNIX::registerTimer: invalid arguments");
294 return;
295 } else if (obj->thread() != thread() || thread() != QThread::currentThread()) {
296 qWarning(msg: "QEventDispatcherUNIX::registerTimer: timers cannot be started from another thread");
297 return;
298 }
299#endif
300
301 Q_D(QEventDispatcherUNIX);
302 d->timerList.registerTimer(timerId, interval: std::chrono::milliseconds{ interval }, timerType, object: obj);
303}
304
305/*!
306 \internal
307*/
308bool QEventDispatcherUNIX::unregisterTimer(int timerId)
309{
310#ifndef QT_NO_DEBUG
311 if (timerId < 1) {
312 qWarning(msg: "QEventDispatcherUNIX::unregisterTimer: invalid argument");
313 return false;
314 } else if (thread() != QThread::currentThread()) {
315 qWarning(msg: "QEventDispatcherUNIX::unregisterTimer: timers cannot be stopped from another thread");
316 return false;
317 }
318#endif
319
320 Q_D(QEventDispatcherUNIX);
321 return d->timerList.unregisterTimer(timerId);
322}
323
324/*!
325 \internal
326*/
327bool QEventDispatcherUNIX::unregisterTimers(QObject *object)
328{
329#ifndef QT_NO_DEBUG
330 if (!object) {
331 qWarning(msg: "QEventDispatcherUNIX::unregisterTimers: invalid argument");
332 return false;
333 } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
334 qWarning(msg: "QEventDispatcherUNIX::unregisterTimers: timers cannot be stopped from another thread");
335 return false;
336 }
337#endif
338
339 Q_D(QEventDispatcherUNIX);
340 return d->timerList.unregisterTimers(object);
341}
342
343QList<QEventDispatcherUNIX::TimerInfo>
344QEventDispatcherUNIX::registeredTimers(QObject *object) const
345{
346 if (!object) {
347 qWarning(msg: "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
359void QEventDispatcherUNIX::registerSocketNotifier(QSocketNotifier *notifier)
360{
361 Q_ASSERT(notifier);
362 int sockfd = notifier->socket();
363 QSocketNotifier::Type type = notifier->type();
364#ifndef QT_NO_DEBUG
365 if (notifier->thread() != thread() || thread() != QThread::currentThread()) {
366 qWarning(msg: "QSocketNotifier: socket notifiers cannot be enabled from another thread");
367 return;
368 }
369#endif
370
371 Q_D(QEventDispatcherUNIX);
372 QSocketNotifierSetUNIX &sn_set = d->socketNotifiers[sockfd];
373
374 if (sn_set.notifiers[type] && sn_set.notifiers[type] != notifier)
375 qWarning(msg: "%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
381void QEventDispatcherUNIX::unregisterSocketNotifier(QSocketNotifier *notifier)
382{
383 Q_ASSERT(notifier);
384 int sockfd = notifier->socket();
385 QSocketNotifier::Type type = notifier->type();
386#ifndef QT_NO_DEBUG
387 if (notifier->thread() != thread() || thread() != QThread::currentThread()) {
388 qWarning(msg: "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
398 Q_D(QEventDispatcherUNIX);
399
400 d->pendingNotifiers.removeOne(t: notifier);
401
402 auto i = d->socketNotifiers.find(key: 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(msg: "%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(it: i);
421}
422
423bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags)
424{
425 Q_D(QEventDispatcherUNIX);
426 d->interrupt.storeRelaxed(newValue: 0);
427
428 // we are awake, broadcast it
429 emit awake();
430
431 auto threadData = d->threadData.loadRelaxed();
432 QCoreApplicationPrivate::sendPostedEvents(receiver: nullptr, event_type: 0, data: 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)
443 emit aboutToBlock();
444
445 if (d->interrupt.loadRelaxed())
446 return false;
447
448 timespec *tm = nullptr;
449 timespec wait_tm = { .tv_sec: 0, .tv_nsec: 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(asize: 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(t: qt_make_pollfd(fd: it.key(), events: it.value().events()));
460
461 // This must be last, as it's popped off the end below
462 d->pollfds.append(t: d->threadPipe.prepare());
463
464 int nevents = 0;
465
466 switch (qt_safe_poll(fds: d->pollfds.data(), nfds: d->pollfds.size(), timeout_ts: tm)) {
467 case -1:
468 qErrnoWarning(msg: "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(pfd: 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
488int QEventDispatcherUNIX::remainingTime(int timerId)
489{
490#ifndef QT_NO_DEBUG
491 if (timerId < 1) {
492 qWarning(msg: "QEventDispatcherUNIX::remainingTime: invalid argument");
493 return -1;
494 }
495#endif
496
497 Q_D(QEventDispatcherUNIX);
498 return d->timerList.timerRemainingTime(timerId);
499}
500
501void QEventDispatcherUNIX::wakeUp()
502{
503 Q_D(QEventDispatcherUNIX);
504 d->threadPipe.wakeUp();
505}
506
507void QEventDispatcherUNIX::interrupt()
508{
509 Q_D(QEventDispatcherUNIX);
510 d->interrupt.storeRelaxed(newValue: 1);
511 wakeUp();
512}
513
514QT_END_NAMESPACE
515
516#include "moc_qeventdispatcher_unix_p.cpp"
517

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