Help with timer and JavaScript slot.
-
@SPlatten said in Help with timer and JavaScript slot.:
@kshegunov , please see edit of my last post which includes the body of the lambda. The execution doesn't stop, there is just output to the debug.
I have a fairly good idea where the problem may be, but I prefer not to speculate at this point. If the program doesn't stop and the mentioned environment variable is set, then you're not running your application in debug mode - check this and adjust accordingly.
-
@kshegunov , 100% I'm in debug, I'm running in Qt Creator debug and developing. I have a function called createTimer here is the prototype:
Q_INVOKABLE void createTimer(const QString& crstrID ,const uint cuintInterval ,const QString& crstrFunction);
The implementation:
/** * @brief clsScriptHelper::createTimer * @param crstrID : Constant reference to Timer ID * @param cuintInterval : Constant millisecond interval * @param crstrFunction : Constant reference to JSfile@Function */ void clsScriptHelper::createTimer(const QString& crstrID, const uint cuintInterval ,const QString& crstrFunction) { if ( crstrID.trimmed().isEmpty() == true || cuintInterval == 0 || crstrFunction.trimmed().isEmpty() == true || crstrFunction.indexOf(clsXMLnode::msccScriptDelimiter) == -1 ) { //One or more of the parameters are invalid, do nothing return; } //Get Application instance clsMainWnd* pAppInstance(clsMainWnd::pobjGetAppWnd()); if ( pAppInstance == nullptr ) { //Cannot get application instance! return; } //Look for registered timer QTimer* ptmrScript(pAppInstance->ptmrGetTimer(crstrID)); if ( ptmrScript != nullptr ) { //Already registered, do nothing return; } //No timer locatd with that ID, its safe to proceed QStringList slstScript = crstrFunction.split(clsXMLnode::msccScriptDelimiter); mpScripts* pmpScriptsMap; if ( slstScript.length() == SSF_PARAM_COUNT && (pmpScriptsMap = clsMainWnd::pmpGetScriptsMap()) != nullptr ) { //Look for the script in the scripts map mpScripts::iterator itrScript(pmpScriptsMap->find(slstScript[SFF_FILE_IDX])); if ( itrScript != pmpScriptsMap->end() ) { //Found it, build the script up ready for the signal connection QString strScript(itrScript->second); //Get pointer to script engine QJSEngine* pobjScriptEng(clsJSON::pobjGetScriptEng()); //Create new timer ptmrScript = new QTimer(); //Register the new timer using the supplied ID pAppInstance->registerTimer(ptmrScript, crstrID); //Add any globals to the script clsScriptHelper::pobjGetInstance()->addGlobals(pobjScriptEng); //Build up script function call QString strFile(slstScript[SFF_FILE_IDX]) ,strCall(slstScript[SSF_FUNCTION_IDX]) ,strScriptWithCall(strScript) ,strFunc(strCall + QString(clsDebugService::msccBrktOpen) + QString(clsDebugService::msccBrktClose) + QString(clsXMLnode::msccScriptFunctionTerminator)); if ( strScriptWithCall.endsWith(clsXMLnode::msccScriptFunctionTerminator) != true ) { strScriptWithCall += clsXMLnode::msccScriptFunctionTerminator; } strScriptWithCall += strFunc; //Create connection to timer and script QMetaObject::Connection cn = QObject::connect(ptmrScript ,&QTimer::timeout ,[pobjScriptEng ,ptmrScript ,strFile ,strFunc ,strScriptWithCall]() { QJSValue objResult(pobjScriptEng->evaluate(strScriptWithCall)); QString strError; if ( objResult.isError() == true ) { strError = QString("%1\n%2\n%3").arg(strFile).arg(strFunc) .arg(objResult.toString()); } else { QString strResult(objResult.toString()); if ( strResult.compare("undefined") != 0 ) { strError = strResult; } } if ( strError.isEmpty() != true ) { clsXMLnode* pobjRoot(clsXMLnode::spobjGetRoot()); emit pobjRoot->error(strError); ptmrScript->deleteLater(); } }); //Start the timer ptmrScript->start(cuintInterval); } } }
In my JavaScript I call the C++ function:
xmleng.createTimer("t1", 50, "simon2.js@testTimer");
The JavaScript lambda function, it doesn't get called because the slot doesn't get called:
function testTimer() { //All properties area stored as strings, convert to integer var intStageCtr = parseInt(xmleng.getProperty(cstrFormTest, cstrStageCtr)); xmleng.log(0, "testTimer[" + intStageCtr + "]", cstrThis, 316); //Increment stage counter intStageCtr++; //Convert back to string and store xmleng.setProperty(cstrFormTest, cstrStageCtr, String(intStageCtr)); }
-
Put a breakpoint in
clsMainWnd::ptmrGetTimer
and confirm it gets triggered (the breakpoint).Additionally provide its source, and finally add this to the very beginning of the mentioned function:
qDebug() << QThread::currentThread();
-
@kshegunov , I've stepped through this logic, I expect the initial call to ptmrGetTimer to return nullptr as on first call the timer won't be registered which it isn't. I've stepped through the logic to the line:
ptmrScript->start(cuintInterval);
No issues except the slot isn't called when the timer expires. And the mentioned debug output.
-
@SPlatten said in Help with timer and JavaScript slot.:
@kshegunov , I've stepped through this logic, I expect the initial call to ptmrGetTimer to return nullptr as on first call the timer won't be registered which it isn't.
I requested something very specific with a very specific purpose. Please do what I asked and provide the output of the mentioned
qDebug
call. -
@kshegunov Adding this now. I've actually changed the code to add:
QThread* pobjCurrThread(QThread::currentThread()); //Create new timer ptmrScript = new QTimer(); ptmrScript->moveToThread(pobjCurrThread); qdbg() << "pobjCurrThread: " << pobjCurrThread;
Output:
pobjCurrThread: QThread(0x600000269c80)
I have a breakpoint in the lambda function, first line, it doesn't get triggered.
-
@SPlatten said in Help with timer and JavaScript slot.:
I have a breakpoint in the lambda function, first line, it doesn't get triggered.
I don't expect it to get trigerred, but again, this is not what I wrote. Here's a reminder:
Put a breakpoint in clsMainWnd::ptmrGetTimer and confirm it gets triggered (the breakpoint).
Additionally provide its source, and finally add this to the very beginning of the mentioned function:
[snip] -
@kshegunov , here is there requested source to the function:
/** * @brief clsMainWnd::ptmrGetTimer * @param crstrID : Constant reference to ID to look-up timer * @return A pointer to the timer or nullptr if not found */ QTimer* clsMainWnd::ptmrGetTimer(const QString& crstrID) { mpTimers::iterator itTimer(mmpTimers.find(crstrID)); QTimer* pTimer(nullptr); if ( itTimer != mmpTimers.end() ) { pTimer = itTimer->second; } return pTimer; }
I put breakpoint on first line of this, it does what I expected, nullptr is returned because on the first call the timer hasn't been registered.
-
@kshegunov , this is the modified source:
QThread* pobjCurrThread(QThread::currentThread()); //Create new timer qdbg() << "Before Timer creation: " << pobjCurrThread; ptmrScript = new QTimer(pobjCurrThread); //Register the new timer using the supplied ID pAppInstance->registerTimer(ptmrScript, crstrID); //Add any globals to the script clsScriptHelper::pobjGetInstance()->addGlobals(pobjScriptEng); //Build up script function call QString strFile(slstScript[SFF_FILE_IDX]) ,strCall(slstScript[SSF_FUNCTION_IDX]) ,strScriptWithCall(strScript) ,strFunc(strCall + QString(clsDebugService::msccBrktOpen) + QString(clsDebugService::msccBrktClose) + QString(clsXMLnode::msccScriptFunctionTerminator)); if ( strScriptWithCall.endsWith(clsXMLnode::msccScriptFunctionTerminator) != true ) { strScriptWithCall += clsXMLnode::msccScriptFunctionTerminator; } strScriptWithCall += strFunc; //Create connection to timer and script qdbg() << "Before QObject::connect: " << QThread::currentThread();
And the resulting output in debug:
Before Timer creation: QThread(0x60000026c140) S000000000029E000000010940T08:43:44.084W:QObject: Cannot create children for a parent that is in a different thread. (Parent is QThread(0x60000026c140), parent's thread is QThread(0x600000004180), current thread is QThread(0x60000026c140) S000000000030E000000010940T08:43:44.084DL00000661F../clsScriptHelper.cpp[void clsScriptHelper::createTimer] Before QObject::connect: QThread(0x60000026c140)
-
@SPlatten , I've reduced the source to just:
QJSEngine* pobjScriptEng(clsJSON::pobjGetScriptEng()); QThread* pobjCurrThread(QThread::currentThread()); //Create new timer qdbg() << "Before Timer creation: " << pobjCurrThread; ptmrScript = new QTimer(pobjCurrThread); //Create connection to timer and script qdbg() << "Before QObject::connect: " << QThread::currentThread();
And the output is still:
Before Timer creation: QThread(0x600000264a00) S000000000029E000000000768T08:50:01.847W:QObject: Cannot create children for a parent that is in a different thread. (Parent is QThread(0x600000264a00), parent's thread is QThread(0x6000000042c0), current thread is QThread(0x600000264a00) S000000000030E000000000768T08:50:01.847DL00000661F../clsScriptHelper.cpp[void clsScriptHelper::createTimer] Before QObject::connect: QThread(0x600000264a00)
-
@kshegunov , another attempt:
QThread* pobjCurrThread(QThread::currentThread()); //Create new timer qdbg() << "Before Timer creation: " << pobjCurrThread; ptmrScript = new QTimer; ptmrScript->moveToThread(pobjCurrThread); //Create connection to timer and script qdbg() << "Before QObject::connect: " << QThread::currentThread();
Output:
Before Timer creation: QThread(0x600000264d00) S000000000029E000000000717T08:52:29.017DL00000662F../clsScriptHelper.cpp[void clsScriptHelper::createTimer] Before QObject::connect: QThread(0x600000264d00)
Not seeing any errors in the debug, but execution isn't stopping in lambda either.
-
Hold your horses! You have managed to confuse me about what's modified and what goes in which function. In any case in the last piece of code; after this:
//Create new timer qdbg() << "Before Timer creation: " << pobjCurrThread; ptmrScript = new QTimer(pobjCurrThread); //Register the new timer using the supplied ID
adding
Q_ASSERT(clsJSON::pobjGetScriptEng()->currentThread() == QThread::currentThread());
should get tripped.
PS.
This does nothing:ptmrScript = new QTimer; ptmrScript->moveToThread(pobjCurrThread);
-
@kshegunov said in Help with timer and JavaScript slot.:
Q_ASSERT(clsJSON::pobjGetScriptEng()->currentThread() == QThread::currentThread());
I cannot call currentThread() from pobjGetScriptEng(), it returns a pointer to QJSEngine.
-
@SPlatten said in Help with timer and JavaScript slot.:
I cannot call currentThread() from pobjGetScriptEng(), it returns a pointer to QJSEngine.
Correct. I meant to write:
Q_ASSERT(clsJSON::pobjGetScriptEng()->thread() == QThread::currentThread());
-
@kshegunov , realised that after posting, modified output to:
qdbg() << "Script Eng Thread: " << pobjScriptEng->thread(); qdbg() << "Before Timer creation: " << pobjCurrThread;
Result is:
S000000000028E000000000784T09:03:45.243DL00000642F../clsScriptHelper.cpp[void clsScriptHelper::createTimer] Script Eng Thread: QThread(0x60000000c0d0) S000000000029E000000000784T09:03:45.243DL00000643F../clsScriptHelper.cpp[void clsScriptHelper::createTimer] Before Timer creation: QThread(0x60000026f260) S000000000030E000000000784T09:03:45.243W:QObject: Cannot create children for a parent that is in a different thread. (Parent is clsScriptHelper(0x600000208d80), parent's thread is QThread(0x60000000c0d0), current thread is QThread(0x60000026f260) S000000000031E000000000784T09:03:45.244DL00000664F../clsScriptHelper.cpp[void clsScriptHelper::createTimer] Before QObject::connect: QThread(0x60000026f260)
-
@kshegunov , have now changed code to:
ptmrScript = new QTimer(this); ptmrScript->moveToThread(pobjScriptEngThread); ptmrScript->setInterval(cuintInterval);
Output is now different:
setupForm() !!!! S000000000028E000000000716T09:06:20.132W:QObject: Cannot create children for a parent that is in a different thread. (Parent is clsScriptHelper(0x600000202820), parent's thread is QThread(0x600000004150), current thread is QThread(0x60000025e1c0) S000000000029E000000000716T09:06:20.132W:QObject::startTimer: Timers cannot be started from another thread S000000000030E000000000716T09:06:20.132DL00000688F../clsScriptHelper.cpp[void clsScriptHelper::createTimer] Is timer active: true S000000000031E000000000716T09:06:20.132DL00000207Fsimon2.js[void clsScriptHelper::log] setupForm() !!!!
I will add another API function to allow the script to start the timer.
-
@SPlatten said in Help with timer and JavaScript slot.:
Cannot create children for a parent that is in a different thread.
You still have this. Excuse my complete guesswork, but do you need to go
new QTimer();
rather thannew QTimer(this);
to move to another thread? (I have never been sure which statement causes the error message, which you can discover by stepping over or putting inqDebug()
markers.)But if all this is beyond me and being solved by @kshegunov please ignore me --- I'm just watching the discussion :)
-
Right, so now comes the million bucks question(s):
Why is
clsScriptHelper::createTimer
called from thread different from the onepobjScriptEng
has affinity for?
Why do you need to use threads to begin with?As for the "solution":
QMetaObject::Connection cn = QObject::connect(ptmrScript ,&QTimer::timeout
Shall provide a context object for the execution. Looking at the body, I'd say it should be something like:
QMetaObject::Connection cn = QObject::connect(ptmrScript , &QTimer::timeout, pobjScriptEng ,[pobjScriptEng ,ptmrScript ,strFile ,strFunc ,strScriptWithCall]() {
Also the timer should be created with the correct affinity, I assume:
ptmrScript = new QTimer(pobjScriptEng);
The original questions stand notwithstanding
PS: You have race conditions all over the place ...
-
@kshegunov , createTimer is called by JavaScript:
function setupForm() { //Query all people xmleng.log(0, "setupForm() !!!!", cstrThis, 202); //Create request counter property xmleng.setProperty(cstrFormTest, cstrStageCtr, "0"); //Create timer to issue SQL requests xmleng.createTimer("t1", 50, "simon2.js@testTimer"); xmleng.log(0, "setupForm() !!!!", cstrThis, 207); xmleng.startTimer("t1"); };
-
@kshegunov , I've modified my source to:
QMetaObject::Connection cn = QObject::connect(ptmrScript ,&QTimer::timeout ,pobjScriptEng ,[pobjScriptEng ,ptmrScript ,strFile ,strFunc ,strScriptWithCall]() {
still getting:
S000000000027E000000000747T09:17:30.915DL00000202Fsimon2.js[void clsScriptHelper::log] setupForm() !!!! S000000000028E000000000747T09:17:30.915DL00000207Fsimon2.js[void clsScriptHelper::log] setupForm() !!!! S000000000029E000000000747T09:17:30.915W:QObject::startTimer: Timers cannot be started from another thread