Qt 4.8
qeventdispatcher_blackberry.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Research In Motion <blackberry-qt@qnx.com>
4 ** Contact: http://www.qt-project.org/legal
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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/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 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
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 
60 {
61 public:
62  BpsChannelScopeSwitcher(int scopeChannel) : innerChannel(scopeChannel)
63  {
64  outerChannel = bps_channel_get_active();
66  bps_channel_set_active(innerChannel);
67  }
68 
70  {
72  bps_channel_set_active(outerChannel);
73  }
74 
75 private:
78 };
79 
81 {
83 
84 public:
86  : d(p)
87  { ++d->loop_level; }
88 
90  { --d->loop_level; }
91 };
92 
94 {
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 
108 static int bpsIOHandler(int fd, int io_events, void *data)
109 {
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 
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 
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 
197 {
198  return -1; // no fd's used
199 }
200 
202 {
203  Q_UNUSED(nsel);
204  return wakeUps.fetchAndStoreRelaxed(0);
205 }
206 
208 
211 {
212 }
213 
215  : QEventDispatcherUNIX(dd, parent)
216 {
217 }
218 
220 {
221 }
222 
224 {
225  Q_ASSERT(notifier);
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) {
247  qEventDispatcherDebug << "Registering" << sockfd << "for Reads";
248  io_events |= BPS_IO_INPUT;
249  break;
251  qEventDispatcherDebug << "Registering" << sockfd << "for Writes";
252  io_events |= BPS_IO_OUTPUT;
253  break;
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
267 }
268 
270 {
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()!)
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 
300 static inline int timevalToMillisecs(const timeval &tv)
301 {
302  return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
303 }
304 
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 
314 int QEventDispatcherBlackberry::select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
315  timeval *timeout)
316 {
317  Q_UNUSED(nfds);
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)
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 
475 {
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 
490 {
491  int io_events = 0;
492 
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 
double d
Definition: qnumeric_p.h:62
int type
Definition: qmetatype.cpp:239
bool filterEvent(void *message)
Sends message through the event filter that was set by setEventFilter().
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
EventRef event
QEventDispatcherBlackberryPrivate * d
void wakeUp()
Wakes up the event loop.
void registerSocketNotifier(QSocketNotifier *notifier)
Registers notifier with the event loop.
void unregisterSocketNotifier(QSocketNotifier *notifier)
Unregisters notifier from the event dispatcher.
#define Q_ASSERT(cond)
Definition: qglobal.h:1823
The QObject class is the base class of all Qt objects.
Definition: qobject.h:111
virtual bool event(QEvent *)
This virtual function receives events to an object and should return true if the event e was recogniz...
Definition: qobject.cpp:1200
#define Q_D(Class)
Definition: qglobal.h:2482
static bool isMonotonic()
Returns true if this is a monotonic clock, false otherwise.
Type type() const
Returns the socket event type specified to the constructor.
The QSocketNotifier class provides support for monitoring activity on a file descriptor.
void registerSocketNotifier(QSocketNotifier *notifier)
Registers notifier with the event loop.
BBScopedLoopLevelCounter(QEventDispatcherBlackberryPrivate *p)
void awake()
This signal is emitted after the event loop returns from a function that could block.
static int bpsUnblockDomain
#define emit
Definition: qobjectdefs.h:76
static int timevalToMillisecs(const timeval &tv)
Q_CORE_EXPORT void qWarning(const char *,...)
static const char * data(const QByteArray &arr)
static void destroyHeldBpsEvent(int holding_channel)
#define qEventDispatcherDebug
Q_CORE_EXPORT void qFatal(const char *,...)
void aboutToBlock()
This signal is emitted before the event loop calls a function that could block.
QObject * parent() const
Returns a pointer to the parent object.
Definition: qobject.h:273
#define Q_UNLIKELY(x)
Hints to the compiler that the enclosed condition, expr, is likely to evaluate to false...
Definition: qglobal.h:823
#define Q_LIKELY(x)
Hints to the compiler that the enclosed condition, expr, is likely to evaluate to true...
Definition: qglobal.h:820
QObject * parent
Definition: qobject.h:92
static int bpsIOHandler(int fd, int io_events, void *data)
int fetchAndStoreRelaxed(int newValue)
Atomic fetch-and-store.
timeval qt_gettime()
#define Q_UNUSED(x)
Indicates to the compiler that the parameter with the specified name is not used in the body of a fun...
Definition: qglobal.h:1729
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, timeval *timeout)
void unregisterSocketNotifier(QSocketNotifier *notifier)
Unregisters notifier from the event dispatcher.
int socket() const
Returns the socket identifier specified to the constructor.
#define Q_FUNC_INFO
Definition: qglobal.h:1871
#define forever
This macro is provided for convenience for writing infinite loops.
Definition: qglobal.h:2452