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

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