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