Search code examples
c++qtluaqthread

Calling luaL_error in another thread throws qWarning


When calling luaL_error() on a lua state running in a separate thread from the GUI, the warning QObject::~QObject: Timers cannot be stopped from another thread is printed and the application closes.

After a lot of testing, I was able to reproduce this in a compact sample program simulating my current workflow. Below is the code:

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QDebug>
#include <QThread>
#include "lua_src/lua.hpp"

class Worker : public QObject
{
    Q_OBJECT
public:
    Worker() : QObject(){}

public slots:
    void process()
    {
        lua_State *L = luaL_newstate();
        luaL_dostring(L, "x=5");
        luaL_error(L, "test error");
        lua_close(L);
        emit finished();
    }

signals:
    void finished();
};

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

    Worker *worker;
    QThread *workerThread;
private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    worker = new Worker();
    workerThread = new QThread();
    worker->moveToThread(workerThread);
    connect(workerThread, SIGNAL(started()), worker, SLOT(process()));
    connect(worker, SIGNAL(finished()), workerThread, SLOT(quit()));
    connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
    connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
    workerThread->start();
}

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

main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

I would have expected that the worker thread process would continue and the lua state would simply close. However this renders lua error handling useless. I've included Lua 5.3's source for this project.

One thing I did notice is that this problem appears to only happens for GUI applications and not console applications.

Does anyone know what's going on here and how to handle lua errors from inside a QThread? Thanks for your time.


Solution

  • The documentation for luaL_error states:

    This function never returns, ...

    Elsewhere, the lua error handling documentation has:

    If an error happens outside any protected environment, Lua calls a panic function (see lua_atpanic) and then calls abort, thus exiting the host application. Your panic function can avoid this exit by never returning (e.g., doing a long jump to your own recovery point outside Lua).

    So you can think of luaL_error (and lua_error) as raising fatal errors. They're fatal for the script in protected mode, or fatal for your process outside of protected mode.

    If your C code is being called from a script, you can start the script in protected mode with lua_pcall. This is the usual case. If your C code isn't called from a script then you probably don't want to use luaL_error since its main advantage is reporting information about where the error occurred in a script.

    You can also get information about the location of the error with luaL_where and report it to the user in some other way.