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
76 QT_BEGIN_NAMESPACE
77
socketType(QSocketNotifier::Type type)78 static 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
QThreadPipe()92 QThreadPipe::QThreadPipe()
93 {
94 fds[0] = -1;
95 fds[1] = -1;
96 #if defined(Q_OS_VXWORKS)
97 name[0] = '\0';
98 #endif
99 }
100
~QThreadPipe()101 QThreadPipe::~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)
initThreadPipeFD(int fd)115 static 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
init()131 bool QThreadPipe::init()
132 {
133 #if defined(Q_OS_NACL) || 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
prepare() const168 pollfd QThreadPipe::prepare() const
169 {
170 return qt_make_pollfd(fds[0], POLLIN);
171 }
172
wakeUp()173 void 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
check(const pollfd & pfd)190 int 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
QEventDispatcherUNIXPrivate()225 QEventDispatcherUNIXPrivate::QEventDispatcherUNIXPrivate()
226 {
227 if (Q_UNLIKELY(threadPipe.init() == false))
228 qFatal("QEventDispatcherUNIXPrivate(): Cannot continue without a thread pipe");
229 }
230
~QEventDispatcherUNIXPrivate()231 QEventDispatcherUNIXPrivate::~QEventDispatcherUNIXPrivate()
232 {
233 // cleanup timers
234 qDeleteAll(timerList);
235 }
236
setSocketNotifierPending(QSocketNotifier * notifier)237 void QEventDispatcherUNIXPrivate::setSocketNotifierPending(QSocketNotifier *notifier)
238 {
239 Q_ASSERT(notifier);
240
241 if (pendingNotifiers.contains(notifier))
242 return;
243
244 pendingNotifiers << notifier;
245 }
246
activateTimers()247 int QEventDispatcherUNIXPrivate::activateTimers()
248 {
249 return timerList.activateTimers();
250 }
251
markPendingSocketNotifiers()252 void 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
activateSocketNotifiers()292 int 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
QEventDispatcherUNIX(QObject * parent)311 QEventDispatcherUNIX::QEventDispatcherUNIX(QObject *parent)
312 : QAbstractEventDispatcher(*new QEventDispatcherUNIXPrivate, parent)
313 { }
314
QEventDispatcherUNIX(QEventDispatcherUNIXPrivate & dd,QObject * parent)315 QEventDispatcherUNIX::QEventDispatcherUNIX(QEventDispatcherUNIXPrivate &dd, QObject *parent)
316 : QAbstractEventDispatcher(dd, parent)
317 { }
318
~QEventDispatcherUNIX()319 QEventDispatcherUNIX::~QEventDispatcherUNIX()
320 { }
321
322 /*!
323 \internal
324 */
registerTimer(int timerId,int interval,Qt::TimerType timerType,QObject * obj)325 void QEventDispatcherUNIX::registerTimer(int timerId, int 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 */
unregisterTimer(int timerId)344 bool 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 */
unregisterTimers(QObject * object)363 bool 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
379 QList<QEventDispatcherUNIX::TimerInfo>
registeredTimers(QObject * object) const380 QEventDispatcherUNIX::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
registerSocketNotifier(QSocketNotifier * notifier)395 void 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
unregisterSocketNotifier(QSocketNotifier * notifier)417 void 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
processEvents(QEventLoop::ProcessEventsFlags flags)459 bool 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
hasPendingEvents()522 bool QEventDispatcherUNIX::hasPendingEvents()
523 {
524 extern uint qGlobalPostedEventsCount(); // from qapplication.cpp
525 return qGlobalPostedEventsCount();
526 }
527
remainingTime(int timerId)528 int QEventDispatcherUNIX::remainingTime(int timerId)
529 {
530 #ifndef QT_NO_DEBUG
531 if (timerId < 1) {
532 qWarning("QEventDispatcherUNIX::remainingTime: invalid argument");
533 return -1;
534 }
535 #endif
536
537 Q_D(QEventDispatcherUNIX);
538 return d->timerList.timerRemainingTime(timerId);
539 }
540
wakeUp()541 void QEventDispatcherUNIX::wakeUp()
542 {
543 Q_D(QEventDispatcherUNIX);
544 d->threadPipe.wakeUp();
545 }
546
interrupt()547 void QEventDispatcherUNIX::interrupt()
548 {
549 Q_D(QEventDispatcherUNIX);
550 d->interrupt.storeRelaxed(1);
551 wakeUp();
552 }
553
flush()554 void QEventDispatcherUNIX::flush()
555 { }
556
557 QT_END_NAMESPACE
558
559 #include "moc_qeventdispatcher_unix_p.cpp"
560