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

source code of qtbase/src/corelib/kernel/qeventdispatcher_glib.cpp