1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qplatformdefs.h"
43 #include "qapplication.h"
44 #include "qeventdispatcher_qpa_p.h"
45 #include "private/qeventdispatcher_unix_p.h"
46 #include "qapplication_p.h"
47 #include "qplatformeventloopintegration_qpa.h"
48 
49 #include <QWindowSystemInterface>
50 #include <QtCore/QElapsedTimer>
51 #include <QtCore/QAtomicInt>
52 #include <QtCore/QSemaphore>
53 
54 #include <QtCore/QDebug>
55 
56 #include <errno.h>
57 #include <limits.h>
58 
59 QT_BEGIN_NAMESPACE
60 
61 QT_USE_NAMESPACE
62 
63 class Rendezvous
64 {
65 public:
checkpoint()66     void checkpoint()
67     {
68         if (state.testAndSetOrdered(0,1)) {
69             semaphore.acquire();
70         } else if (state.testAndSetAcquire(1,0)) {
71             semaphore.release();
72         } else {
73             qWarning("Barrier internal error");
74         }
75     }
76 private:
77     QSemaphore semaphore;
78     QAtomicInt state;
79 };
80 
81 class SelectWorker : public QThread
82 {
83 public:
SelectWorker(QEventDispatcherQPAPrivate * eventDispatcherPrivate)84     SelectWorker(QEventDispatcherQPAPrivate *eventDispatcherPrivate)
85         : QThread(),
86           m_edPrivate(eventDispatcherPrivate),
87           m_retVal(0)
88     {
89     }
90 
setSelectValues(int nfds,fd_set * readfds,fd_set * writefds,fd_set * exceptfds)91     void setSelectValues(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds)
92     {
93         m_nfds = nfds;
94         m_readfds = readfds;
95         m_writefds = writefds;
96         m_exceptfds = exceptfds;
97 
98 
99     }
100 
retVal() const101     int retVal() const {
102         return m_retVal;
103     }
104 
105 protected:
106     void run();
107 
108 private:
109     QEventDispatcherQPAPrivate *m_edPrivate;
110     int m_retVal;
111 
112     int m_nfds;
113     fd_set *m_readfds, *m_writefds, *m_exceptfds;
114 };
115 
116 class QEventDispatcherQPAPrivate : public QEventDispatcherUNIXPrivate
117 {
118     Q_DECLARE_PUBLIC(QEventDispatcherQPA)
119 public:
QEventDispatcherQPAPrivate()120     QEventDispatcherQPAPrivate()
121         :  eventLoopIntegration(0),
122            barrierBeforeBlocking(0),
123            barrierReturnValue(0),
124            selectReturnMutex(0),
125            selectWorkerNeedsSync(true),
126            selectWorkerHasResult(false),
127            selectWorker(0),
128            m_integrationInitialised(false),
129            m_hasIntegration(false),
130            m_isEventLoopIntegrationRunning(false)
131     {
132 
133     }
134 
~QEventDispatcherQPAPrivate()135     ~QEventDispatcherQPAPrivate()
136     {
137         delete selectWorker;
138         delete eventLoopIntegration;
139         delete barrierBeforeBlocking;
140         delete barrierReturnValue;
141         delete selectReturnMutex;
142     }
143 
hasIntegration() const144     bool hasIntegration() const
145     {
146         if (!m_integrationInitialised) {
147             QEventDispatcherQPAPrivate *that = const_cast<QEventDispatcherQPAPrivate *>(this);
148             if (qApp && (qApp->thread() == QThread::currentThread())) { // guiThread
149                 if (QApplicationPrivate::platformIntegration()) {
150                     that->eventLoopIntegration = QApplicationPrivate::platformIntegration()->createEventLoopIntegration();
151                     if (that->eventLoopIntegration) {
152                         that->selectWorker = new SelectWorker(that);
153                         that->barrierBeforeBlocking = new Rendezvous;
154                         that->barrierReturnValue = new Rendezvous;
155                         that->selectReturnMutex = new QMutex;
156                         that->selectWorker->start();
157                         that->m_hasIntegration = true;
158                         if (!QElapsedTimer::isMonotonic())
159                             qWarning("Having eventloop integration without monotonic timers can lead to undefined behaviour");
160                     }
161                 }
162             }
163             that->m_integrationInitialised = true;
164         }
165         return m_hasIntegration;
166     }
167 
isEventLoopIntegrationRunning() const168     bool isEventLoopIntegrationRunning() const
169     {
170         return m_isEventLoopIntegrationRunning;
171     }
172 
runEventLoopIntegration()173     void runEventLoopIntegration()
174     {
175         if (qApp && (qApp->thread() == QThread::currentThread())) {
176             m_isEventLoopIntegrationRunning = true;
177             eventLoopIntegration->startEventLoop();
178         }
179     }
180 
181     QPlatformEventLoopIntegration *eventLoopIntegration;
182     Rendezvous *barrierBeforeBlocking;
183     Rendezvous *barrierReturnValue;
184 
185     QMutex *selectReturnMutex;
186     bool selectWorkerNeedsSync;
187     bool selectWorkerHasResult;
188 
189     SelectWorker *selectWorker;
190 private:
191     bool m_integrationInitialised;
192     bool m_hasIntegration;
193     bool m_isEventLoopIntegrationRunning;
194 };
195 
QEventDispatcherQPA(QObject * parent)196 QEventDispatcherQPA::QEventDispatcherQPA(QObject *parent)
197     : QEventDispatcherUNIX(*new QEventDispatcherQPAPrivate, parent)
198 { }
199 
~QEventDispatcherQPA()200 QEventDispatcherQPA::~QEventDispatcherQPA()
201 { }
202 
processEvents(QEventLoop::ProcessEventsFlags flags)203 bool QEventDispatcherQPA::processEvents(QEventLoop::ProcessEventsFlags flags)
204 {
205     Q_D(QEventDispatcherQPA);
206 
207     if (d->hasIntegration()) {
208         if (!d->isEventLoopIntegrationRunning()) {
209             d->runEventLoopIntegration();
210         }
211         if (d->threadData->quitNow) {
212             d->eventLoopIntegration->quitEventLoop();
213             return false;
214         }
215     }
216 
217     int nevents = 0;
218 
219     // handle gui and posted events
220     d->interrupt = false;
221     QApplication::sendPostedEvents();
222 
223     while (!d->interrupt) {        // also flushes output buffer ###can be optimized
224         QWindowSystemInterfacePrivate::WindowSystemEvent *event;
225         if (!(flags & QEventLoop::ExcludeUserInputEvents)
226             && QWindowSystemInterfacePrivate::windowSystemEventsQueued() > 0) {
227             // process a pending user input event
228             event = QWindowSystemInterfacePrivate::getWindowSystemEvent();
229             if (!event)
230                 break;
231         } else {
232             break;
233         }
234 
235         if (filterEvent(event)) {
236             delete event;
237             continue;
238         }
239         nevents++;
240 
241         QApplicationPrivate::processWindowSystemEvent(event);
242         delete event;
243     }
244 
245     if (!d->interrupt) {
246         if (QEventDispatcherUNIX::processEvents(flags))
247             return true;
248     }
249 
250     return (nevents > 0);
251 }
252 
hasPendingEvents()253 bool QEventDispatcherQPA::hasPendingEvents()
254 {
255     extern uint qGlobalPostedEventsCount(); // from qapplication.cpp
256     return qGlobalPostedEventsCount() || QWindowSystemInterfacePrivate::windowSystemEventsQueued();
257 }
258 
registerSocketNotifier(QSocketNotifier * notifier)259 void QEventDispatcherQPA::registerSocketNotifier(QSocketNotifier *notifier)
260 {
261     Q_D(QEventDispatcherQPA);
262     QEventDispatcherUNIX::registerSocketNotifier(notifier);
263     if (d->hasIntegration())
264         wakeUp();
265 
266 }
267 
unregisterSocketNotifier(QSocketNotifier * notifier)268 void QEventDispatcherQPA::unregisterSocketNotifier(QSocketNotifier *notifier)
269 {
270     Q_D(QEventDispatcherQPA);
271     QEventDispatcherUNIX::unregisterSocketNotifier(notifier);
272     if (d->hasIntegration())
273         wakeUp();
274 }
275 
flush()276 void QEventDispatcherQPA::flush()
277 {
278     if(qApp)
279         qApp->sendPostedEvents();
280 }
281 
select(int nfds,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,timeval * timeout)282 int QEventDispatcherQPA::select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
283                                 timeval *timeout)
284 {
285     Q_D(QEventDispatcherQPA);
286     int retVal = 0;
287     if (d->hasIntegration()) {
288         qint64 timeoutmsec = LONG_MAX; // wait if we don't have timers
289         if (timeout)
290             timeoutmsec = timeout->tv_sec * 1000 + (timeout->tv_usec/1000);
291         d->selectReturnMutex->lock();
292         if (d->selectWorkerNeedsSync) {
293             if (d->selectWorkerHasResult) {
294                 retVal = d->selectWorker->retVal();
295                 d->selectWorkerHasResult = false;
296 
297                 d->selectReturnMutex->unlock();
298                 d->barrierReturnValue->checkpoint();
299                 d->eventLoopIntegration->setNextTimerEvent(0);
300                 return retVal;
301             } else {
302                 d->selectWorkerNeedsSync = false;
303                 d->selectWorker->setSelectValues(nfds,readfds, writefds, exceptfds);
304                 d->barrierBeforeBlocking->checkpoint();
305             }
306         }
307         d->selectReturnMutex->unlock();
308         d->eventLoopIntegration->setNextTimerEvent(timeoutmsec);
309         retVal = 0; //is 0 if select has not returned
310     } else {
311         retVal = QEventDispatcherUNIX::select(nfds, readfds, writefds, exceptfds, timeout);
312     }
313     return retVal;
314 }
315 
316 
run()317 void SelectWorker::run()
318 {
319 
320     while(true) {
321         m_retVal = 0;
322         m_edPrivate->barrierBeforeBlocking->checkpoint(); // wait for mainthread
323         int tmpRet = qt_safe_select(m_nfds,m_readfds,m_writefds,m_exceptfds,0);
324         m_edPrivate->selectReturnMutex->lock();
325         m_edPrivate->eventLoopIntegration->qtNeedsToProcessEvents();
326 
327         m_edPrivate->selectWorkerNeedsSync = true;
328         m_edPrivate->selectWorkerHasResult = true;
329         m_retVal = tmpRet;
330 
331         m_edPrivate->selectReturnMutex->unlock();
332         m_edPrivate->barrierReturnValue->checkpoint();
333     }
334 }
335 QT_END_NAMESPACE
336