nanosleep() call from Lua script paused QT GUI thread

I am developing a test tool to generate waveform from PC parallel port. This tools is designed to generate any pattern of waveform with timing accuracy of ms, so I use Lua script to define the waveform pattern, the GUI start new QThread to run the script when user clicks [Start] button.

The following three functions for Lua are implemented as C++ global functions:

  • pwrite: write data to parallel port.
  • msleep: wait for certain ms (implemented using nanosleep())
  • print: overwrite Lua default print function, this one will append message to one QTextEdit widget.
  • when pwrite is called, the written data is stored in global variable, then the GUI is updated with 20ms interval to update the parallel port data on the GUI. (this 20ms interval refresh is not a good design, but I haven't figure out how to use signal to make GUI update when data changed).

    The tool is basically functional now. The waveform output has no problem, but the parallel port data updating has some problem:

    When Lua call msleep, GUI thread is stopped, the parallel port data updates only after msleep ends.

    So my questions are:

  • How to implement the sleep method so that it won't stop the GUI thread from updating?

  • How to implement the pwrite, so that the GUI can receive a signal to update the parallel port data when written data changed?

  • Program GUI as the link below: 程序GUI

    The related code:

        /* common.cpp file */
    
    int L_MSleep(lua_State* l)
    {
        int milisec=0;
        struct timespec req={0, 0};
        time_t sec;
    
        milisec=luaL_optint(l,1,0); // obtain parameter
    
        if (milisec==0)
           return 0;
    
        sec=(int)(milisec/1000);
    
        milisec=milisec-(sec*1000);
        req.tv_sec=sec;
        req.tv_nsec=milisec*1000000L;
    
        while(nanosleep(&req,&req)==-1)
             continue;
    
        return 1;
    }
    
    
    /* LuaRunner.cpp file */
    LuaRunner::LuaRunner(QObject *parent) :
        QThread(parent)
    {
        runlua = false;
    }
    
    void LuaRunner::run()
    {
        QString err = "";
    
        runlua = true;
        LUA_RunScript(this->ff, err);
        runlua = false;
    
        if(err != "")
        {
            emit errorMessage(err);
        }
    }
    
    int LuaRunner::LUA_RunScript(QString ff, QString &err)
    {
        L = lua_open();
        luaL_openlibs(L);
    
        if (luaL_loadfile(L, ff.toAscii()) || lua_pcall(L, 0, 0, 0))
        {
            err = QString(lua_tostring(L, -1));
            return -1;
        }
    
        lua_register(L, "ssleep", L_SSleep);
        lua_register(L, "msleep", L_MSleep);
        lua_register(L, "pwrite", L_PortWrite);
        lua_register(L, "print", L_Log);
    
        lua_getglobal(L, "dotest");
        if (!lua_isfunction(L, -1))
        {
            err = QString("Test function(dotest) should be a function");
            return -1;
        }
    
        if(lua_pcall(L, 0, 0, 0))
        {
            err = QString(lua_tostring(L, -1));
            return -1;
        }
    
        lua_close(L);
    
        return 0;
    }
    

    You correctly run your Lua script in dedicated thread. That's the right way to do it -- almost. You are restarting the thread each time you want to run the script. That's wrong. You are also accessing the data in the GUI thread from the LUA thread without any sort of synchronization. That's not good. Qt provides an excellent mechanism for that in the form of queued connections between signals and slots. When signal-slot calls pass thread boundaries, the parameters get wrapped in a QEvent and get asynchronously delivered to the target QObject . Within each thread, the event delivery is serialized and thus you don't need to worry about data corruption etc.

    Here's how it should be done:

    // LUAObject.h
    #include <QObject>
    
    class LUAObject : public QObject
    {
       Q_OBJECT
    public:
       LUAObject(QObject * parent = 0);
    public slots:
       void setScript(const QString &);
       void runScript();
       void stop();
    
    signals:
       void hasError(const QString &);
       void finished();
       void hasParallelData(int);
       void hasMessage(const QString &);
    
    private:
       QString script;
       bool stop;
    }
    
    // LUAObject.cpp
    
    // whatever Lua includes you need etc
    
    LUAObject::LUAObject(QObject* p) : QObject(p)
    {}
    
    void LUAObject::stop() { stopped = true; }    
    
    void LUAObject::setScript(const QString & scr)
    { script = scr; }
    
    int L_PWrite(lua_State* l)
    {
       int data = luaL_optint(l, 1, -1);
       if (data != -1) {
          // access the parallel port HERE, NOT in the GUI thread!
          emit hasParallelData(luaL_optint(l, 1, 0));
       }
       return 0;
    }
    
    // returns a bool - true means we are stopped and should exit
    int L_MSleep(lua_State* l)
    {
       int ms = luaL_optint(l, 1, -1);
       if (ms == -1) return 0;
       QApplication::processEvents(QEventLoop::WaitForMoreEvents, ms);
       lua_pushBoolean(l, stopped); // event processing would run the stop() slot call
       return 1;
    }
    
    int L_SSleep(lua_State* l)
    {
       int secs = luaL_optint(l, 1, -1);
       if (secs == -1) return 0;
       QApplication::processEvents(QEventLoop::WaitForMoreEvents, secs*1000);
       lua_pushBoolean(l, stopped); // event processing would run the stop() slot call
       return 1;
    }
    
    int L_Log(lua_State* l)
    {
       const char * msg = luaL_optstring(l, 1, 0);
       if (!msg) return 0;
       emit hasMessage(msg);
       return 0;
    }
    
    class Lua // RAII
    {
    public:
       explicit Lua(lua_state * l) : L(l) {}
       ~Lua() { lua_close(L); }
       operator lua_state*() const { return L; }
    private:
       lua_state * L;
       Q_DISABLE_COPY(LUA)
    };
    
    LUAObject::runScript()
    {
       stopped = false;
       Lua L(lua_open());
       luaL_openlibs(L);
    
       if (luaL_loadbuffer(L, script.toAscii().constData(), script.length(), "script") || lua_pcall(L, 0, 0, 0))
       {
           emit hasError(lua_tostring(L, -1));
           return;
       }
    
       lua_register(L, "ssleep", L_SSleep);
       lua_register(L, "msleep", L_MSleep);
       lua_register(L, "pwrite", L_PWrite);
       lua_register(L, "print", L_Log);
    
       lua_getglobal(L, "dotest");
       if (!lua_isfunction(L, -1))
       {
          emit hasError("Test function(dotest) should be a function");
          return;
       }
    
       if(lua_pcall(L, 0, 0, 0))
       {
          emit hasError(lua_tostring(L, -1));
          return;
       }
    
       emit finished();
    }
    
    // main.cpp
    
    #include <QApplication>
    #include <QMetaMethod>
    #include "LUAObject.h"
    
    ...
    
    int main(int argc, char** argv)
    {
       QApplication(argc, argv);
    
       MainWindow window;
    
       ...
       QThread thread;
       LUAObject lua;
       thread.start(QThread::TimeCriticalPriority);
       lua.moveToThread(&thread);
    
       ...
    
       // NOTE: you can ONLY connect to LUAObject slots, you CANNOT call them
       // directly since it runs in a separate thread!
       connect(&window, SIGNAL(startClicked()), &lua, SLOT(runScript());
       connect(&lua, SIGNAL(hasError(QString)), &window, SLOT(luaError(QString)));
    
       ...
       window.show();
       int rc = qApp->exec();
       QMetaObject::invokeMethod(&lua, SLOT(stop())); // cross-thread slot invocation
       thread.exit();
       thread.wait();
       return rc;
    }
    

    I leave the implementation of the UI to your imagination. Note that it's untested code. It may blow up your computer for all I know.


    也许你应该使用QT的msleep,因为它提供给QThread使用

    链接地址: http://www.djcxy.com/p/55606.html

    上一篇: 分享到Facebook / Twitter / E

    下一篇: 来自Lua脚本的nanosleep()调用暂停了QT GUI线程