Changeset 175


Ignore:
Timestamp:
Sep 9, 2009, 10:02:31 PM (16 years ago)
Author:
Dmitry A. Kuminov
Message:

corelib: Improved timers: don't block other threads by constantly posting close-to-zero timers to the message queue in cases when the target thread isn't fast enough to process them. This fixes spontaneous hangs during the termination of applications that use many timers and run other threads they wait for at exit.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/corelib/kernel/qeventdispatcher_pm.cpp

    r161 r175  
    235235}
    236236
     237
     238
     239
     240
     241
    237242inline timeval &operator+=(timeval &t1, const timeval &t2)
    238243{
     
    286291    static bool removeTimers(QObject *object);
    287292    static QList<QPair<int, int> > knownTimers(QObject *object);
    288     static QObject *getTimerObject(int timerId);
     293    static QObject *getTimerObject(int timerId);
    289294
    290295    static void attachThread();
     
    340345        QObject *obj;     // - object to receive event
    341346        HWND hwnd;        // - where to post the timer message
     347
    342348    };
    343349
     
    347353    typedef QMap<timeval, TimerInfo*> TimevalMap;
    348354    TimevalMap timersByTimeout;
     355
    349356
    350357    timeval currentTime;
     
    463470    t->obj = object;
    464471    t->hwnd = hwnd;
     472
    465473
    466474    instance->timers.insert(t->id, t);
     
    546554    that owns the timer (creates/registers/unregisters it).
    547555
     556
     557
     558
    548559    May return 0 if the timer is stopped/deleted after WM_U_SEM_TIMER was issued
    549560    for it but before this message gets processed by the owning thread.
    550561*/
    551562// static
    552 QObject *QSelectThread::getTimerObject(int timerId)
     563QObject *QSelectThread::getTimerObject(int timerId)
    553564{
    554565    QMutexLocker locker(&mutex);
     
    556567
    557568    TimerInfo *t = instance->timers.value(timerId);
    558     if (t)
     569    if (t) {
     570        if (reset) {
     571            t->posted = false;
     572            if (instance->allTimersPosted) {
     573                // the select thread's been sleeping forever; wake it up -- we
     574                // now have at least one timer that can be posted again
     575                instance->allTimersPosted = false;
     576                instance->cancelSelectOrIdle();
     577            } else {
     578                // also wake it up if this timer was skipped when choosing the
     579                // shortest wait interval so that a longer once could be chosen
     580                bool haveNonPosted = false;
     581                for (TimevalMap::const_iterator it = instance->timersByTimeout.begin();
     582                     it != instance->timersByTimeout.end() && !haveNonPosted &&
     583                     it.value() != t;
     584                     ++it) {
     585                    if (!it.value()->posted)
     586                        haveNonPosted = true;
     587                }
     588                if (!haveNonPosted)
     589                    instance->cancelSelectOrIdle();
     590            }
     591        }
    559592        return t->obj;
     593
    560594
    561595    return 0;
     
    656690}
    657691
    658 QSelectThread::QSelectThread() : finish(false), refcnt(0), cancelWait(false)
     692QSelectThread::QSelectThread()
     693    : finish(false), refcnt(0), cancelWait(false), allTimersPosted(false)
    659694{
    660695    // initialize socket stuff
     
    771806            mutex.lock();
    772807        } else {
    773             ulong msecs = timeout ?
    774                 timeout->tv_sec * 1000 + timeout->tv_usec / 1000 : ULONG_MAX;
    775             nsel = -1; // cause to avoid activateTimers() when cancelled
     808            nsel = -1;
     809            errno = EINTR;
    776810            if (!cancelWait) {
     811
     812
    777813                if (!cond.wait(&mutex, msecs))
    778814                    nsel = 0; // indicate timeout
     
    781817        }
    782818
    783         if (nsel == 0) {
    784             // timeout, check if there are expired timers
     819        if (nsel == 0 || (nsel == -1 && errno == EINTR)) {
     820            // interrupt or timeout; check if there are expired or processed
     821            // timers
    785822            activateTimers();
    786823        }
     
    892929
    893930/*
    894   Returns the time to wait for the next timer, or null if no timers
    895   are waiting.
     931  Returns the time to wait for the next timer, or
     932  waiting.
    896933*/
    897934bool QSelectThread::timerWait(timeval &tm)
     
    902939        return false;
    903940
    904     TimerInfo *t = timersByTimeout.begin().value();    // first waiting timer
     941    // find first waiting timer with posted flag reset
     942    TimevalMap::iterator it = timersByTimeout.begin();
     943    while (it.value()->posted && it != timersByTimeout.end())
     944        ++it;
     945    if (it == timersByTimeout.end()) {
     946        // all timers've been posted but not yet processed by the target window;
     947        // set a flag to make sure the first getTimerObject(...,true) call will
     948        // interrupt the indefinte wait we are about to enter
     949        allTimersPosted = true;
     950        return false;
     951    }
     952
     953    TimerInfo *t = it.value();
    905954    if (currentTime < t->timeout) {
    906955        // time to wait
     
    925974    updateCurrentTime();
    926975
    927     while(true) {
    928         TimerInfo *t = timersByTimeout.begin().value();
     976    TimevalMap::iterator it = timersByTimeout.begin();
     977
     978    while (it != timersByTimeout.end()) {
     979        TimerInfo *t = it.value();
    929980
    930981        if (currentTime < t->timeout)
    931982            break; // no timer has expired
    932983
     984
     985
     986
     987
     988
     989
    933990        // remove from map
    934         timersByTimeout.erase(timersByTimeout.begin());
    935 
    936         // determine next timeout time
     991        timersByTimeout.erase();
     992
     993        // determine next timeout time
    937994        if (t->interval.tv_sec == 0 && t->interval.tv_usec == 0) {
    938             // zero timer; make sure it won't fire again in this loop
    939             t->timeout.tv_sec = 0;
    940             t->timeout.tv_usec = 1;
    941             t->timeout += currentTime;
     995            // zero timer
     996            t->timeout = currentTime;
    942997        } else {
    943             // normal timer; also make sure it won't fire again in this loop
    944             t->timeout += t->interval;
    945             if (t->timeout < currentTime)
    946                 t->timeout = currentTime + t->interval;
    947         }
     998            register qint64 time = t->timeout.tv_sec * 1000ll + t->timeout.tv_usec / 1000ll;
     999            register qint64 curr = currentTime.tv_sec * 1000ll + currentTime.tv_usec / 1000ll;
     1000            register qint64 ival = t->interval.tv_sec * 1000ll + t->interval.tv_usec / 1000ll;
     1001            curr += (ival - ((curr - time) % ival));
     1002            t->timeout.tv_sec = curr / 1000ll;
     1003            t->timeout.tv_usec = (curr % 1000ll) * 1000ll;
     1004        }
     1005
     1006        // set the posted flag to avoid polluting the message queue with timer
     1007        // messages if the target window is not fast enough to process them
     1008        t->posted = true;
    9481009
    9491010        // reinsert timer (in proper sort order)
     
    9521013        // post the timer message
    9531014        WinPostMsg(t->hwnd, WM_U_SEM_TIMER, MPFROMLONG(t->id), 0);
     1015
     1016
     1017
    9541018    }
    9551019
     
    9841048        return QList<QPair<int, int> >();
    9851049    }
    986     static QObject *getTimerObject(int timerId);
     1050    static QObject *getTimerObject(int timerId);
    9871051
    9881052    static void attachThread() {}
     
    10921156        case WM_U_SEM_TIMER: {
    10931157            int timerId = LONGFROMMP(mp1);
    1094             QObject *obj = QSelectThread::getTimerObject(timerId);
     1158            QObject *obj = QSelectThread::getTimerObject(timerId);
    10951159
    10961160            if (obj && !timersInSend.contains(timerId)) {
Note: See TracChangeset for help on using the changeset viewer.