Strange undocumented QTimer/QEventLoop behaviour after the timer is manually restarted

1.3k views Asked by At

I have recently stumbled upon this while working with QTimer that calls a function with internal QEventLoop

So, say we have a QTimer instance

QTimer* timer = new QTimer;

somewhere in the constructor we start it and it begins ticking away once every 100ms

timer->start(100);

now the fun part, we connect it to a slot that has internal QEventLoop

void SlotFunction()
{
    qDebug() << "entered";
    QEventLoop loop;
    loop.exec();
}

putting aside how stupid this loop really is, we see that we will never finish processing the slot and timer's subsequent timeouts will keep stacking into execution queue. Everything is ok and as it should be.

What is NOT as it should be comes next: since QEventLoop makes sure our app stays responsive while the slot mindlessly idles away we can make a button and its clicked() slot that looks like:

void OnClicked()
{
    timer->start(100);
}

what I am doing here is essentially restarting current timer cycle, nothing less, nothing more. Right? Nope! After this restart, SlotFunction fires again suggesting that tick after timer's restart is not in fact equal to all other ticks that were issued before it...

My only question is : WTF?! Why manually restarting the timer enables it to enter the slot additional time? I've asked on freenode but the only answer I got was "It is as it should be"

1

There are 1 answers

2
Sebastian Lange On BEST ANSWER

I tried this and every click creates another "entered" line.

The main Eventloop cannot handle another event since we are stuck in a new eventloop.

This is quite easy to see when implementing a second slot and also connecting this slot to the timeout signal.

  • The maineventloop will get stuck when calling the next eventloop and not processing any more queued events.
  • The timer itself will also not queue any more events, since the queueing up itself would be done in the now stuck main-eventloop. The timer does not run in its own eventloop (thats why Qtimers are no precision timers).
  • As soon as the button is clicked the new eventloop checks the timer if an event timeout() should be generated.
  • As soon as the new event is handled we again are stuck in another eventloop...
  • This will go on until we exit the application.
  • When exiting the application we see the loops reversing and calling the second slot as often as we clicked the button and ran into the first slot

Code:

#include <QDebug>
#include <QTime>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    timer = new QTimer;
    timer->setInterval(2000);
    connect(timer,SIGNAL(timeout()),this,SLOT(timerslot()));
    connect(timer,SIGNAL(timeout()),this,SLOT(timerslot2()));
    timer->start();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_clicked()
{
    timer->start(2000);
}

void MainWindow::timerslot()
{
    qDebug()<<"In";
    QEventLoop loop;
    loop.exec();
}

void MainWindow::timerslot2()
{
    qDebug()<<"More";
}

Output on start:

In

Output on every click:

In

Output after 3 clicks:

In
In
In
In

Output exiting the application:

In
In
In
In
More
More
More
More