1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Research In Motion <blackberry-qt@qnx.com>
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore 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 "qeventdispatcher_blackberry_p.h"
43 #include "qsocketnotifier.h"
44 #include "qdebug.h"
45 #include "qelapsedtimer.h"
46 #include "private/qthread_p.h"
47 
48 #include <bps/bps.h>
49 #include <bps/event.h>
50 
51 //#define QEVENTDISPATCHERBLACKBERRY_DEBUG
52 
53 #ifdef QEVENTDISPATCHERBLACKBERRY_DEBUG
54 #define qEventDispatcherDebug qDebug() << QThread::currentThread()
55 #else
56 #define qEventDispatcherDebug QT_NO_QDEBUG_MACRO()
57 #endif
58 
59 class BpsChannelScopeSwitcher
60 {
61 public:
BpsChannelScopeSwitcher(int scopeChannel)62     BpsChannelScopeSwitcher(int scopeChannel) : innerChannel(scopeChannel)
63     {
64         outerChannel = bps_channel_get_active();
65         if (outerChannel != innerChannel)
66             bps_channel_set_active(innerChannel);
67     }
68 
~BpsChannelScopeSwitcher()69     ~BpsChannelScopeSwitcher()
70     {
71         if (outerChannel != innerChannel)
72             bps_channel_set_active(outerChannel);
73     }
74 
75 private:
76     int innerChannel;
77     int outerChannel;
78 };
79 
80 class BBScopedLoopLevelCounter
81 {
82     QEventDispatcherBlackberryPrivate *d;
83 
84 public:
BBScopedLoopLevelCounter(QEventDispatcherBlackberryPrivate * p)85     inline BBScopedLoopLevelCounter(QEventDispatcherBlackberryPrivate *p)
86         : d(p)
87     { ++d->loop_level; }
88 
~BBScopedLoopLevelCounter()89     inline ~BBScopedLoopLevelCounter()
90     { --d->loop_level; }
91 };
92 
93 struct bpsIOHandlerData
94 {
bpsIOHandlerDatabpsIOHandlerData95     bpsIOHandlerData()
96         : count(0), readfds(0), writefds(0), exceptfds(0)
97     {
98     }
99 
100     int count;
101     fd_set *readfds;
102     fd_set *writefds;
103     fd_set *exceptfds;
104 };
105 
106 static int bpsUnblockDomain = -1;
107 
bpsIOHandler(int fd,int io_events,void * data)108 static int bpsIOHandler(int fd, int io_events, void *data)
109 {
110     qEventDispatcherDebug << Q_FUNC_INFO;
111     // decode callback payload
112     bpsIOHandlerData *ioData = static_cast<bpsIOHandlerData*>(data);
113 
114     // check if first file is ready
115     bool firstReady = (ioData->count == 0);
116 
117     // update ready state of file
118     if (io_events & BPS_IO_INPUT) {
119         qEventDispatcherDebug << fd << "ready for Read";
120         FD_SET(fd, ioData->readfds);
121         ioData->count++;
122     }
123 
124     if (io_events & BPS_IO_OUTPUT) {
125         qEventDispatcherDebug << fd << "ready for Write";
126         FD_SET(fd, ioData->writefds);
127         ioData->count++;
128     }
129 
130     if (io_events & BPS_IO_EXCEPT) {
131         qEventDispatcherDebug << fd << "ready for Exception";
132         FD_SET(fd, ioData->exceptfds);
133         ioData->count++;
134     }
135 
136     // force bps_get_event() to return immediately by posting an event to ourselves;
137     // but this only needs to happen once if multiple files become ready at the same time
138     if (firstReady) {
139         qEventDispatcherDebug << "Sending bpsIOReadyDomain event";
140         // create unblock event
141         bps_event_t *event;
142         int result = bps_event_create(&event, bpsUnblockDomain, 0, NULL, NULL);
143         if (Q_UNLIKELY(result != BPS_SUCCESS)) {
144             qWarning("QEventDispatcherBlackberry: bps_event_create failed");
145             return BPS_FAILURE;
146         }
147 
148         // post unblock event to our thread; in this callback the bps channel is
149         // guaranteed to be the same that was active when bps_add_fd was called
150         result = bps_push_event(event);
151         if (Q_UNLIKELY(result != BPS_SUCCESS)) {
152             qWarning("QEventDispatcherBlackberry: bps_push_event failed");
153             bps_event_destroy(event);
154             return BPS_FAILURE;
155         }
156     }
157 
158     return BPS_SUCCESS;
159 }
160 
QEventDispatcherBlackberryPrivate()161 QEventDispatcherBlackberryPrivate::QEventDispatcherBlackberryPrivate()
162     : loop_level(0)
163     , ioData(new bpsIOHandlerData)
164 {
165     // prepare to use BPS
166     int result = bps_initialize();
167     if (Q_UNLIKELY(result != BPS_SUCCESS))
168         qFatal("QEventDispatcherBlackberry: bps_initialize failed");
169 
170     bps_channel = bps_channel_get_active();
171 
172     if (bps_channel_create(&holding_channel, 0) != BPS_SUCCESS) {
173         qWarning("QEventDispatcherBlackberry: bps_channel_create failed");
174         holding_channel = -1;
175     }
176 
177     // get domain for IO ready and wake up events - ignoring race condition here for now
178     if (bpsUnblockDomain == -1) {
179         bpsUnblockDomain = bps_register_domain();
180         if (Q_UNLIKELY(bpsUnblockDomain == -1))
181             qWarning("QEventDispatcherBlackberry: bps_register_domain failed");
182     }
183 }
184 
~QEventDispatcherBlackberryPrivate()185 QEventDispatcherBlackberryPrivate::~QEventDispatcherBlackberryPrivate()
186 {
187     if ((holding_channel != -1) &&
188         (bps_channel_destroy(holding_channel) != BPS_SUCCESS)) {
189         qWarning("QEventDispatcherBlackberry: bps_channel_destroy failed");
190     }
191 
192     // we're done using BPS
193     bps_shutdown();
194 }
195 
initThreadWakeUp()196 int QEventDispatcherBlackberryPrivate::initThreadWakeUp()
197 {
198     return -1;  // no fd's used
199 }
200 
processThreadWakeUp(int nsel)201 int QEventDispatcherBlackberryPrivate::processThreadWakeUp(int nsel)
202 {
203     Q_UNUSED(nsel);
204     return wakeUps.fetchAndStoreRelaxed(0);
205 }
206 
207 /////////////////////////////////////////////////////////////////////////////
208 
QEventDispatcherBlackberry(QObject * parent)209 QEventDispatcherBlackberry::QEventDispatcherBlackberry(QObject *parent)
210     : QEventDispatcherUNIX(*new QEventDispatcherBlackberryPrivate, parent)
211 {
212 }
213 
QEventDispatcherBlackberry(QEventDispatcherBlackberryPrivate & dd,QObject * parent)214 QEventDispatcherBlackberry::QEventDispatcherBlackberry(QEventDispatcherBlackberryPrivate &dd, QObject *parent)
215     : QEventDispatcherUNIX(dd, parent)
216 {
217 }
218 
~QEventDispatcherBlackberry()219 QEventDispatcherBlackberry::~QEventDispatcherBlackberry()
220 {
221 }
222 
registerSocketNotifier(QSocketNotifier * notifier)223 void QEventDispatcherBlackberry::registerSocketNotifier(QSocketNotifier *notifier)
224 {
225     Q_ASSERT(notifier);
226     Q_D(QEventDispatcherBlackberry);
227 
228     int sockfd = notifier->socket();
229     int type = notifier->type();
230 
231     qEventDispatcherDebug << Q_FUNC_INFO << "fd =" << sockfd;
232 
233     if (Q_UNLIKELY(sockfd >= FD_SETSIZE)) {
234         qWarning() << "QEventDispatcherBlackberry: cannot register QSocketNotifier (fd too high)"
235                    << sockfd;
236         return;
237     }
238 
239     // Register the fd with bps
240     BpsChannelScopeSwitcher channelSwitcher(d->bps_channel);
241     int io_events = ioEvents(sockfd);
242     if (io_events)
243         bps_remove_fd(sockfd);
244 
245     switch (type) {
246     case QSocketNotifier::Read:
247         qEventDispatcherDebug << "Registering" << sockfd << "for Reads";
248         io_events |= BPS_IO_INPUT;
249         break;
250     case QSocketNotifier::Write:
251         qEventDispatcherDebug << "Registering" << sockfd << "for Writes";
252         io_events |= BPS_IO_OUTPUT;
253         break;
254     case QSocketNotifier::Exception:
255     default:
256         qEventDispatcherDebug << "Registering" << sockfd << "for Exceptions";
257         io_events |= BPS_IO_EXCEPT;
258         break;
259     }
260 
261     const int result = bps_add_fd(sockfd, io_events, &bpsIOHandler, d->ioData.data());
262     if (Q_UNLIKELY(result != BPS_SUCCESS))
263         qWarning() << "QEventDispatcherBlackberry: bps_add_fd failed";
264 
265     // Call the base Unix implementation. Needed to allow select() to be called correctly
266     QEventDispatcherUNIX::registerSocketNotifier(notifier);
267 }
268 
unregisterSocketNotifier(QSocketNotifier * notifier)269 void QEventDispatcherBlackberry::unregisterSocketNotifier(QSocketNotifier *notifier)
270 {
271     Q_D(QEventDispatcherBlackberry);
272 
273     int sockfd = notifier->socket();
274 
275     qEventDispatcherDebug << Q_FUNC_INFO << "fd =" << sockfd;
276 
277     if (Q_UNLIKELY(sockfd >= FD_SETSIZE)) {
278         qWarning() << "QEventDispatcherBlackberry: cannot unregister QSocketNotifier" << sockfd;
279         return;
280     }
281 
282     // Allow the base Unix implementation to unregister the fd too (before call to ioEvents()!)
283     QEventDispatcherUNIX::unregisterSocketNotifier(notifier);
284 
285     // Unregister the fd with bps
286     BpsChannelScopeSwitcher channelSwitcher(d->bps_channel);
287     int result = bps_remove_fd(sockfd);
288     if (Q_UNLIKELY(result != BPS_SUCCESS))
289         qWarning() << "QEventDispatcherBlackberry: bps_remove_fd failed" << sockfd;
290 
291     const int io_events = ioEvents(sockfd);
292     // if other socket notifier is watching sockfd, readd it
293     if (io_events) {
294         result = bps_add_fd(sockfd, io_events, &bpsIOHandler, d->ioData.data());
295         if (Q_UNLIKELY(result != BPS_SUCCESS))
296             qWarning("QEventDispatcherBlackberry: bps_add_fd error");
297     }
298 }
299 
timevalToMillisecs(const timeval & tv)300 static inline int timevalToMillisecs(const timeval &tv)
301 {
302     return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
303 }
304 
destroyHeldBpsEvent(int holding_channel)305 static inline void destroyHeldBpsEvent(int holding_channel)
306 {
307     // Switch to the holding channel and use bps_get_event() to trigger its destruction.  We
308     // don't care about the return value from this call to bps_get_event().
309     BpsChannelScopeSwitcher holdingChannelSwitcher(holding_channel);
310     bps_event_t *held_event = 0;
311     (void)bps_get_event(&held_event, 0);
312  }
313 
select(int nfds,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,timeval * timeout)314 int QEventDispatcherBlackberry::select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
315                                        timeval *timeout)
316 {
317     Q_UNUSED(nfds);
318     Q_D(QEventDispatcherBlackberry);
319     const BBScopedLoopLevelCounter bbLoopCounter(d);
320 
321     BpsChannelScopeSwitcher channelSwitcher(d->bps_channel);
322 
323     // prepare file sets for bps callback
324     d->ioData->count = 0;
325     d->ioData->readfds = readfds;
326     d->ioData->writefds = writefds;
327     d->ioData->exceptfds = exceptfds;
328 
329     // reset all file sets
330     if (readfds)
331         FD_ZERO(readfds);
332 
333     if (writefds)
334         FD_ZERO(writefds);
335 
336     if (exceptfds)
337         FD_ZERO(exceptfds);
338 
339     bps_event_t *event = 0;
340     unsigned int eventCount = 0;
341 
342     // If an event handler called through filterEvent() starts a nested event loop by creating a
343     // new QEventLoop, we will recursively enter this function again.  However, each time
344     // bps_get_event() is called, it destroys the last event it handed out before returning the
345     // next event.  We don't want it to destroy the event that triggered the nested event loop,
346     // since there may still be more handlers that need to get that event, once the nested event
347     // loop is done and control returns to the outer event loop.
348     //
349     // So we move an event to a holding channel, which takes ownership of the event.  Putting
350     // the event on our own channel allows us to manage when it is destroyed, keeping it alive
351     // until we know we are done with it.  Each recursive call of this function needs to have
352     // it's own holding channel, since a channel is a queue, not a stack.
353     //
354     // However, a recursive call into this function happens very rarely compared to the many
355     // times this function is called.  We don't want to create a holding channel for each time
356     // this function is called, only when it is called recursively.  Thus we have the instance
357     // variable d->holding_channel to use in the common case.  We keep track of recursive calls
358     // with d->loop_level.  If we are in a recursive call, then we create a new holding channel
359     // for this run.
360     int holding_channel = d->holding_channel;
361     if ((d->loop_level > 1) &&
362         Q_UNLIKELY(bps_channel_create(&holding_channel, 0) != BPS_SUCCESS)) {
363         qWarning("QEventDispatcherBlackberry: bps_channel_create failed");
364         holding_channel = -1;
365     }
366 
367     // Convert timeout to milliseconds
368     int timeoutTotal = -1;
369     if (timeout)
370         timeoutTotal = timevalToMillisecs(*timeout);
371     int timeoutLeft = timeoutTotal;
372     timeval startTime = qt_gettime();
373 
374     // This loop exists such that we can drain the bps event queue of all native events
375     // more efficiently than if we were to return control to Qt after each event. This
376     // is important for handling touch events which can come in rapidly.
377     forever {
378         // Only emit the awake() and aboutToBlock() signals in the second iteration. For the
379         // first iteration, the UNIX event dispatcher will have taken care of that already.
380         // Also native events are actually processed one loop iteration after they were
381         // retrieved with bps_get_event().
382 
383         // Filtering the native event should happen between the awake() and aboutToBlock()
384         // signal emissions. The calls awake() - filterNativeEvent() - aboutToBlock() -
385         // bps_get_event() need not to be interrupted by a break or return statement.
386         if (eventCount > 0) {
387             if (event) {
388                 emit awake();
389                 filterEvent(static_cast<void*>(event));
390                 emit aboutToBlock();
391 
392                 if (Q_LIKELY(holding_channel != -1)) {
393                     // We are now done with this BPS event.  Destroy it.
394                     destroyHeldBpsEvent(holding_channel);
395                 }
396             }
397 
398             // Update the timeout
399             // Clock source is monotonic, so we can recalculate how much timeout is left
400             if (timeoutTotal != -1) {
401                 timeval t2 = qt_gettime();
402                 timeoutLeft = timeoutTotal
403                               - (timevalToMillisecs(t2) - timevalToMillisecs(startTime));
404                 if (timeoutLeft < 0)
405                     timeoutLeft = 0;
406             }
407 
408             timeval tnext;
409             if (d->timerList.timerWait(tnext)) {
410                 int timeoutNext = timevalToMillisecs(tnext);
411                 if (timeoutNext < timeoutLeft || timeoutTotal == -1) {
412                     timeoutTotal = timeoutLeft = timeoutNext;
413                     startTime = qt_gettime();
414                 }
415             }
416         }
417 
418         event = 0;
419         {   // We need to increase loop level in this scope,
420             // because bps_get_event can also invoke callbacks
421             QScopedLoopLevelCounter loopLevelCounter(d->threadData);
422 
423             // Wait for event or file to be ready
424             const int result = bps_get_event(&event, timeoutLeft);
425             if (Q_UNLIKELY(result != BPS_SUCCESS))
426                 qWarning("QEventDispatcherBlackberry: bps_get_event failed");
427         }
428 
429         if (!event)    // In case of !event, we break out of the loop to let Qt process the timers
430             break;     // (since timeout has expired) and socket notifiers that are now ready.
431 
432         if (bps_event_get_domain(event) == bpsUnblockDomain) {
433             timeoutTotal = 0;   // in order to immediately drain the event queue of native events
434             event = 0;          // (especially touch move events) we don't break out here
435         } else {
436             // Move the event to our holding channel so we can manage when it is destroyed.
437             if (Q_LIKELY(holding_channel != 1) &&
438                 Q_UNLIKELY(bps_channel_push_event(holding_channel, event) != BPS_SUCCESS)) {
439                 qWarning("QEventDispatcherBlackberry: bps_channel_push_event failed");
440             }
441         }
442 
443         ++eventCount;
444 
445         // Make sure we are not trapped in this loop due to continuous native events
446         // also we cannot recalculate the timeout without a monotonic clock as the time may have changed
447         const unsigned int maximumEventCount = 12;
448         if (Q_UNLIKELY((eventCount > maximumEventCount && timeoutLeft == 0)
449                        || !QElapsedTimer::isMonotonic())) {
450             if (event) {
451                 filterEvent(static_cast<void*>(event));
452 
453                 if (Q_LIKELY(holding_channel != -1)) {
454                     // We are now done with this BPS event.  Destroy it.
455                     destroyHeldBpsEvent(holding_channel);
456                 }
457             }
458             break;
459         }
460     }
461 
462     // If this was a recursive call into this function, a new holding channel was created for
463     // this run, so destroy it now.
464     if ((holding_channel != d->holding_channel) &&
465         Q_LIKELY(holding_channel != -1) &&
466         Q_UNLIKELY(bps_channel_destroy(holding_channel) != BPS_SUCCESS)) {
467         qWarning("QEventDispatcherBlackberry: bps_channel_destroy failed");
468     }
469 
470     // the number of bits set in the file sets
471     return d->ioData->count;
472 }
473 
wakeUp()474 void QEventDispatcherBlackberry::wakeUp()
475 {
476     Q_D(QEventDispatcherBlackberry);
477     if (d->wakeUps.testAndSetAcquire(0, 1)) {
478         bps_event_t *event;
479         if (Q_LIKELY(bps_event_create(&event, bpsUnblockDomain, 0, 0, 0) == BPS_SUCCESS)) {
480             if (Q_LIKELY(bps_channel_push_event(d->bps_channel, event) == BPS_SUCCESS))
481                 return;
482             else
483                 bps_event_destroy(event);
484         }
485         qWarning("QEventDispatcherBlackberry: wakeUp failed");
486     }
487 }
488 
ioEvents(int fd)489 int QEventDispatcherBlackberry::ioEvents(int fd)
490 {
491     int io_events = 0;
492 
493     Q_D(QEventDispatcherBlackberry);
494 
495     if (FD_ISSET(fd, &d->sn_vec[0].enabled_fds))
496         io_events |= BPS_IO_INPUT;
497 
498     if (FD_ISSET(fd, &d->sn_vec[1].enabled_fds))
499         io_events |= BPS_IO_OUTPUT;
500 
501     if (FD_ISSET(fd, &d->sn_vec[2].enabled_fds))
502         io_events |= BPS_IO_EXCEPT;
503 
504     return io_events;
505 }
506 
507 QT_END_NAMESPACE
508