1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Copyright (C) 2016 Intel Corporation.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtCore module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include "qplatformdefs.h"
42
43#include "qcoreapplication.h"
44#include "qpair.h"
45#include "qsocketnotifier.h"
46#include "qthread.h"
47#include "qelapsedtimer.h"
48
49#include "qeventdispatcher_unix_p.h"
50#include <private/qthread_p.h>
51#include <private/qcoreapplication_p.h>
52#include <private/qcore_unix_p.h>
53
54#include <errno.h>
55#include <stdio.h>
56#include <stdlib.h>
57
58#ifndef QT_NO_EVENTFD
59# include <sys/eventfd.h>
60#endif
61
62// VxWorks doesn't correctly set the _POSIX_... options
63#if defined(Q_OS_VXWORKS)
64# if defined(_POSIX_MONOTONIC_CLOCK) && (_POSIX_MONOTONIC_CLOCK <= 0)
65# undef _POSIX_MONOTONIC_CLOCK
66# define _POSIX_MONOTONIC_CLOCK 1
67# endif
68# include <pipeDrv.h>
69# include <sys/time.h>
70#endif
71
72#if (_POSIX_MONOTONIC_CLOCK-0 <= 0) || defined(QT_BOOTSTRAPPED)
73# include <sys/times.h>
74#endif
75
76QT_BEGIN_NAMESPACE
77
78static const char *socketType(QSocketNotifier::Type type)
79{
80 switch (type) {
81 case QSocketNotifier::Read:
82 return "Read";
83 case QSocketNotifier::Write:
84 return "Write";
85 case QSocketNotifier::Exception:
86 return "Exception";
87 }
88
89 Q_UNREACHABLE();
90}
91
92QThreadPipe::QThreadPipe()
93{
94 fds[0] = -1;
95 fds[1] = -1;
96#if defined(Q_OS_VXWORKS)
97 name[0] = '\0';
98#endif
99}
100
101QThreadPipe::~QThreadPipe()
102{
103 if (fds[0] >= 0)
104 close(fds[0]);
105
106 if (fds[1] >= 0)
107 close(fds[1]);
108
109#if defined(Q_OS_VXWORKS)
110 pipeDevDelete(name, true);
111#endif
112}
113
114#if defined(Q_OS_VXWORKS)
115static void initThreadPipeFD(int fd)
116{
117 int ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
118 if (ret == -1)
119 perror("QEventDispatcherUNIXPrivate: Unable to init thread pipe");
120
121 int flags = fcntl(fd, F_GETFL);
122 if (flags == -1)
123 perror("QEventDispatcherUNIXPrivate: Unable to get flags on thread pipe");
124
125 ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
126 if (ret == -1)
127 perror("QEventDispatcherUNIXPrivate: Unable to set flags on thread pipe");
128}
129#endif
130
131bool QThreadPipe::init()
132{
133#if defined(Q_OS_WASM)
134 // do nothing.
135#elif defined(Q_OS_VXWORKS)
136 qsnprintf(name, sizeof(name), "/pipe/qt_%08x", int(taskIdSelf()));
137
138 // make sure there is no pipe with this name
139 pipeDevDelete(name, true);
140
141 // create the pipe
142 if (pipeDevCreate(name, 128 /*maxMsg*/, 1 /*maxLength*/) != OK) {
143 perror("QThreadPipe: Unable to create thread pipe device %s", name);
144 return false;
145 }
146
147 if ((fds[0] = open(name, O_RDWR, 0)) < 0) {
148 perror("QThreadPipe: Unable to open pipe device %s", name);
149 return false;
150 }
151
152 initThreadPipeFD(fds[0]);
153 fds[1] = fds[0];
154#else
155# ifndef QT_NO_EVENTFD
156 if ((fds[0] = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC)) >= 0)
157 return true;
158# endif
159 if (qt_safe_pipe(fds, O_NONBLOCK) == -1) {
160 perror("QThreadPipe: Unable to create pipe");
161 return false;
162 }
163#endif
164
165 return true;
166}
167
168pollfd QThreadPipe::prepare() const
169{
170 return qt_make_pollfd(fds[0], POLLIN);
171}
172
173void QThreadPipe::wakeUp()
174{
175 if (wakeUps.testAndSetAcquire(0, 1)) {
176#ifndef QT_NO_EVENTFD
177 if (fds[1] == -1) {
178 // eventfd
179 eventfd_t value = 1;
180 int ret;
181 EINTR_LOOP(ret, eventfd_write(fds[0], value));
182 return;
183 }
184#endif
185 char c = 0;
186 qt_safe_write(fds[1], &c, 1);
187 }
188}
189
190int QThreadPipe::check(const pollfd &pfd)
191{
192 Q_ASSERT(pfd.fd == fds[0]);
193
194 char c[16];
195 const int readyread = pfd.revents & POLLIN;
196
197 if (readyread) {
198 // consume the data on the thread pipe so that
199 // poll doesn't immediately return next time
200#if defined(Q_OS_VXWORKS)
201 ::read(fds[0], c, sizeof(c));
202 ::ioctl(fds[0], FIOFLUSH, 0);
203#else
204# ifndef QT_NO_EVENTFD
205 if (fds[1] == -1) {
206 // eventfd
207 eventfd_t value;
208 eventfd_read(fds[0], &value);
209 } else
210# endif
211 {
212 while (::read(fds[0], c, sizeof(c)) > 0) {}
213 }
214#endif
215
216 if (!wakeUps.testAndSetRelease(1, 0)) {
217 // hopefully, this is dead code
218 qWarning("QThreadPipe: internal error, wakeUps.testAndSetRelease(1, 0) failed!");
219 }
220 }
221
222 return readyread;
223}
224
225QEventDispatcherUNIXPrivate::QEventDispatcherUNIXPrivate()
226{
227 if (Q_UNLIKELY(threadPipe.init() == false))
228 qFatal("QEventDispatcherUNIXPrivate(): Cannot continue without a thread pipe");
229}
230
231QEventDispatcherUNIXPrivate::~QEventDispatcherUNIXPrivate()
232{
233 // cleanup timers
234 qDeleteAll(timerList);
235}
236
237void QEventDispatcherUNIXPrivate::setSocketNotifierPending(QSocketNotifier *notifier)
238{
239 Q_ASSERT(notifier);
240
241 if (pendingNotifiers.contains(notifier))
242 return;
243
244 pendingNotifiers << notifier;
245}
246
247int QEventDispatcherUNIXPrivate::activateTimers()
248{
249 return timerList.activateTimers();
250}
251
252void QEventDispatcherUNIXPrivate::markPendingSocketNotifiers()
253{
254 for (const pollfd &pfd : qAsConst(pollfds)) {
255 if (pfd.fd < 0 || pfd.revents == 0)
256 continue;
257
258 auto it = socketNotifiers.find(pfd.fd);
259 Q_ASSERT(it != socketNotifiers.end());
260
261 const QSocketNotifierSetUNIX &sn_set = it.value();
262
263 static const struct {
264 QSocketNotifier::Type type;
265 short flags;
266 } notifiers[] = {
267 { QSocketNotifier::Read, POLLIN | POLLHUP | POLLERR },
268 { QSocketNotifier::Write, POLLOUT | POLLHUP | POLLERR },
269 { QSocketNotifier::Exception, POLLPRI | POLLHUP | POLLERR }
270 };
271
272 for (const auto &n : notifiers) {
273 QSocketNotifier *notifier = sn_set.notifiers[n.type];
274
275 if (!notifier)
276 continue;
277
278 if (pfd.revents & POLLNVAL) {
279 qWarning("QSocketNotifier: Invalid socket %d with type %s, disabling...",
280 it.key(), socketType(n.type));
281 notifier->setEnabled(false);
282 }
283
284 if (pfd.revents & n.flags)
285 setSocketNotifierPending(notifier);
286 }
287 }
288
289 pollfds.clear();
290}
291
292int QEventDispatcherUNIXPrivate::activateSocketNotifiers()
293{
294 markPendingSocketNotifiers();
295
296 if (pendingNotifiers.isEmpty())
297 return 0;
298
299 int n_activated = 0;
300 QEvent event(QEvent::SockAct);
301
302 while (!pendingNotifiers.isEmpty()) {
303 QSocketNotifier *notifier = pendingNotifiers.takeFirst();
304 QCoreApplication::sendEvent(notifier, &event);
305 ++n_activated;
306 }
307
308 return n_activated;
309}
310
311QEventDispatcherUNIX::QEventDispatcherUNIX(QObject *parent)
312 : QAbstractEventDispatcher(*new QEventDispatcherUNIXPrivate, parent)
313{ }
314
315QEventDispatcherUNIX::QEventDispatcherUNIX(QEventDispatcherUNIXPrivate &dd, QObject *parent)
316 : QAbstractEventDispatcher(dd, parent)
317{ }
318
319QEventDispatcherUNIX::~QEventDispatcherUNIX()
320{ }
321
322/*!
323 \internal
324*/
325void QEventDispatcherUNIX::registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *obj)
326{
327#ifndef QT_NO_DEBUG
328 if (timerId < 1 || interval < 0 || !obj) {
329 qWarning("QEventDispatcherUNIX::registerTimer: invalid arguments");
330 return;
331 } else if (obj->thread() != thread() || thread() != QThread::currentThread()) {
332 qWarning("QEventDispatcherUNIX::registerTimer: timers cannot be started from another thread");
333 return;
334 }
335#endif
336
337 Q_D(QEventDispatcherUNIX);
338 d->timerList.registerTimer(timerId, interval, timerType, obj);
339}
340
341/*!
342 \internal
343*/
344bool QEventDispatcherUNIX::unregisterTimer(int timerId)
345{
346#ifndef QT_NO_DEBUG
347 if (timerId < 1) {
348 qWarning("QEventDispatcherUNIX::unregisterTimer: invalid argument");
349 return false;
350 } else if (thread() != QThread::currentThread()) {
351 qWarning("QEventDispatcherUNIX::unregisterTimer: timers cannot be stopped from another thread");
352 return false;
353 }
354#endif
355
356 Q_D(QEventDispatcherUNIX);
357 return d->timerList.unregisterTimer(timerId);
358}
359
360/*!
361 \internal
362*/
363bool QEventDispatcherUNIX::unregisterTimers(QObject *object)
364{
365#ifndef QT_NO_DEBUG
366 if (!object) {
367 qWarning("QEventDispatcherUNIX::unregisterTimers: invalid argument");
368 return false;
369 } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
370 qWarning("QEventDispatcherUNIX::unregisterTimers: timers cannot be stopped from another thread");
371 return false;
372 }
373#endif
374
375 Q_D(QEventDispatcherUNIX);
376 return d->timerList.unregisterTimers(object);
377}
378
379QList<QEventDispatcherUNIX::TimerInfo>
380QEventDispatcherUNIX::registeredTimers(QObject *object) const
381{
382 if (!object) {
383 qWarning("QEventDispatcherUNIX:registeredTimers: invalid argument");
384 return QList<TimerInfo>();
385 }
386
387 Q_D(const QEventDispatcherUNIX);
388 return d->timerList.registeredTimers(object);
389}
390
391/*****************************************************************************
392 QEventDispatcher implementations for UNIX
393 *****************************************************************************/
394
395void QEventDispatcherUNIX::registerSocketNotifier(QSocketNotifier *notifier)
396{
397 Q_ASSERT(notifier);
398 int sockfd = notifier->socket();
399 QSocketNotifier::Type type = notifier->type();
400#ifndef QT_NO_DEBUG
401 if (notifier->thread() != thread() || thread() != QThread::currentThread()) {
402 qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread");
403 return;
404 }
405#endif
406
407 Q_D(QEventDispatcherUNIX);
408 QSocketNotifierSetUNIX &sn_set = d->socketNotifiers[sockfd];
409
410 if (sn_set.notifiers[type] && sn_set.notifiers[type] != notifier)
411 qWarning("%s: Multiple socket notifiers for same socket %d and type %s",
412 Q_FUNC_INFO, sockfd, socketType(type));
413
414 sn_set.notifiers[type] = notifier;
415}
416
417void QEventDispatcherUNIX::unregisterSocketNotifier(QSocketNotifier *notifier)
418{
419 Q_ASSERT(notifier);
420 int sockfd = notifier->socket();
421 QSocketNotifier::Type type = notifier->type();
422#ifndef QT_NO_DEBUG
423 if (notifier->thread() != thread() || thread() != QThread::currentThread()) {
424 qWarning("QSocketNotifier: socket notifier (fd %d) cannot be disabled from another thread.\n"
425 "(Notifier's thread is %s(%p), event dispatcher's thread is %s(%p), current thread is %s(%p))",
426 sockfd,
427 notifier->thread() ? notifier->thread()->metaObject()->className() : "QThread", notifier->thread(),
428 thread() ? thread()->metaObject()->className() : "QThread", thread(),
429 QThread::currentThread() ? QThread::currentThread()->metaObject()->className() : "QThread", QThread::currentThread());
430 return;
431 }
432#endif
433
434 Q_D(QEventDispatcherUNIX);
435
436 d->pendingNotifiers.removeOne(notifier);
437
438 auto i = d->socketNotifiers.find(sockfd);
439 if (i == d->socketNotifiers.end())
440 return;
441
442 QSocketNotifierSetUNIX &sn_set = i.value();
443
444 if (sn_set.notifiers[type] == nullptr)
445 return;
446
447 if (sn_set.notifiers[type] != notifier) {
448 qWarning("%s: Multiple socket notifiers for same socket %d and type %s",
449 Q_FUNC_INFO, sockfd, socketType(type));
450 return;
451 }
452
453 sn_set.notifiers[type] = nullptr;
454
455 if (sn_set.isEmpty())
456 d->socketNotifiers.erase(i);
457}
458
459bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags)
460{
461 Q_D(QEventDispatcherUNIX);
462 d->interrupt.storeRelaxed(0);
463
464 // we are awake, broadcast it
465 emit awake();
466
467 auto threadData = d->threadData.loadRelaxed();
468 QCoreApplicationPrivate::sendPostedEvents(nullptr, 0, threadData);
469
470 const bool include_timers = (flags & QEventLoop::X11ExcludeTimers) == 0;
471 const bool include_notifiers = (flags & QEventLoop::ExcludeSocketNotifiers) == 0;
472 const bool wait_for_events = flags & QEventLoop::WaitForMoreEvents;
473
474 const bool canWait = (threadData->canWaitLocked()
475 && !d->interrupt.loadRelaxed()
476 && wait_for_events);
477
478 if (canWait)
479 emit aboutToBlock();
480
481 if (d->interrupt.loadRelaxed())
482 return false;
483
484 timespec *tm = nullptr;
485 timespec wait_tm = { 0, 0 };
486
487 if (!canWait || (include_timers && d->timerList.timerWait(wait_tm)))
488 tm = &wait_tm;
489
490 d->pollfds.clear();
491 d->pollfds.reserve(1 + (include_notifiers ? d->socketNotifiers.size() : 0));
492
493 if (include_notifiers)
494 for (auto it = d->socketNotifiers.cbegin(); it != d->socketNotifiers.cend(); ++it)
495 d->pollfds.append(qt_make_pollfd(it.key(), it.value().events()));
496
497 // This must be last, as it's popped off the end below
498 d->pollfds.append(d->threadPipe.prepare());
499
500 int nevents = 0;
501
502 switch (qt_safe_poll(d->pollfds.data(), d->pollfds.size(), tm)) {
503 case -1:
504 perror("qt_safe_poll");
505 break;
506 case 0:
507 break;
508 default:
509 nevents += d->threadPipe.check(d->pollfds.takeLast());
510 if (include_notifiers)
511 nevents += d->activateSocketNotifiers();
512 break;
513 }
514
515 if (include_timers)
516 nevents += d->activateTimers();
517
518 // return true if we handled events, false otherwise
519 return (nevents > 0);
520}
521
522int QEventDispatcherUNIX::remainingTime(int timerId)
523{
524#ifndef QT_NO_DEBUG
525 if (timerId < 1) {
526 qWarning("QEventDispatcherUNIX::remainingTime: invalid argument");
527 return -1;
528 }
529#endif
530
531 Q_D(QEventDispatcherUNIX);
532 return d->timerList.timerRemainingTime(timerId);
533}
534
535void QEventDispatcherUNIX::wakeUp()
536{
537 Q_D(QEventDispatcherUNIX);
538 d->threadPipe.wakeUp();
539}
540
541void QEventDispatcherUNIX::interrupt()
542{
543 Q_D(QEventDispatcherUNIX);
544 d->interrupt.storeRelaxed(1);
545 wakeUp();
546}
547
548QT_END_NAMESPACE
549
550#include "moc_qeventdispatcher_unix_p.cpp"
551