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 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_symbian_p.h"
43 #include <private/qthread_p.h>
44 #include <qcoreapplication.h>
45 #include <private/qcoreapplication_p.h>
46 #include <qsemaphore.h>
47 
48 #include <unistd.h>
49 #include <errno.h>
50 
51 QT_BEGIN_NAMESPACE
52 
53 #ifdef SYMBIAN_GRAPHICS_WSERV_QT_EFFECTS
54 // when the system UI is Qt based, priority drop is not needed as CPU starved processes will not be killed.
55 #undef QT_SYMBIAN_PRIORITY_DROP
56 #else
57 #define QT_SYMBIAN_PRIORITY_DROP
58 #endif
59 
60 #define WAKE_UP_PRIORITY CActive::EPriorityStandard
61 #define TIMER_PRIORITY CActive::EPriorityHigh
62 #define COMPLETE_DEFERRED_ACTIVE_OBJECTS_PRIORITY CActive::EPriorityIdle
63 
64 class Incrementer {
65     int &variable;
66 public:
Incrementer(int & variable)67     inline Incrementer(int &variable) : variable(variable)
68     { ++variable; }
~Incrementer()69     inline ~Incrementer()
70     { --variable; }
71 };
72 
73 class Decrementer {
74     int &variable;
75 public:
Decrementer(int & variable)76     inline Decrementer(int &variable) : variable(variable)
77     { --variable; }
~Decrementer()78     inline ~Decrementer()
79     { ++variable; }
80 };
81 
qt_pipe_write(int socket,const char * data,qint64 len)82 static inline int qt_pipe_write(int socket, const char *data, qint64 len)
83 {
84 	return ::write(socket, data, len);
85 }
86 #if defined(write)
87 # undef write
88 #endif
89 
qt_pipe_close(int socket)90 static inline int qt_pipe_close(int socket)
91 {
92 	return ::close(socket);
93 }
94 #if defined(close)
95 # undef close
96 #endif
97 
qt_pipe_fcntl(int socket,int command)98 static inline int qt_pipe_fcntl(int socket, int command)
99 {
100 	return ::fcntl(socket, command);
101 }
qt_pipe2_fcntl(int socket,int command,int option)102 static inline int qt_pipe2_fcntl(int socket, int command, int option)
103 {
104 	return ::fcntl(socket, command, option);
105 }
106 #if defined(fcntl)
107 # undef fcntl
108 #endif
109 
qt_socket_select(int nfds,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout)110 static inline int qt_socket_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
111 {
112 	return ::select(nfds, readfds, writefds, exceptfds, timeout);
113 }
114 
115 // This simply interrupts the select and locks the mutex until destroyed.
116 class QSelectMutexGrabber
117 {
118 public:
QSelectMutexGrabber(int writeFd,int readFd,QMutex * mutex)119     QSelectMutexGrabber(int writeFd, int readFd, QMutex *mutex)
120         : m_mutex(mutex)
121     {
122         if (m_mutex->tryLock())
123             return;
124 
125         char dummy = 0;
126         qt_pipe_write(writeFd, &dummy, 1);
127 
128         m_mutex->lock();
129 
130         char buffer;
131         while (::read(readFd, &buffer, 1) > 0) {}
132     }
133 
~QSelectMutexGrabber()134     ~QSelectMutexGrabber()
135     {
136         m_mutex->unlock();
137     }
138 
139 private:
140     QMutex *m_mutex;
141 };
142 
143 /*
144  * This class is designed to aid in implementing event handling in a more round robin fashion,
145  * when Qt active objects are used outside of QtRRActiveScheduler.
146  * We cannot change active objects that we do not own, but active objects that Qt owns may use
147  * this as a base class with convenience functions.
148  *
149  * Here is how it works: On every RunL, the deriving class should call maybeQueueForLater().
150  * This will return whether the active object has been queued, or whether it should run immediately.
151  * Queued objects will run again after other events have been processed.
152  *
153  * The QCompleteDeferredAOs class is a special object that runs after all others, which will
154  * reactivate the objects that were previously not run.
155  * Socket active objects can use it to defer their activity.
156  */
QActiveObject(TInt priority,QEventDispatcherSymbian * dispatcher)157 QActiveObject::QActiveObject(TInt priority, QEventDispatcherSymbian *dispatcher)
158     : CActive(priority),
159       m_dispatcher(dispatcher),
160       m_threadData(QThreadData::current()),
161       m_hasAlreadyRun(false),
162       m_hasRunAgain(false),
163       m_iterationCount(1)
164 {
165 }
166 
~QActiveObject()167 QActiveObject::~QActiveObject()
168 {
169     if (m_hasRunAgain)
170         m_dispatcher->removeDeferredActiveObject(this);
171 }
172 
maybeQueueForLater()173 bool QActiveObject::maybeQueueForLater()
174 {
175     Q_ASSERT(!m_hasRunAgain);
176 
177     if (!m_hasAlreadyRun || m_dispatcher->iterationCount() != m_iterationCount) {
178         // First occurrence of this event in this iteration.
179         m_hasAlreadyRun = true;
180         m_iterationCount = m_dispatcher->iterationCount();
181         return false;
182     } else {
183         // The event has already occurred.
184         m_dispatcher->addDeferredActiveObject(this);
185         m_hasRunAgain = true;
186         return true;
187     }
188 }
189 
maybeDeferSocketEvent()190 bool QActiveObject::maybeDeferSocketEvent()
191 {
192     Q_ASSERT(m_dispatcher);
193     if (!m_dispatcher->areSocketEventsBlocked()) {
194         return false;
195     }
196     m_dispatcher->addDeferredSocketActiveObject(this);
197     return true;
198 }
199 
reactivateAndComplete()200 void QActiveObject::reactivateAndComplete()
201 {
202     TInt error = iStatus.Int();
203     iStatus = KRequestPending;
204     SetActive();
205     TRequestStatus *status = &iStatus;
206     QEventDispatcherSymbian::RequestComplete(status, error);
207 
208     m_hasRunAgain = false;
209     m_hasAlreadyRun = false;
210 }
211 
QWakeUpActiveObject(QEventDispatcherSymbian * dispatcher)212 QWakeUpActiveObject::QWakeUpActiveObject(QEventDispatcherSymbian *dispatcher)
213     : CActive(WAKE_UP_PRIORITY),
214       m_dispatcher(dispatcher)
215 {
216     m_hostThreadId = RThread().Id();
217     CActiveScheduler::Add(this);
218     iStatus = KRequestPending;
219     SetActive();
220 }
221 
~QWakeUpActiveObject()222 QWakeUpActiveObject::~QWakeUpActiveObject()
223 {
224     Cancel();
225 }
226 
DoCancel()227 void QWakeUpActiveObject::DoCancel()
228 {
229     if (iStatus.Int() == KRequestPending) {
230         TRequestStatus *status = &iStatus;
231         QEventDispatcherSymbian::RequestComplete(status, KErrNone);
232     } else if (IsActive() && m_hostThreadId != RThread().Id()) {
233         // This is being cancelled in the adopted monitor thread, which can happen if an adopted thread with
234         // an event loop has exited. The event loop creates an event dispatcher with this active object, which may be complete but not run on exit.
235         // We force a cancellation in this thread, because a) the object cannot be deleted while active and b) without a cancellation
236         // the thread semaphore will be one count down.
237         // It is possible for this problem to affect other active objects. They symptom would be that finished signals
238         // from adopted threads are not sent, or they arrive much later than they should.
239         TRequestStatus *status = &iStatus;
240         User::RequestComplete(status, KErrNone);
241     }
242 }
243 
RunL()244 void QWakeUpActiveObject::RunL()
245 {
246     iStatus = KRequestPending;
247     SetActive();
248     QT_TRYCATCH_LEAVING(m_dispatcher->wakeUpWasCalled(this));
249 }
250 
QTimerActiveObject(QEventDispatcherSymbian * dispatcher,SymbianTimerInfo * timerInfo)251 QTimerActiveObject::QTimerActiveObject(QEventDispatcherSymbian *dispatcher, SymbianTimerInfo *timerInfo)
252     : QActiveObject(TIMER_PRIORITY, dispatcher),
253       m_timerInfo(timerInfo), m_expectedTimeSinceLastEvent(0)
254 {
255     // start the timeout timer to ensure initialisation
256     m_timeoutTimer.start();
257 }
258 
~QTimerActiveObject()259 QTimerActiveObject::~QTimerActiveObject()
260 {
261     Cancel();
262     // deletion in the wrong thread (eg adoptedThreadMonitor thread) must avoid using the RTimer, which is local
263     // to the thread it was created in.
264     if (QThreadData::current() == m_threadData)
265         m_rTimer.Close(); //close of null handle is safe
266 }
267 
DoCancel()268 void QTimerActiveObject::DoCancel()
269 {
270     // RTimer is thread local and cannot be cancelled outside of the thread it was created in
271     if (QThreadData::current() == m_threadData) {
272         if (m_timerInfo->interval > 0) {
273             m_rTimer.Cancel();
274         } else {
275             if (iStatus.Int() == KRequestPending) {
276                 TRequestStatus *status = &iStatus;
277                 QEventDispatcherSymbian::RequestComplete(status, KErrNone);
278             }
279         }
280     } else {
281         // Cancel requires a signal to continue, we're in the wrong thread to use the RTimer
282         if (m_threadData->symbian_thread_handle.ExitType() == EExitPending) {
283             // owner thread is still running, it will receive a stray event if the timer fires now.
284             RDebug::Print(_L("QTimerActiveObject cancelled from wrong thread, owner thread will probably panic with stray signal"));
285         }
286         TRequestStatus *status = &iStatus;
287         User::RequestComplete(status, KErrCancel);
288     }
289 }
290 
RunL()291 void QTimerActiveObject::RunL()
292 {
293     int error = KErrNone;
294     if (iStatus == KErrNone) {
295         QT_TRYCATCH_ERROR(error, Run());
296     } else {
297         error = iStatus.Int();
298     }
299     // All Symbian error codes are negative.
300     if (error < 0) {
301         CActiveScheduler::Current()->Error(error);  // stop and report here, as this timer will be deleted on scope exit
302     }
303 }
304 
305 #define MAX_SYMBIAN_TIMEOUT_MS 2000000
StartTimer()306 void QTimerActiveObject::StartTimer()
307 {
308     if (m_timerInfo->msLeft > MAX_SYMBIAN_TIMEOUT_MS) {
309         //There is loss of accuracy anyway due to needing to restart the timer every 33 minutes,
310         //so the 1/64s res of After() is acceptable for these very long timers.
311         m_rTimer.After(iStatus, MAX_SYMBIAN_TIMEOUT_MS * 1000);
312         m_timerInfo->msLeft -= MAX_SYMBIAN_TIMEOUT_MS;
313     } else {
314         // this algorithm implements drift correction for repeating timers
315         // calculate how late we are for this event
316         int timeSinceLastEvent = m_timeoutTimer.restart();
317         int overshoot = timeSinceLastEvent - m_expectedTimeSinceLastEvent;
318         if (overshoot > m_timerInfo->msLeft) {
319             // we skipped a whole timeout, restart from here
320             overshoot = 0;
321         }
322         // calculate when the next event should happen
323         int waitTime = m_timerInfo->msLeft - overshoot;
324         m_expectedTimeSinceLastEvent = waitTime;
325         // limit the actual ms wait time to avoid wild corrections
326         // this will cause the real event time to slowly drift back to the expected event time
327         // measurements show that Symbian timers always fire 1 or 2 ms late
328         const int limit = 4;
329         waitTime = qMax(m_timerInfo->msLeft - limit, waitTime);
330         m_rTimer.HighRes(iStatus, waitTime * 1000);
331         m_timerInfo->msLeft = 0;
332     }
333     SetActive();
334 }
335 
Run()336 void QTimerActiveObject::Run()
337 {
338     //restart timer immediately, if the timeout has been split because it overflows max for platform.
339     if (m_timerInfo->msLeft > 0) {
340         StartTimer();
341         return;
342     }
343 
344     if (maybeQueueForLater())
345         return;
346 
347     if (m_timerInfo->interval > 0) {
348         // Start a new timer immediately so that we don't lose time.
349         m_timerInfo->msLeft = m_timerInfo->interval;
350         StartTimer();
351 
352         m_timerInfo->dispatcher->timerFired(m_timerInfo->timerId, this);
353     } else {
354         // However, we only complete zero timers after the event has finished,
355         // in order to prevent busy looping when doing nested loops.
356 
357         // Keep the refpointer around in order to avoid deletion until the end of this function.
358         SymbianTimerInfoPtr timerInfoPtr(m_timerInfo);
359 
360         m_timerInfo->dispatcher->timerFired(m_timerInfo->timerId, this);
361 
362         iStatus = KRequestPending;
363         SetActive();
364         TRequestStatus *status = &iStatus;
365         QEventDispatcherSymbian::RequestComplete(status, KErrNone);
366     }
367 }
368 
Start()369 void QTimerActiveObject::Start()
370 {
371     CActiveScheduler::Add(this);
372     m_timerInfo->msLeft = m_timerInfo->interval;
373     if (m_timerInfo->interval > 0) {
374         if (!m_rTimer.Handle()) {
375             qt_symbian_throwIfError(m_rTimer.CreateLocal());
376             m_threadData = QThreadData::current();
377         }
378         m_timeoutTimer.start();
379         m_expectedTimeSinceLastEvent = 0;
380         StartTimer();
381     } else {
382         iStatus = KRequestPending;
383         SetActive();
384         TRequestStatus *status = &iStatus;
385         QEventDispatcherSymbian::RequestComplete(status, KErrNone);
386     }
387 }
388 
SymbianTimerInfo()389 SymbianTimerInfo::SymbianTimerInfo()
390     : timerAO(0)
391 {
392 }
393 
~SymbianTimerInfo()394 SymbianTimerInfo::~SymbianTimerInfo()
395 {
396     delete timerAO;
397 }
398 
QCompleteDeferredAOs(QEventDispatcherSymbian * dispatcher)399 QCompleteDeferredAOs::QCompleteDeferredAOs(QEventDispatcherSymbian *dispatcher)
400     : CActive(COMPLETE_DEFERRED_ACTIVE_OBJECTS_PRIORITY),
401       m_dispatcher(dispatcher)
402 {
403     CActiveScheduler::Add(this);
404     iStatus = KRequestPending;
405     SetActive();
406 }
407 
~QCompleteDeferredAOs()408 QCompleteDeferredAOs::~QCompleteDeferredAOs()
409 {
410     Cancel();
411 }
412 
complete()413 void QCompleteDeferredAOs::complete()
414 {
415     if (iStatus.Int() == KRequestPending) {
416         TRequestStatus *status = &iStatus;
417         QEventDispatcherSymbian::RequestComplete(status, KErrNone);
418     }
419 }
420 
DoCancel()421 void QCompleteDeferredAOs::DoCancel()
422 {
423     if (iStatus.Int() == KRequestPending) {
424         TRequestStatus *status = &iStatus;
425         QEventDispatcherSymbian::RequestComplete(status, KErrNone);
426     }
427 }
428 
RunL()429 void QCompleteDeferredAOs::RunL()
430 {
431     iStatus = KRequestPending;
432     SetActive();
433 
434     QT_TRYCATCH_LEAVING(m_dispatcher->reactivateDeferredActiveObjects());
435 }
436 
QSelectThread()437 QSelectThread::QSelectThread()
438     : m_quit(false)
439 {
440     if (::pipe(m_pipeEnds) != 0) {
441         qWarning("Select thread was unable to open a pipe, errno: %i", errno);
442     } else {
443         int flags0 = qt_pipe_fcntl(m_pipeEnds[0], F_GETFL);
444         int flags1 = qt_pipe_fcntl(m_pipeEnds[1], F_GETFL);
445         // We should check the error code here, but Open C has a bug that returns
446         // failure even though the operation was successful.
447         qt_pipe2_fcntl(m_pipeEnds[0], F_SETFL, flags0 | O_NONBLOCK);
448         qt_pipe2_fcntl(m_pipeEnds[1], F_SETFL, flags1 | O_NONBLOCK);
449     }
450 }
451 
~QSelectThread()452 QSelectThread::~QSelectThread()
453 {
454     qt_pipe_close(m_pipeEnds[1]);
455     qt_pipe_close(m_pipeEnds[0]);
456 }
457 
run()458 void QSelectThread::run()
459 {
460     Q_D(QThread);
461 
462     m_mutex.lock();
463 
464     while (!m_quit) {
465         fd_set readfds;
466         fd_set writefds;
467         fd_set exceptionfds;
468 
469         FD_ZERO(&readfds);
470         FD_ZERO(&writefds);
471         FD_ZERO(&exceptionfds);
472 
473         int maxfd = 0;
474         maxfd = qMax(maxfd, updateSocketSet(QSocketNotifier::Read, &readfds));
475         maxfd = qMax(maxfd, updateSocketSet(QSocketNotifier::Write, &writefds));
476         maxfd = qMax(maxfd, updateSocketSet(QSocketNotifier::Exception, &exceptionfds));
477         maxfd = qMax(maxfd, m_pipeEnds[0]);
478         maxfd++;
479 
480         FD_SET(m_pipeEnds[0], &readfds);
481 
482         int ret;
483         int savedSelectErrno;
484         ret = qt_socket_select(maxfd, &readfds, &writefds, &exceptionfds, 0);
485         savedSelectErrno = errno;
486 
487         if(ret == 0) {
488          // do nothing
489         } else if (ret < 0) {
490             switch (savedSelectErrno) {
491             case EBADF:
492             case EINVAL:
493             case ENOMEM:
494             case EFAULT:
495                 qWarning("::select() returned an error: %i", savedSelectErrno);
496                 break;
497             case ECONNREFUSED:
498             case EPIPE:
499                 qWarning("::select() returned an error: %i (go through sockets)", savedSelectErrno);
500                 // prepare to go through all sockets
501                 // mark in fd sets both:
502                 //     good ones
503                 //     ones that return -1 in select
504                 // after loop update notifiers for all of them
505 
506                 // as we don't have "exception" notifier type
507                 // we should force monitoring fd_set of this
508                 // type as well
509 
510                 // clean @ start
511                 FD_ZERO(&readfds);
512                 FD_ZERO(&writefds);
513                 FD_ZERO(&exceptionfds);
514                 for (QHash<QSocketNotifier *, TRequestStatus *>::const_iterator i = m_AOStatuses.begin();
515                         i != m_AOStatuses.end(); ++i) {
516 
517                     fd_set onefds;
518                     FD_ZERO(&onefds);
519                     FD_SET(i.key()->socket(), &onefds);
520 
521                     fd_set excfds;
522                     FD_ZERO(&excfds);
523                     FD_SET(i.key()->socket(), &excfds);
524 
525                     maxfd = i.key()->socket() + 1;
526 
527                     struct timeval timeout;
528                     timeout.tv_sec = 0;
529                     timeout.tv_usec = 0;
530 
531                     ret = 0;
532 
533                     if(i.key()->type() == QSocketNotifier::Read) {
534                         ret = ::select(maxfd, &onefds, 0, &excfds, &timeout);
535                         if(ret != 0) FD_SET(i.key()->socket(), &readfds);
536                     } else if(i.key()->type() == QSocketNotifier::Write) {
537                         ret = ::select(maxfd, 0, &onefds, &excfds, &timeout);
538                         if(ret != 0) FD_SET(i.key()->socket(), &writefds);
539                     }
540 
541                 } // end for
542 
543                 // traversed all, so update
544                 updateActivatedNotifiers(QSocketNotifier::Exception, &exceptionfds);
545                 updateActivatedNotifiers(QSocketNotifier::Read, &readfds);
546                 updateActivatedNotifiers(QSocketNotifier::Write, &writefds);
547 
548                 break;
549             case EINTR: // Should never occur on Symbian, but this is future proof!
550             default:
551                 qWarning("::select() returned an unknown error: %i", savedSelectErrno);
552 
553                 break;
554             }
555         } else {
556             updateActivatedNotifiers(QSocketNotifier::Exception, &exceptionfds);
557             updateActivatedNotifiers(QSocketNotifier::Read, &readfds);
558             updateActivatedNotifiers(QSocketNotifier::Write, &writefds);
559         }
560 
561         if (FD_ISSET(m_pipeEnds[0], &readfds))
562             m_waitCond.wait(&m_mutex);
563     }
564 
565     m_mutex.unlock();
566 }
567 
requestSocketEvents(QSocketNotifier * notifier,TRequestStatus * status)568 void QSelectThread::requestSocketEvents ( QSocketNotifier *notifier, TRequestStatus *status )
569 {
570     Q_D(QThread);
571 
572     if (!isRunning()) {
573         start();
574     }
575 
576     Q_ASSERT(QThread::currentThread() == this->thread());
577 
578     QSelectMutexGrabber lock(m_pipeEnds[1], m_pipeEnds[0], &m_mutex);
579 
580     Q_ASSERT(!m_AOStatuses.contains(notifier));
581 
582     m_AOStatuses.insert(notifier, status);
583 
584     m_waitCond.wakeAll();
585 }
586 
cancelSocketEvents(QSocketNotifier * notifier)587 void QSelectThread::cancelSocketEvents ( QSocketNotifier *notifier )
588 {
589     Q_ASSERT(QThread::currentThread() == this->thread());
590 
591     QSelectMutexGrabber lock(m_pipeEnds[1], m_pipeEnds[0], &m_mutex);
592 
593     m_AOStatuses.remove(notifier);
594 
595     m_waitCond.wakeAll();
596 }
597 
restart()598 void QSelectThread::restart()
599 {
600     Q_ASSERT(QThread::currentThread() == this->thread());
601 
602     QSelectMutexGrabber lock(m_pipeEnds[1], m_pipeEnds[0], &m_mutex);
603 
604     m_waitCond.wakeAll();
605 }
606 
updateSocketSet(QSocketNotifier::Type type,fd_set * fds)607 int QSelectThread::updateSocketSet(QSocketNotifier::Type type, fd_set *fds)
608 {
609     int maxfd = 0;
610     if(m_AOStatuses.isEmpty()) {
611         /*
612          * Wonder if should return -1
613          * to signal that no descriptors
614          * added to fds
615         */
616         return maxfd;
617     }
618     for ( QHash<QSocketNotifier *, TRequestStatus *>::const_iterator i = m_AOStatuses.begin();
619             i != m_AOStatuses.end(); ++i) {
620         if (i.key()->type() == type) {
621             FD_SET(i.key()->socket(), fds);
622             maxfd = qMax(maxfd, i.key()->socket());
623         } else if(type == QSocketNotifier::Exception) {
624             /*
625              * We are registering existing sockets
626              * always to exception set
627              *
628              * Doing double FD_SET shouldn't
629              * matter
630              */
631             FD_SET(i.key()->socket(), fds);
632             maxfd = qMax(maxfd, i.key()->socket());
633         }
634     }
635 
636     return maxfd;
637 }
638 
updateActivatedNotifiers(QSocketNotifier::Type type,fd_set * fds)639 void QSelectThread::updateActivatedNotifiers(QSocketNotifier::Type type, fd_set *fds)
640 {
641     Q_D(QThread);
642     if(m_AOStatuses.isEmpty()) {
643         return;
644     }
645     QList<QSocketNotifier *> toRemove;
646     for (QHash<QSocketNotifier *, TRequestStatus *>::const_iterator i = m_AOStatuses.begin();
647             i != m_AOStatuses.end(); ++i) {
648         if (i.key()->type() == type && FD_ISSET(i.key()->socket(), fds)) {
649             toRemove.append(i.key());
650             TRequestStatus *status = i.value();
651             // Thread data is still owned by the main thread.
652             QEventDispatcherSymbian::RequestComplete(d->threadData->symbian_thread_handle, status, KErrNone);
653         } else if(type == QSocketNotifier::Exception && FD_ISSET(i.key()->socket(), fds)) {
654             /*
655              * check if socket is in exception set
656              * then signal RequestComplete for it
657              */
658             qWarning("exception on %d [will close the socket handle - hack]", i.key()->socket());
659             // quick fix; there is a bug
660             // when doing read on socket
661             // errors not preoperly mapped
662             // after offline-ing the device
663             // on some devices we do get exception
664             ::close(i.key()->socket());
665             toRemove.append(i.key());
666             TRequestStatus *status = i.value();
667             QEventDispatcherSymbian::RequestComplete(d->threadData->symbian_thread_handle, status, KErrNone);
668         }
669     }
670 
671     for (int c = 0; c < toRemove.size(); ++c) {
672         m_AOStatuses.remove(toRemove[c]);
673     }
674 }
675 
stop()676 void QSelectThread::stop()
677 {
678     m_quit = true;
679     restart();
680     wait();
681 }
682 
QSocketActiveObject(QEventDispatcherSymbian * dispatcher,QSocketNotifier * notifier)683 QSocketActiveObject::QSocketActiveObject(QEventDispatcherSymbian *dispatcher, QSocketNotifier *notifier)
684     : QActiveObject(CActive::EPriorityStandard, dispatcher),
685       m_notifier(notifier),
686       m_inSocketEvent(false),
687       m_deleteLater(false)
688 {
689     CActiveScheduler::Add(this);
690     iStatus = KRequestPending;
691     SetActive();
692 }
693 
~QSocketActiveObject()694 QSocketActiveObject::~QSocketActiveObject()
695 {
696     Cancel();
697 }
698 
DoCancel()699 void QSocketActiveObject::DoCancel()
700 {
701     if (iStatus.Int() == KRequestPending) {
702         TRequestStatus *status = &iStatus;
703         QEventDispatcherSymbian::RequestComplete(status, KErrNone);
704     }
705 }
706 
RunL()707 void QSocketActiveObject::RunL()
708 {
709     if (maybeDeferSocketEvent())
710         return;
711 
712     QT_TRYCATCH_LEAVING(run());
713 }
714 
run()715 void QSocketActiveObject::run()
716 {
717     QEvent e(QEvent::SockAct);
718     m_inSocketEvent = true;
719     QCoreApplication::sendEvent(m_notifier, &e);
720     m_inSocketEvent = false;
721 
722     if (m_deleteLater) {
723         delete this;
724     } else {
725         iStatus = KRequestPending;
726         SetActive();
727         m_dispatcher->reactivateSocketNotifier(m_notifier);
728     }
729 }
730 
deleteLater()731 void QSocketActiveObject::deleteLater()
732 {
733     if (m_inSocketEvent) {
734         m_deleteLater = true;
735     } else {
736         delete this;
737     }
738 }
739 
740 /* Round robin active object scheduling for Qt apps.
741  *
742  * Qt and Symbian have different views on how events should be handled. Qt expects
743  * round-robin event processing, whereas Symbian implements a strict priority based
744  * system.
745  *
746  * This scheduler class, and its use in QEventDispatcherSymbian::processEvents,
747  * introduces round robin scheduling for high priority active objects, but leaves
748  * those with low priorities scheduled in priority order.
749  * The algorithm used is that, during each call to processEvents, any pre-existing
750  * runnable active object may run, but only once. Active objects with priority
751  * lower than EPriorityStandard can only run if no higher priority active object
752  * has run.
753  * This is done by implementing an alternative scheduling algorithm which requires
754  * access to the internal members of the active object system. The iSpare member of
755  * CActive is replaced with a flag indicating that the object is new (CBase zero
756  * initialization sets this), or not run, or ran. Only active objects with the
757  * not run flag are allowed to run.
758  */
759 class QtRRActiveScheduler
760 {
761 public:
762     static void MarkReadyToRun();
763     enum RunResult {
764         NothingFound,
765         ObjectRun,
766         ObjectDelayed
767     };
768     static RunResult RunMarkedIfReady(TInt &runPriority, TInt minimumPriority, QEventDispatcherSymbian *dispatcher);
769     static bool UseRRActiveScheduler();
770     static bool TestAndClearActiveObjectRunningInRRScheduler(CActive* ao);
771 
772 private:
773     // active scheduler access kit, for gaining access to the internals of active objects for
774     // alternative active scheduler implementations.
775     class TRequestStatusAccess
776     {
777     public:
778         enum { ERequestActiveFlags = 3 };  // TRequestStatus::EActive | TRequestStatus::ERequestPending
779         TInt iStatus;
780         TUint iFlags;
781     };
782 
783     class CActiveDataAccess : public CBase
784     {
785     public:
786         TRequestStatusAccess iStatus;
787         TPriQueLink iLink;
788         enum TMarks
789         {
790             ENewObject,         // CBase zero initialization sets this, new objects cannot be run in the processEvents in which they are created
791             ENotRun,            // This object has not yet run in the current processEvents call
792             ERunUnchecked,      // This object is run in the current processEvents call, as yet unacknowledged by the event dispatcher
793             ERunChecked         // This object is run in a processEvents call, the event dispatcher knows which loop level
794         };
795         int iMark;      //TAny* iSpare;
796     };
797 
798     class CActiveFuncAccess : public CActive
799     {
800     public:
801         // these functions are needed in RunMarkedIfReady
802         using CActive::RunL;
803         using CActive::RunError;
804     };
805 
806     class CActiveSchedulerAccess : public CBase
807     {
808     public:
809         using CBase::Extension_;
810         struct TLoop;
811         TLoop* iStack;
812         TPriQue<CActiveFuncAccess> iActiveQ;
813         TAny* iSpare;
814     };
815 };
816 
MarkReadyToRun()817 void QtRRActiveScheduler::MarkReadyToRun()
818 {
819     CActiveScheduler *pS=CActiveScheduler::Current();
820     if (pS!=NULL)
821     {
822         TDblQueIter<CActive> iterator(((CActiveSchedulerAccess*)pS)->iActiveQ);
823         for (CActive* active=iterator++; active!=NULL; active=iterator++) {
824             ((CActiveDataAccess*)active)->iMark = CActiveDataAccess::ENotRun;
825         }
826     }
827 }
828 
RunMarkedIfReady(TInt & runPriority,TInt minimumPriority,QEventDispatcherSymbian * dispatcher)829 QtRRActiveScheduler::RunResult QtRRActiveScheduler::RunMarkedIfReady(TInt &runPriority, TInt minimumPriority, QEventDispatcherSymbian *dispatcher)
830 {
831     RunResult result = NothingFound;
832     TInt error=KErrNone;
833     CActiveScheduler *pS=CActiveScheduler::Current();
834     if (pS!=NULL) {
835         TDblQueIter<CActiveFuncAccess> iterator(((CActiveSchedulerAccess*)pS)->iActiveQ);
836         for (CActiveFuncAccess *active=iterator++; active!=NULL; active=iterator++) {
837             CActiveDataAccess *dataAccess = (CActiveDataAccess*)active;
838             if (active->IsActive() && (active->iStatus!=KRequestPending)) {
839                 int& mark = dataAccess->iMark;
840                 if (mark == CActiveDataAccess::ENotRun && active->Priority()>=minimumPriority) {
841                     mark = CActiveDataAccess::ERunUnchecked;
842                     runPriority = active->Priority();
843                     dataAccess->iStatus.iFlags&=~TRequestStatusAccess::ERequestActiveFlags;
844                     int vptr = *(int*)active;       // vptr can be used to identify type when debugging leaves
845                     TRAP(error, QT_TRYCATCH_LEAVING(active->RunL()));
846                     if (error!=KErrNone) {
847                         if (vptr != *(int*)active)
848                             qWarning("Active object vptr change from 0x%08x to 0x%08x. Error %i not handled.", vptr, *(int*)active, error);
849                         else
850                             error=active->RunError(error);
851                     }
852                     if (error) {
853                         qWarning("Active object (ptr=0x%08x, vptr=0x%08x) leave: %i\n", active, vptr, error);
854                         dispatcher->activeObjectError(error);
855                     }
856                     return ObjectRun;
857                 }
858                 result = ObjectDelayed;
859             }
860         }
861     }
862     return result;
863 }
864 
UseRRActiveScheduler()865 bool QtRRActiveScheduler::UseRRActiveScheduler()
866 {
867     // This code allows euser to declare incompatible active object / scheduler internal data structures
868     // in the future, disabling Qt's round robin scheduler use.
869     // By default the Extension_ function will set the second argument to NULL. We therefore use NULL to indicate
870     // that the data structures are compatible with before when this protocol was recognised.
871     // The extension id used is QtCore's UID.
872     CActiveSchedulerAccess *access = (CActiveSchedulerAccess *)CActiveScheduler::Current();
873     TAny* schedulerCompatibilityNumber;
874     access->Extension_(0x2001B2DC, schedulerCompatibilityNumber, NULL);
875     return schedulerCompatibilityNumber == NULL;
876 }
877 
TestAndClearActiveObjectRunningInRRScheduler(CActive * ao)878 bool QtRRActiveScheduler::TestAndClearActiveObjectRunningInRRScheduler(CActive* ao)
879 {
880     CActiveDataAccess *dataAccess = (CActiveDataAccess*)ao;
881     if (dataAccess->iMark == CActiveDataAccess::ERunUnchecked) {
882         dataAccess->iMark = CActiveDataAccess::ERunChecked;
883         return true;
884     }
885     return false;
886 }
887 
888 #ifdef QT_SYMBIAN_PRIORITY_DROP
889 class QIdleDetectorThread
890 {
891 public:
QIdleDetectorThread()892     QIdleDetectorThread()
893     : m_state(STATE_RUN), m_stop(false), m_running(false)
894     {
895         start();
896     }
897 
~QIdleDetectorThread()898     ~QIdleDetectorThread()
899     {
900         stop();
901     }
902 
start()903     void start()
904     {
905         QMutexLocker lock(&m_mutex);
906         if (m_running)
907             return;
908         m_stop = false;
909         m_state = STATE_RUN;
910         TInt err = m_idleDetectorThread.Create(KNullDesC(), &idleDetectorThreadFunc, 1024, &User::Allocator(), this);
911         if (err != KErrNone)
912             return;     // Fail silently on error. Next kick will try again. Exception might stop the event being processed
913         m_idleDetectorThread.SetPriority(EPriorityAbsoluteBackgroundNormal);
914         m_idleDetectorThread.Resume();
915         m_running = true;
916         // get a callback from QCoreApplication destruction to stop this thread
917         qAddPostRoutine(StopIdleDetectorThread);
918     }
919 
stop()920     void stop()
921     {
922         QMutexLocker lock(&m_mutex);
923         if (!m_running)
924             return;
925         // close down the idle thread because if corelib is loaded temporarily, this would leak threads into the host process
926         m_stop = true;
927         m_kick.release();
928         m_idleDetectorThread.SetPriority(EPriorityNormal);
929         TRequestStatus s;
930         m_idleDetectorThread.Logon(s);
931         User::WaitForRequest(s);
932         m_idleDetectorThread.Close();
933         m_running = false;
934     }
935 
kick()936     void kick()
937     {
938         start();
939         m_state = STATE_KICKED;
940         m_kick.release();
941     }
942 
hasRun()943     bool hasRun()
944     {
945         return m_state == STATE_RUN;
946     }
947 
948 private:
idleDetectorThreadFunc(TAny * self)949     static TInt idleDetectorThreadFunc(TAny* self)
950     {
951         User::RenameThread(_L("IdleDetectorThread"));
952         static_cast<QIdleDetectorThread*>(self)->IdleLoop();
953         return KErrNone;
954     }
955 
IdleLoop()956     void IdleLoop()
957     {
958         // Create cleanup stack.
959         // Mutex handling may contain cleanupstack usage.
960         CTrapCleanup *cleanup = CTrapCleanup::New();
961         q_check_ptr(cleanup);
962         while (!m_stop) {
963             m_kick.acquire();
964             m_state = STATE_RUN;
965         }
966         delete cleanup;
967     }
968 
969     static void StopIdleDetectorThread();
970 
971 private:
972     enum IdleStates {STATE_KICKED, STATE_RUN} m_state;
973     bool m_stop;
974     bool m_running;
975     RThread m_idleDetectorThread;
976     QSemaphore m_kick;
977     QMutex m_mutex;
978 };
979 
980 Q_GLOBAL_STATIC(QIdleDetectorThread, idleDetectorThread);
981 
StopIdleDetectorThread()982 void QIdleDetectorThread::StopIdleDetectorThread()
983 {
984     idleDetectorThread()->stop();
985 }
986 
987 const int maxBusyTime = 2000; // maximum time we allow idle detector to be blocked before worrying, in milliseconds
988 const int baseDelay = 1000; // minimum delay time used when backing off to allow idling, in microseconds
989 #endif
990 
QEventDispatcherSymbian(QObject * parent)991 QEventDispatcherSymbian::QEventDispatcherSymbian(QObject *parent)
992     : QAbstractEventDispatcher(parent),
993       m_selectThread(0),
994       m_activeScheduler(0),
995       m_wakeUpAO(0),
996       m_completeDeferredAOs(0),
997       m_interrupt(false),
998       m_wakeUpDone(0),
999       m_iterationCount(0),
1000       m_insideTimerEvent(false),
1001       m_noSocketEvents(false),
1002       m_oomErrorCount(0)
1003 {
1004 #ifdef QT_SYMBIAN_PRIORITY_DROP
1005     m_delay = baseDelay;
1006     m_avgEventTime = 0;
1007     idleDetectorThread();
1008 #endif
1009     m_oomErrorTimer.start();
1010 }
1011 
~QEventDispatcherSymbian()1012 QEventDispatcherSymbian::~QEventDispatcherSymbian()
1013 {
1014 }
1015 
startingUp()1016 void QEventDispatcherSymbian::startingUp()
1017 {
1018     if( !CActiveScheduler::Current() ) {
1019         m_activeScheduler = q_check_ptr(new CQtActiveScheduler());	// CBase derived class needs to be checked on new
1020         CActiveScheduler::Install(m_activeScheduler);
1021     }
1022     m_wakeUpAO = q_check_ptr(new QWakeUpActiveObject(this));
1023     m_completeDeferredAOs = q_check_ptr(new QCompleteDeferredAOs(this));
1024     // We already might have posted events, wakeup once to process them
1025     wakeUp();
1026 }
1027 
selectThread()1028 QSelectThread& QEventDispatcherSymbian::selectThread() {
1029     if (!m_selectThread)
1030         m_selectThread = new QSelectThread;
1031     return *m_selectThread;
1032 }
1033 
closingDown()1034 void QEventDispatcherSymbian::closingDown()
1035 {
1036     if (m_selectThread && m_selectThread->isRunning()) {
1037         m_selectThread->stop();
1038     }
1039     delete m_selectThread;
1040     m_selectThread = 0;
1041 
1042     delete m_completeDeferredAOs;
1043     delete m_wakeUpAO;
1044     // only delete the active scheduler in its own thread
1045     if (m_activeScheduler && QThread::currentThread() == thread()) {
1046         delete m_activeScheduler;
1047     }
1048 }
1049 
processEvents(QEventLoop::ProcessEventsFlags flags)1050 bool QEventDispatcherSymbian::processEvents ( QEventLoop::ProcessEventsFlags flags )
1051 {
1052     const bool useRRScheduler = QtRRActiveScheduler::UseRRActiveScheduler();
1053     bool handledAnyEvent = false;
1054     bool oldNoSocketEventsValue = m_noSocketEvents;
1055     bool oldInsideTimerEventValue = m_insideTimerEvent;
1056 
1057     m_insideTimerEvent = false;
1058 
1059     QT_TRY {
1060         Q_D(QAbstractEventDispatcher);
1061 
1062         // It is safe if this counter overflows. The main importance is that each
1063         // iteration count is different from the last.
1064         m_iterationCount++;
1065 
1066         RThread &thread = d->threadData->symbian_thread_handle;
1067 
1068         bool block;
1069         if (flags & QEventLoop::WaitForMoreEvents) {
1070             block = true;
1071             emit aboutToBlock();
1072         } else {
1073             block = false;
1074         }
1075 
1076         if (flags & QEventLoop::ExcludeSocketNotifiers) {
1077             m_noSocketEvents = true;
1078         } else {
1079             m_noSocketEvents = false;
1080             handledAnyEvent = sendDeferredSocketEvents();
1081         }
1082 
1083         QtRRActiveScheduler::RunResult handledSymbianEvent = QtRRActiveScheduler::NothingFound;
1084         m_interrupt = false;
1085         int minPriority = KMinTInt;
1086 
1087 #ifdef QT_SYMBIAN_PRIORITY_DROP
1088         QElapsedTimer eventTimer;
1089 #endif
1090 
1091         while (1) {
1092             //native active object callbacks are logically part of the event loop, so inc nesting level
1093             Incrementer inc(d->threadData->loopLevel);
1094             if (block) {
1095                 // This is where Qt will spend most of its time.
1096                 CActiveScheduler::Current()->WaitForAnyRequest();
1097             } else {
1098                 if (thread.RequestCount() == 0) {
1099 #ifdef QT_SYMBIAN_PRIORITY_DROP
1100                     if (idleDetectorThread()->hasRun()) {
1101                         m_lastIdleRequestTimer.start();
1102                         idleDetectorThread()->kick();
1103                     } else if (m_lastIdleRequestTimer.elapsed() > maxBusyTime) {
1104                         User::AfterHighRes(m_delay);
1105                     }
1106 #endif
1107                     break;
1108                 }
1109                 // This one should return without delay.
1110                 CActiveScheduler::Current()->WaitForAnyRequest();
1111             }
1112 
1113             if (useRRScheduler && handledSymbianEvent == QtRRActiveScheduler::NothingFound) {
1114                 QtRRActiveScheduler::MarkReadyToRun();
1115             }
1116 
1117 #ifdef QT_SYMBIAN_PRIORITY_DROP
1118             if (idleDetectorThread()->hasRun()) {
1119                 if (m_delay > baseDelay)
1120                     m_delay -= baseDelay;
1121                 m_lastIdleRequestTimer.start();
1122                 idleDetectorThread()->kick();
1123             } else if (m_lastIdleRequestTimer.elapsed() > maxBusyTime) {
1124                 User::AfterHighRes(m_delay);
1125                 // allow delay to be up to 1/4 of execution time
1126                 if (!idleDetectorThread()->hasRun() && m_delay*3 < m_avgEventTime)
1127                     m_delay += baseDelay;
1128             }
1129             eventTimer.start();
1130 #endif
1131 
1132             if (useRRScheduler) {
1133                 // Standard or above priority AOs are scheduled round robin.
1134                 // Lower priority AOs can only run if nothing higher priority has run.
1135                 int runPriority = minPriority;
1136                 handledSymbianEvent = QtRRActiveScheduler::RunMarkedIfReady(runPriority, minPriority, this);
1137                 minPriority = qMin(runPriority, int(CActive::EPriorityStandard));
1138             } else {
1139                 TInt error;
1140                 handledSymbianEvent =
1141                       CActiveScheduler::RunIfReady(error, minPriority)
1142                     ? QtRRActiveScheduler::ObjectRun
1143                     : QtRRActiveScheduler::NothingFound;
1144                 if (error) {
1145                     qWarning("CActiveScheduler::RunIfReady() returned error: %i\n", error);
1146                     CActiveScheduler::Current()->Error(error);
1147                 }
1148             }
1149 
1150             if (handledSymbianEvent == QtRRActiveScheduler::NothingFound) {
1151                 // no runnable or delayed active object was found, the signal that caused us to get here must be bad
1152                 qFatal("QEventDispatcherSymbian::processEvents(): Caught Symbian stray signal");
1153             } else if (handledSymbianEvent == QtRRActiveScheduler::ObjectDelayed) {
1154                 // signal the thread to compensate for the un-handled signal absorbed
1155                 RThread().RequestSignal();
1156                 break;
1157             }
1158 
1159 #ifdef QT_SYMBIAN_PRIORITY_DROP
1160             int eventDur = eventTimer.elapsed()*1000;
1161             // average is calcualted as a 5% decaying exponential average
1162             m_avgEventTime = (m_avgEventTime * 95 + eventDur * 5) / 100;
1163 #endif
1164 
1165             handledAnyEvent = true;
1166 
1167             if (m_interrupt) {
1168                 break;
1169             }
1170             block = false;
1171         };
1172 
1173         emit awake();
1174     } QT_CATCH (const std::exception& ex) {
1175 #ifndef QT_NO_EXCEPTIONS
1176         CActiveScheduler::Current()->Error(qt_symbian_exception2Error(ex));
1177 #endif
1178     }
1179 
1180     m_noSocketEvents = oldNoSocketEventsValue;
1181     m_insideTimerEvent = oldInsideTimerEventValue;
1182 
1183     return handledAnyEvent;
1184 }
1185 
timerFired(int timerId,QTimerActiveObject * ao)1186 void QEventDispatcherSymbian::timerFired(int timerId, QTimerActiveObject *ao)
1187 {
1188     Q_D(QAbstractEventDispatcher);
1189     QHash<int, SymbianTimerInfoPtr>::iterator i = m_timerList.find(timerId);
1190     if (i == m_timerList.end()) {
1191         // The timer has been deleted. Ignore this event.
1192         return;
1193     }
1194 
1195     SymbianTimerInfoPtr timerInfo = *i;
1196 
1197     // Prevent infinite timer recursion.
1198     if (timerInfo->inTimerEvent) {
1199         return;
1200     }
1201 
1202     timerInfo->inTimerEvent = true;
1203     bool oldInsideTimerEventValue = m_insideTimerEvent;
1204     m_insideTimerEvent = true;
1205 
1206     QTimerEvent event(timerInfo->timerId);
1207     if (QtRRActiveScheduler::TestAndClearActiveObjectRunningInRRScheduler(ao)) {
1208         //undo the added nesting level around RunIfReady, since Qt's event system also nests
1209         Decrementer dec(d->threadData->loopLevel);
1210         QCoreApplication::sendEvent(timerInfo->receiver, &event);
1211     } else {
1212         QCoreApplication::sendEvent(timerInfo->receiver, &event);
1213     }
1214 
1215     m_insideTimerEvent = oldInsideTimerEventValue;
1216     timerInfo->inTimerEvent = false;
1217 
1218     return;
1219 }
1220 
wakeUpWasCalled(QWakeUpActiveObject * ao)1221 void QEventDispatcherSymbian::wakeUpWasCalled(QWakeUpActiveObject *ao)
1222 {
1223     Q_D(QAbstractEventDispatcher);
1224     // The reactivation should happen in RunL, right before the call to this function.
1225     // This is because m_wakeUpDone is the "signal" that the object can be completed
1226     // once more.
1227     // Also, by dispatching the posted events after resetting m_wakeUpDone, we guarantee
1228     // that no posted event notification will be lost. If we did it the other way
1229     // around, it would be possible for another thread to post an event right after
1230     // the sendPostedEvents was done, but before the object was ready to be completed
1231     // again. This could deadlock the application if there are no other posted events.
1232     m_wakeUpDone.fetchAndStoreOrdered(0);
1233     if (QtRRActiveScheduler::TestAndClearActiveObjectRunningInRRScheduler(ao)) {
1234         //undo the added nesting level around RunIfReady, since Qt's event system also nests
1235         Decrementer dec(d->threadData->loopLevel);
1236         sendPostedEvents();
1237     } else {
1238         sendPostedEvents();
1239     }
1240 }
1241 
interrupt()1242 void QEventDispatcherSymbian::interrupt()
1243 {
1244     m_interrupt = true;
1245     wakeUp();
1246 }
1247 
wakeUp()1248 void QEventDispatcherSymbian::wakeUp()
1249 {
1250     Q_D(QAbstractEventDispatcher);
1251 
1252     if (m_wakeUpAO && m_wakeUpDone.testAndSetAcquire(0, 1)) {
1253         TRequestStatus *status = &m_wakeUpAO->iStatus;
1254         QEventDispatcherSymbian::RequestComplete(d->threadData->symbian_thread_handle, status, KErrNone);
1255     }
1256 }
1257 
sendPostedEvents()1258 bool QEventDispatcherSymbian::sendPostedEvents()
1259 {
1260     Q_D(QAbstractEventDispatcher);
1261 
1262     // moveToThread calls this and canWait == true -> Events will never get processed
1263     // if we check for d->threadData->canWait
1264     //
1265     // QCoreApplication::postEvent sets canWait = false, but after the object and events
1266     // are moved to a new thread, the canWait in new thread is true i.e. not changed to reflect
1267     // the flag on old thread. That's why events in a new thread will not get processed.
1268     // This migth be actually bug in moveToThread functionality, but because other platforms
1269     // do not check canWait in wakeUp (where we essentially are now) - decided to remove it from
1270     // here as well.
1271 
1272     //if (!d->threadData->canWait) {
1273         QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
1274         return true;
1275     //}
1276     //return false;
1277 }
1278 
addDeferredSocketActiveObject(QActiveObject * object)1279 inline void QEventDispatcherSymbian::addDeferredSocketActiveObject(QActiveObject *object)
1280 {
1281     m_deferredSocketEvents.append(object);
1282 }
1283 
addDeferredActiveObject(QActiveObject * object)1284 inline void QEventDispatcherSymbian::addDeferredActiveObject(QActiveObject *object)
1285 {
1286     queueDeferredActiveObjectsCompletion();
1287     m_deferredActiveObjects.append(object);
1288 }
1289 
removeDeferredActiveObject(QActiveObject * object)1290 inline void QEventDispatcherSymbian::removeDeferredActiveObject(QActiveObject *object)
1291 {
1292     m_deferredActiveObjects.removeAll(object);
1293 }
1294 
queueDeferredActiveObjectsCompletion()1295 void QEventDispatcherSymbian::queueDeferredActiveObjectsCompletion()
1296 {
1297     m_completeDeferredAOs->complete();
1298 }
1299 
reactivateDeferredActiveObjects()1300 void QEventDispatcherSymbian::reactivateDeferredActiveObjects()
1301 {
1302     while (!m_deferredActiveObjects.isEmpty()) {
1303         QActiveObject *object = m_deferredActiveObjects.takeFirst();
1304         object->reactivateAndComplete();
1305     }
1306 
1307     // We do this because we want to return from processEvents. This is because
1308     // each invocation of processEvents should only run each active object once.
1309     // The active scheduler should run them continously, however.
1310     m_interrupt = true;
1311 }
1312 
sendDeferredSocketEvents()1313 bool QEventDispatcherSymbian::sendDeferredSocketEvents()
1314 {
1315     bool sentAnyEvents = false;
1316     while (!m_deferredSocketEvents.isEmpty()) {
1317         sentAnyEvents = true;
1318         QActiveObject *object = m_deferredSocketEvents.takeFirst();
1319         object->reactivateAndComplete();
1320     }
1321 
1322     return sentAnyEvents;
1323 }
1324 
flush()1325 void QEventDispatcherSymbian::flush()
1326 {
1327 }
1328 
hasPendingEvents()1329 bool QEventDispatcherSymbian::hasPendingEvents()
1330 {
1331     Q_D(QAbstractEventDispatcher);
1332     return (d->threadData->symbian_thread_handle.RequestCount() != 0
1333             || !d->threadData->canWait || !m_deferredSocketEvents.isEmpty());
1334 }
1335 
registerSocketNotifier(QSocketNotifier * notifier)1336 void QEventDispatcherSymbian::registerSocketNotifier ( QSocketNotifier * notifier )
1337 {
1338     //check socket descriptor is usable
1339     if (notifier->socket() >= FD_SETSIZE || notifier->socket() < 0) {
1340         //same warning message as the unix event dispatcher for easy testing
1341         qWarning("QSocketNotifier: Internal error");
1342         return;
1343     }
1344     //note - this is only for "open C" file descriptors
1345     //for native sockets, an active object in the symbian socket engine handles this
1346     QSocketActiveObject *socketAO = new QSocketActiveObject(this, notifier);
1347     Q_CHECK_PTR(socketAO);
1348     m_notifiers.insert(notifier, socketAO);
1349     selectThread().requestSocketEvents(notifier, &socketAO->iStatus);
1350 }
1351 
unregisterSocketNotifier(QSocketNotifier * notifier)1352 void QEventDispatcherSymbian::unregisterSocketNotifier ( QSocketNotifier * notifier )
1353 {
1354     //note - this is only for "open C" file descriptors
1355     //for native sockets, an active object in the symbian socket engine handles this
1356     if (m_selectThread)
1357         m_selectThread->cancelSocketEvents(notifier);
1358     if (m_notifiers.contains(notifier)) {
1359         QSocketActiveObject *sockObj = *m_notifiers.find(notifier);
1360         m_deferredSocketEvents.removeAll(sockObj);
1361         sockObj->deleteLater();
1362         m_notifiers.remove(notifier);
1363     }
1364 }
1365 
reactivateSocketNotifier(QSocketNotifier * notifier)1366 void QEventDispatcherSymbian::reactivateSocketNotifier(QSocketNotifier *notifier)
1367 {
1368     selectThread().requestSocketEvents(notifier, &m_notifiers[notifier]->iStatus);
1369 }
1370 
registerTimer(int timerId,int interval,QObject * object)1371 void QEventDispatcherSymbian::registerTimer ( int timerId, int interval, QObject * object )
1372 {
1373     if (interval < 0) {
1374         qWarning("Timer interval < 0");
1375         interval = 0;
1376     }
1377 
1378     SymbianTimerInfoPtr timer(new SymbianTimerInfo);
1379     timer->timerId      = timerId;
1380     timer->interval     = interval;
1381     timer->inTimerEvent = false;
1382     timer->receiver     = object;
1383     timer->dispatcher   = this;
1384     timer->timerAO      = q_check_ptr(new QTimerActiveObject(this, timer.data()));
1385     m_timerList.insert(timerId, timer);
1386 
1387     timer->timerAO->Start();
1388 
1389     if (m_insideTimerEvent)
1390         // If we are inside a timer event, we need to prevent event starvation
1391         // by preventing newly created timers from running in the same event processing
1392         // iteration. Do this by calling the maybeQueueForLater() function to "fake" that we have
1393         // already run once. This will cause the next run to be added to the deferred
1394         // queue instead.
1395         timer->timerAO->maybeQueueForLater();
1396 }
1397 
unregisterTimer(int timerId)1398 bool QEventDispatcherSymbian::unregisterTimer ( int timerId )
1399 {
1400     if (!m_timerList.contains(timerId)) {
1401         return false;
1402     }
1403 
1404     SymbianTimerInfoPtr timerInfo = m_timerList.take(timerId);
1405 
1406     if (!QObjectPrivate::get(timerInfo->receiver)->inThreadChangeEvent)
1407         QAbstractEventDispatcherPrivate::releaseTimerId(timerId);
1408 
1409     return true;
1410 }
1411 
unregisterTimers(QObject * object)1412 bool QEventDispatcherSymbian::unregisterTimers ( QObject * object )
1413 {
1414     if (m_timerList.isEmpty())
1415         return false;
1416 
1417     bool unregistered = false;
1418     for (QHash<int, SymbianTimerInfoPtr>::iterator i = m_timerList.begin(); i != m_timerList.end(); ) {
1419         if ((*i)->receiver == object) {
1420             i = m_timerList.erase(i);
1421             unregistered = true;
1422         } else {
1423             ++i;
1424         }
1425     }
1426 
1427     return unregistered;
1428 }
1429 
registeredTimers(QObject * object) const1430 QList<QEventDispatcherSymbian::TimerInfo> QEventDispatcherSymbian::registeredTimers ( QObject * object ) const
1431 {
1432     QList<TimerInfo> list;
1433     for (QHash<int, SymbianTimerInfoPtr>::const_iterator i = m_timerList.begin(); i != m_timerList.end(); ++i) {
1434         if ((*i)->receiver == object) {
1435             list.push_back(TimerInfo((*i)->timerId, (*i)->interval));
1436         }
1437     }
1438 
1439     return list;
1440 }
1441 
activeObjectError(int error)1442 void QEventDispatcherSymbian::activeObjectError(int error)
1443 {
1444     if (error == KErrNoMemory) {
1445         // limit the number of reported out of memory errors, as the disappearance of the warning
1446         // dialog can trigger further OOM errors causing a loop.
1447         if (m_oomErrorTimer.restart() > 60000)  // 1 minute
1448             m_oomErrorCount = 0;
1449         if (m_oomErrorCount++ >= 5)
1450             return;
1451     }
1452     CActiveScheduler::Current()->Error(error);
1453 }
1454 
1455 /*
1456  * This active scheduler class implements a simple report and continue policy, for Symbian OS leaves
1457  * or exceptions from Qt that fall back to the scheduler.
1458  * It will be used in cases where there is no existing active scheduler installed.
1459  * Apps which link to qts60main.lib will have the UI active scheduler installed in the main thread
1460  * instead of this one. But this would be used in other threads in the UI.
1461  * An app could replace this behaviour by installing an alternative active scheduler.
1462  */
Error(TInt aError) const1463 void CQtActiveScheduler::Error(TInt aError) const
1464 {
1465     QT_TRY {
1466         qWarning("Error from active scheduler %d", aError);
1467     }
1468     QT_CATCH (const std::bad_alloc&) {}    // ignore alloc fails, nothing more can be done
1469 }
1470 
1471 QT_END_NAMESPACE
1472 
1473 #include "moc_qeventdispatcher_symbian_p.cpp"
1474