0

I have implemented an idle timer on a resource (class) instances of which can be open in several applications at once. Hence, the idleTimer is not only a simple QTimer, but the slot (trigger) needs to verify if no other applications have accessed the same resources during the last N minutes. If that is the case, the timer is reset (without updating the lastAccessedTime value), otherwise the resource is closed. The timer is thus a singleshot one, and lastAccessTime is kept in a QSharedMemory object.

Here's some trace output:

### "Google Contacts () of type Google Contacts" Idle timeout 6 min. for KWallet::Wallet(0x105d1f900) "kdewallet" handle 0 ; elapsed minutes= 5.83601 timer QTimer(0x11d273d60) triggered 1 times 
### slotIdleTimedOut ->handleIdleTiming: setting QTimer(0x11d273d60) for wallet "kdewallet" handle 0 timeout to 6 
### "Google Contacts () of type Google Contacts" Idle timeout 6 min. for KWallet::Wallet(0x105d1f900) "kdewallet" handle 0 ;     elapsed minutes= 5.83634 timer QTimer(0x11d273d60) triggered 2 times 
### "Google Contacts () of type Google Contacts" Idle timeout 6 min. for KWallet::Wallet(0x105d1f900) "kdewallet" handle 0 ; elapsed minutes= 5.83634 timer QTimer(0x11d273d60) triggered 3 times 
### "Google Contacts ()of type Google Contacts" Idle timeout 6 min. for KWallet::Wallet(0x105d1f900) "kdewallet" handle 0 ; elapsed minutes= 5.83634 timer QTimer(0x11d273d60) triggered 4 times 
### "Google Contacts () of type Google Contacts" Idle timeout 6 min. for KWallet::Wallet(0x105d1f900) "kdewallet" handle 0 ; elapsed minutes= 5.83634 timer QTimer(0x11d273d60) triggered 5 times 
### "Google Contacts () of type Google Contacts" Idle timeout 6 min. for KWallet::Wallet(0x105d1f900) "kdewallet" handle 0 ; elapsed minutes= 5.83635 timer QTimer(0x11d273d60) triggered 6 times 
### "Google Contacts () of type Google Contacts" Idle timeout 6 min. for KWallet::Wallet(0x105d1f900) "kdewallet" handle 0 ;     elapsed minutes= 5.83635 timer QTimer(0x11d273d60) triggered 7 times 
### "Google Contacts () of type Google Contacts" Idle timeout 6 min. for KWallet::Wallet(0x105d1f900) "kdewallet" handle 0 ; elapsed minutes= 5.83635 timer QTimer(0x11d273d60) triggered 8 times 
### "KMail" Idle timeout 6 min. for KWallet::Wallet(0x1083f1ac0) "kdewallet" handle 0 ; elapsed minutes= 6 timer QTimer(0x120a1b5f0) triggered 1 times 
### "KMail" Idle timeout 6 min. for KWallet::Wallet(0x1083f1ac0) "kdewallet" handle -1 ; elapsed minutes= 6.00008 timer QObject(0x0)  triggered 2 times 
### "KMail" Idle timeout 6 min. for KWallet::Wallet(0x1083f1ac0) "kdewallet" handle -1 ; elapsed minutes= 6.00009 timer QObject(0x0)  triggered 3 times 
### "KMail" Idle timeout 6 min. for KWallet::Wallet(0x1083f1ac0) "kdewallet" handle -1 ; elapsed minutes= 6.00012 timer QObject(0x0)  triggered 4 times 
### "KMail" Idle timeout 6 min. for KWallet::Wallet(0x1083f1ac0) "kdewallet" handle -1 ; elapsed minutes= 6.00012 timer QObject(0x0)  triggered 5 times 
### "KMail" Idle timeout 6 min. for KWallet::Wallet(0x1083f1ac0) "kdewallet" handle -1 ; elapsed minutes= 6.00012 timer QObject(0x0)  triggered 6 times 
### "KMail" Idle timeout 6 min. for KWallet::Wallet(0x1083f1ac0) "kdewallet" handle -1 ; elapsed minutes= 6.00012 timer QObject(0x0)  triggered 7 times 
### "KMail" Idle timeout 6 min. for KWallet::Wallet(0x1083f1ac0) "kdewallet" handle -1 ; elapsed minutes= 6.00012 timer QObject(0x0)  triggered 8 times 

The principle works, but I notice 2 things:

  • the timer fires a bit early. Of course that causes the timer to be reset.
  • it fires several times in fast succession. The fact that an early fire should reset it doesn't have the slightest effect.

Below is the relevant part of my code, including the function that resets the timer at each resource access, and the timer's trigger slot.

Any idea what I'm doing wrong? I stop the timer before (re)setting it to singleshot mode and starting it (anew). The object and application identifiers show that it is indeed the same timer that triggers multiple times, and that it can get triggered even after I deleted the timer object.

Could it be that the trigger slot is not application (or even instance) specific, somehow leading to 1 instance receiving the idleTimer trigger signals from all other instances across the various applications that set an instance of this timer? idleTimer gets set to NULL only in the class destructor and/or when timeOut is <=0, so I'm stymied that my trigger slot can get called with a NULL timer object!

From the timer install function (handleIdleTiming, a member of KWallet::Wallet as is the idleTimer itself):

// This function is to be called at every operation that is supposed to launch or reset
// the idle timing. @p timeOut is a time in minutes.
void handleIdleTiming(const char *caller="", bool touchAccessTime=true)
{
    // ...
    if( timeOut >= 0 ){
        if( !idleTimer ){
            idleTimer = new QTimer(0);
        }
        else{
            idleTimer->stop();
        }
        // when the idle timer fires, the wallet is supposed to be closed. There is thus
        // no reason to use a repeating timer.
        idleTimer->setSingleShot(true);
        connect( idleTimer, SIGNAL(timeout()), q, SLOT(slotIdleTimedOut()) );
        if( touchAccessTime ){
            if( lastAccessTime.lock() ){
                *((double*)lastAccessTime.data()) = HRTime_Time();
                lastAccessTime.unlock();
            }
            else{
                qDebug() << "Cannot lock lastAccessTime for wallet" << name << "error" << lastAccessTime.errorString();
            }
        }
        idleTimer->start( timeOut * 60 * 1000 );

The timer trigger slot:

void Wallet::slotIdleTimedOut()
{   double lastAccessTime = 0;
    // check the last time anyone accessed this wallet:
    if( d->lastAccessTime.lock() ){
        lastAccessTime = *((double*)d->lastAccessTime.data());
        d->lastAccessTime.unlock();
    }
    else{
        qDebug() << "Cannot lock lastAccessTime for wallet" << d->name << "error" << d->lastAccessTime.errorString();
    }
    // the time elapsed since that last access, in minutes:
    double elapsed = (HRTime_Time() - lastAccessTime) / 60;
    d->idleTimerTriggered += 1;
    qDebug() << "###" << appid() << "Idle timeout" << d->timeOut << "min. for" << this << d->name << "handle" << d->handle
        << "; elapsed minutes=" << elapsed << "timer" << d->idleTimer << "triggered" << d->idleTimerTriggered << "times";
    if( elapsed >= d->timeOut ){
        // we have a true timeout, i.e. we didn't access the wallet in timeOut minutes, and no one else did either.
        slotWalletClosed(d->handle);
    }
    else{
        // false alarm, reset the timer, but there's no need to count this as an access!
        d->handleIdleTiming(__FUNCTION__, false);
    }
}
RJVB
  • 698
  • 8
  • 18
  • Could this be because I call connect() (potentially) repeatedly instead of once only after `new QTimer(0)` ? – RJVB Sep 10 '14 at 15:25

1 Answers1

0

Must indeed have been because I issued a connect statement each time I reset the timer, instead of only once after creating it.

RJVB
  • 698
  • 8
  • 18