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