PDA

View Full Version : 64-bit Qt for windows: QtScript crashes.



eyeofhell
19th January 2010, 08:55
Hello.

I have compiled Qt for 64-bit architecture under windows and all works fine except QtScript. The following simple code, working perfectly with 32-bit Qt for windows installed from qtsoftware.com, crashes if compiled with 64-bit Qt :(. Maybe it's an error in my code? Or Qt is not compatible with 64-bit? Or it is a bug and i need to submit it somehow?

Qt console project.
main.h



#pragma once

#include <QObject>
#include <QScriptEngine>

class Script : public QObject
{
Q_OBJECT;
public slots: void Test();
private: QScriptEngine m_oEngine;
};

class App : public QObject
{
Q_OBJECT;
signals: void ScriptTest();
protected: void timerEvent( QTimerEvent* );
};


main.cpp



#include <QtCore/QCoreApplication>
#include <QThread>
#include "main.h"

void Script::Test()
{
QString sScript = "function OnStart() { var a = [ [ 0 ] ]; "
"for( var i = 0; i < 3; i ++ ) { for( var j = 0; j < 100; j ++ ) "
"{ \"a\" + a[ 0 ][ 0 ]; } } }";
m_oEngine.evaluate( sScript );
QScriptValue oFnStart = m_oEngine.evaluate( "OnStart" );
oFnStart.call();
}

void App::timerEvent( QTimerEvent* ) { ScriptTest(); }

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QThread scriptThread;
scriptThread.start();
Script script;
script.moveToThread( & scriptThread );
App app;
script.connect( & app, SIGNAL( ScriptTest() ), SLOT( Test() ) );
app.startTimer( 50 );
return a.exec();
}

wysota
19th January 2010, 09:51
Where does it crash exactly?

eyeofhell
19th January 2010, 09:57
Inside QtScript internals. Call stack:

0 QtScriptd4!QTJSC::JSValue::get jsobject.h 641 0x1800b313a
1 QtScriptd4!QTJSC::JSValue::get jsobject.h 628 0x1800b304d
2 QtScriptd4!QTJSC::Interpreter::privateExecute interpreter.cpp 2539 0x1800a6c7e
3 QtScriptd4!QTJSC::Interpreter::execute interpreter.cpp 753 0x18009fa53
4 QtScriptd4!QTJSC::JSFunction::call jsfunction.cpp 120 0x1801018ad
5 QtScriptd4!QTJSC::call calldata.cpp 62 0x1800c7d06
6 QtScriptd4!QScriptValue::call qscriptvalue.cpp 1880 0x1801b9636
7 qt_script_test!Script::Test main.cpp 12 0x14000126d
8 qt_script_test!Script::qt_metacall moc_main.cpp 75 0x1400019d3
9 QtCored4!QMetaObject::metacall qmetaobject.cpp 238 0x672219c1
10 QtCored4!QMetaCallEvent::placeMetaCall qobject.cpp 575 0x6723643c
11 QtCored4!QObject::event qobject.cpp 1252 0x672378ad
12 QtCored4!QCoreApplicationPrivate::notify_helper qcoreapplication.cpp 840 0x67216cc6
13 QtCored4!QCoreApplication::notify qcoreapplication.cpp 785 0x67216a49
14 QtCored4!QCoreApplication::notifyInternal qcoreapplication.cpp 704 0x6721693a
15 QtCored4!QCoreApplication::sendEvent qcoreapplication.h 215 0x6721cd20
16 QtCored4!QCoreApplicationPrivate::sendPostedEvents qcoreapplication.cpp 1345 0x67217d7e
17 QtCored4!qt_internal_proc qeventdispatcher_win.cpp 509 0x67264eda
18 USER32!GetSystemMetrics USER32 0 0x771bc3c1
19 USER32!GetSystemMetrics USER32 0 0x771bc60a
20 QtCored4!QEventDispatcherWin32::processEvents qeventdispatcher_win.cpp 753 0x6726603f
21 QtCored4!QEventLoop::processEvents qeventloop.cpp 150 0x67213d6a
22 QtCored4!QEventLoop::exec qeventloop.cpp 201 0x67213ef1
23 QtCored4!QThread::exec qthread.cpp 487 0x6706e2c5
24 QtCored4!QThread::run qthread.cpp 547 0x6706e403
25 QtCored4!QThreadPrivate::start qthread_win.cpp 315 0x67072d87
26 MSVCR90D!beginthreadex MSVCR90D 0 0x73754ee5
27 MSVCR90D!beginthreadex MSVCR90D 0 0x73754ead
28 kernel32!BaseThreadInitThunk kernel32 0 0x772bf56d
29 ntdll!RtlUserThreadStart ntdll 0 0x773f3281

I have submitted a bug (http://bugreports.qt.nokia.com/browse/QTBUG-7433) but i'm still unsure is it a Qt bug or i'm doing something wrong -_-.

numbat
19th January 2010, 10:04
QScriptEngine lives in the main thread where it was created and is called by the second thread where script lives. QScriptEngine is not thread-safe so this is bad.
moveToThread also moves child objects so you could make the script engine a child of the Script object and they would hapily live in the same thread. Something like:


class Script : public QObject
{
Q_OBJECT;
public: Script() : m_oEngine(this) {}
public slots: void Test();
private: QScriptEngine m_oEngine;
};

eyeofhell
19th January 2010, 10:31
I have already checked this. Removing moveToThread() from code changes nothing - same crash in same place :(

wysota
19th January 2010, 11:15
Just to be certain, modify your code this way:


class MyThread : public QThread {
Q_OBJECT
public:
MyThread() : QThread() { m_interpreter = 0; }
void run() {
m_interpreter = new QScriptEngine(this);
exec();
delete m_interpreter;
m_interpreter = 0;
}
public slots:
void test() {
if(!m_interpreter) return;
m_interpreter->evaluate(...);
}
private:
QScriptEngine *m_interpreter;
};
Then connect appropriate signals, etc.

eyeofhell
19th January 2010, 11:47
I have modified the code following way:

main.h




#pragma once

#include <QObject>
#include <QScriptEngine>
#include <QThread>

class MyThread : public QThread {
Q_OBJECT
public:
MyThread() : QThread() { m_interpreter = 0; }
void run();
public slots:
void test();
private:
QScriptEngine *m_interpreter;
};

class App : public QObject
{
Q_OBJECT;

signals: void ScriptTest();
protected: void timerEvent( QTimerEvent* );
};


main.cpp



#include <QtCore/QCoreApplication>
#include <QThread>
#include "main.h"

void MyThread::run()
{
m_interpreter = new QScriptEngine(this);
exec();
delete m_interpreter;
m_interpreter = 0;
}

void MyThread::test()
{
if(!m_interpreter) return;
QString sScript = "function OnStart() { var a = [ [ 0 ] ]; "
"for( var i = 0; i < 3; i ++ ) { for( var j = 0; j < 100; j ++ ) "
"{ \"a\" + a[ 0 ][ 0 ]; } } }";
m_interpreter->evaluate( sScript );
}

void App::timerEvent( QTimerEvent* ) { ScriptTest(); }

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

MyThread oThread;
oThread.start();

App app;
QObject::connect( & app, SIGNAL( ScriptTest() ), & oThread, SLOT( test() ) );
app.startTimer( 50 );

return a.exec();
}


It shows following debug information at runtime:

QObject: Cannot create children for a parent that is in a different thread.
(Parent is MyThread(0x12fe60), parent's thread is QThread(0x1b98960), current th
read is MyThread(0x12fe60)

At string:



m_interpreter = new QScriptEngine(this);


It seems i have modified something wrong?

wysota
19th January 2010, 12:10
You forgot to move the thread to its own thread.

eyeofhell
19th January 2010, 12:37
You forgot to move the thread to its own thread.


OMG. I was sure that QThread::run() will know what thread it is O_O. This is very strange design :).

Anyway, i have started code and... it did not crash O_O. The only thing i can suggest is that QScriptEngine somehow remembers a thread it was CREATED in and ignores moveToThread() after it is created. I will perform more deep tests to ensure that all is correct. Lots of thanks to you. It's really good to have a guru status in some technology - you can use magic :).

eyeofhell
19th January 2010, 12:49
Ah, no :(
It seems your test code missed


QScriptValue oFnStart = m_oEngine.evaluate( "OnStart" );
oFnStart.call();


After this is added, code start crashing again in same place :(.

faldzip
19th January 2010, 13:30
OMG. I was sure that QThread::run() will know what thread it is O_O. This is very strange design :).

Anyway, i have started code and... it did not crash O_O. The only thing i can suggest is that QScriptEngine somehow remembers a thread it was CREATED in and ignores moveToThread() after it is created. I will perform more deep tests to ensure that all is correct. Lots of thanks to you. It's really good to have a guru status in some technology - you can use magic :).

OMG. I don't know how many times it has to be repeated... Only the body of run() method is in newly created thread, so everything created outside of this method is still in old thread...:


MyTread mt;
mt.start(); // <--- this creates new thread!
// mt object is still in old thread
// so everything cerated in its constructor also

And this is nothing new. Did you ever looked at Qt Examples & Demos and their documentation (like threaded fortune server?)

eyeofhell
19th January 2010, 13:37
OMG. I don't know how many times it has to be repeated... Only the body of run() method is in newly created thread, so everything created outside of this method is still in old thread...:

As you can see in the code, engine is created exactly in run() method. I was a little dissapointed that QThread object itself is counted to be in the different thread than it's own run() method :). It's a QThread after all.

Anyway, the QtScriptEngine still crashes by reasons unknown - and that's the main problem. And it crashes regardless of what thread it is in :(.

wysota
19th January 2010, 13:39
There is no "oFnStart" object in our test program. Could you add it there and post the code again? Please include the code of the evaluated script and the result of QScriptValue::isFunction().

By the way, to correct what has been said earlier:


class Object : public QObject {
public:
// ...
private:
QScriptEngine engine;
};
Running
Object *o = new Object;
o->moveToThread(...);
will not move the "engine" object to the target thread. "engine" is a member and not a child of the "o" object.

wysota
19th January 2010, 13:43
As you can see in the code, engine is created exactly in run() method. I was a little dissapointed that QThread object itself is counted to be in the different thread than it's own run() method :). It's a QThread after all.

Read again what faldżip has written. He means exactly that - how can an object (QThread instance) created before the thread is spawned be handled by that thread? What do you think the following should do:

class Thread : public QThread {
Q_OBJECT
public:
Thread() ...
public slots:
void callMe(){ ... }
};

//...

Thread thread;
QMetaObject::invokeMethod(&thread, "callMe");
In context of which thread would it execute? Note there is no call to QThread::start() anywhere in the code.

And then where would this execute?

Thread thread;
thread.start();
thread.wait(); // hangs until thread is finished
QMetaObject::invokeMethod(&thread, "callMe");

QThread doesn't represent the thread... it is a controller of the thread.

eyeofhell
19th January 2010, 14:21
There is no "oFnStart" object in our test program. Could you add it there and post the code again?

Complete code of latest test, based on your proposals. main.cpp:



#include <QtCore/QCoreApplication>
#include <QThread>
#include "main.h"

void MyThread::run()
{
m_interpreter = new QScriptEngine(this);
exec();
delete m_interpreter;
m_interpreter = 0;
}

void MyThread::test()
{
if(!m_interpreter) return;
QString sScript = "function OnStart() { var a = [ [ 0 ] ]; "
"for( var i = 0; i < 3; i ++ ) { for( var j = 0; j < 100; j ++ ) "
"{ \"a\" + a[ 0 ][ 0 ]; } } }";
m_interpreter->evaluate( sScript );
QScriptValue oFnStart = m_interpreter->evaluate( "OnStart" );
Q_ASSERT( oFnStart.isFunction() );
oFnStart.call();
}

void App::timerEvent( QTimerEvent* ) { ScriptTest(); }

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

MyThread oThread;
oThread.moveToThread( & oThread );
oThread.start();

App app;
QObject::connect( & app, SIGNAL( ScriptTest() ), & oThread, SLOT( test() ) );
app.startTimer( 50 );

return a.exec();
}


main.h



#pragma once

#include <QObject>
#include <QScriptEngine>
#include <QThread>

class MyThread : public QThread {
Q_OBJECT
public:
MyThread() : QThread() { m_interpreter = 0; }
void run();
public slots:
void test();
private:
QScriptEngine *m_interpreter;
};

class App : public QObject
{
Q_OBJECT;

signals: void ScriptTest();
protected: void timerEvent( QTimerEvent* );
};



Please include the code of the evaluated script

Do you means the text inside evaluate call()? It's in code:



QString sScript = "function OnStart() { var a = [ [ 0 ] ]; "
"for( var i = 0; i < 3; i ++ ) { for( var j = 0; j < 100; j ++ ) "
"{ \"a\" + a[ 0 ][ 0 ]; } } }";
m_interpreter->evaluate( sScript );


and the result of QScriptValue::isFunction().

It's always true, assert never trigger:


Q_ASSERT( oFnStart.isFunction() );

wysota
19th January 2010, 15:58
If it crashes on call() then you have to first verify your function is correct. I suggest adding some debug statements inside. If evaluate() returns then it seems the engine itself works correctly.

eyeofhell
19th January 2010, 16:38
If it crashes on call() then you have to first verify your function is correct

Function is included in code. It's extremly simple. And i was sure that QtScript interpreter is stable and will not crash on wrong script code. Is it not stable?

wysota
19th January 2010, 17:25
Don't ask me, verify it. There is probably some race condition there that is causing the crash. It is important to know where exactly it crashes.

eyeofhell
19th January 2010, 19:12
I have successfully shrinked down the code to following minimum. No threads at all, no race conditions. Still crashes at call():


#include <QtCore/QCoreApplication>
#include <QScriptEngine>

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

QScriptEngine oEngine;
QString sScript = "function OnStart() { var a = [ [ 0 ] ]; "
"for( var i = 0; i < 3; i ++ ) { for( var j = 0; j < 100; j ++ ) "
"{ \"a\" + a[ 0 ][ 0 ]; } } }";
oEngine.evaluate( sScript );
for(;;)
{
QScriptValue oFnStart = oEngine.evaluate( "OnStart" );
Q_ASSERT( oFnStart.isFunction() );
oFnStart.call();
}
}

wysota
19th January 2010, 20:29
Why don't you just do what I said and debug the ecma script code?

eyeofhell
19th January 2010, 21:50
It's many megabytes of ECMAScript parser :(. Very hard to find out what causes access violation. Really lots of code O_O.

wysota
19th January 2010, 22:59
I don't want you to debug the parser, just your script.

eyeofhell
20th January 2010, 06:44
Already done, this is a stripped down version of 30k+ script that was originally crashing. If i remove or change ANY line from this extremly simple script, crash WILL NOT reproduce O_O. Crash is reproduces randomly inside for(;; ) loop - if step-by-step script code crash will not reproduce.


function OnStart()
{
var a = [ [ 0 ] ];
for( var i = 0; i < 3; i ++ )
{
for( var j = 0; j < 100; j ++ )
{
"a" + a[ 0 ][ 0 ];
}
}
}

wysota
20th January 2010, 08:17
Of course a step by step crash won't reproduce if the problem is a race condition. Please insert some debugging statements in the script to see where it crashes. Also does it change anything if you just evaluate the contents of the function instead of calling the function?

eyeofhell
20th January 2010, 10:54
Of course a step by step crash won't reproduce if the problem is a race condition

Latest crash sample is a single threaded code. How race condition can occur in one thread?


Please insert some debugging statements in the script to see where it crashes

Not needed, it's clear by looking at crash code that it crashes at bytecode interpreter while executing bytecode equivalent of

a[ 0 ][ 0 ]


Also does it change anything if you just evaluate the contents of the function instead of calling the function?

No crash if function is not called.

wysota
20th January 2010, 11:51
Not needed, it's clear by looking at crash code that it crashes at bytecode interpreter while executing bytecode equivalent of

a[ 0 ][ 0 ]
Is the "a" array really two dimentional (check, don't assume)?


No crash if function is not called.

So this works?

engine.evaluate(" var a = [ [ 0 ] ]; \
for( var i = 0; i < 3; i ++ ) \
{ \
for( var j = 0; j < 100; j ++ ) \
{ \
\"a\" + a[ 0 ][ 0 ]; \
} \
}");

eyeofhell
20th January 2010, 12:53
Is the "a" array really two dimentional (check, don't assume)?

In my script it is:


var a = [ [ 0 ] ];

I don't have expert ECMAScript knowledge, but such definition looks surely to be two-dimensional.


So this works? <evaluate skipped>

Yep. And code after it crash:


for(;;)
{
QScriptValue oFnStart = oEngine.evaluate( "OnStart" );
Q_ASSERT( oFnStart.isFunction() );
oFnStart.call(); // Crash here in deep bytecode interpreter internals.
}

wysota
20th January 2010, 13:01
In my script it is:


var a = [ [ 0 ] ];

I don't have expert ECMAScript knowledge, but such definition looks surely to be two-dimensional.

See my last post again.




Yep. And code after it crash:


for(;;)
{
QScriptValue oFnStart = oEngine.evaluate( "OnStart" );
Q_ASSERT( oFnStart.isFunction() );
oFnStart.call(); // Crash here in deep bytecode interpreter internals.
}

See my last post again.

Read it carefully this time.

eyeofhell
20th January 2010, 13:32
Is the "a" array really two dimentional (check, don't assume)?

Yes, "a" array is 2-dimentional. Calling evaluate( "a.length" ) returns 1 and calling evaluate( "a[ 0 ].length" ) also returns 1 - it's 1x1 2-dimentional array.


So this works?

engine.evaluate(" var a = [ [ 0 ] ]; \
for( var i = 0; i < 3; i ++ ) \
{ \
for( var j = 0; j < 100; j ++ ) \
{ \
\"a\" + a[ 0 ][ 0 ]; \
} \
}");


Yes, this works.

wysota
21st January 2010, 02:09
So the actual problem is with
oFnStart.call(); and not the script itself. Please check if the example in Qt docs for QScriptValue::call() works for you.

eyeofhell
21st January 2010, 12:56
Original Qt call() example did not crash:



QScriptEngine engine;
engine.evaluate("function fullName() { return this.firstName + ' ' + this.lastName; }");
engine.evaluate("somePerson = { firstName: 'John', lastName: 'Doe' }");

QScriptValue global = engine.globalObject();
QScriptValue fullName = global.property("fullName");
QScriptValue who = global.property("somePerson");
qDebug() << fullName.call(who).toString(); // "John Doe"

engine.evaluate("function cube(x) { return x * x * x; }");
QScriptValue cube = global.property("cube");
QScriptValueList args;
args << 3;
qDebug() << cube.call(QScriptValue(), args).toNumber(); // 27


After adding my script and loop to Qt call() example it crashes:



QScriptEngine engine;
engine.evaluate( "function fullName(){var a=[[0]]; a[0][0];}" );
QScriptValue global = engine.globalObject();
QScriptValue fullName = global.property("fullName");
for(;;)
{
fullName.call();
}

wysota
21st January 2010, 16:36
What if you add the loop to the example program? And what if you remove the loop from your program?

eyeofhell
21st January 2010, 21:59
If i add a loop to example program it will not crash - Qt script must be exactly what i have specified.
If i remove loop from my program it will not crash.

This is some kind of weird combination of exactly specific script and a loop O_O. The script is a reduced version of my original 30+ kb script, so i think it will crash on any script large and complex enough, with a loop :(. On 64-bit windows only.

wysota
22nd January 2010, 01:37
i think it will crash on any script large and complex enough, with a loop :(. On 64-bit windows only.

I don't think so. In my opinion the problem is with QScriptValue::call() and not the script itself. Add a loop around the code I have written in post #26 of this thread. If it doesn't crash then complexity of the script is irrelevant (I'd be surprised if it was relevant).

Come to think of it, what exactly do you hope to obtain with your code? What is the point of using QScriptValue::call() in this situation? Why not simply do:

oEngine.evaluate( "OnStart()" );

I'm sure you can add code to check if OnStart() is callable at all and even if not, you will get an exception from the script where you can easily determine what happened.

By the way, what edition of Qt are you using? LGPL or commercial? They have different QtScript backends by default. The LGPL one has JIT compilation, maybe that is causing the problem.

eyeofhell
22nd January 2010, 06:48
I don't think so. In my opinion the problem is with QScriptValue::call() and not the script itself.

Yes, i mean script complex enough, 64-bit, loop and call inside a loop :).


Why not simply do: oEngine.evaluate( "OnStart()" );

It will crash :(. Crash is inside QtScript code execution. I have carefully tested all ways to invoke a script function: evaluate( "foo" ).call(), globalObject().getProperty( "foo" ).call(), evaluate( "foo()" ) and connecting signals to slots inside of script. Anything will crash on QtScript execution if script is from my example, it's execution is inside a loop and windows is 64-bit :(.


By the way, what edition of Qt are you using? LGPL or commercial? They have different QtScript backends by default. The LGPL one has JIT compilation, maybe that is causing the problem.

I'm using LGPL version. Is it possible to disable JIT compilation in LGPL version?

wysota
22nd January 2010, 11:07
I'm using LGPL version. Is it possible to disable JIT compilation in LGPL version?

You can rebuild QtScript with the other backend and see if it helps.

eyeofhell
22nd January 2010, 14:11
You can rebuild QtScript with the other backend and see if it helps.

Thanks. What i need to do in order to rebuild QtScript with different back-end? Some defines, nmake targets or what?

wysota
22nd January 2010, 14:26
To disable JIT, issue:

configure -no-javascript-jit

and rebuild Qt.

I'm not sure how to change the backend, you have to search for it in the docs, I'm sure I have seen it somewhere (maybe it was somewhere in the Internet? At Qt Labs maybe?). Probably issuing -no-webkit would do the trick but that's a bit of a drastic method of doing this.

eyeofhell
22nd January 2010, 16:18
To disable JIT, issue:
configure -no-javascript-jit
and rebuild Qt.

Windows version of 'configure' don't support '-no-javascript-jit'. The only 'configure' key on windows i can see that is related to QtScript subsystem are '-script' and '-noscript'.