source: trunk/src/corelib/kernel/qeventdispatcher_symbian.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

  • Property svn:eol-style set to native
File size: 33.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtCore module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qeventdispatcher_symbian_p.h"
43#include <private/qthread_p.h>
44#include <qcoreapplication.h>
45#include <private/qcoreapplication_p.h>
46
47#include <unistd.h>
48#include <errno.h>
49
50QT_BEGIN_NAMESPACE
51
52#ifdef SYMBIAN_GRAPHICS_WSERV_QT_EFFECTS
53// when the system UI is Qt based, priority drop is not needed as CPU starved processes will not be killed.
54#undef QT_SYMBIAN_PRIORITY_DROP
55#else
56#define QT_SYMBIAN_PRIORITY_DROP
57#endif
58
59#define WAKE_UP_PRIORITY CActive::EPriorityStandard
60#define TIMER_PRIORITY CActive::EPriorityHigh
61#define NULLTIMER_PRIORITY CActive::EPriorityLow
62#define COMPLETE_DEFERRED_ACTIVE_OBJECTS_PRIORITY CActive::EPriorityIdle
63
64static inline int qt_pipe_write(int socket, const char *data, qint64 len)
65{
66 return ::write(socket, data, len);
67}
68#if defined(write)
69# undef write
70#endif
71
72static inline int qt_pipe_close(int socket)
73{
74 return ::close(socket);
75}
76#if defined(close)
77# undef close
78#endif
79
80static inline int qt_pipe_fcntl(int socket, int command)
81{
82 return ::fcntl(socket, command);
83}
84static inline int qt_pipe2_fcntl(int socket, int command, int option)
85{
86 return ::fcntl(socket, command, option);
87}
88#if defined(fcntl)
89# undef fcntl
90#endif
91
92static inline int qt_socket_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
93{
94 return ::select(nfds, readfds, writefds, exceptfds, timeout);
95}
96
97// This simply interrupts the select and locks the mutex until destroyed.
98class QSelectMutexGrabber
99{
100public:
101 QSelectMutexGrabber(int writeFd, int readFd, QMutex *mutex)
102 : m_mutex(mutex)
103 {
104 if (m_mutex->tryLock())
105 return;
106
107 char dummy = 0;
108 qt_pipe_write(writeFd, &dummy, 1);
109
110 m_mutex->lock();
111
112 char buffer;
113 while (::read(readFd, &buffer, 1) > 0) {}
114 }
115
116 ~QSelectMutexGrabber()
117 {
118 m_mutex->unlock();
119 }
120
121private:
122 QMutex *m_mutex;
123};
124
125/*
126 * This class is designed to aid in implementing event handling in a more round robin fashion. We
127 * cannot change active objects that we do not own, but the active objects that Qt owns will use
128 * this as a base class with convenience functions.
129 *
130 * Here is how it works: On every RunL, the deriving class should call maybeQueueForLater().
131 * This will return whether the active object has been queued, or whether it should run immediately.
132 * Queued objects will run again after other events have been processed.
133 *
134 * The QCompleteDeferredAOs class is a special object that runs after all others, which will
135 * reactivate the objects that were previously not run.
136 */
137inline QActiveObject::QActiveObject(TInt priority, QEventDispatcherSymbian *dispatcher)
138 : CActive(priority),
139 m_dispatcher(dispatcher),
140 m_hasAlreadyRun(false),
141 m_hasRunAgain(false),
142 m_iterationCount(1)
143{
144}
145
146QActiveObject::~QActiveObject()
147{
148 if (m_hasRunAgain)
149 m_dispatcher->removeDeferredActiveObject(this);
150}
151
152bool QActiveObject::maybeQueueForLater()
153{
154 Q_ASSERT(!m_hasRunAgain);
155
156 if (!m_hasAlreadyRun || m_dispatcher->iterationCount() != m_iterationCount) {
157 // First occurrence of this event in this iteration.
158 m_hasAlreadyRun = true;
159 m_iterationCount = m_dispatcher->iterationCount();
160 return false;
161 } else {
162 // The event has already occurred.
163 m_dispatcher->addDeferredActiveObject(this);
164 m_hasRunAgain = true;
165 return true;
166 }
167}
168
169void QActiveObject::reactivateAndComplete()
170{
171 iStatus = KRequestPending;
172 SetActive();
173 TRequestStatus *status = &iStatus;
174 QEventDispatcherSymbian::RequestComplete(status, KErrNone);
175
176 m_hasRunAgain = false;
177 m_hasAlreadyRun = false;
178}
179
180QWakeUpActiveObject::QWakeUpActiveObject(QEventDispatcherSymbian *dispatcher)
181 : QActiveObject(WAKE_UP_PRIORITY, dispatcher)
182{
183 CActiveScheduler::Add(this);
184 iStatus = KRequestPending;
185 SetActive();
186}
187
188QWakeUpActiveObject::~QWakeUpActiveObject()
189{
190 Cancel();
191}
192
193void QWakeUpActiveObject::DoCancel()
194{
195 if (iStatus.Int() == KRequestPending) {
196 TRequestStatus *status = &iStatus;
197 QEventDispatcherSymbian::RequestComplete(status, KErrNone);
198 }
199}
200
201void QWakeUpActiveObject::RunL()
202{
203 if (maybeQueueForLater())
204 return;
205
206 iStatus = KRequestPending;
207 SetActive();
208 QT_TRYCATCH_LEAVING(m_dispatcher->wakeUpWasCalled());
209}
210
211QTimerActiveObject::QTimerActiveObject(QEventDispatcherSymbian *dispatcher, SymbianTimerInfo *timerInfo)
212 : QActiveObject((timerInfo->interval) ? TIMER_PRIORITY : NULLTIMER_PRIORITY , dispatcher),
213 m_timerInfo(timerInfo)
214{
215}
216
217QTimerActiveObject::~QTimerActiveObject()
218{
219 Cancel();
220 m_rTimer.Close(); //close of null handle is safe
221}
222
223void QTimerActiveObject::DoCancel()
224{
225 if (m_timerInfo->interval > 0) {
226 m_rTimer.Cancel();
227 } else {
228 if (iStatus.Int() == KRequestPending) {
229 TRequestStatus *status = &iStatus;
230 QEventDispatcherSymbian::RequestComplete(status, KErrNone);
231 }
232 }
233}
234
235void QTimerActiveObject::RunL()
236{
237 int error = KErrNone;
238 if (iStatus == KErrNone) {
239 QT_TRYCATCH_ERROR(error, Run());
240 } else {
241 error = iStatus.Int();
242 }
243 // All Symbian error codes are negative.
244 if (error < 0) {
245 CActiveScheduler::Current()->Error(error); // stop and report here, as this timer will be deleted on scope exit
246 }
247}
248
249#define MAX_SYMBIAN_TIMEOUT_MS 2000000
250void QTimerActiveObject::StartTimer()
251{
252 if (m_timerInfo->msLeft > MAX_SYMBIAN_TIMEOUT_MS) {
253 //There is loss of accuracy anyway due to needing to restart the timer every 33 minutes,
254 //so the 1/64s res of After() is acceptable for these very long timers.
255 m_rTimer.After(iStatus, MAX_SYMBIAN_TIMEOUT_MS * 1000);
256 m_timerInfo->msLeft -= MAX_SYMBIAN_TIMEOUT_MS;
257 } else {
258 //HighRes gives the 1ms accuracy expected by Qt, the +1 is to ensure that
259 //"Timers will never time out earlier than the specified timeout value"
260 //condition is always met.
261 m_rTimer.HighRes(iStatus, (m_timerInfo->msLeft + 1) * 1000);
262 m_timerInfo->msLeft = 0;
263 }
264 SetActive();
265}
266
267void QTimerActiveObject::Run()
268{
269 //restart timer immediately, if the timeout has been split because it overflows max for platform.
270 if (m_timerInfo->msLeft > 0) {
271 StartTimer();
272 return;
273 }
274
275 if (maybeQueueForLater())
276 return;
277
278 if (m_timerInfo->interval > 0) {
279 // Start a new timer immediately so that we don't lose time.
280 m_timerInfo->msLeft = m_timerInfo->interval;
281 StartTimer();
282
283 m_timerInfo->dispatcher->timerFired(m_timerInfo->timerId);
284 } else {
285 // However, we only complete zero timers after the event has finished,
286 // in order to prevent busy looping when doing nested loops.
287
288 // Keep the refpointer around in order to avoid deletion until the end of this function.
289 SymbianTimerInfoPtr timerInfoPtr(m_timerInfo);
290
291 m_timerInfo->dispatcher->timerFired(m_timerInfo->timerId);
292
293 iStatus = KRequestPending;
294 SetActive();
295 TRequestStatus *status = &iStatus;
296 QEventDispatcherSymbian::RequestComplete(status, KErrNone);
297 }
298}
299
300void QTimerActiveObject::Start()
301{
302 CActiveScheduler::Add(this);
303 m_timerInfo->msLeft = m_timerInfo->interval;
304 if (m_timerInfo->interval > 0) {
305 if (!m_rTimer.Handle()) {
306 qt_symbian_throwIfError(m_rTimer.CreateLocal());
307 }
308 StartTimer();
309 } else {
310 iStatus = KRequestPending;
311 SetActive();
312 TRequestStatus *status = &iStatus;
313 QEventDispatcherSymbian::RequestComplete(status, KErrNone);
314 }
315}
316
317SymbianTimerInfo::SymbianTimerInfo()
318 : timerAO(0)
319{
320}
321
322SymbianTimerInfo::~SymbianTimerInfo()
323{
324 delete timerAO;
325}
326
327QCompleteDeferredAOs::QCompleteDeferredAOs(QEventDispatcherSymbian *dispatcher)
328 : CActive(COMPLETE_DEFERRED_ACTIVE_OBJECTS_PRIORITY),
329 m_dispatcher(dispatcher)
330{
331 CActiveScheduler::Add(this);
332 iStatus = KRequestPending;
333 SetActive();
334}
335
336QCompleteDeferredAOs::~QCompleteDeferredAOs()
337{
338 Cancel();
339}
340
341void QCompleteDeferredAOs::complete()
342{
343 if (iStatus.Int() == KRequestPending) {
344 TRequestStatus *status = &iStatus;
345 QEventDispatcherSymbian::RequestComplete(status, KErrNone);
346 }
347}
348
349void QCompleteDeferredAOs::DoCancel()
350{
351 if (iStatus.Int() == KRequestPending) {
352 TRequestStatus *status = &iStatus;
353 QEventDispatcherSymbian::RequestComplete(status, KErrNone);
354 }
355}
356
357void QCompleteDeferredAOs::RunL()
358{
359 iStatus = KRequestPending;
360 SetActive();
361
362 QT_TRYCATCH_LEAVING(m_dispatcher->reactivateDeferredActiveObjects());
363}
364
365QSelectThread::QSelectThread()
366 : m_quit(false)
367{
368 if (::pipe(m_pipeEnds) != 0) {
369 qWarning("Select thread was unable to open a pipe, errno: %i", errno);
370 } else {
371 int flags0 = qt_pipe_fcntl(m_pipeEnds[0], F_GETFL);
372 int flags1 = qt_pipe_fcntl(m_pipeEnds[1], F_GETFL);
373 // We should check the error code here, but Open C has a bug that returns
374 // failure even though the operation was successful.
375 qt_pipe2_fcntl(m_pipeEnds[0], F_SETFL, flags0 | O_NONBLOCK);
376 qt_pipe2_fcntl(m_pipeEnds[1], F_SETFL, flags1 | O_NONBLOCK);
377 }
378}
379
380QSelectThread::~QSelectThread()
381{
382 qt_pipe_close(m_pipeEnds[1]);
383 qt_pipe_close(m_pipeEnds[0]);
384}
385
386void QSelectThread::run()
387{
388 Q_D(QThread);
389
390 m_mutex.lock();
391
392 while (!m_quit) {
393 fd_set readfds;
394 fd_set writefds;
395 fd_set exceptionfds;
396
397 FD_ZERO(&readfds);
398 FD_ZERO(&writefds);
399 FD_ZERO(&exceptionfds);
400
401 int maxfd = 0;
402 maxfd = qMax(maxfd, updateSocketSet(QSocketNotifier::Read, &readfds));
403 maxfd = qMax(maxfd, updateSocketSet(QSocketNotifier::Write, &writefds));
404 maxfd = qMax(maxfd, updateSocketSet(QSocketNotifier::Exception, &exceptionfds));
405 maxfd = qMax(maxfd, m_pipeEnds[0]);
406 maxfd++;
407
408 FD_SET(m_pipeEnds[0], &readfds);
409
410 int ret;
411 int savedSelectErrno;
412 ret = qt_socket_select(maxfd, &readfds, &writefds, &exceptionfds, 0);
413 savedSelectErrno = errno;
414
415 if(ret == 0) {
416 // do nothing
417 } else if (ret < 0) {
418 switch (savedSelectErrno) {
419 case EBADF:
420 case EINVAL:
421 case ENOMEM:
422 case EFAULT:
423 qWarning("::select() returned an error: %i", savedSelectErrno);
424 break;
425 case ECONNREFUSED:
426 case EPIPE:
427 qWarning("::select() returned an error: %i (go through sockets)", savedSelectErrno);
428 // prepare to go through all sockets
429 // mark in fd sets both:
430 // good ones
431 // ones that return -1 in select
432 // after loop update notifiers for all of them
433
434 // as we don't have "exception" notifier type
435 // we should force monitoring fd_set of this
436 // type as well
437
438 // clean @ start
439 FD_ZERO(&readfds);
440 FD_ZERO(&writefds);
441 FD_ZERO(&exceptionfds);
442 for (QHash<QSocketNotifier *, TRequestStatus *>::const_iterator i = m_AOStatuses.begin();
443 i != m_AOStatuses.end(); ++i) {
444
445 fd_set onefds;
446 FD_ZERO(&onefds);
447 FD_SET(i.key()->socket(), &onefds);
448
449 fd_set excfds;
450 FD_ZERO(&excfds);
451 FD_SET(i.key()->socket(), &excfds);
452
453 maxfd = i.key()->socket() + 1;
454
455 struct timeval timeout;
456 timeout.tv_sec = 0;
457 timeout.tv_usec = 0;
458
459 ret = 0;
460
461 if(i.key()->type() == QSocketNotifier::Read) {
462 ret = ::select(maxfd, &onefds, 0, &excfds, &timeout);
463 if(ret != 0) FD_SET(i.key()->socket(), &readfds);
464 } else if(i.key()->type() == QSocketNotifier::Write) {
465 ret = ::select(maxfd, 0, &onefds, &excfds, &timeout);
466 if(ret != 0) FD_SET(i.key()->socket(), &writefds);
467 }
468
469 } // end for
470
471 // traversed all, so update
472 updateActivatedNotifiers(QSocketNotifier::Exception, &exceptionfds);
473 updateActivatedNotifiers(QSocketNotifier::Read, &readfds);
474 updateActivatedNotifiers(QSocketNotifier::Write, &writefds);
475
476 break;
477 case EINTR: // Should never occur on Symbian, but this is future proof!
478 default:
479 qWarning("::select() returned an unknown error: %i", savedSelectErrno);
480
481 break;
482 }
483 } else {
484 updateActivatedNotifiers(QSocketNotifier::Exception, &exceptionfds);
485 updateActivatedNotifiers(QSocketNotifier::Read, &readfds);
486 updateActivatedNotifiers(QSocketNotifier::Write, &writefds);
487 }
488
489 if (FD_ISSET(m_pipeEnds[0], &readfds))
490 m_waitCond.wait(&m_mutex);
491 }
492
493 m_mutex.unlock();
494}
495
496void QSelectThread::requestSocketEvents ( QSocketNotifier *notifier, TRequestStatus *status )
497{
498 Q_D(QThread);
499
500 if (!isRunning()) {
501 start();
502 }
503
504 Q_ASSERT(QThread::currentThread() == this->thread());
505
506 QSelectMutexGrabber lock(m_pipeEnds[1], m_pipeEnds[0], &m_mutex);
507
508 Q_ASSERT(!m_AOStatuses.contains(notifier));
509
510 m_AOStatuses.insert(notifier, status);
511
512 m_waitCond.wakeAll();
513}
514
515void QSelectThread::cancelSocketEvents ( QSocketNotifier *notifier )
516{
517 Q_ASSERT(QThread::currentThread() == this->thread());
518
519 QSelectMutexGrabber lock(m_pipeEnds[1], m_pipeEnds[0], &m_mutex);
520
521 m_AOStatuses.remove(notifier);
522
523 m_waitCond.wakeAll();
524}
525
526void QSelectThread::restart()
527{
528 Q_ASSERT(QThread::currentThread() == this->thread());
529
530 QSelectMutexGrabber lock(m_pipeEnds[1], m_pipeEnds[0], &m_mutex);
531
532 m_waitCond.wakeAll();
533}
534
535int QSelectThread::updateSocketSet(QSocketNotifier::Type type, fd_set *fds)
536{
537 int maxfd = 0;
538 if(m_AOStatuses.isEmpty()) {
539 /*
540 * Wonder if should return -1
541 * to signal that no descriptors
542 * added to fds
543 */
544 return maxfd;
545 }
546 for ( QHash<QSocketNotifier *, TRequestStatus *>::const_iterator i = m_AOStatuses.begin();
547 i != m_AOStatuses.end(); ++i) {
548 if (i.key()->type() == type) {
549 FD_SET(i.key()->socket(), fds);
550 maxfd = qMax(maxfd, i.key()->socket());
551 } else if(type == QSocketNotifier::Exception) {
552 /*
553 * We are registering existing sockets
554 * always to exception set
555 *
556 * Doing double FD_SET shouldn't
557 * matter
558 */
559 FD_SET(i.key()->socket(), fds);
560 maxfd = qMax(maxfd, i.key()->socket());
561 }
562 }
563
564 return maxfd;
565}
566
567void QSelectThread::updateActivatedNotifiers(QSocketNotifier::Type type, fd_set *fds)
568{
569 Q_D(QThread);
570 if(m_AOStatuses.isEmpty()) {
571 return;
572 }
573 QList<QSocketNotifier *> toRemove;
574 for (QHash<QSocketNotifier *, TRequestStatus *>::const_iterator i = m_AOStatuses.begin();
575 i != m_AOStatuses.end(); ++i) {
576 if (i.key()->type() == type && FD_ISSET(i.key()->socket(), fds)) {
577 toRemove.append(i.key());
578 TRequestStatus *status = i.value();
579 // Thread data is still owned by the main thread.
580 QEventDispatcherSymbian::RequestComplete(d->threadData->symbian_thread_handle, status, KErrNone);
581 } else if(type == QSocketNotifier::Exception && FD_ISSET(i.key()->socket(), fds)) {
582 /*
583 * check if socket is in exception set
584 * then signal RequestComplete for it
585 */
586 qWarning("exception on %d [will close the socket handle - hack]", i.key()->socket());
587 // quick fix; there is a bug
588 // when doing read on socket
589 // errors not preoperly mapped
590 // after offline-ing the device
591 // on some devices we do get exception
592 ::close(i.key()->socket());
593 toRemove.append(i.key());
594 TRequestStatus *status = i.value();
595 QEventDispatcherSymbian::RequestComplete(d->threadData->symbian_thread_handle, status, KErrNone);
596 }
597 }
598
599 for (int c = 0; c < toRemove.size(); ++c) {
600 m_AOStatuses.remove(toRemove[c]);
601 }
602}
603
604void QSelectThread::stop()
605{
606 m_quit = true;
607 restart();
608 wait();
609}
610
611QSocketActiveObject::QSocketActiveObject(QEventDispatcherSymbian *dispatcher, QSocketNotifier *notifier)
612 : QActiveObject(CActive::EPriorityStandard, dispatcher),
613 m_notifier(notifier),
614 m_inSocketEvent(false),
615 m_deleteLater(false)
616{
617 CActiveScheduler::Add(this);
618 iStatus = KRequestPending;
619 SetActive();
620}
621
622QSocketActiveObject::~QSocketActiveObject()
623{
624 Cancel();
625}
626
627void QSocketActiveObject::DoCancel()
628{
629 if (iStatus.Int() == KRequestPending) {
630 TRequestStatus *status = &iStatus;
631 QEventDispatcherSymbian::RequestComplete(status, KErrNone);
632 }
633}
634
635void QSocketActiveObject::RunL()
636{
637 if (maybeQueueForLater())
638 return;
639
640 QT_TRYCATCH_LEAVING(m_dispatcher->socketFired(this));
641}
642
643void QSocketActiveObject::deleteLater()
644{
645 if (m_inSocketEvent) {
646 m_deleteLater = true;
647 } else {
648 delete this;
649 }
650}
651
652#ifdef QT_SYMBIAN_PRIORITY_DROP
653class QIdleDetectorThread
654{
655public:
656 QIdleDetectorThread()
657 : m_state(STATE_RUN), m_stop(false)
658 {
659 qt_symbian_throwIfError(m_lock.CreateLocal(0));
660 TInt err = m_idleDetectorThread.Create(KNullDesC(), &idleDetectorThreadFunc, 1024, NULL, this);
661 if (err != KErrNone)
662 m_lock.Close();
663 qt_symbian_throwIfError(err);
664 m_idleDetectorThread.SetPriority(EPriorityAbsoluteBackgroundNormal);
665 m_idleDetectorThread.Resume();
666 }
667
668 ~QIdleDetectorThread()
669 {
670 // close down the idle thread because if corelib is loaded temporarily, this would leak threads into the host process
671 m_stop = true;
672 m_lock.Signal();
673 m_idleDetectorThread.SetPriority(EPriorityNormal);
674 TRequestStatus s;
675 m_idleDetectorThread.Logon(s);
676 User::WaitForRequest(s);
677 m_idleDetectorThread.Close();
678 m_lock.Close();
679 }
680
681 void kick()
682 {
683 m_state = STATE_KICKED;
684 m_lock.Signal();
685 }
686
687 bool hasRun()
688 {
689 return m_state == STATE_RUN;
690 }
691
692private:
693 static TInt idleDetectorThreadFunc(TAny* self)
694 {
695 static_cast<QIdleDetectorThread*>(self)->IdleLoop();
696 return KErrNone;
697 }
698
699 void IdleLoop()
700 {
701 while (!m_stop) {
702 m_lock.Wait();
703 m_state = STATE_RUN;
704 }
705 }
706
707private:
708 enum IdleStates {STATE_KICKED, STATE_RUN} m_state;
709 bool m_stop;
710 RThread m_idleDetectorThread;
711 RSemaphore m_lock;
712};
713
714Q_GLOBAL_STATIC(QIdleDetectorThread, idleDetectorThread);
715
716const int maxBusyTime = 2000; // maximum time we allow idle detector to be blocked before worrying, in milliseconds
717const int baseDelay = 1000; // minimum delay time used when backing off to allow idling, in microseconds
718#endif
719
720QEventDispatcherSymbian::QEventDispatcherSymbian(QObject *parent)
721 : QAbstractEventDispatcher(parent),
722 m_selectThread(0),
723 m_activeScheduler(0),
724 m_wakeUpAO(0),
725 m_completeDeferredAOs(0),
726 m_interrupt(false),
727 m_wakeUpDone(0),
728 m_iterationCount(0),
729 m_insideTimerEvent(false),
730 m_noSocketEvents(false)
731{
732#ifdef QT_SYMBIAN_PRIORITY_DROP
733 m_delay = baseDelay;
734 m_avgEventTime = 0;
735 idleDetectorThread();
736#endif
737}
738
739QEventDispatcherSymbian::~QEventDispatcherSymbian()
740{
741}
742
743void QEventDispatcherSymbian::startingUp()
744{
745 if( !CActiveScheduler::Current() ) {
746 m_activeScheduler = q_check_ptr(new CQtActiveScheduler()); // CBase derived class needs to be checked on new
747 CActiveScheduler::Install(m_activeScheduler);
748 }
749 m_wakeUpAO = q_check_ptr(new QWakeUpActiveObject(this));
750 m_completeDeferredAOs = q_check_ptr(new QCompleteDeferredAOs(this));
751 // We already might have posted events, wakeup once to process them
752 wakeUp();
753}
754
755QSelectThread& QEventDispatcherSymbian::selectThread() {
756 if (!m_selectThread)
757 m_selectThread = new QSelectThread;
758 return *m_selectThread;
759}
760
761void QEventDispatcherSymbian::closingDown()
762{
763 if (m_selectThread && m_selectThread->isRunning()) {
764 m_selectThread->stop();
765 }
766 delete m_selectThread;
767 m_selectThread = 0;
768
769 delete m_completeDeferredAOs;
770 delete m_wakeUpAO;
771 if (m_activeScheduler) {
772 delete m_activeScheduler;
773 }
774}
775
776bool QEventDispatcherSymbian::processEvents ( QEventLoop::ProcessEventsFlags flags )
777{
778 bool handledAnyEvent = false;
779 bool oldNoSocketEventsValue = m_noSocketEvents;
780 bool oldInsideTimerEventValue = m_insideTimerEvent;
781
782 m_insideTimerEvent = false;
783
784 QT_TRY {
785 Q_D(QAbstractEventDispatcher);
786
787 // It is safe if this counter overflows. The main importance is that each
788 // iteration count is different from the last.
789 m_iterationCount++;
790
791 RThread &thread = d->threadData->symbian_thread_handle;
792
793 bool block;
794 if (flags & QEventLoop::WaitForMoreEvents) {
795 block = true;
796 emit aboutToBlock();
797 } else {
798 block = false;
799 }
800
801 if (flags & QEventLoop::ExcludeSocketNotifiers) {
802 m_noSocketEvents = true;
803 } else {
804 m_noSocketEvents = false;
805 handledAnyEvent = sendDeferredSocketEvents();
806 }
807
808 bool handledSymbianEvent = false;
809 m_interrupt = false;
810
811#ifdef QT_SYMBIAN_PRIORITY_DROP
812 QTime eventTimer;
813#endif
814
815 while (1) {
816 if (block) {
817 // This is where Qt will spend most of its time.
818 CActiveScheduler::Current()->WaitForAnyRequest();
819 } else {
820 if (thread.RequestCount() == 0) {
821 break;
822 }
823 // This one should return without delay.
824 CActiveScheduler::Current()->WaitForAnyRequest();
825 }
826
827#ifdef QT_SYMBIAN_PRIORITY_DROP
828 if (idleDetectorThread()->hasRun()) {
829 if (m_delay > baseDelay)
830 m_delay -= baseDelay;
831 m_lastIdleRequestTimer.start();
832 idleDetectorThread()->kick();
833 } else if (m_lastIdleRequestTimer.elapsed() > maxBusyTime) {
834 User::AfterHighRes(m_delay);
835 // allow delay to be up to 1/4 of execution time
836 if (!idleDetectorThread()->hasRun() && m_delay*3 < m_avgEventTime)
837 m_delay += baseDelay;
838 }
839 eventTimer.start();
840#endif
841
842 TInt error;
843 handledSymbianEvent = CActiveScheduler::RunIfReady(error, KMinTInt);
844 if (error) {
845 qWarning("CActiveScheduler::RunIfReady() returned error: %i\n", error);
846 CActiveScheduler::Current()->Error(error);
847 }
848
849#ifdef QT_SYMBIAN_PRIORITY_DROP
850 int eventDur = eventTimer.elapsed()*1000;
851 // average is calcualted as a 5% decaying exponential average
852 m_avgEventTime = (m_avgEventTime * 95 + eventDur * 5) / 100;
853#endif
854
855 if (!handledSymbianEvent) {
856 qFatal("QEventDispatcherSymbian::processEvents(): Caught Symbian stray signal");
857 }
858 handledAnyEvent = true;
859 if (m_interrupt) {
860 break;
861 }
862 block = false;
863 };
864
865 emit awake();
866 } QT_CATCH (const std::exception& ex) {
867#ifndef QT_NO_EXCEPTIONS
868 CActiveScheduler::Current()->Error(qt_symbian_exception2Error(ex));
869#endif
870 }
871
872 m_noSocketEvents = oldNoSocketEventsValue;
873 m_insideTimerEvent = oldInsideTimerEventValue;
874
875 return handledAnyEvent;
876}
877
878void QEventDispatcherSymbian::timerFired(int timerId)
879{
880 QHash<int, SymbianTimerInfoPtr>::iterator i = m_timerList.find(timerId);
881 if (i == m_timerList.end()) {
882 // The timer has been deleted. Ignore this event.
883 return;
884 }
885
886 SymbianTimerInfoPtr timerInfo = *i;
887
888 // Prevent infinite timer recursion.
889 if (timerInfo->inTimerEvent) {
890 return;
891 }
892
893 timerInfo->inTimerEvent = true;
894 bool oldInsideTimerEventValue = m_insideTimerEvent;
895 m_insideTimerEvent = true;
896
897 QTimerEvent event(timerInfo->timerId);
898 QCoreApplication::sendEvent(timerInfo->receiver, &event);
899
900 m_insideTimerEvent = oldInsideTimerEventValue;
901 timerInfo->inTimerEvent = false;
902
903 return;
904}
905
906void QEventDispatcherSymbian::socketFired(QSocketActiveObject *socketAO)
907{
908 if (m_noSocketEvents) {
909 m_deferredSocketEvents.append(socketAO);
910 return;
911 }
912
913 QEvent e(QEvent::SockAct);
914 socketAO->m_inSocketEvent = true;
915 QCoreApplication::sendEvent(socketAO->m_notifier, &e);
916 socketAO->m_inSocketEvent = false;
917
918 if (socketAO->m_deleteLater) {
919 delete socketAO;
920 } else {
921 socketAO->iStatus = KRequestPending;
922 socketAO->SetActive();
923 reactivateSocketNotifier(socketAO->m_notifier);
924 }
925}
926
927void QEventDispatcherSymbian::wakeUpWasCalled()
928{
929 // The reactivation should happen in RunL, right before the call to this function.
930 // This is because m_wakeUpDone is the "signal" that the object can be completed
931 // once more.
932 // Also, by dispatching the posted events after resetting m_wakeUpDone, we guarantee
933 // that no posted event notification will be lost. If we did it the other way
934 // around, it would be possible for another thread to post an event right after
935 // the sendPostedEvents was done, but before the object was ready to be completed
936 // again. This could deadlock the application if there are no other posted events.
937 m_wakeUpDone.fetchAndStoreOrdered(0);
938 sendPostedEvents();
939}
940
941void QEventDispatcherSymbian::interrupt()
942{
943 m_interrupt = true;
944 wakeUp();
945}
946
947void QEventDispatcherSymbian::wakeUp()
948{
949 Q_D(QAbstractEventDispatcher);
950
951 if (m_wakeUpAO && m_wakeUpDone.testAndSetAcquire(0, 1)) {
952 TRequestStatus *status = &m_wakeUpAO->iStatus;
953 QEventDispatcherSymbian::RequestComplete(d->threadData->symbian_thread_handle, status, KErrNone);
954 }
955}
956
957bool QEventDispatcherSymbian::sendPostedEvents()
958{
959 Q_D(QAbstractEventDispatcher);
960
961 // moveToThread calls this and canWait == true -> Events will never get processed
962 // if we check for d->threadData->canWait
963 //
964 // QCoreApplication::postEvent sets canWait = false, but after the object and events
965 // are moved to a new thread, the canWait in new thread is true i.e. not changed to reflect
966 // the flag on old thread. That's why events in a new thread will not get processed.
967 // This migth be actually bug in moveToThread functionality, but because other platforms
968 // do not check canWait in wakeUp (where we essentially are now) - decided to remove it from
969 // here as well.
970
971 //if (!d->threadData->canWait) {
972 QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
973 return true;
974 //}
975 //return false;
976}
977
978inline void QEventDispatcherSymbian::addDeferredActiveObject(QActiveObject *object)
979{
980 queueDeferredActiveObjectsCompletion();
981 m_deferredActiveObjects.append(object);
982}
983
984inline void QEventDispatcherSymbian::removeDeferredActiveObject(QActiveObject *object)
985{
986 m_deferredActiveObjects.removeAll(object);
987}
988
989void QEventDispatcherSymbian::queueDeferredActiveObjectsCompletion()
990{
991 m_completeDeferredAOs->complete();
992}
993
994void QEventDispatcherSymbian::reactivateDeferredActiveObjects()
995{
996 while (!m_deferredActiveObjects.isEmpty()) {
997 QActiveObject *object = m_deferredActiveObjects.takeFirst();
998 object->reactivateAndComplete();
999 }
1000
1001 // We do this because we want to return from processEvents. This is because
1002 // each invocation of processEvents should only run each active object once.
1003 // The active scheduler should run them continously, however.
1004 m_interrupt = true;
1005}
1006
1007bool QEventDispatcherSymbian::sendDeferredSocketEvents()
1008{
1009 bool sentAnyEvents = false;
1010 while (!m_deferredSocketEvents.isEmpty()) {
1011 sentAnyEvents = true;
1012 socketFired(m_deferredSocketEvents.takeFirst());
1013 }
1014
1015 return sentAnyEvents;
1016}
1017
1018void QEventDispatcherSymbian::flush()
1019{
1020}
1021
1022bool QEventDispatcherSymbian::hasPendingEvents()
1023{
1024 Q_D(QAbstractEventDispatcher);
1025 return (d->threadData->symbian_thread_handle.RequestCount() != 0
1026 || !d->threadData->canWait || !m_deferredSocketEvents.isEmpty());
1027}
1028
1029void QEventDispatcherSymbian::registerSocketNotifier ( QSocketNotifier * notifier )
1030{
1031 QSocketActiveObject *socketAO = q_check_ptr(new QSocketActiveObject(this, notifier));
1032 m_notifiers.insert(notifier, socketAO);
1033 selectThread().requestSocketEvents(notifier, &socketAO->iStatus);
1034}
1035
1036void QEventDispatcherSymbian::unregisterSocketNotifier ( QSocketNotifier * notifier )
1037{
1038 if (m_selectThread)
1039 m_selectThread->cancelSocketEvents(notifier);
1040 if (m_notifiers.contains(notifier)) {
1041 QSocketActiveObject *sockObj = *m_notifiers.find(notifier);
1042 m_deferredSocketEvents.removeAll(sockObj);
1043 sockObj->deleteLater();
1044 m_notifiers.remove(notifier);
1045 }
1046}
1047
1048void QEventDispatcherSymbian::reactivateSocketNotifier(QSocketNotifier *notifier)
1049{
1050 selectThread().requestSocketEvents(notifier, &m_notifiers[notifier]->iStatus);
1051}
1052
1053void QEventDispatcherSymbian::registerTimer ( int timerId, int interval, QObject * object )
1054{
1055 if (interval < 0) {
1056 qWarning("Timer interval < 0");
1057 interval = 0;
1058 }
1059
1060 SymbianTimerInfoPtr timer(new SymbianTimerInfo);
1061 timer->timerId = timerId;
1062 timer->interval = interval;
1063 timer->inTimerEvent = false;
1064 timer->receiver = object;
1065 timer->dispatcher = this;
1066 timer->timerAO = q_check_ptr(new QTimerActiveObject(this, timer.data()));
1067 m_timerList.insert(timerId, timer);
1068
1069 timer->timerAO->Start();
1070
1071 if (m_insideTimerEvent)
1072 // If we are inside a timer event, we need to prevent event starvation
1073 // by preventing newly created timers from running in the same event processing
1074 // iteration. Do this by calling the maybeQueueForLater() function to "fake" that we have
1075 // already run once. This will cause the next run to be added to the deferred
1076 // queue instead.
1077 timer->timerAO->maybeQueueForLater();
1078}
1079
1080bool QEventDispatcherSymbian::unregisterTimer ( int timerId )
1081{
1082 if (!m_timerList.contains(timerId)) {
1083 return false;
1084 }
1085
1086 SymbianTimerInfoPtr timerInfo = m_timerList.take(timerId);
1087
1088 if (!QObjectPrivate::get(timerInfo->receiver)->inThreadChangeEvent)
1089 QAbstractEventDispatcherPrivate::releaseTimerId(timerId);
1090
1091 return true;
1092}
1093
1094bool QEventDispatcherSymbian::unregisterTimers ( QObject * object )
1095{
1096 if (m_timerList.isEmpty())
1097 return false;
1098
1099 bool unregistered = false;
1100 for (QHash<int, SymbianTimerInfoPtr>::iterator i = m_timerList.begin(); i != m_timerList.end(); ) {
1101 if ((*i)->receiver == object) {
1102 i = m_timerList.erase(i);
1103 unregistered = true;
1104 } else {
1105 ++i;
1106 }
1107 }
1108
1109 return unregistered;
1110}
1111
1112QList<QEventDispatcherSymbian::TimerInfo> QEventDispatcherSymbian::registeredTimers ( QObject * object ) const
1113{
1114 QList<TimerInfo> list;
1115 for (QHash<int, SymbianTimerInfoPtr>::const_iterator i = m_timerList.begin(); i != m_timerList.end(); ++i) {
1116 if ((*i)->receiver == object) {
1117 list.push_back(TimerInfo((*i)->timerId, (*i)->interval));
1118 }
1119 }
1120
1121 return list;
1122}
1123
1124/*
1125 * This active scheduler class implements a simple report and continue policy, for Symbian OS leaves
1126 * or exceptions from Qt that fall back to the scheduler.
1127 * It will be used in cases where there is no existing active scheduler installed.
1128 * Apps which link to qts60main.lib will have the UI active scheduler installed in the main thread
1129 * instead of this one. But this would be used in other threads in the UI.
1130 * An app could replace this behaviour by installing an alternative active scheduler.
1131 */
1132void CQtActiveScheduler::Error(TInt aError) const
1133{
1134 QT_TRY {
1135 qWarning("Error from active scheduler %d", aError);
1136 }
1137 QT_CATCH (const std::bad_alloc&) {} // ignore alloc fails, nothing more can be done
1138}
1139
1140QT_END_NAMESPACE
1141
1142#include "moc_qeventdispatcher_symbian_p.cpp"
Note: See TracBrowser for help on using the repository browser.