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