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::saturating_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
221
222static gboolean postEventSourcePrepare(GSource *s, gint *timeout)
223{
224 QThreadData *data = QThreadData::current();
225 if (!data)
226 return false;
227
228 gint dummy;
229 if (!timeout)
230 timeout = &dummy;
231 const bool canWait = data->canWaitLocked();
232 *timeout = canWait ? -1 : 0;
233
234 GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);
235 source->d->wakeUpCalled = source->serialNumber.loadRelaxed() != source->lastSerialNumber;
236 return !canWait || source->d->wakeUpCalled;
237}
238
239static gboolean postEventSourceCheck(GSource *source)
240{
241 return postEventSourcePrepare(source, nullptr);
242}
243
245{
246 GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);
247 source->pendingSerialNumber = source->serialNumber.loadRelaxed();
248 QCoreApplication::sendPostedEvents();
250 source->d->runTimersOnceWithNormalPriority();
251 return true; // i dunno, george...
252}
253
254Q_CONSTINIT static GSourceFuncs postEventSourceFuncs = {
255 postEventSourcePrepare,
256 postEventSourceCheck,
257 postEventSourceDispatch,
258 nullptr,
259 nullptr,
260 nullptr
261};
262
263
264QEventDispatcherGlibPrivate::QEventDispatcherGlibPrivate(GMainContext *context)
265 : mainContext(context)
266{
267#if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 32
268 if (qEnvironmentVariableIsEmpty("QT_NO_THREADED_GLIB")) {
269 Q_CONSTINIT static QBasicMutex mutex;
270 QMutexLocker locker(&mutex);
271 if (!g_thread_supported())
272 g_thread_init(NULL);
273 }
274#endif
275
276 if (mainContext) {
277 g_main_context_ref(mainContext);
278 } else {
279 QCoreApplication *app = QCoreApplication::instance();
280 if (app && QThread::currentThread() == app->thread()) {
281 mainContext = g_main_context_default();
282 g_main_context_ref(mainContext);
283 } else {
284 mainContext = g_main_context_new();
285 }
286 }
287
288#if GLIB_CHECK_VERSION (2, 22, 0)
289 g_main_context_push_thread_default (mainContext);
290#endif
291
292 // setup post event source
293 GSource *source = g_source_new(&postEventSourceFuncs, sizeof(GPostEventSource));
294 g_source_set_name(source, "[Qt] GPostEventSource");
295 postEventSource = reinterpret_cast<GPostEventSource *>(source);
296
297 postEventSource->serialNumber.storeRelaxed(1);
298 postEventSource->d = this;
299 g_source_set_can_recurse(&postEventSource->source, true);
300 g_source_attach(&postEventSource->source, mainContext);
301
302 // setup socketNotifierSource
303 source = g_source_new(&socketNotifierSourceFuncs, sizeof(GSocketNotifierSource));
304 g_source_set_name(source, "[Qt] GSocketNotifierSource");
305 socketNotifierSource = reinterpret_cast<GSocketNotifierSource *>(source);
306 (void) new (&socketNotifierSource->pollfds) QList<GPollFDWithQSocketNotifier *>();
307 g_source_set_can_recurse(&socketNotifierSource->source, true);
308 g_source_attach(&socketNotifierSource->source, mainContext);
309
310 // setup normal and idle timer sources
311 source = g_source_new(&timerSourceFuncs, sizeof(GTimerSource));
312 g_source_set_name(source, "[Qt] GTimerSource");
313 timerSource = reinterpret_cast<GTimerSource *>(source);
314 (void) new (&timerSource->timerList) QTimerInfoList();
315 timerSource->processEventsFlags = QEventLoop::AllEvents;
316 timerSource->runWithIdlePriority = false;
317 g_source_set_can_recurse(&timerSource->source, true);
318 g_source_attach(&timerSource->source, mainContext);
319
320 source = g_source_new(&idleTimerSourceFuncs, sizeof(GIdleTimerSource));
321 g_source_set_name(source, "[Qt] GIdleTimerSource");
322 idleTimerSource = reinterpret_cast<GIdleTimerSource *>(source);
323 idleTimerSource->timerSource = timerSource;
324 g_source_set_can_recurse(&idleTimerSource->source, true);
325 g_source_attach(&idleTimerSource->source, mainContext);
326}
327
328QEventDispatcherGlibPrivate::~QEventDispatcherGlibPrivate()
329 = default;
330
331void QEventDispatcherGlibPrivate::runTimersOnceWithNormalPriority()
332{
333 timerSource->runWithIdlePriority = false;
334}
335
336QEventDispatcherGlib::QEventDispatcherGlib(QObject *parent)
337 : QAbstractEventDispatcherV2(*(new QEventDispatcherGlibPrivate), parent)
338{
339}
340
341QEventDispatcherGlib::QEventDispatcherGlib(GMainContext *mainContext, QObject *parent)
342 : QAbstractEventDispatcherV2(*(new QEventDispatcherGlibPrivate(mainContext)), parent)
343{ }
344
345QEventDispatcherGlib::~QEventDispatcherGlib()
346{
347 Q_D(QEventDispatcherGlib);
348
349 // destroy all timer sources
350 d->timerSource->timerList.clearTimers();
351 d->timerSource->timerList.~QTimerInfoList();
352 g_source_destroy(&d->timerSource->source);
353 g_source_unref(&d->timerSource->source);
354 d->timerSource = nullptr;
355 g_source_destroy(&d->idleTimerSource->source);
356 g_source_unref(&d->idleTimerSource->source);
357 d->idleTimerSource = nullptr;
358
359 // destroy socket notifier source
360 for (qsizetype i = 0; i < d->socketNotifierSource->pollfds.size(); ++i) {
361 GPollFDWithQSocketNotifier *p = d->socketNotifierSource->pollfds.at(i);
362 g_source_remove_poll(&d->socketNotifierSource->source, &p->pollfd);
363 delete p;
364 }
365 d->socketNotifierSource->pollfds.~QList<GPollFDWithQSocketNotifier *>();
366 g_source_destroy(&d->socketNotifierSource->source);
367 g_source_unref(&d->socketNotifierSource->source);
368 d->socketNotifierSource = nullptr;
369
370 // destroy post event source
371 g_source_destroy(&d->postEventSource->source);
372 g_source_unref(&d->postEventSource->source);
373 d->postEventSource = nullptr;
374
375 Q_ASSERT(d->mainContext != nullptr);
376#if GLIB_CHECK_VERSION (2, 22, 0)
377 g_main_context_pop_thread_default (d->mainContext);
378#endif
379 g_main_context_unref(d->mainContext);
380 d->mainContext = nullptr;
381}
382
383bool QEventDispatcherGlib::processEvents(QEventLoop::ProcessEventsFlags flags)
384{
385 Q_D(QEventDispatcherGlib);
386
387 const bool canWait = flags.testAnyFlag(QEventLoop::WaitForMoreEvents);
388 if (canWait)
389 emit aboutToBlock();
390 else
391 emit awake();
392
393 // tell postEventSourcePrepare() and timerSource about any new flags
394 QEventLoop::ProcessEventsFlags savedFlags = d->timerSource->processEventsFlags;
395 d->timerSource->processEventsFlags = flags;
396
397 if (!(flags & QEventLoop::EventLoopExec)) {
398 // force timers to be sent at normal priority
399 d->timerSource->runWithIdlePriority = false;
400 }
401
402 bool result = g_main_context_iteration(d->mainContext, canWait);
403 while (!result && canWait)
404 result = g_main_context_iteration(d->mainContext, canWait);
405
406 d->timerSource->processEventsFlags = savedFlags;
407
408 if (canWait)
409 emit awake();
410
411 return result;
412}
413
414void QEventDispatcherGlib::registerSocketNotifier(QSocketNotifier *notifier)
415{
416 Q_ASSERT(notifier);
417 int sockfd = int(notifier->socket());
418 const auto type = notifier->type();
419#ifndef QT_NO_DEBUG
420 if (sockfd < 0) {
421 qWarning("QSocketNotifier: Internal error");
422 return;
423 } else if (notifier->thread() != thread()
424 || thread() != QThread::currentThread()) {
425 qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread");
426 return;
427 }
428#endif
429
430 Q_D(QEventDispatcherGlib);
431
432
433 GPollFDWithQSocketNotifier *p = new GPollFDWithQSocketNotifier;
434 p->pollfd.fd = sockfd;
435 switch (type) {
436 case QSocketNotifier::Read:
437 p->pollfd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
438 break;
439 case QSocketNotifier::Write:
440 p->pollfd.events = G_IO_OUT | G_IO_HUP | G_IO_ERR;
441 break;
442 case QSocketNotifier::Exception:
443 p->pollfd.events = G_IO_PRI | G_IO_HUP | G_IO_ERR;
444 break;
445 }
446 p->socketNotifier = notifier;
447
448 d->socketNotifierSource->pollfds.append(p);
449
450 g_source_add_poll(&d->socketNotifierSource->source, &p->pollfd);
451}
452
453void QEventDispatcherGlib::unregisterSocketNotifier(QSocketNotifier *notifier)
454{
455 Q_ASSERT(notifier);
456#ifndef QT_NO_DEBUG
457 if (notifier->socket() < 0) {
458 qWarning("QSocketNotifier: Internal error");
459 return;
460 } else if (notifier->thread() != thread()
461 || thread() != QThread::currentThread()) {
462 qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread");
463 return;
464 }
465#endif
466
467 Q_D(QEventDispatcherGlib);
468
469 for (qsizetype i = 0; i < d->socketNotifierSource->pollfds.size(); ++i) {
470 GPollFDWithQSocketNotifier *p = d->socketNotifierSource->pollfds.at(i);
471 if (p->socketNotifier == notifier) {
472 // found it
473 g_source_remove_poll(&d->socketNotifierSource->source, &p->pollfd);
474
475 d->socketNotifierSource->pollfds.removeAt(i);
476 delete p;
477
478 // Keep a position in the list for the next item.
479 if (i <= d->socketNotifierSource->activeNotifierPos)
480 --d->socketNotifierSource->activeNotifierPos;
481
482 return;
483 }
484 }
485}
486
487void QEventDispatcherGlib::registerTimer(Qt::TimerId timerId, Duration interval,
488 Qt::TimerType timerType, QObject *object)
489{
490#ifndef QT_NO_DEBUG
491 if (qToUnderlying(timerId) < 1 || interval < 0ns || !object) {
492 qWarning("QEventDispatcherGlib::registerTimer: invalid arguments");
493 return;
494 } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
495 qWarning("QEventDispatcherGlib::registerTimer: timers cannot be started from another thread");
496 return;
497 }
498#endif
499
500 Q_D(QEventDispatcherGlib);
501 d->timerSource->timerList.registerTimer(timerId, interval, timerType, object);
502}
503
504bool QEventDispatcherGlib::unregisterTimer(Qt::TimerId timerId)
505{
506#ifndef QT_NO_DEBUG
507 if (qToUnderlying(timerId) < 1) {
508 qWarning("QEventDispatcherGlib::unregisterTimer: invalid argument");
509 return false;
510 } else if (thread() != QThread::currentThread()) {
511 qWarning("QEventDispatcherGlib::unregisterTimer: timers cannot be stopped from another thread");
512 return false;
513 }
514#endif
515
516 Q_D(QEventDispatcherGlib);
517 return d->timerSource->timerList.unregisterTimer(timerId);
518}
519
520bool QEventDispatcherGlib::unregisterTimers(QObject *object)
521{
522#ifndef QT_NO_DEBUG
523 if (!object) {
524 qWarning("QEventDispatcherGlib::unregisterTimers: invalid argument");
525 return false;
526 } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
527 qWarning("QEventDispatcherGlib::unregisterTimers: timers cannot be stopped from another thread");
528 return false;
529 }
530#endif
531
532 Q_D(QEventDispatcherGlib);
533 return d->timerSource->timerList.unregisterTimers(object);
534}
535
536QList<QEventDispatcherGlib::TimerInfoV2> QEventDispatcherGlib::timersForObject(QObject *object) const
537{
538#ifndef QT_NO_DEBUG
539 if (!object) {
540 qWarning("QEventDispatcherGlib:timersForObject: invalid argument");
541 return {};
542 }
543#endif
544
545 Q_D(const QEventDispatcherGlib);
546 return d->timerSource->timerList.registeredTimers(object);
547}
548
549QEventDispatcherGlib::Duration QEventDispatcherGlib::remainingTime(Qt::TimerId timerId) const
550{
551#ifndef QT_NO_DEBUG
552 if (qToUnderlying(timerId) < 1) {
553 qWarning("QEventDispatcherGlib::remainingTimeTime: invalid argument");
554 return Duration::min();
555 }
556#endif
557
558 Q_D(const QEventDispatcherGlib);
559 return d->timerSource->timerList.remainingDuration(timerId);
560}
561
562void QEventDispatcherGlib::interrupt()
563{
564 wakeUp();
565}
566
567void QEventDispatcherGlib::wakeUp()
568{
569 Q_D(QEventDispatcherGlib);
570 d->postEventSource->serialNumber.ref();
571 g_main_context_wakeup(d->mainContext);
572}
573
574bool QEventDispatcherGlib::versionSupported()
575{
576#if !defined(GLIB_MAJOR_VERSION) || !defined(GLIB_MINOR_VERSION) || !defined(GLIB_MICRO_VERSION)
577 return false;
578#else
579 return ((GLIB_MAJOR_VERSION << 16) + (GLIB_MINOR_VERSION << 8) + GLIB_MICRO_VERSION) >= 0x020301;
580#endif
581}
582
583QEventDispatcherGlib::QEventDispatcherGlib(QEventDispatcherGlibPrivate &dd, QObject *parent)
584 : QAbstractEventDispatcherV2(dd, parent)
585{
586}
587
588QT_END_NAMESPACE
589
590#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