Fixes high CPU usage on timer expiration when using glib event loop.
[profile/ivi/qtbase.git] / src / corelib / kernel / qeventdispatcher_glib.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qeventdispatcher_glib_p.h"
43 #include "qeventdispatcher_unix_p.h"
44
45 #include <private/qthread_p.h>
46
47 #include "qcoreapplication.h"
48 #include "qsocketnotifier.h"
49
50 #include <QtCore/qhash.h>
51 #include <QtCore/qlist.h>
52 #include <QtCore/qpair.h>
53
54 #include <glib.h>
55
56 QT_BEGIN_NAMESPACE
57
58 struct GPollFDWithQSocketNotifier
59 {
60     GPollFD pollfd;
61     QSocketNotifier *socketNotifier;
62 };
63
64 struct GSocketNotifierSource
65 {
66     GSource source;
67     QList<GPollFDWithQSocketNotifier *> pollfds;
68 };
69
70 static gboolean socketNotifierSourcePrepare(GSource *, gint *timeout)
71 {
72     if (timeout)
73         *timeout = -1;
74     return false;
75 }
76
77 static gboolean socketNotifierSourceCheck(GSource *source)
78 {
79     GSocketNotifierSource *src = reinterpret_cast<GSocketNotifierSource *>(source);
80
81     bool pending = false;
82     for (int i = 0; !pending && i < src->pollfds.count(); ++i) {
83         GPollFDWithQSocketNotifier *p = src->pollfds.at(i);
84
85         if (p->pollfd.revents & G_IO_NVAL) {
86             // disable the invalid socket notifier
87             static const char *t[] = { "Read", "Write", "Exception" };
88             qWarning("QSocketNotifier: Invalid socket %d and type '%s', disabling...",
89                      p->pollfd.fd, t[int(p->socketNotifier->type())]);
90             // ### note, modifies src->pollfds!
91             p->socketNotifier->setEnabled(false);
92         }
93
94         pending = ((p->pollfd.revents & p->pollfd.events) != 0);
95     }
96
97     return pending;
98 }
99
100 static gboolean socketNotifierSourceDispatch(GSource *source, GSourceFunc, gpointer)
101 {
102     QEvent event(QEvent::SockAct);
103
104     GSocketNotifierSource *src = reinterpret_cast<GSocketNotifierSource *>(source);
105     for (int i = 0; i < src->pollfds.count(); ++i) {
106         GPollFDWithQSocketNotifier *p = src->pollfds.at(i);
107
108         if ((p->pollfd.revents & p->pollfd.events) != 0)
109             QCoreApplication::sendEvent(p->socketNotifier, &event);
110     }
111
112     return true; // ??? don't remove, right?
113 }
114
115 static GSourceFuncs socketNotifierSourceFuncs = {
116     socketNotifierSourcePrepare,
117     socketNotifierSourceCheck,
118     socketNotifierSourceDispatch,
119     NULL,
120     NULL,
121     NULL
122 };
123
124 struct GTimerSource
125 {
126     GSource source;
127     QTimerInfoList timerList;
128     QEventLoop::ProcessEventsFlags processEventsFlags;
129     bool runWithIdlePriority;
130 };
131
132 static gboolean timerSourcePrepareHelper(GTimerSource *src, gint *timeout)
133 {
134     timeval tv = { 0l, 0l };
135     if (!(src->processEventsFlags & QEventLoop::X11ExcludeTimers) && src->timerList.timerWait(tv))
136         *timeout = (tv.tv_sec * 1000) + ((tv.tv_usec + 999) / 1000);
137     else
138         *timeout = -1;
139
140     return (*timeout == 0);
141 }
142
143 static gboolean timerSourceCheckHelper(GTimerSource *src)
144 {
145     if (src->timerList.isEmpty()
146         || (src->processEventsFlags & QEventLoop::X11ExcludeTimers))
147         return false;
148
149     if (src->timerList.updateCurrentTime() < src->timerList.first()->timeout)
150         return false;
151
152     return true;
153 }
154
155 static gboolean timerSourcePrepare(GSource *source, gint *timeout)
156 {
157     gint dummy;
158     if (!timeout)
159         timeout = &dummy;
160
161     GTimerSource *src = reinterpret_cast<GTimerSource *>(source);
162     if (src->runWithIdlePriority) {
163         if (timeout)
164             *timeout = -1;
165         return false;
166     }
167
168     return timerSourcePrepareHelper(src, timeout);
169 }
170
171 static gboolean timerSourceCheck(GSource *source)
172 {
173     GTimerSource *src = reinterpret_cast<GTimerSource *>(source);
174     if (src->runWithIdlePriority)
175         return false;
176     return timerSourceCheckHelper(src);
177 }
178
179 static gboolean timerSourceDispatch(GSource *source, GSourceFunc, gpointer)
180 {
181     GTimerSource *timerSource = reinterpret_cast<GTimerSource *>(source);
182     if (timerSource->processEventsFlags & QEventLoop::X11ExcludeTimers)
183         return true;
184     timerSource->runWithIdlePriority = true;
185     (void) timerSource->timerList.activateTimers();
186     return true; // ??? don't remove, right again?
187 }
188
189 static GSourceFuncs timerSourceFuncs = {
190     timerSourcePrepare,
191     timerSourceCheck,
192     timerSourceDispatch,
193     NULL,
194     NULL,
195     NULL
196 };
197
198 struct GIdleTimerSource
199 {
200     GSource source;
201     GTimerSource *timerSource;
202 };
203
204 static gboolean idleTimerSourcePrepare(GSource *source, gint *timeout)
205 {
206     GIdleTimerSource *idleTimerSource = reinterpret_cast<GIdleTimerSource *>(source);
207     GTimerSource *timerSource = idleTimerSource->timerSource;
208     if (!timerSource->runWithIdlePriority) {
209         // Yield to the normal priority timer source
210         if (timeout)
211             *timeout = -1;
212         return false;
213     }
214
215     return timerSourcePrepareHelper(timerSource, timeout);
216 }
217
218 static gboolean idleTimerSourceCheck(GSource *source)
219 {
220     GIdleTimerSource *idleTimerSource = reinterpret_cast<GIdleTimerSource *>(source);
221     GTimerSource *timerSource = idleTimerSource->timerSource;
222     if (!timerSource->runWithIdlePriority) {
223         // Yield to the normal priority timer source
224         return false;
225     }
226     return timerSourceCheckHelper(timerSource);
227 }
228
229 static gboolean idleTimerSourceDispatch(GSource *source, GSourceFunc, gpointer)
230 {
231     GTimerSource *timerSource = reinterpret_cast<GIdleTimerSource *>(source)->timerSource;
232     (void) timerSourceDispatch(&timerSource->source, 0, 0);
233     return true;
234 }
235
236 static GSourceFuncs idleTimerSourceFuncs = {
237     idleTimerSourcePrepare,
238     idleTimerSourceCheck,
239     idleTimerSourceDispatch,
240     NULL,
241     NULL,
242     NULL
243 };
244
245 struct GPostEventSource
246 {
247     GSource source;
248     QAtomicInt serialNumber;
249     int lastSerialNumber;
250     QEventDispatcherGlibPrivate *d;
251 };
252
253 static gboolean postEventSourcePrepare(GSource *s, gint *timeout)
254 {
255     QThreadData *data = QThreadData::current();
256     if (!data)
257         return false;
258
259     gint dummy;
260     if (!timeout)
261         timeout = &dummy;
262     *timeout = data->canWait ? -1 : 0;
263
264     GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);
265     return (!data->canWait
266             || (source->serialNumber.load() != source->lastSerialNumber));
267 }
268
269 static gboolean postEventSourceCheck(GSource *source)
270 {
271     return postEventSourcePrepare(source, 0);
272 }
273
274 static gboolean postEventSourceDispatch(GSource *s, GSourceFunc, gpointer)
275 {
276     GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);
277     source->lastSerialNumber = source->serialNumber.load();
278     QCoreApplication::sendPostedEvents();
279     source->d->runTimersOnceWithNormalPriority();
280     return true; // i dunno, george...
281 }
282
283 static GSourceFuncs postEventSourceFuncs = {
284     postEventSourcePrepare,
285     postEventSourceCheck,
286     postEventSourceDispatch,
287     NULL,
288     NULL,
289     NULL
290 };
291
292
293 QEventDispatcherGlibPrivate::QEventDispatcherGlibPrivate(GMainContext *context)
294     : mainContext(context)
295 {
296     if (qgetenv("QT_NO_THREADED_GLIB").isEmpty()) {
297         static QBasicMutex mutex;
298         QMutexLocker locker(&mutex);
299         if (!g_thread_supported())
300             g_thread_init(NULL);
301     }
302
303     if (mainContext) {
304         g_main_context_ref(mainContext);
305     } else {
306         QCoreApplication *app = QCoreApplication::instance();
307         if (app && QThread::currentThread() == app->thread()) {
308             mainContext = g_main_context_default();
309             g_main_context_ref(mainContext);
310         } else {
311             mainContext = g_main_context_new();
312         }
313     }
314
315 #if GLIB_CHECK_VERSION (2, 22, 0)
316     g_main_context_push_thread_default (mainContext);
317 #endif
318
319     // setup post event source
320     postEventSource = reinterpret_cast<GPostEventSource *>(g_source_new(&postEventSourceFuncs,
321                                                                         sizeof(GPostEventSource)));
322     postEventSource->serialNumber.store(1);
323     postEventSource->d = this;
324     g_source_set_can_recurse(&postEventSource->source, true);
325     g_source_attach(&postEventSource->source, mainContext);
326
327     // setup socketNotifierSource
328     socketNotifierSource =
329         reinterpret_cast<GSocketNotifierSource *>(g_source_new(&socketNotifierSourceFuncs,
330                                                                sizeof(GSocketNotifierSource)));
331     (void) new (&socketNotifierSource->pollfds) QList<GPollFDWithQSocketNotifier *>();
332     g_source_set_can_recurse(&socketNotifierSource->source, true);
333     g_source_attach(&socketNotifierSource->source, mainContext);
334
335     // setup normal and idle timer sources
336     timerSource = reinterpret_cast<GTimerSource *>(g_source_new(&timerSourceFuncs,
337                                                                 sizeof(GTimerSource)));
338     (void) new (&timerSource->timerList) QTimerInfoList();
339     timerSource->processEventsFlags = QEventLoop::AllEvents;
340     timerSource->runWithIdlePriority = false;
341     g_source_set_can_recurse(&timerSource->source, true);
342     g_source_attach(&timerSource->source, mainContext);
343
344     idleTimerSource = reinterpret_cast<GIdleTimerSource *>(g_source_new(&idleTimerSourceFuncs,
345                                                                         sizeof(GIdleTimerSource)));
346     idleTimerSource->timerSource = timerSource;
347     g_source_set_can_recurse(&idleTimerSource->source, true);
348     g_source_set_priority(&idleTimerSource->source, G_PRIORITY_DEFAULT_IDLE);
349     g_source_attach(&idleTimerSource->source, mainContext);
350 }
351
352 void QEventDispatcherGlibPrivate::runTimersOnceWithNormalPriority()
353 {
354     timerSource->runWithIdlePriority = false;
355 }
356
357 QEventDispatcherGlib::QEventDispatcherGlib(QObject *parent)
358     : QAbstractEventDispatcher(*(new QEventDispatcherGlibPrivate), parent)
359 {
360 }
361
362 QEventDispatcherGlib::QEventDispatcherGlib(GMainContext *mainContext, QObject *parent)
363     : QAbstractEventDispatcher(*(new QEventDispatcherGlibPrivate(mainContext)), parent)
364 { }
365
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 = 0;
376     g_source_destroy(&d->idleTimerSource->source);
377     g_source_unref(&d->idleTimerSource->source);
378     d->idleTimerSource = 0;
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 = 0;
390
391     // destroy post event source
392     g_source_destroy(&d->postEventSource->source);
393     g_source_unref(&d->postEventSource->source);
394     d->postEventSource = 0;
395
396     Q_ASSERT(d->mainContext != 0);
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 = 0;
402 }
403
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
435 bool QEventDispatcherGlib::hasPendingEvents()
436 {
437     Q_D(QEventDispatcherGlib);
438     return g_main_context_pending(d->mainContext);
439 }
440
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
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
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("QObject::startTimer: 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
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("QObject::killTimer: 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
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("QObject::killTimers: 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
559 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
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
583 void QEventDispatcherGlib::interrupt()
584 {
585     wakeUp();
586 }
587
588 void QEventDispatcherGlib::wakeUp()
589 {
590     Q_D(QEventDispatcherGlib);
591     d->postEventSource->serialNumber.ref();
592     g_main_context_wakeup(d->mainContext);
593 }
594
595 void QEventDispatcherGlib::flush()
596 {
597 }
598
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
608 QEventDispatcherGlib::QEventDispatcherGlib(QEventDispatcherGlibPrivate &dd, QObject *parent)
609     : QAbstractEventDispatcher(dd, parent)
610 {
611 }
612
613 QT_END_NAMESPACE