Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qeventdispatcher_glib.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
7
8#include <private/qthread_p.h>
9
12
13#include <QtCore/qlist.h>
14
15#include <QtCore/q26numeric.h>
16
17#include <glib.h>
18
19using namespace std::chrono;
20using namespace std::chrono_literals;
21
22QT_BEGIN_NAMESPACE
23
24struct GPollFDWithQSocketNotifier
25{
26 GPollFD pollfd;
27 QSocketNotifier *socketNotifier;
28};
29
36
37static gboolean socketNotifierSourcePrepare(GSource *, gint *timeout)
38{
39 if (timeout)
40 *timeout = -1;
41 return false;
42}
43
44static gboolean socketNotifierSourceCheck(GSource *source)
45{
46 GSocketNotifierSource *src = reinterpret_cast<GSocketNotifierSource *>(source);
47
48 bool pending = false;
49 for (qsizetype i = 0; !pending && i < src->pollfds.size(); ++i) {
50 GPollFDWithQSocketNotifier *p = src->pollfds.at(i);
51
52 if (p->pollfd.revents & G_IO_NVAL) {
53 // disable the invalid socket notifier
54 const char * const t[] = { "Read", "Write", "Exception" };
55 qWarning("QSocketNotifier: Invalid socket %d and type '%s', disabling...",
56 p->pollfd.fd, t[p->socketNotifier->type()]);
57 // ### note, modifies src->pollfds, turning this loop quadratic (QTBUG-143853)!
58 p->socketNotifier->setEnabled(false);
59 i--;
60 } else {
61 pending = pending || ((p->pollfd.revents & p->pollfd.events) != 0);
62 }
63 }
64
65 return pending;
66}
67
69{
70 QEvent event(QEvent::SockAct);
71
72 GSocketNotifierSource *src = reinterpret_cast<GSocketNotifierSource *>(source);
73 for (src->activeNotifierPos = 0; src->activeNotifierPos < src->pollfds.size();
74 ++src->activeNotifierPos) {
75 GPollFDWithQSocketNotifier *p = src->pollfds.at(src->activeNotifierPos);
76
77 if ((p->pollfd.revents & p->pollfd.events) != 0)
78 QCoreApplication::sendEvent(p->socketNotifier, &event);
79 }
80
81 return true; // ??? don't remove, right?
82}
83
84Q_CONSTINIT static GSourceFuncs socketNotifierSourceFuncs = {
85 socketNotifierSourcePrepare,
86 socketNotifierSourceCheck,
87 socketNotifierSourceDispatch,
88 nullptr,
89 nullptr,
90 nullptr
91};
92
100
102{
103 if (src->processEventsFlags & QEventLoop::X11ExcludeTimers) {
104 *timeout = -1;
105 return true;
106 }
107
108 auto remaining = src->timerList.timerWait().value_or(-1ms);
109 *timeout = q26::saturate_cast<gint>(ceil<milliseconds>(remaining).count());
110
111 return (*timeout == 0);
112}
113
115{
116 if (src->timerList.isEmpty()
117 || (src->processEventsFlags & QEventLoop::X11ExcludeTimers))
118 return false;
119
120 return !src->timerList.hasPendingTimers();
121}
122
123static gboolean timerSourcePrepare(GSource *source, gint *timeout)
124{
125 gint dummy;
126 if (!timeout)
127 timeout = &dummy;
128
129 GTimerSource *src = reinterpret_cast<GTimerSource *>(source);
130 if (src->runWithIdlePriority) {
131 if (timeout)
132 *timeout = -1;
133 return false;
134 }
135
136 return timerSourcePrepareHelper(src, timeout);
137}
138
139static gboolean timerSourceCheck(GSource *source)
140{
141 GTimerSource *src = reinterpret_cast<GTimerSource *>(source);
143 return false;
144 return timerSourceCheckHelper(src);
145}
146
148{
149 GTimerSource *timerSource = reinterpret_cast<GTimerSource *>(source);
150 if (timerSource->processEventsFlags & QEventLoop::X11ExcludeTimers)
151 return true;
152 timerSource->runWithIdlePriority = true;
153 (void) timerSource->timerList.activateTimers();
154 return true; // ??? don't remove, right again?
155}
156
157Q_CONSTINIT static GSourceFuncs timerSourceFuncs = {
158 timerSourcePrepare,
159 timerSourceCheck,
160 timerSourceDispatch,
161 nullptr,
162 nullptr,
163 nullptr
164};
165
171
172static gboolean idleTimerSourcePrepare(GSource *source, gint *timeout)
173{
174 GIdleTimerSource *idleTimerSource = reinterpret_cast<GIdleTimerSource *>(source);
175 GTimerSource *timerSource = idleTimerSource->timerSource;
176 if (!timerSource->runWithIdlePriority) {
177 // Yield to the normal priority timer source
178 if (timeout)
179 *timeout = -1;
180 return false;
181 }
182
183 return timerSourcePrepareHelper(timerSource, timeout);
184}
185
186static gboolean idleTimerSourceCheck(GSource *source)
187{
188 GIdleTimerSource *idleTimerSource = reinterpret_cast<GIdleTimerSource *>(source);
189 GTimerSource *timerSource = idleTimerSource->timerSource;
190 if (!timerSource->runWithIdlePriority) {
191 // Yield to the normal priority timer source
192 return false;
193 }
194 return timerSourceCheckHelper(timerSource);
195}
196
198{
199 GTimerSource *timerSource = reinterpret_cast<GIdleTimerSource *>(source)->timerSource;
200 (void) timerSourceDispatch(&timerSource->source, nullptr, nullptr);
201 return true;
202}
203
204Q_CONSTINIT static GSourceFuncs idleTimerSourceFuncs = {
205 idleTimerSourcePrepare,
206 idleTimerSourceCheck,
207 idleTimerSourceDispatch,
208 nullptr,
209 nullptr,
210 nullptr
211};
212
214{
218 QEventDispatcherGlibPrivate *d;
219};
220
221static gboolean postEventSourcePrepare(GSource *s, gint *timeout)
222{
223 QThreadData *data = QThreadData::current();
224 if (!data)
225 return false;
226
227 gint dummy;
228 if (!timeout)
229 timeout = &dummy;
230 const bool canWait = data->canWaitLocked();
231 *timeout = canWait ? -1 : 0;
232
233 GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);
234 source->d->wakeUpCalled = source->serialNumber.loadRelaxed() != source->lastSerialNumber;
235 return !canWait || source->d->wakeUpCalled;
236}
237
238static gboolean postEventSourceCheck(GSource *source)
239{
240 return postEventSourcePrepare(source, nullptr);
241}
242
244{
245 GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);
246 source->lastSerialNumber = source->serialNumber.loadRelaxed();
247 QCoreApplication::sendPostedEvents();
248 source->d->runTimersOnceWithNormalPriority();
249 return true; // i dunno, george...
250}
251
252Q_CONSTINIT static GSourceFuncs postEventSourceFuncs = {
253 postEventSourcePrepare,
254 postEventSourceCheck,
255 postEventSourceDispatch,
256 nullptr,
257 nullptr,
258 nullptr
259};
260
261
262QEventDispatcherGlibPrivate::QEventDispatcherGlibPrivate(GMainContext *context)
263 : mainContext(context)
264{
265#if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 32
266 if (qEnvironmentVariableIsEmpty("QT_NO_THREADED_GLIB")) {
267 Q_CONSTINIT static QBasicMutex mutex;
268 QMutexLocker locker(&mutex);
269 if (!g_thread_supported())
270 g_thread_init(NULL);
271 }
272#endif
273
274 if (mainContext) {
275 g_main_context_ref(mainContext);
276 } else {
277 QCoreApplication *app = QCoreApplication::instance();
278 if (app && QThread::currentThread() == app->thread()) {
279 mainContext = g_main_context_default();
280 g_main_context_ref(mainContext);
281 } else {
282 mainContext = g_main_context_new();
283 }
284 }
285
286#if GLIB_CHECK_VERSION (2, 22, 0)
287 g_main_context_push_thread_default (mainContext);
288#endif
289
290 // setup post event source
291 GSource *source = g_source_new(&postEventSourceFuncs, sizeof(GPostEventSource));
292 g_source_set_name(source, "[Qt] GPostEventSource");
293 postEventSource = reinterpret_cast<GPostEventSource *>(source);
294
295 postEventSource->serialNumber.storeRelaxed(1);
296 postEventSource->d = this;
297 g_source_set_can_recurse(&postEventSource->source, true);
298 g_source_attach(&postEventSource->source, mainContext);
299
300 // setup socketNotifierSource
301 source = g_source_new(&socketNotifierSourceFuncs, sizeof(GSocketNotifierSource));
302 g_source_set_name(source, "[Qt] GSocketNotifierSource");
303 socketNotifierSource = reinterpret_cast<GSocketNotifierSource *>(source);
304 (void) new (&socketNotifierSource->pollfds) QList<GPollFDWithQSocketNotifier *>();
305 g_source_set_can_recurse(&socketNotifierSource->source, true);
306 g_source_attach(&socketNotifierSource->source, mainContext);
307
308 // setup normal and idle timer sources
309 source = g_source_new(&timerSourceFuncs, sizeof(GTimerSource));
310 g_source_set_name(source, "[Qt] GTimerSource");
311 timerSource = reinterpret_cast<GTimerSource *>(source);
312 (void) new (&timerSource->timerList) QTimerInfoList();
313 timerSource->processEventsFlags = QEventLoop::AllEvents;
314 timerSource->runWithIdlePriority = false;
315 g_source_set_can_recurse(&timerSource->source, true);
316 g_source_attach(&timerSource->source, mainContext);
317
318 source = g_source_new(&idleTimerSourceFuncs, sizeof(GIdleTimerSource));
319 g_source_set_name(source, "[Qt] GIdleTimerSource");
320 idleTimerSource = reinterpret_cast<GIdleTimerSource *>(source);
321 idleTimerSource->timerSource = timerSource;
322 g_source_set_can_recurse(&idleTimerSource->source, true);
323 g_source_attach(&idleTimerSource->source, mainContext);
324}
325
326QEventDispatcherGlibPrivate::~QEventDispatcherGlibPrivate()
327 = default;
328
329void QEventDispatcherGlibPrivate::runTimersOnceWithNormalPriority()
330{
331 timerSource->runWithIdlePriority = false;
332}
333
334QEventDispatcherGlib::QEventDispatcherGlib(QObject *parent)
335 : QAbstractEventDispatcherV2(*(new QEventDispatcherGlibPrivate), parent)
336{
337}
338
339QEventDispatcherGlib::QEventDispatcherGlib(GMainContext *mainContext, QObject *parent)
340 : QAbstractEventDispatcherV2(*(new QEventDispatcherGlibPrivate(mainContext)), parent)
341{ }
342
343QEventDispatcherGlib::~QEventDispatcherGlib()
344{
345 Q_D(QEventDispatcherGlib);
346
347 // destroy all timer sources
348 d->timerSource->timerList.clearTimers();
349 d->timerSource->timerList.~QTimerInfoList();
350 g_source_destroy(&d->timerSource->source);
351 g_source_unref(&d->timerSource->source);
352 d->timerSource = nullptr;
353 g_source_destroy(&d->idleTimerSource->source);
354 g_source_unref(&d->idleTimerSource->source);
355 d->idleTimerSource = nullptr;
356
357 // destroy socket notifier source
358 for (qsizetype i = 0; i < d->socketNotifierSource->pollfds.size(); ++i) {
359 GPollFDWithQSocketNotifier *p = d->socketNotifierSource->pollfds.at(i);
360 g_source_remove_poll(&d->socketNotifierSource->source, &p->pollfd);
361 delete p;
362 }
363 d->socketNotifierSource->pollfds.~QList<GPollFDWithQSocketNotifier *>();
364 g_source_destroy(&d->socketNotifierSource->source);
365 g_source_unref(&d->socketNotifierSource->source);
366 d->socketNotifierSource = nullptr;
367
368 // destroy post event source
369 g_source_destroy(&d->postEventSource->source);
370 g_source_unref(&d->postEventSource->source);
371 d->postEventSource = nullptr;
372
373 Q_ASSERT(d->mainContext != nullptr);
374#if GLIB_CHECK_VERSION (2, 22, 0)
375 g_main_context_pop_thread_default (d->mainContext);
376#endif
377 g_main_context_unref(d->mainContext);
378 d->mainContext = nullptr;
379}
380
381bool QEventDispatcherGlib::processEvents(QEventLoop::ProcessEventsFlags flags)
382{
383 Q_D(QEventDispatcherGlib);
384
385 const bool canWait = flags.testAnyFlag(QEventLoop::WaitForMoreEvents);
386 if (canWait)
387 emit aboutToBlock();
388 else
389 emit awake();
390
391 // tell postEventSourcePrepare() and timerSource about any new flags
392 QEventLoop::ProcessEventsFlags savedFlags = d->timerSource->processEventsFlags;
393 d->timerSource->processEventsFlags = flags;
394
395 if (!(flags & QEventLoop::EventLoopExec)) {
396 // force timers to be sent at normal priority
397 d->timerSource->runWithIdlePriority = false;
398 }
399
400 bool result = g_main_context_iteration(d->mainContext, canWait);
401 while (!result && canWait)
402 result = g_main_context_iteration(d->mainContext, canWait);
403
404 d->timerSource->processEventsFlags = savedFlags;
405
406 if (canWait)
407 emit awake();
408
409 return result;
410}
411
412void QEventDispatcherGlib::registerSocketNotifier(QSocketNotifier *notifier)
413{
414 Q_ASSERT(notifier);
415 int sockfd = int(notifier->socket());
416 const auto type = notifier->type();
417#ifndef QT_NO_DEBUG
418 if (sockfd < 0) {
419 qWarning("QSocketNotifier: Internal error");
420 return;
421 } else if (notifier->thread() != thread()
422 || thread() != QThread::currentThread()) {
423 qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread");
424 return;
425 }
426#endif
427
428 Q_D(QEventDispatcherGlib);
429
430
431 GPollFDWithQSocketNotifier *p = new GPollFDWithQSocketNotifier;
432 p->pollfd.fd = sockfd;
433 switch (type) {
434 case QSocketNotifier::Read:
435 p->pollfd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
436 break;
437 case QSocketNotifier::Write:
438 p->pollfd.events = G_IO_OUT | G_IO_HUP | G_IO_ERR;
439 break;
440 case QSocketNotifier::Exception:
441 p->pollfd.events = G_IO_PRI | G_IO_HUP | G_IO_ERR;
442 break;
443 }
444 p->socketNotifier = notifier;
445
446 d->socketNotifierSource->pollfds.append(p);
447
448 g_source_add_poll(&d->socketNotifierSource->source, &p->pollfd);
449}
450
451void QEventDispatcherGlib::unregisterSocketNotifier(QSocketNotifier *notifier)
452{
453 Q_ASSERT(notifier);
454#ifndef QT_NO_DEBUG
455 if (notifier->socket() < 0) {
456 qWarning("QSocketNotifier: Internal error");
457 return;
458 } else if (notifier->thread() != thread()
459 || thread() != QThread::currentThread()) {
460 qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread");
461 return;
462 }
463#endif
464
465 Q_D(QEventDispatcherGlib);
466
467 for (qsizetype i = 0; i < d->socketNotifierSource->pollfds.size(); ++i) {
468 GPollFDWithQSocketNotifier *p = d->socketNotifierSource->pollfds.at(i);
469 if (p->socketNotifier == notifier) {
470 // found it
471 g_source_remove_poll(&d->socketNotifierSource->source, &p->pollfd);
472
473 d->socketNotifierSource->pollfds.removeAt(i);
474 delete p;
475
476 // Keep a position in the list for the next item.
477 if (i <= d->socketNotifierSource->activeNotifierPos)
478 --d->socketNotifierSource->activeNotifierPos;
479
480 return;
481 }
482 }
483}
484
485void QEventDispatcherGlib::registerTimer(Qt::TimerId timerId, Duration interval,
486 Qt::TimerType timerType, QObject *object)
487{
488#ifndef QT_NO_DEBUG
489 if (qToUnderlying(timerId) < 1 || interval < 0ns || !object) {
490 qWarning("QEventDispatcherGlib::registerTimer: invalid arguments");
491 return;
492 } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
493 qWarning("QEventDispatcherGlib::registerTimer: timers cannot be started from another thread");
494 return;
495 }
496#endif
497
498 Q_D(QEventDispatcherGlib);
499 d->timerSource->timerList.registerTimer(timerId, interval, timerType, object);
500}
501
502bool QEventDispatcherGlib::unregisterTimer(Qt::TimerId timerId)
503{
504#ifndef QT_NO_DEBUG
505 if (qToUnderlying(timerId) < 1) {
506 qWarning("QEventDispatcherGlib::unregisterTimer: invalid argument");
507 return false;
508 } else if (thread() != QThread::currentThread()) {
509 qWarning("QEventDispatcherGlib::unregisterTimer: timers cannot be stopped from another thread");
510 return false;
511 }
512#endif
513
514 Q_D(QEventDispatcherGlib);
515 return d->timerSource->timerList.unregisterTimer(timerId);
516}
517
518bool QEventDispatcherGlib::unregisterTimers(QObject *object)
519{
520#ifndef QT_NO_DEBUG
521 if (!object) {
522 qWarning("QEventDispatcherGlib::unregisterTimers: invalid argument");
523 return false;
524 } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
525 qWarning("QEventDispatcherGlib::unregisterTimers: timers cannot be stopped from another thread");
526 return false;
527 }
528#endif
529
530 Q_D(QEventDispatcherGlib);
531 return d->timerSource->timerList.unregisterTimers(object);
532}
533
534QList<QEventDispatcherGlib::TimerInfoV2> QEventDispatcherGlib::timersForObject(QObject *object) const
535{
536#ifndef QT_NO_DEBUG
537 if (!object) {
538 qWarning("QEventDispatcherGlib:timersForObject: invalid argument");
539 return {};
540 }
541#endif
542
543 Q_D(const QEventDispatcherGlib);
544 return d->timerSource->timerList.registeredTimers(object);
545}
546
547QEventDispatcherGlib::Duration QEventDispatcherGlib::remainingTime(Qt::TimerId timerId) const
548{
549#ifndef QT_NO_DEBUG
550 if (qToUnderlying(timerId) < 1) {
551 qWarning("QEventDispatcherGlib::remainingTimeTime: invalid argument");
552 return Duration::min();
553 }
554#endif
555
556 Q_D(const QEventDispatcherGlib);
557 return d->timerSource->timerList.remainingDuration(timerId);
558}
559
560void QEventDispatcherGlib::interrupt()
561{
562 wakeUp();
563}
564
565void QEventDispatcherGlib::wakeUp()
566{
567 Q_D(QEventDispatcherGlib);
568 d->postEventSource->serialNumber.ref();
569 g_main_context_wakeup(d->mainContext);
570}
571
572bool QEventDispatcherGlib::versionSupported()
573{
574#if !defined(GLIB_MAJOR_VERSION) || !defined(GLIB_MINOR_VERSION) || !defined(GLIB_MICRO_VERSION)
575 return false;
576#else
577 return ((GLIB_MAJOR_VERSION << 16) + (GLIB_MINOR_VERSION << 8) + GLIB_MICRO_VERSION) >= 0x020301;
578#endif
579}
580
581QEventDispatcherGlib::QEventDispatcherGlib(QEventDispatcherGlibPrivate &dd, QObject *parent)
582 : QAbstractEventDispatcherV2(dd, parent)
583{
584}
585
586QT_END_NAMESPACE
587
588#include "moc_qeventdispatcher_glib_p.cpp"
static gboolean timerSourcePrepareHelper(GTimerSource *src, gint *timeout)
static gboolean postEventSourceDispatch(GSource *s, GSourceFunc, gpointer)
static gboolean timerSourceCheck(GSource *source)
static gboolean idleTimerSourcePrepare(GSource *source, gint *timeout)
static gboolean socketNotifierSourcePrepare(GSource *, gint *timeout)
static gboolean idleTimerSourceDispatch(GSource *source, GSourceFunc, gpointer)
static gboolean postEventSourcePrepare(GSource *s, gint *timeout)
static gboolean idleTimerSourceCheck(GSource *source)
static gboolean timerSourceCheckHelper(GTimerSource *src)
static gboolean socketNotifierSourceCheck(GSource *source)
static gboolean postEventSourceCheck(GSource *source)
static gboolean socketNotifierSourceDispatch(GSource *source, GSourceFunc, gpointer)
static gboolean timerSourcePrepare(GSource *source, gint *timeout)
static gboolean timerSourceDispatch(GSource *source, GSourceFunc, gpointer)
QEventDispatcherGlibPrivate * d
QList< GPollFDWithQSocketNotifier * > pollfds
QTimerInfoList timerList
QEventLoop::ProcessEventsFlags processEventsFlags