1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qeventdispatcher_glib_p.h"
41 #include "qeventdispatcher_unix_p.h"
42 
43 #include <private/qthread_p.h>
44 
45 #include "qcoreapplication.h"
46 #include "qsocketnotifier.h"
47 
48 #include <QtCore/qlist.h>
49 #include <QtCore/qpair.h>
50 
51 #include <glib.h>
52 
53 QT_BEGIN_NAMESPACE
54 
55 struct GPollFDWithQSocketNotifier
56 {
57     GPollFD pollfd;
58     QSocketNotifier *socketNotifier;
59 };
60 
61 struct GSocketNotifierSource
62 {
63     GSource source;
64     QList<GPollFDWithQSocketNotifier *> pollfds;
65 };
66 
socketNotifierSourcePrepare(GSource *,gint * timeout)67 static gboolean socketNotifierSourcePrepare(GSource *, gint *timeout)
68 {
69     if (timeout)
70         *timeout = -1;
71     return false;
72 }
73 
socketNotifierSourceCheck(GSource * source)74 static gboolean socketNotifierSourceCheck(GSource *source)
75 {
76     GSocketNotifierSource *src = reinterpret_cast<GSocketNotifierSource *>(source);
77 
78     bool pending = false;
79     for (int i = 0; !pending && i < src->pollfds.count(); ++i) {
80         GPollFDWithQSocketNotifier *p = src->pollfds.at(i);
81 
82         if (p->pollfd.revents & G_IO_NVAL) {
83             // disable the invalid socket notifier
84             static const char *t[] = { "Read", "Write", "Exception" };
85             qWarning("QSocketNotifier: Invalid socket %d and type '%s', disabling...",
86                      p->pollfd.fd, t[int(p->socketNotifier->type())]);
87             // ### note, modifies src->pollfds!
88             p->socketNotifier->setEnabled(false);
89             i--;
90         } else {
91             pending = pending || ((p->pollfd.revents & p->pollfd.events) != 0);
92         }
93     }
94 
95     return pending;
96 }
97 
socketNotifierSourceDispatch(GSource * source,GSourceFunc,gpointer)98 static gboolean socketNotifierSourceDispatch(GSource *source, GSourceFunc, gpointer)
99 {
100     QEvent event(QEvent::SockAct);
101 
102     GSocketNotifierSource *src = reinterpret_cast<GSocketNotifierSource *>(source);
103     for (int i = 0; i < src->pollfds.count(); ++i) {
104         GPollFDWithQSocketNotifier *p = src->pollfds.at(i);
105 
106         if ((p->pollfd.revents & p->pollfd.events) != 0)
107             QCoreApplication::sendEvent(p->socketNotifier, &event);
108     }
109 
110     return true; // ??? don't remove, right?
111 }
112 
113 static GSourceFuncs socketNotifierSourceFuncs = {
114     socketNotifierSourcePrepare,
115     socketNotifierSourceCheck,
116     socketNotifierSourceDispatch,
117     nullptr,
118     nullptr,
119     nullptr
120 };
121 
122 struct GTimerSource
123 {
124     GSource source;
125     QTimerInfoList timerList;
126     QEventLoop::ProcessEventsFlags processEventsFlags;
127     bool runWithIdlePriority;
128 };
129 
timerSourcePrepareHelper(GTimerSource * src,gint * timeout)130 static gboolean timerSourcePrepareHelper(GTimerSource *src, gint *timeout)
131 {
132     timespec tv = { 0l, 0l };
133     if (!(src->processEventsFlags & QEventLoop::X11ExcludeTimers) && src->timerList.timerWait(tv))
134         *timeout = (tv.tv_sec * 1000) + ((tv.tv_nsec + 999999) / 1000 / 1000);
135     else
136         *timeout = -1;
137 
138     return (*timeout == 0);
139 }
140 
timerSourceCheckHelper(GTimerSource * src)141 static gboolean timerSourceCheckHelper(GTimerSource *src)
142 {
143     if (src->timerList.isEmpty()
144         || (src->processEventsFlags & QEventLoop::X11ExcludeTimers))
145         return false;
146 
147     if (src->timerList.updateCurrentTime() < src->timerList.constFirst()->timeout)
148         return false;
149 
150     return true;
151 }
152 
timerSourcePrepare(GSource * source,gint * timeout)153 static gboolean timerSourcePrepare(GSource *source, gint *timeout)
154 {
155     gint dummy;
156     if (!timeout)
157         timeout = &dummy;
158 
159     GTimerSource *src = reinterpret_cast<GTimerSource *>(source);
160     if (src->runWithIdlePriority) {
161         if (timeout)
162             *timeout = -1;
163         return false;
164     }
165 
166     return timerSourcePrepareHelper(src, timeout);
167 }
168 
timerSourceCheck(GSource * source)169 static gboolean timerSourceCheck(GSource *source)
170 {
171     GTimerSource *src = reinterpret_cast<GTimerSource *>(source);
172     if (src->runWithIdlePriority)
173         return false;
174     return timerSourceCheckHelper(src);
175 }
176 
timerSourceDispatch(GSource * source,GSourceFunc,gpointer)177 static gboolean timerSourceDispatch(GSource *source, GSourceFunc, gpointer)
178 {
179     GTimerSource *timerSource = reinterpret_cast<GTimerSource *>(source);
180     if (timerSource->processEventsFlags & QEventLoop::X11ExcludeTimers)
181         return true;
182     timerSource->runWithIdlePriority = true;
183     (void) timerSource->timerList.activateTimers();
184     return true; // ??? don't remove, right again?
185 }
186 
187 static GSourceFuncs timerSourceFuncs = {
188     timerSourcePrepare,
189     timerSourceCheck,
190     timerSourceDispatch,
191     nullptr,
192     nullptr,
193     nullptr
194 };
195 
196 struct GIdleTimerSource
197 {
198     GSource source;
199     GTimerSource *timerSource;
200 };
201 
idleTimerSourcePrepare(GSource * source,gint * timeout)202 static gboolean idleTimerSourcePrepare(GSource *source, gint *timeout)
203 {
204     GIdleTimerSource *idleTimerSource = reinterpret_cast<GIdleTimerSource *>(source);
205     GTimerSource *timerSource = idleTimerSource->timerSource;
206     if (!timerSource->runWithIdlePriority) {
207         // Yield to the normal priority timer source
208         if (timeout)
209             *timeout = -1;
210         return false;
211     }
212 
213     return timerSourcePrepareHelper(timerSource, timeout);
214 }
215 
idleTimerSourceCheck(GSource * source)216 static gboolean idleTimerSourceCheck(GSource *source)
217 {
218     GIdleTimerSource *idleTimerSource = reinterpret_cast<GIdleTimerSource *>(source);
219     GTimerSource *timerSource = idleTimerSource->timerSource;
220     if (!timerSource->runWithIdlePriority) {
221         // Yield to the normal priority timer source
222         return false;
223     }
224     return timerSourceCheckHelper(timerSource);
225 }
226 
idleTimerSourceDispatch(GSource * source,GSourceFunc,gpointer)227 static gboolean idleTimerSourceDispatch(GSource *source, GSourceFunc, gpointer)
228 {
229     GTimerSource *timerSource = reinterpret_cast<GIdleTimerSource *>(source)->timerSource;
230     (void) timerSourceDispatch(&timerSource->source, nullptr, nullptr);
231     return true;
232 }
233 
234 static GSourceFuncs idleTimerSourceFuncs = {
235     idleTimerSourcePrepare,
236     idleTimerSourceCheck,
237     idleTimerSourceDispatch,
238     nullptr,
239     nullptr,
240     nullptr
241 };
242 
243 struct GPostEventSource
244 {
245     GSource source;
246     QAtomicInt serialNumber;
247     int lastSerialNumber;
248     QEventDispatcherGlibPrivate *d;
249 };
250 
postEventSourcePrepare(GSource * s,gint * timeout)251 static gboolean postEventSourcePrepare(GSource *s, gint *timeout)
252 {
253     QThreadData *data = QThreadData::current();
254     if (!data)
255         return false;
256 
257     gint dummy;
258     if (!timeout)
259         timeout = &dummy;
260     const bool canWait = data->canWaitLocked();
261     *timeout = canWait ? -1 : 0;
262 
263     GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);
264     source->d->wakeUpCalled = source->serialNumber.loadRelaxed() != source->lastSerialNumber;
265     return !canWait || source->d->wakeUpCalled;
266 }
267 
postEventSourceCheck(GSource * source)268 static gboolean postEventSourceCheck(GSource *source)
269 {
270     return postEventSourcePrepare(source, nullptr);
271 }
272 
postEventSourceDispatch(GSource * s,GSourceFunc,gpointer)273 static gboolean postEventSourceDispatch(GSource *s, GSourceFunc, gpointer)
274 {
275     GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);
276     source->lastSerialNumber = source->serialNumber.loadRelaxed();
277     QCoreApplication::sendPostedEvents();
278     source->d->runTimersOnceWithNormalPriority();
279     return true; // i dunno, george...
280 }
281 
282 static GSourceFuncs postEventSourceFuncs = {
283     postEventSourcePrepare,
284     postEventSourceCheck,
285     postEventSourceDispatch,
286     nullptr,
287     nullptr,
288     nullptr
289 };
290 
291 
QEventDispatcherGlibPrivate(GMainContext * context)292 QEventDispatcherGlibPrivate::QEventDispatcherGlibPrivate(GMainContext *context)
293     : mainContext(context)
294 {
295 #if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 32
296     if (qEnvironmentVariableIsEmpty("QT_NO_THREADED_GLIB")) {
297         static QBasicMutex mutex;
298         QMutexLocker locker(&mutex);
299         if (!g_thread_supported())
300             g_thread_init(NULL);
301     }
302 #endif
303 
304     if (mainContext) {
305         g_main_context_ref(mainContext);
306     } else {
307         QCoreApplication *app = QCoreApplication::instance();
308         if (app && QThread::currentThread() == app->thread()) {
309             mainContext = g_main_context_default();
310             g_main_context_ref(mainContext);
311         } else {
312             mainContext = g_main_context_new();
313         }
314     }
315 
316 #if GLIB_CHECK_VERSION (2, 22, 0)
317     g_main_context_push_thread_default (mainContext);
318 #endif
319 
320     // setup post event source
321     postEventSource = reinterpret_cast<GPostEventSource *>(g_source_new(&postEventSourceFuncs,
322                                                                         sizeof(GPostEventSource)));
323     postEventSource->serialNumber.storeRelaxed(1);
324     postEventSource->d = this;
325     g_source_set_can_recurse(&postEventSource->source, true);
326     g_source_attach(&postEventSource->source, mainContext);
327 
328     // setup socketNotifierSource
329     socketNotifierSource =
330         reinterpret_cast<GSocketNotifierSource *>(g_source_new(&socketNotifierSourceFuncs,
331                                                                sizeof(GSocketNotifierSource)));
332     (void) new (&socketNotifierSource->pollfds) QList<GPollFDWithQSocketNotifier *>();
333     g_source_set_can_recurse(&socketNotifierSource->source, true);
334     g_source_attach(&socketNotifierSource->source, mainContext);
335 
336     // setup normal and idle timer sources
337     timerSource = reinterpret_cast<GTimerSource *>(g_source_new(&timerSourceFuncs,
338                                                                 sizeof(GTimerSource)));
339     (void) new (&timerSource->timerList) QTimerInfoList();
340     timerSource->processEventsFlags = QEventLoop::AllEvents;
341     timerSource->runWithIdlePriority = false;
342     g_source_set_can_recurse(&timerSource->source, true);
343     g_source_attach(&timerSource->source, mainContext);
344 
345     idleTimerSource = reinterpret_cast<GIdleTimerSource *>(g_source_new(&idleTimerSourceFuncs,
346                                                                         sizeof(GIdleTimerSource)));
347     idleTimerSource->timerSource = timerSource;
348     g_source_set_can_recurse(&idleTimerSource->source, true);
349     g_source_attach(&idleTimerSource->source, mainContext);
350 }
351 
runTimersOnceWithNormalPriority()352 void QEventDispatcherGlibPrivate::runTimersOnceWithNormalPriority()
353 {
354     timerSource->runWithIdlePriority = false;
355 }
356 
QEventDispatcherGlib(QObject * parent)357 QEventDispatcherGlib::QEventDispatcherGlib(QObject *parent)
358     : QAbstractEventDispatcher(*(new QEventDispatcherGlibPrivate), parent)
359 {
360 }
361 
QEventDispatcherGlib(GMainContext * mainContext,QObject * parent)362 QEventDispatcherGlib::QEventDispatcherGlib(GMainContext *mainContext, QObject *parent)
363     : QAbstractEventDispatcher(*(new QEventDispatcherGlibPrivate(mainContext)), parent)
364 { }
365 
~QEventDispatcherGlib()366 QEventDispatcherGlib::~QEventDispatcherGlib()
367 {
368     Q_D(QEventDispatcherGlib);
369 
370     // destroy all timer sources
371     qDeleteAll(d->timerSource->timerList);
372     d->timerSource->timerList.~QTimerInfoList();
373     g_source_destroy(&d->timerSource->source);
374     g_source_unref(&d->timerSource->source);
375     d->timerSource = nullptr;
376     g_source_destroy(&d->idleTimerSource->source);
377     g_source_unref(&d->idleTimerSource->source);
378     d->idleTimerSource = nullptr;
379 
380     // destroy socket notifier source
381     for (int i = 0; i < d->socketNotifierSource->pollfds.count(); ++i) {
382         GPollFDWithQSocketNotifier *p = d->socketNotifierSource->pollfds[i];
383         g_source_remove_poll(&d->socketNotifierSource->source, &p->pollfd);
384         delete p;
385     }
386     d->socketNotifierSource->pollfds.~QList<GPollFDWithQSocketNotifier *>();
387     g_source_destroy(&d->socketNotifierSource->source);
388     g_source_unref(&d->socketNotifierSource->source);
389     d->socketNotifierSource = nullptr;
390 
391     // destroy post event source
392     g_source_destroy(&d->postEventSource->source);
393     g_source_unref(&d->postEventSource->source);
394     d->postEventSource = nullptr;
395 
396     Q_ASSERT(d->mainContext != nullptr);
397 #if GLIB_CHECK_VERSION (2, 22, 0)
398     g_main_context_pop_thread_default (d->mainContext);
399 #endif
400     g_main_context_unref(d->mainContext);
401     d->mainContext = nullptr;
402 }
403 
processEvents(QEventLoop::ProcessEventsFlags flags)404 bool QEventDispatcherGlib::processEvents(QEventLoop::ProcessEventsFlags flags)
405 {
406     Q_D(QEventDispatcherGlib);
407 
408     const bool canWait = (flags & QEventLoop::WaitForMoreEvents);
409     if (canWait)
410         emit aboutToBlock();
411     else
412         emit awake();
413 
414     // tell postEventSourcePrepare() and timerSource about any new flags
415     QEventLoop::ProcessEventsFlags savedFlags = d->timerSource->processEventsFlags;
416     d->timerSource->processEventsFlags = flags;
417 
418     if (!(flags & QEventLoop::EventLoopExec)) {
419         // force timers to be sent at normal priority
420         d->timerSource->runWithIdlePriority = false;
421     }
422 
423     bool result = g_main_context_iteration(d->mainContext, canWait);
424     while (!result && canWait)
425         result = g_main_context_iteration(d->mainContext, canWait);
426 
427     d->timerSource->processEventsFlags = savedFlags;
428 
429     if (canWait)
430         emit awake();
431 
432     return result;
433 }
434 
hasPendingEvents()435 bool QEventDispatcherGlib::hasPendingEvents()
436 {
437     Q_D(QEventDispatcherGlib);
438     return g_main_context_pending(d->mainContext);
439 }
440 
registerSocketNotifier(QSocketNotifier * notifier)441 void QEventDispatcherGlib::registerSocketNotifier(QSocketNotifier *notifier)
442 {
443     Q_ASSERT(notifier);
444     int sockfd = notifier->socket();
445     int type = notifier->type();
446 #ifndef QT_NO_DEBUG
447     if (sockfd < 0) {
448         qWarning("QSocketNotifier: Internal error");
449         return;
450     } else if (notifier->thread() != thread()
451                || thread() != QThread::currentThread()) {
452         qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread");
453         return;
454     }
455 #endif
456 
457     Q_D(QEventDispatcherGlib);
458 
459 
460     GPollFDWithQSocketNotifier *p = new GPollFDWithQSocketNotifier;
461     p->pollfd.fd = sockfd;
462     switch (type) {
463     case QSocketNotifier::Read:
464         p->pollfd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
465         break;
466     case QSocketNotifier::Write:
467         p->pollfd.events = G_IO_OUT | G_IO_ERR;
468         break;
469     case QSocketNotifier::Exception:
470         p->pollfd.events = G_IO_PRI | G_IO_ERR;
471         break;
472     }
473     p->socketNotifier = notifier;
474 
475     d->socketNotifierSource->pollfds.append(p);
476 
477     g_source_add_poll(&d->socketNotifierSource->source, &p->pollfd);
478 }
479 
unregisterSocketNotifier(QSocketNotifier * notifier)480 void QEventDispatcherGlib::unregisterSocketNotifier(QSocketNotifier *notifier)
481 {
482     Q_ASSERT(notifier);
483 #ifndef QT_NO_DEBUG
484     int sockfd = notifier->socket();
485     if (sockfd < 0) {
486         qWarning("QSocketNotifier: Internal error");
487         return;
488     } else if (notifier->thread() != thread()
489                || thread() != QThread::currentThread()) {
490         qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread");
491         return;
492     }
493 #endif
494 
495     Q_D(QEventDispatcherGlib);
496 
497     for (int i = 0; i < d->socketNotifierSource->pollfds.count(); ++i) {
498         GPollFDWithQSocketNotifier *p = d->socketNotifierSource->pollfds.at(i);
499         if (p->socketNotifier == notifier) {
500             // found it
501             g_source_remove_poll(&d->socketNotifierSource->source, &p->pollfd);
502 
503             d->socketNotifierSource->pollfds.removeAt(i);
504             delete p;
505 
506             return;
507         }
508     }
509 }
510 
registerTimer(int timerId,int interval,Qt::TimerType timerType,QObject * object)511 void QEventDispatcherGlib::registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object)
512 {
513 #ifndef QT_NO_DEBUG
514     if (timerId < 1 || interval < 0 || !object) {
515         qWarning("QEventDispatcherGlib::registerTimer: invalid arguments");
516         return;
517     } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
518         qWarning("QEventDispatcherGlib::registerTimer: timers cannot be started from another thread");
519         return;
520     }
521 #endif
522 
523     Q_D(QEventDispatcherGlib);
524     d->timerSource->timerList.registerTimer(timerId, interval, timerType, object);
525 }
526 
unregisterTimer(int timerId)527 bool QEventDispatcherGlib::unregisterTimer(int timerId)
528 {
529 #ifndef QT_NO_DEBUG
530     if (timerId < 1) {
531         qWarning("QEventDispatcherGlib::unregisterTimer: invalid argument");
532         return false;
533     } else if (thread() != QThread::currentThread()) {
534         qWarning("QEventDispatcherGlib::unregisterTimer: timers cannot be stopped from another thread");
535         return false;
536     }
537 #endif
538 
539     Q_D(QEventDispatcherGlib);
540     return d->timerSource->timerList.unregisterTimer(timerId);
541 }
542 
unregisterTimers(QObject * object)543 bool QEventDispatcherGlib::unregisterTimers(QObject *object)
544 {
545 #ifndef QT_NO_DEBUG
546     if (!object) {
547         qWarning("QEventDispatcherGlib::unregisterTimers: invalid argument");
548         return false;
549     } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
550         qWarning("QEventDispatcherGlib::unregisterTimers: timers cannot be stopped from another thread");
551         return false;
552     }
553 #endif
554 
555     Q_D(QEventDispatcherGlib);
556     return d->timerSource->timerList.unregisterTimers(object);
557 }
558 
registeredTimers(QObject * object) const559 QList<QEventDispatcherGlib::TimerInfo> QEventDispatcherGlib::registeredTimers(QObject *object) const
560 {
561     if (!object) {
562         qWarning("QEventDispatcherUNIX:registeredTimers: invalid argument");
563         return QList<TimerInfo>();
564     }
565 
566     Q_D(const QEventDispatcherGlib);
567     return d->timerSource->timerList.registeredTimers(object);
568 }
569 
remainingTime(int timerId)570 int QEventDispatcherGlib::remainingTime(int timerId)
571 {
572 #ifndef QT_NO_DEBUG
573     if (timerId < 1) {
574         qWarning("QEventDispatcherGlib::remainingTimeTime: invalid argument");
575         return -1;
576     }
577 #endif
578 
579     Q_D(QEventDispatcherGlib);
580     return d->timerSource->timerList.timerRemainingTime(timerId);
581 }
582 
interrupt()583 void QEventDispatcherGlib::interrupt()
584 {
585     wakeUp();
586 }
587 
wakeUp()588 void QEventDispatcherGlib::wakeUp()
589 {
590     Q_D(QEventDispatcherGlib);
591     d->postEventSource->serialNumber.ref();
592     g_main_context_wakeup(d->mainContext);
593 }
594 
flush()595 void QEventDispatcherGlib::flush()
596 {
597 }
598 
versionSupported()599 bool QEventDispatcherGlib::versionSupported()
600 {
601 #if !defined(GLIB_MAJOR_VERSION) || !defined(GLIB_MINOR_VERSION) || !defined(GLIB_MICRO_VERSION)
602     return false;
603 #else
604     return ((GLIB_MAJOR_VERSION << 16) + (GLIB_MINOR_VERSION << 8) + GLIB_MICRO_VERSION) >= 0x020301;
605 #endif
606 }
607 
QEventDispatcherGlib(QEventDispatcherGlibPrivate & dd,QObject * parent)608 QEventDispatcherGlib::QEventDispatcherGlib(QEventDispatcherGlibPrivate &dd, QObject *parent)
609     : QAbstractEventDispatcher(dd, parent)
610 {
611 }
612 
613 QT_END_NAMESPACE
614 
615 #include "moc_qeventdispatcher_glib_p.cpp"
616