1
/****************************************************************************
2
**
3
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4
** All rights reserved.
5
** Contact: Nokia Corporation (qt-info@nokia.com)
6
**
7
** This file is part of the QtCore module of the Qt Toolkit.
8
**
9
** $QT_BEGIN_LICENSE:LGPL$
10
** GNU Lesser General Public License Usage
11
** This file may be used under the terms of the GNU Lesser General Public
12
** License version 2.1 as published by the Free Software Foundation and
13
** appearing in the file LICENSE.LGPL included in the packaging of this
14
** file. Please review the following information to ensure the GNU Lesser
15
** General Public License version 2.1 requirements will be met:
16
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17
**
18
** In addition, as a special exception, Nokia gives you certain additional
19
** rights. These rights are described in the Nokia Qt LGPL Exception
20
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21
**
22
** GNU General Public License Usage
23
** Alternatively, this file may be used under the terms of the GNU General
24
** Public License version 3.0 as published by the Free Software Foundation
25
** and appearing in the file LICENSE.GPL included in the packaging of this
26
** file. Please review the following information to ensure the GNU General
27
** Public License version 3.0 requirements will be met:
28
** http://www.gnu.org/copyleft/gpl.html.
29
**
30
** Other Usage
31
** Alternatively, this file may be used in accordance with the terms and
32
** conditions contained in a signed written agreement between you and Nokia.
33
**
34
**
35
**
36
**
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:
67
    inline Incrementer(int &variable) : variable(variable)
68
    { ++variable; }
69
    inline ~Incrementer()
70
    { --variable; }
71
};
72
73
class Decrementer {
74
    int &variable;
75
public:
76
    inline Decrementer(int &variable) : variable(variable)
77
    { --variable; }
78
    inline ~Decrementer()
79
    { ++variable; }
80
};
81
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
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
98
static inline int qt_pipe_fcntl(int socket, int command)
99
{
100
	return ::fcntl(socket, command);
101
}
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
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:
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
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
 */
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
167
QActiveObject::~QActiveObject()
168
{
169
    if (m_hasRunAgain)
170
        m_dispatcher->removeDeferredActiveObject(this);
171
}
172
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
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
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
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
222
QWakeUpActiveObject::~QWakeUpActiveObject()
223
{
224
    Cancel();
225
}
226
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
244
void QWakeUpActiveObject::RunL()
245
{
246
    iStatus = KRequestPending;
247
    SetActive();
248
    QT_TRYCATCH_LEAVING(m_dispatcher->wakeUpWasCalled(this));
249
}
250
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
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
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
            qFatal("QTimerActiveObject cancelled from wrong thread");
285
        }
286
        TRequestStatus *status = &iStatus;
287
        User::RequestComplete(status, KErrCancel);
288
    }
289
}
290
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
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
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
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
389
SymbianTimerInfo::SymbianTimerInfo()
390
    : timerAO(0)
391
{
392
}
393
394
SymbianTimerInfo::~SymbianTimerInfo()
395
{
396
    delete timerAO;
397
}
398
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
408
QCompleteDeferredAOs::~QCompleteDeferredAOs()
409
{
410
    Cancel();
411
}
412
413
void QCompleteDeferredAOs::complete()
414
{
415
    if (iStatus.Int() == KRequestPending) {
416
        TRequestStatus *status = &iStatus;
417
        QEventDispatcherSymbian::RequestComplete(status, KErrNone);
418
    }
419
}
420
421
void QCompleteDeferredAOs::DoCancel()
422
{
423
    if (iStatus.Int() == KRequestPending) {
424
        TRequestStatus *status = &iStatus;
425
        QEventDispatcherSymbian::RequestComplete(status, KErrNone);
426
    }
427
}
428
429
void QCompleteDeferredAOs::RunL()
430
{
431
    iStatus = KRequestPending;
432
    SetActive();
433
434
    QT_TRYCATCH_LEAVING(m_dispatcher->reactivateDeferredActiveObjects());
435
}
436
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
452
QSelectThread::~QSelectThread()
453
{
454
    qt_pipe_close(m_pipeEnds[1]);
455
    qt_pipe_close(m_pipeEnds[0]);
456
}
457
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
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
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
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
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
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
676
void QSelectThread::stop()
677
{
678
    m_quit = true;
679
    restart();
680
    wait();
681
}
682
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
694
QSocketActiveObject::~QSocketActiveObject()
695
{
696
    Cancel();
697
}
698
699
void QSocketActiveObject::DoCancel()
700
{
701
    if (iStatus.Int() == KRequestPending) {
702
        TRequestStatus *status = &iStatus;
703
        QEventDispatcherSymbian::RequestComplete(status, KErrNone);
704
    }
705
}
706
707
void QSocketActiveObject::RunL()
708
{
709
    if (maybeDeferSocketEvent())
710
        return;
711
712
    QT_TRYCATCH_LEAVING(run());
713
}
714
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
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
            ERunningUnchecked,  // This object is running in the current processEvents call, as yet unacknowledged by the event dispatcher
793
            ERunningChecked,    // This object is running in a processEvents call, the event dispatcher knows which loop level
794
            ERan                // This object has run in the current processEvents call
795
        };
796
        int iMark;      //TAny* iSpare;
797
    };
798
799
    class CActiveFuncAccess : public CActive
800
    {
801
    public:
802
        // these functions are needed in RunMarkedIfReady
803
        using CActive::RunL;
804
        using CActive::RunError;
805
    };
806
807
    class CActiveSchedulerAccess : public CBase
808
    {
809
    public:
810
        using CBase::Extension_;
811
        struct TLoop;
812
        TLoop* iStack;
813
        TPriQue<CActiveFuncAccess> iActiveQ;
814
        TAny* iSpare;
815
    };
816
};
817
818
void QtRRActiveScheduler::MarkReadyToRun()
819
{
820
    CActiveScheduler *pS=CActiveScheduler::Current();
821
    if (pS!=NULL)
822
    {
823
        TDblQueIter<CActive> iterator(((CActiveSchedulerAccess*)pS)->iActiveQ);
824
        for (CActive* active=iterator++; active!=NULL; active=iterator++) {
825
            ((CActiveDataAccess*)active)->iMark = CActiveDataAccess::ENotRun;
826
        }
827
    }
828
}
829
830
QtRRActiveScheduler::RunResult QtRRActiveScheduler::RunMarkedIfReady(TInt &runPriority, TInt minimumPriority, QEventDispatcherSymbian *dispatcher)
831
{
832
    RunResult result = NothingFound;
833
    TInt error=KErrNone;
834
    CActiveScheduler *pS=CActiveScheduler::Current();
835
    if (pS!=NULL) {
836
        TDblQueIter<CActiveFuncAccess> iterator(((CActiveSchedulerAccess*)pS)->iActiveQ);
837
        for (CActiveFuncAccess *active=iterator++; active!=NULL; active=iterator++) {
838
            CActiveDataAccess *dataAccess = (CActiveDataAccess*)active;
839
            if (active->IsActive() && (active->iStatus!=KRequestPending)) {
840
                int& mark = dataAccess->iMark;
841
                if (mark == CActiveDataAccess::ENotRun && active->Priority()>=minimumPriority) {
842
                    mark = CActiveDataAccess::ERunningUnchecked;
843
                    runPriority = active->Priority();
844
                    dataAccess->iStatus.iFlags&=~TRequestStatusAccess::ERequestActiveFlags;
845
                    int vptr = *(int*)active;       // vptr can be used to identify type when debugging leaves
846
                    TRAP(error, QT_TRYCATCH_LEAVING(active->RunL()));
847
                    mark = CActiveDataAccess::ERan;
848
                    if (error!=KErrNone)
849
                        error=active->RunError(error);
850
                    if (error) {
851
                        qWarning("Active object (ptr=0x%08x, vptr=0x%08x) leave: %i\n", active, vptr, error);
852
                        dispatcher->activeObjectError(error);
853
                    }
854
                    return ObjectRun;
855
                }
856
                result = ObjectDelayed;
857
            }
858
        }
859
    }
860
    return result;
861
}
862
863
bool QtRRActiveScheduler::UseRRActiveScheduler()
864
{
865
    // This code allows euser to declare incompatible active object / scheduler internal data structures
866
    // in the future, disabling Qt's round robin scheduler use.
867
    // By default the Extension_ function will set the second argument to NULL. We therefore use NULL to indicate
868
    // that the data structures are compatible with before when this protocol was recognised.
869
    // The extension id used is QtCore's UID.
870
    CActiveSchedulerAccess *access = (CActiveSchedulerAccess *)CActiveScheduler::Current();
871
    TAny* schedulerCompatibilityNumber;
872
    access->Extension_(0x2001B2DC, schedulerCompatibilityNumber, NULL);
873
    return schedulerCompatibilityNumber == NULL;
874
}
875
876
bool QtRRActiveScheduler::TestAndClearActiveObjectRunningInRRScheduler(CActive* ao)
877
{
878
    CActiveDataAccess *dataAccess = (CActiveDataAccess*)ao;
879
    if (dataAccess->iMark == CActiveDataAccess::ERunningUnchecked) {
880
        dataAccess->iMark = CActiveDataAccess::ERunningChecked;
881
        return true;
882
    }
883
    return false;
884
}
885
886
#ifdef QT_SYMBIAN_PRIORITY_DROP
887
class QIdleDetectorThread
888
{
889
public:
890
    QIdleDetectorThread()
891
    : m_state(STATE_RUN), m_stop(false), m_running(false)
892
    {
893
        start();
894
    }
895
896
    ~QIdleDetectorThread()
897
    {
898
        stop();
899
    }
900
901
    void start()
902
    {
903
        QMutexLocker lock(&m_mutex);
904
        if (m_running)
905
            return;
906
        m_stop = false;
907
        m_state = STATE_RUN;
908
        TInt err = m_idleDetectorThread.Create(KNullDesC(), &idleDetectorThreadFunc, 1024, &User::Allocator(), this);
909
        if (err != KErrNone)
910
            return;     // Fail silently on error. Next kick will try again. Exception might stop the event being processed
911
        m_idleDetectorThread.SetPriority(EPriorityAbsoluteBackgroundNormal);
912
        m_idleDetectorThread.Resume();
913
        m_running = true;
914
        // get a callback from QCoreApplication destruction to stop this thread
915
        qAddPostRoutine(StopIdleDetectorThread);
916
    }
917
918
    void stop()
919
    {
920
        QMutexLocker lock(&m_mutex);
921
        if (!m_running)
922
            return;
923
        // close down the idle thread because if corelib is loaded temporarily, this would leak threads into the host process
924
        m_stop = true;
925
        m_kick.release();
926
        m_idleDetectorThread.SetPriority(EPriorityNormal);
927
        TRequestStatus s;
928
        m_idleDetectorThread.Logon(s);
929
        User::WaitForRequest(s);
930
        m_idleDetectorThread.Close();
931
        m_running = false;
932
    }
933
934
    void kick()
935
    {
936
        start();
937
        m_state = STATE_KICKED;
938
        m_kick.release();
939
    }
940
941
    bool hasRun()
942
    {
943
        return m_state == STATE_RUN;
944
    }
945
946
private:
947
    static TInt idleDetectorThreadFunc(TAny* self)
948
    {
949
        User::RenameThread(_L("IdleDetectorThread"));
950
        static_cast<QIdleDetectorThread*>(self)->IdleLoop();
951
        return KErrNone;
952
    }
953
954
    void IdleLoop()
955
    {
956
        // Create cleanup stack. 
957
        // Mutex handling may contain cleanupstack usage.
958
        CTrapCleanup *cleanup = CTrapCleanup::New();
959
        q_check_ptr(cleanup);
960
        while (!m_stop) {
961
            m_kick.acquire();
962
            m_state = STATE_RUN;
963
        }
964
        delete cleanup;
965
    }
966
967
    static void StopIdleDetectorThread();
968
969
private:
970
    enum IdleStates {STATE_KICKED, STATE_RUN} m_state;
971
    bool m_stop;
972
    bool m_running;
973
    RThread m_idleDetectorThread;
974
    QSemaphore m_kick;
975
    QMutex m_mutex;
976
};
977
978
Q_GLOBAL_STATIC(QIdleDetectorThread, idleDetectorThread);
979
980
void QIdleDetectorThread::StopIdleDetectorThread()
981
{
982
    idleDetectorThread()->stop();
983
}
984
985
const int maxBusyTime = 2000; // maximum time we allow idle detector to be blocked before worrying, in milliseconds
986
const int baseDelay = 1000; // minimum delay time used when backing off to allow idling, in microseconds
987
#endif
988
989
QEventDispatcherSymbian::QEventDispatcherSymbian(QObject *parent)
990
    : QAbstractEventDispatcher(parent),
991
      m_selectThread(0),
992
      m_activeScheduler(0),
993
      m_wakeUpAO(0),
994
      m_completeDeferredAOs(0),
995
      m_interrupt(false),
996
      m_wakeUpDone(0),
997
      m_iterationCount(0),
998
      m_insideTimerEvent(false),
999
      m_noSocketEvents(false),
1000
      m_oomErrorCount(0)
1001
{
1002
#ifdef QT_SYMBIAN_PRIORITY_DROP
1003
    m_delay = baseDelay;
1004
    m_avgEventTime = 0;
1005
    idleDetectorThread();
1006
#endif
1007
    m_oomErrorTimer.start();
1008
}
1009
1010
QEventDispatcherSymbian::~QEventDispatcherSymbian()
1011
{
1012
}
1013
1014
void QEventDispatcherSymbian::startingUp()
1015
{
1016
    if( !CActiveScheduler::Current() ) {
1017
        m_activeScheduler = q_check_ptr(new CQtActiveScheduler());	// CBase derived class needs to be checked on new
1018
        CActiveScheduler::Install(m_activeScheduler);
1019
    }
1020
    m_wakeUpAO = q_check_ptr(new QWakeUpActiveObject(this));
1021
    m_completeDeferredAOs = q_check_ptr(new QCompleteDeferredAOs(this));
1022
    // We already might have posted events, wakeup once to process them
1023
    wakeUp();
1024
}
1025
1026
QSelectThread& QEventDispatcherSymbian::selectThread() {
1027
    if (!m_selectThread)
1028
        m_selectThread = new QSelectThread;
1029
    return *m_selectThread;
1030
}
1031
1032
void QEventDispatcherSymbian::closingDown()
1033
{
1034
    if (m_selectThread && m_selectThread->isRunning()) {
1035
        m_selectThread->stop();
1036
    }
1037
    delete m_selectThread;
1038
    m_selectThread = 0;
1039
1040
    delete m_completeDeferredAOs;
1041
    delete m_wakeUpAO;
1042
    if (m_activeScheduler) {
1043
        delete m_activeScheduler;
1044
    }
1045
}
1046
1047
bool QEventDispatcherSymbian::processEvents ( QEventLoop::ProcessEventsFlags flags )
1048
{
1049
    const bool useRRScheduler = QtRRActiveScheduler::UseRRActiveScheduler();
1050
    bool handledAnyEvent = false;
1051
    bool oldNoSocketEventsValue = m_noSocketEvents;
1052
    bool oldInsideTimerEventValue = m_insideTimerEvent;
1053
1054
    m_insideTimerEvent = false;
1055
1056
    QT_TRY {
1057
        Q_D(QAbstractEventDispatcher);
1058
1059
        // It is safe if this counter overflows. The main importance is that each
1060
        // iteration count is different from the last.
1061
        m_iterationCount++;
1062
1063
        RThread &thread = d->threadData->symbian_thread_handle;
1064
1065
        bool block;
1066
        if (flags & QEventLoop::WaitForMoreEvents) {
1067
            block = true;
1068
            emit aboutToBlock();
1069
        } else {
1070
            block = false;
1071
        }
1072
1073
        if (flags & QEventLoop::ExcludeSocketNotifiers) {
1074
            m_noSocketEvents = true;
1075
        } else {
1076
            m_noSocketEvents = false;
1077
            handledAnyEvent = sendDeferredSocketEvents();
1078
        }
1079
1080
        QtRRActiveScheduler::RunResult handledSymbianEvent = QtRRActiveScheduler::NothingFound;
1081
        m_interrupt = false;
1082
        int minPriority = KMinTInt;
1083
1084
#ifdef QT_SYMBIAN_PRIORITY_DROP
1085
        QElapsedTimer eventTimer;
1086
#endif
1087
1088
        while (1) {
1089
            //native active object callbacks are logically part of the event loop, so inc nesting level
1090
            Incrementer inc(d->threadData->loopLevel);
1091
            if (block) {
1092
                // This is where Qt will spend most of its time.
1093
                CActiveScheduler::Current()->WaitForAnyRequest();
1094
            } else {
1095
                if (thread.RequestCount() == 0) {
1096
#ifdef QT_SYMBIAN_PRIORITY_DROP
1097
                    if (idleDetectorThread()->hasRun()) {
1098
                        m_lastIdleRequestTimer.start();
1099
                        idleDetectorThread()->kick();
1100
                    } else if (m_lastIdleRequestTimer.elapsed() > maxBusyTime) {
1101
                        User::AfterHighRes(m_delay);
1102
                    }
1103
#endif
1104
                    break;
1105
                }
1106
                // This one should return without delay.
1107
                CActiveScheduler::Current()->WaitForAnyRequest();
1108
            }
1109
1110
            if (useRRScheduler && handledSymbianEvent == QtRRActiveScheduler::NothingFound) {
1111
                QtRRActiveScheduler::MarkReadyToRun();
1112
            }
1113
1114
#ifdef QT_SYMBIAN_PRIORITY_DROP
1115
            if (idleDetectorThread()->hasRun()) {
1116
                if (m_delay > baseDelay)
1117
                    m_delay -= baseDelay;
1118
                m_lastIdleRequestTimer.start();
1119
                idleDetectorThread()->kick();
1120
            } else if (m_lastIdleRequestTimer.elapsed() > maxBusyTime) {
1121
                User::AfterHighRes(m_delay);
1122
                // allow delay to be up to 1/4 of execution time
1123
                if (!idleDetectorThread()->hasRun() && m_delay*3 < m_avgEventTime)
1124
                    m_delay += baseDelay;
1125
            }
1126
            eventTimer.start();
1127
#endif
1128
1129
            if (useRRScheduler) {
1130
                // Standard or above priority AOs are scheduled round robin.
1131
                // Lower priority AOs can only run if nothing higher priority has run.
1132
                int runPriority = minPriority;
1133
                handledSymbianEvent = QtRRActiveScheduler::RunMarkedIfReady(runPriority, minPriority, this);
1134
                minPriority = qMin(runPriority, int(CActive::EPriorityStandard));
1135
            } else {
1136
                TInt error;
1137
                handledSymbianEvent =
1138
                      CActiveScheduler::RunIfReady(error, minPriority)
1139
                    ? QtRRActiveScheduler::ObjectRun
1140
                    : QtRRActiveScheduler::NothingFound;
1141
                if (error) {
1142
                    qWarning("CActiveScheduler::RunIfReady() returned error: %i\n", error);
1143
                    CActiveScheduler::Current()->Error(error);
1144
                }
1145
            }
1146
1147
            if (handledSymbianEvent == QtRRActiveScheduler::NothingFound) {
1148
                // no runnable or delayed active object was found, the signal that caused us to get here must be bad
1149
                qFatal("QEventDispatcherSymbian::processEvents(): Caught Symbian stray signal");
1150
            } else if (handledSymbianEvent == QtRRActiveScheduler::ObjectDelayed) {
1151
                // signal the thread to compensate for the un-handled signal absorbed
1152
                RThread().RequestSignal();
1153
                break;
1154
            }
1155
1156
#ifdef QT_SYMBIAN_PRIORITY_DROP
1157
            int eventDur = eventTimer.elapsed()*1000;
1158
            // average is calcualted as a 5% decaying exponential average
1159
            m_avgEventTime = (m_avgEventTime * 95 + eventDur * 5) / 100;
1160
#endif
1161
1162
            handledAnyEvent = true;
1163
1164
            if (m_interrupt) {
1165
                break;
1166
            }
1167
            block = false;
1168
        };
1169
1170
        emit awake();
1171
    } QT_CATCH (const std::exception& ex) {
1172
#ifndef QT_NO_EXCEPTIONS
1173
        CActiveScheduler::Current()->Error(qt_symbian_exception2Error(ex));
1174
#endif
1175
    }
1176
1177
    m_noSocketEvents = oldNoSocketEventsValue;
1178
    m_insideTimerEvent = oldInsideTimerEventValue;
1179
1180
    return handledAnyEvent;
1181
}
1182
1183
void QEventDispatcherSymbian::timerFired(int timerId, QTimerActiveObject *ao)
1184
{
1185
    Q_D(QAbstractEventDispatcher);
1186
    QHash<int, SymbianTimerInfoPtr>::iterator i = m_timerList.find(timerId);
1187
    if (i == m_timerList.end()) {
1188
        // The timer has been deleted. Ignore this event.
1189
        return;
1190
    }
1191
1192
    SymbianTimerInfoPtr timerInfo = *i;
1193
1194
    // Prevent infinite timer recursion.
1195
    if (timerInfo->inTimerEvent) {
1196
        return;
1197
    }
1198
1199
    timerInfo->inTimerEvent = true;
1200
    bool oldInsideTimerEventValue = m_insideTimerEvent;
1201
    m_insideTimerEvent = true;
1202
1203
    QTimerEvent event(timerInfo->timerId);
1204
    if (QtRRActiveScheduler::TestAndClearActiveObjectRunningInRRScheduler(ao)) {
1205
        //undo the added nesting level around RunIfReady, since Qt's event system also nests
1206
        Decrementer dec(d->threadData->loopLevel);
1207
        QCoreApplication::sendEvent(timerInfo->receiver, &event);
1208
    } else {
1209
        QCoreApplication::sendEvent(timerInfo->receiver, &event);
1210
    }
1211
1212
    m_insideTimerEvent = oldInsideTimerEventValue;
1213
    timerInfo->inTimerEvent = false;
1214
1215
    return;
1216
}
1217
1218
void QEventDispatcherSymbian::wakeUpWasCalled(QWakeUpActiveObject *ao)
1219
{
1220
    Q_D(QAbstractEventDispatcher);
1221
    // The reactivation should happen in RunL, right before the call to this function.
1222
    // This is because m_wakeUpDone is the "signal" that the object can be completed
1223
    // once more.
1224
    // Also, by dispatching the posted events after resetting m_wakeUpDone, we guarantee
1225
    // that no posted event notification will be lost. If we did it the other way
1226
    // around, it would be possible for another thread to post an event right after
1227
    // the sendPostedEvents was done, but before the object was ready to be completed
1228
    // again. This could deadlock the application if there are no other posted events.
1229
    m_wakeUpDone.fetchAndStoreOrdered(0);
1230
    if (QtRRActiveScheduler::TestAndClearActiveObjectRunningInRRScheduler(ao)) {
1231
        //undo the added nesting level around RunIfReady, since Qt's event system also nests
1232
        Decrementer dec(d->threadData->loopLevel);
1233
        sendPostedEvents();
1234
    } else {
1235
        sendPostedEvents();
1236
    }
1237
}
1238
1239
void QEventDispatcherSymbian::interrupt()
1240
{
1241
    m_interrupt = true;
1242
    wakeUp();
1243
}
1244
1245
void QEventDispatcherSymbian::wakeUp()
1246
{
1247
    Q_D(QAbstractEventDispatcher);
1248
1249
    if (m_wakeUpAO && m_wakeUpDone.testAndSetAcquire(0, 1)) {
1250
        TRequestStatus *status = &m_wakeUpAO->iStatus;
1251
        QEventDispatcherSymbian::RequestComplete(d->threadData->symbian_thread_handle, status, KErrNone);
1252
    }
1253
}
1254
1255
bool QEventDispatcherSymbian::sendPostedEvents()
1256
{
1257
    Q_D(QAbstractEventDispatcher);
1258
1259
    // moveToThread calls this and canWait == true -> Events will never get processed
1260
    // if we check for d->threadData->canWait
1261
    //
1262
    // QCoreApplication::postEvent sets canWait = false, but after the object and events
1263
    // are moved to a new thread, the canWait in new thread is true i.e. not changed to reflect
1264
    // the flag on old thread. That's why events in a new thread will not get processed.
1265
    // This migth be actually bug in moveToThread functionality, but because other platforms
1266
    // do not check canWait in wakeUp (where we essentially are now) - decided to remove it from
1267
    // here as well.
1268
1269
    //if (!d->threadData->canWait) {
1270
        QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
1271
        return true;
1272
    //}
1273
    //return false;
1274
}
1275
1276
inline void QEventDispatcherSymbian::addDeferredSocketActiveObject(QActiveObject *object)
1277
{
1278
    m_deferredSocketEvents.append(object);
1279
}
1280
1281
inline void QEventDispatcherSymbian::addDeferredActiveObject(QActiveObject *object)
1282
{
1283
    queueDeferredActiveObjectsCompletion();
1284
    m_deferredActiveObjects.append(object);
1285
}
1286
1287
inline void QEventDispatcherSymbian::removeDeferredActiveObject(QActiveObject *object)
1288
{
1289
    m_deferredActiveObjects.removeAll(object);
1290
}
1291
1292
void QEventDispatcherSymbian::queueDeferredActiveObjectsCompletion()
1293
{
1294
    m_completeDeferredAOs->complete();
1295
}
1296
1297
void QEventDispatcherSymbian::reactivateDeferredActiveObjects()
1298
{
1299
    while (!m_deferredActiveObjects.isEmpty()) {
1300
        QActiveObject *object = m_deferredActiveObjects.takeFirst();
1301
        object->reactivateAndComplete();
1302
    }
1303
1304
    // We do this because we want to return from processEvents. This is because
1305
    // each invocation of processEvents should only run each active object once.
1306
    // The active scheduler should run them continously, however.
1307
    m_interrupt = true;
1308
}
1309
1310
bool QEventDispatcherSymbian::sendDeferredSocketEvents()
1311
{
1312
    bool sentAnyEvents = false;
1313
    while (!m_deferredSocketEvents.isEmpty()) {
1314
        sentAnyEvents = true;
1315
        QActiveObject *object = m_deferredSocketEvents.takeFirst();
1316
        object->reactivateAndComplete();
1317
    }
1318
1319
    return sentAnyEvents;
1320
}
1321
1322
void QEventDispatcherSymbian::flush()
1323
{
1324
}
1325
1326
bool QEventDispatcherSymbian::hasPendingEvents()
1327
{
1328
    Q_D(QAbstractEventDispatcher);
1329
    return (d->threadData->symbian_thread_handle.RequestCount() != 0
1330
            || !d->threadData->canWait || !m_deferredSocketEvents.isEmpty());
1331
}
1332
1333
void QEventDispatcherSymbian::registerSocketNotifier ( QSocketNotifier * notifier )
1334
{
1335
    //check socket descriptor is usable
1336
    if (notifier->socket() >= FD_SETSIZE || notifier->socket() < 0) {
1337
        //same warning message as the unix event dispatcher for easy testing
1338
        qWarning("QSocketNotifier: Internal error");
1339
        return;
1340
    }
1341
    //note - this is only for "open C" file descriptors
1342
    //for native sockets, an active object in the symbian socket engine handles this
1343
    QSocketActiveObject *socketAO = new QSocketActiveObject(this, notifier);
1344
    Q_CHECK_PTR(socketAO);
1345
    m_notifiers.insert(notifier, socketAO);
1346
    selectThread().requestSocketEvents(notifier, &socketAO->iStatus);
1347
}
1348
1349
void QEventDispatcherSymbian::unregisterSocketNotifier ( QSocketNotifier * notifier )
1350
{
1351
    //note - this is only for "open C" file descriptors
1352
    //for native sockets, an active object in the symbian socket engine handles this
1353
    if (m_selectThread)
1354
        m_selectThread->cancelSocketEvents(notifier);
1355
    if (m_notifiers.contains(notifier)) {
1356
        QSocketActiveObject *sockObj = *m_notifiers.find(notifier);
1357
        m_deferredSocketEvents.removeAll(sockObj);
1358
        sockObj->deleteLater();
1359
        m_notifiers.remove(notifier);
1360
    }
1361
}
1362
1363
void QEventDispatcherSymbian::reactivateSocketNotifier(QSocketNotifier *notifier)
1364
{
1365
    selectThread().requestSocketEvents(notifier, &m_notifiers[notifier]->iStatus);
1366
}
1367
1368
void QEventDispatcherSymbian::registerTimer ( int timerId, int interval, QObject * object )
1369
{
1370
    if (interval < 0) {
1371
        qWarning("Timer interval < 0");
1372
        interval = 0;
1373
    }
1374
1375
    SymbianTimerInfoPtr timer(new SymbianTimerInfo);
1376
    timer->timerId      = timerId;
1377
    timer->interval     = interval;
1378
    timer->inTimerEvent = false;
1379
    timer->receiver     = object;
1380
    timer->dispatcher   = this;
1381
    timer->timerAO      = q_check_ptr(new QTimerActiveObject(this, timer.data()));
1382
    m_timerList.insert(timerId, timer);
1383
1384
    timer->timerAO->Start();
1385
1386
    if (m_insideTimerEvent)
1387
        // If we are inside a timer event, we need to prevent event starvation
1388
        // by preventing newly created timers from running in the same event processing
1389
        // iteration. Do this by calling the maybeQueueForLater() function to "fake" that we have
1390
        // already run once. This will cause the next run to be added to the deferred
1391
        // queue instead.
1392
        timer->timerAO->maybeQueueForLater();
1393
}
1394
1395
bool QEventDispatcherSymbian::unregisterTimer ( int timerId )
1396
{
1397
    if (!m_timerList.contains(timerId)) {
1398
        return false;
1399
    }
1400
1401
    SymbianTimerInfoPtr timerInfo = m_timerList.take(timerId);
1402
1403
    if (!QObjectPrivate::get(timerInfo->receiver)->inThreadChangeEvent)
1404
        QAbstractEventDispatcherPrivate::releaseTimerId(timerId);
1405
1406
    return true;
1407
}
1408
1409
bool QEventDispatcherSymbian::unregisterTimers ( QObject * object )
1410
{
1411
    if (m_timerList.isEmpty())
1412
        return false;
1413
1414
    bool unregistered = false;
1415
    for (QHash<int, SymbianTimerInfoPtr>::iterator i = m_timerList.begin(); i != m_timerList.end(); ) {
1416
        if ((*i)->receiver == object) {
1417
            i = m_timerList.erase(i);
1418
            unregistered = true;
1419
        } else {
1420
            ++i;
1421
        }
1422
    }
1423
1424
    return unregistered;
1425
}
1426
1427
QList<QEventDispatcherSymbian::TimerInfo> QEventDispatcherSymbian::registeredTimers ( QObject * object ) const
1428
{
1429
    QList<TimerInfo> list;
1430
    for (QHash<int, SymbianTimerInfoPtr>::const_iterator i = m_timerList.begin(); i != m_timerList.end(); ++i) {
1431
        if ((*i)->receiver == object) {
1432
            list.push_back(TimerInfo((*i)->timerId, (*i)->interval));
1433
        }
1434
    }
1435
1436
    return list;
1437
}
1438
1439
void QEventDispatcherSymbian::activeObjectError(int error)
1440
{
1441
    if (error == KErrNoMemory) {
1442
        // limit the number of reported out of memory errors, as the disappearance of the warning
1443
        // dialog can trigger further OOM errors causing a loop.
1444
        if (m_oomErrorTimer.restart() > 60000)  // 1 minute
1445
            m_oomErrorCount = 0;
1446
        if (m_oomErrorCount++ >= 5)
1447
            return;
1448
    }
1449
    CActiveScheduler::Current()->Error(error);
1450
}
1451
1452
/*
1453
 * This active scheduler class implements a simple report and continue policy, for Symbian OS leaves
1454
 * or exceptions from Qt that fall back to the scheduler.
1455
 * It will be used in cases where there is no existing active scheduler installed.
1456
 * Apps which link to qts60main.lib will have the UI active scheduler installed in the main thread
1457
 * instead of this one. But this would be used in other threads in the UI.
1458
 * An app could replace this behaviour by installing an alternative active scheduler.
1459
 */
1460
void CQtActiveScheduler::Error(TInt aError) const
1461
{
1462
    QT_TRY {
1463
        qWarning("Error from active scheduler %d", aError);
1464
    }
1465
    QT_CATCH (const std::bad_alloc&) {}    // ignore alloc fails, nothing more can be done
1466
}
1467
1468
QT_END_NAMESPACE
1469
1470
#include "moc_qeventdispatcher_symbian_p.cpp"