summaryrefslogtreecommitdiff
path: root/src/corelib/kernel/qeventdispatcher_symbian.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/kernel/qeventdispatcher_symbian.cpp')
-rw-r--r--src/corelib/kernel/qeventdispatcher_symbian.cpp196
1 files changed, 166 insertions, 30 deletions
diff --git a/src/corelib/kernel/qeventdispatcher_symbian.cpp b/src/corelib/kernel/qeventdispatcher_symbian.cpp
index 4c01bdee6e..e0eeb0819a 100644
--- a/src/corelib/kernel/qeventdispatcher_symbian.cpp
+++ b/src/corelib/kernel/qeventdispatcher_symbian.cpp
@@ -135,7 +135,7 @@ private:
* The QCompleteDeferredAOs class is a special object that runs after all others, which will
* reactivate the objects that were previously not run.
*/
-inline QActiveObject::QActiveObject(TInt priority, QEventDispatcherSymbian *dispatcher)
+QActiveObject::QActiveObject(TInt priority, QEventDispatcherSymbian *dispatcher)
: CActive(priority),
m_dispatcher(dispatcher),
m_hasAlreadyRun(false),
@@ -167,12 +167,25 @@ bool QActiveObject::maybeQueueForLater()
}
}
+bool QActiveObject::maybeDeferSocketEvent()
+{
+ Q_ASSERT(!m_hasRunAgain);
+ Q_ASSERT(m_dispatcher);
+ if (!m_dispatcher->areSocketEventsBlocked()) {
+ return false;
+ }
+ m_hasRunAgain = true;
+ m_dispatcher->addDeferredSocketActiveObject(this);
+ return true;
+}
+
void QActiveObject::reactivateAndComplete()
{
+ TInt error = iStatus.Int();
iStatus = KRequestPending;
SetActive();
TRequestStatus *status = &iStatus;
- QEventDispatcherSymbian::RequestComplete(status, KErrNone);
+ QEventDispatcherSymbian::RequestComplete(status, error);
m_hasRunAgain = false;
m_hasAlreadyRun = false;
@@ -181,6 +194,7 @@ void QActiveObject::reactivateAndComplete()
QWakeUpActiveObject::QWakeUpActiveObject(QEventDispatcherSymbian *dispatcher)
: QActiveObject(WAKE_UP_PRIORITY, dispatcher)
{
+ m_hostThreadId = RThread().Id();
CActiveScheduler::Add(this);
iStatus = KRequestPending;
SetActive();
@@ -196,6 +210,15 @@ void QWakeUpActiveObject::DoCancel()
if (iStatus.Int() == KRequestPending) {
TRequestStatus *status = &iStatus;
QEventDispatcherSymbian::RequestComplete(status, KErrNone);
+ } else if (IsActive() && m_hostThreadId != RThread().Id()) {
+ // This is being cancelled in the adopted monitor thread, which can happen if an adopted thread with
+ // 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.
+ // We force a cancellation in this thread, because a) the object cannot be deleted while active and b) without a cancellation
+ // the thread semaphore will be one count down.
+ // It is possible for this problem to affect other active objects. They symptom would be that finished signals
+ // from adopted threads are not sent, or they arrive much later than they should.
+ TRequestStatus *status = &iStatus;
+ User::RequestComplete(status, KErrNone);
}
}
@@ -211,8 +234,10 @@ void QWakeUpActiveObject::RunL()
QTimerActiveObject::QTimerActiveObject(QEventDispatcherSymbian *dispatcher, SymbianTimerInfo *timerInfo)
: QActiveObject((timerInfo->interval) ? TIMER_PRIORITY : NULLTIMER_PRIORITY , dispatcher),
- m_timerInfo(timerInfo)
+ m_timerInfo(timerInfo), m_expectedTimeSinceLastEvent(0)
{
+ // start the timeout timer to ensure initialisation
+ m_timeoutTimer.start();
}
QTimerActiveObject::~QTimerActiveObject()
@@ -256,10 +281,23 @@ void QTimerActiveObject::StartTimer()
m_rTimer.After(iStatus, MAX_SYMBIAN_TIMEOUT_MS * 1000);
m_timerInfo->msLeft -= MAX_SYMBIAN_TIMEOUT_MS;
} else {
- //HighRes gives the 1ms accuracy expected by Qt, the +1 is to ensure that
- //"Timers will never time out earlier than the specified timeout value"
- //condition is always met.
- m_rTimer.HighRes(iStatus, (m_timerInfo->msLeft + 1) * 1000);
+ // this algorithm implements drift correction for repeating timers
+ // calculate how late we are for this event
+ int timeSinceLastEvent = m_timeoutTimer.restart();
+ int overshoot = timeSinceLastEvent - m_expectedTimeSinceLastEvent;
+ if (overshoot > m_timerInfo->msLeft) {
+ // we skipped a whole timeout, restart from here
+ overshoot = 0;
+ }
+ // calculate when the next event should happen
+ int waitTime = m_timerInfo->msLeft - overshoot;
+ m_expectedTimeSinceLastEvent = waitTime;
+ // limit the actual ms wait time to avoid wild corrections
+ // this will cause the real event time to slowly drift back to the expected event time
+ // measurements show that Symbian timers always fire 1 or 2 ms late
+ const int limit = 4;
+ waitTime = qMax(m_timerInfo->msLeft - limit, waitTime);
+ m_rTimer.HighRes(iStatus, waitTime * 1000);
m_timerInfo->msLeft = 0;
}
SetActive();
@@ -306,6 +344,8 @@ void QTimerActiveObject::Start()
if (!m_rTimer.Handle()) {
qt_symbian_throwIfError(m_rTimer.CreateLocal());
}
+ m_timeoutTimer.start();
+ m_expectedTimeSinceLastEvent = 0;
StartTimer();
} else {
iStatus = KRequestPending;
@@ -635,10 +675,28 @@ void QSocketActiveObject::DoCancel()
void QSocketActiveObject::RunL()
{
+ if (maybeDeferSocketEvent())
+ return;
if (maybeQueueForLater())
return;
- QT_TRYCATCH_LEAVING(m_dispatcher->socketFired(this));
+ QT_TRYCATCH_LEAVING(run());
+}
+
+void QSocketActiveObject::run()
+{
+ QEvent e(QEvent::SockAct);
+ m_inSocketEvent = true;
+ QCoreApplication::sendEvent(m_notifier, &e);
+ m_inSocketEvent = false;
+
+ if (m_deleteLater) {
+ delete this;
+ } else {
+ iStatus = KRequestPending;
+ SetActive();
+ m_dispatcher->reactivateSocketNotifier(m_notifier);
+ }
}
void QSocketActiveObject::deleteLater()
@@ -849,6 +907,14 @@ bool QEventDispatcherSymbian::processEvents ( QEventLoop::ProcessEventsFlags fla
CActiveScheduler::Current()->WaitForAnyRequest();
} else {
if (thread.RequestCount() == 0) {
+#ifdef QT_SYMBIAN_PRIORITY_DROP
+ if (idleDetectorThread()->hasRun()) {
+ m_lastIdleRequestTimer.start();
+ idleDetectorThread()->kick();
+ } else if (m_lastIdleRequestTimer.elapsed() > maxBusyTime) {
+ User::AfterHighRes(m_delay);
+ }
+#endif
break;
}
// This one should return without delay.
@@ -934,27 +1000,6 @@ void QEventDispatcherSymbian::timerFired(int timerId)
return;
}
-void QEventDispatcherSymbian::socketFired(QSocketActiveObject *socketAO)
-{
- if (m_noSocketEvents) {
- m_deferredSocketEvents.append(socketAO);
- return;
- }
-
- QEvent e(QEvent::SockAct);
- socketAO->m_inSocketEvent = true;
- QCoreApplication::sendEvent(socketAO->m_notifier, &e);
- socketAO->m_inSocketEvent = false;
-
- if (socketAO->m_deleteLater) {
- delete socketAO;
- } else {
- socketAO->iStatus = KRequestPending;
- socketAO->SetActive();
- reactivateSocketNotifier(socketAO->m_notifier);
- }
-}
-
void QEventDispatcherSymbian::wakeUpWasCalled()
{
// The reactivation should happen in RunL, right before the call to this function.
@@ -1015,6 +1060,12 @@ inline void QEventDispatcherSymbian::addDeferredActiveObject(QActiveObject *obje
inline void QEventDispatcherSymbian::removeDeferredActiveObject(QActiveObject *object)
{
m_deferredActiveObjects.removeAll(object);
+ m_deferredSocketEvents.removeAll(object);
+}
+
+inline void QEventDispatcherSymbian::addDeferredSocketActiveObject(QActiveObject *object)
+{
+ m_deferredSocketEvents.append(object);
}
void QEventDispatcherSymbian::queueDeferredActiveObjectsCompletion()
@@ -1040,7 +1091,8 @@ bool QEventDispatcherSymbian::sendDeferredSocketEvents()
bool sentAnyEvents = false;
while (!m_deferredSocketEvents.isEmpty()) {
sentAnyEvents = true;
- socketFired(m_deferredSocketEvents.takeFirst());
+ QActiveObject *object = m_deferredSocketEvents.takeFirst();
+ object->reactivateAndComplete();
}
return sentAnyEvents;
@@ -1059,6 +1111,8 @@ bool QEventDispatcherSymbian::hasPendingEvents()
void QEventDispatcherSymbian::registerSocketNotifier ( QSocketNotifier * notifier )
{
+ //note - this is only for "open C" file descriptors
+ //for native sockets, an active object in the symbian socket engine handles this
QSocketActiveObject *socketAO = new QSocketActiveObject(this, notifier);
Q_CHECK_PTR(socketAO);
m_notifiers.insert(notifier, socketAO);
@@ -1067,6 +1121,8 @@ void QEventDispatcherSymbian::registerSocketNotifier ( QSocketNotifier * notifie
void QEventDispatcherSymbian::unregisterSocketNotifier ( QSocketNotifier * notifier )
{
+ //note - this is only for "open C" file descriptors
+ //for native sockets, an active object in the symbian socket engine handles this
if (m_selectThread)
m_selectThread->cancelSocketEvents(notifier);
if (m_notifiers.contains(notifier)) {
@@ -1169,6 +1225,86 @@ void CQtActiveScheduler::Error(TInt aError) const
QT_CATCH (const std::bad_alloc&) {} // ignore alloc fails, nothing more can be done
}
+bool QActiveObject::wait(CActive* ao, int ms)
+{
+ if (!ao->IsActive())
+ return true; //request already complete
+ bool timedout = false;
+ if (ms > 0) {
+ TRequestStatus tstat;
+ RTimer t;
+ if (KErrNone != t.CreateLocal())
+ return false;
+ t.HighRes(tstat, ms*1000);
+ User::WaitForRequest(tstat, ao->iStatus);
+ if (tstat != KRequestPending) {
+ timedout = true;
+ } else {
+ t.Cancel();
+ //balance thread semaphore
+ User::WaitForRequest(tstat);
+ }
+ t.Close();
+ } else {
+ User::WaitForRequest(ao->iStatus);
+ }
+ if (timedout)
+ return false;
+
+ //evil cast to allow calling of protected virtual
+ ((QActiveObject*)ao)->RunL();
+
+ //clear active & pending flags
+ ao->iStatus = TRequestStatus();
+
+ return true;
+}
+
+bool QActiveObject::wait(QList<CActive*> aos, int ms)
+{
+ QVector<TRequestStatus*> stati;
+ stati.reserve(aos.count() + 1);
+ foreach (CActive* ao, aos) {
+ if (!ao->IsActive())
+ return true; //request already complete
+ stati.append(&(ao->iStatus));
+ }
+ bool timedout = false;
+ TRequestStatus tstat;
+ RTimer t;
+ if (ms > 0) {
+ if (KErrNone != t.CreateLocal())
+ return false;
+ t.HighRes(tstat, ms*1000);
+ stati.append(&tstat);
+ }
+ User::WaitForNRequest(stati.data(), stati.count());
+ if (ms > 0) {
+ if (tstat != KRequestPending) {
+ timedout = true;
+ } else {
+ t.Cancel();
+ //balance thread semaphore
+ User::WaitForRequest(tstat);
+ }
+ t.Close();
+ }
+ if (timedout)
+ return false;
+
+ foreach (CActive* ao, aos) {
+ if (ao->iStatus != KRequestPending) {
+ //evil cast to allow calling of protected virtual
+ ((QActiveObject*)ao)->RunL();
+
+ //clear active & pending flags
+ ao->iStatus = TRequestStatus();
+ break; //only call one
+ }
+ }
+ return true;
+}
+
QT_END_NAMESPACE
#include "moc_qeventdispatcher_symbian_p.cpp"