PDA

View Full Version : Is there any way to abort QScriptValue::call() ?



lukass
29th February 2012, 13:30
Hello,
QScriptValue::call() can block thread. Is there any way to abort QScriptValue::call()? I tried to use QScriptEngine::abortEvaluation(), but this not working. Can anyone help?

wysota
29th February 2012, 13:39
Are you calling a javascript function or a native one?

lukass
29th February 2012, 13:56
Are you calling a javascript function or a native one?
I'm calling JS function:

function init()
{
for(var i = 0; i < 9000000000; ++i)
for(var j = 0; j < 9000000000; ++j)
var x = 10;
}

wysota
29th February 2012, 14:28
The script engine periodically calls processEvents(), so if you want to abort a call, you would have to do that using an event or as a result of a timer signal being emitted.

lukass
29th February 2012, 15:19
The script engine periodically calls processEvents(), so if you want to abort a call, you would have to do that using an event or as a result of a timer signal being emitted.
Yes, I know. I do this, but not working!

In C++ I initialize:

engine.setProcessEventsInterval(100);
wait_dialog = new QProgressDialog(tr("Please wait..."), tr("Cancel"), 0, 0);
wait_dialog->setValue(-1);
connect(wait_dialog, SIGNAL(canceled()), SLOT(wait_dialog_canceled()));
engine is object of QScriptEngine.

Now I run script:

wait_dialog->show();
QScriptValue init_funct = engine.evaluate("init");
init_funct.call(this_object);
When I click 'Cancel' in wait_dialog then:

void sth_engine::wait_dialog_canceled()
{
engine.abortEvaluation();
}
but 'init_funct.call(this_object);' is still running and blocking thread periodically.

wysota
29th February 2012, 15:23
Yes, I know. I do this,
I didn't say anything about clicking any buttons.

lukass
29th February 2012, 15:38
I didn't say anything about clicking any buttons.
Ok, here is example with timer.

Initialization:

engine.setProcessEventsInterval(100);
this_object = engine.newQObject(this);
timer = new QTimer(this);
timer->setSingleShot(true);
connect(timer, SIGNAL(timeout()), SLOT(timeout()));

I run script:

QScriptValue init_funct = engine.evaluate("init");
timer->start(10000);
init_funct.call(this_object);

If timer timeout then:

void sth::timeout()
{
engine.abortEvaluation();
}
but after 10 seconds 'init_funct.call(this_object);' is still running and blocking thread periodically.

wysota
29th February 2012, 16:33
Apparently you are doing something wrong. This works just fine:


#include <QScriptEngine>
#include <QtCore>

class Object : public QObject {
Q_OBJECT
public:
Object() {
connect(&timer, SIGNAL(timeout()), this, SLOT(abort()));
engine.setProcessEventsInterval(1000);
}
void startEval() {
timer.start(1000);
engine.evaluate("while(true);");
}
private slots:
void abort() {
static int cnt = 0;
bool ev = engine.isEvaluating();

qDebug() << "isEvaluating?" << ev;
if(!ev)
timer.stop();
++cnt;
if(cnt==5) {
qDebug() << "Aborting evaluation";
engine.abortEvaluation();
}
}
private:
QScriptEngine engine;
QTimer timer;
};

#include "main.moc"

int main(int argc, char **argv){
QCoreApplication app(argc, argv);
Object o;
o.startEval();

// return app.exec();
return 0;
}

lukass
29th February 2012, 16:56
Apparently you are doing something wrong. This works just fine:
The topic is: Is there any way to abort QScriptValue::call() ? and you give me example code with QSciptEngine::evaluate(). :D

Ok, here my full example:

#include <QScriptEngine>
#include <QtCore>

class Object : public QObject {
Q_OBJECT
public:
Object() {
connect(&timer, SIGNAL(timeout()), this, SLOT(abort()));
engine.setProcessEventsInterval(1000);
this_object = engine.newQObject(this);
}
void startEval() {
engine.evaluate(
"function init()"
"{"
" for(var i = 0; i < 9000000000; ++i)"
" for(var j = 0; j < 9000000000; ++j)"
" var x = 10;"
"}");

QScriptValue init_funct = engine.evaluate("init");
timer.start(1000);
init_funct.call(this_object);
qDebug() << "end of startEval()";
}
private slots:
void abort() {
static int cnt = 0;
bool ev = engine.isEvaluating();

qDebug() << "isEvaluating?" << ev;
if(!ev)
timer.stop();
++cnt;
if(cnt==5) {
qDebug() << "Aborting evaluation";
engine.abortEvaluation();
}
}
private:
QScriptEngine engine;
QTimer timer;
QScriptValue this_object;
};

#include "main.moc"

int main(int argc, char **argv){
QCoreApplication app(argc, argv);
Object o;
o.startEval();

// return app.exec();
return 0;
}
results: "isEvaluating? false" and 'init_funct.call(this_object);' is still running and blocking thread periodically.

wysota
29th February 2012, 17:31
class Object : public QObject {
Q_OBJECT
public:
Object() {
connect(&timer, SIGNAL(timeout()), this, SLOT(abort()));
engine.setProcessEventsInterval(1000);
fun = engine.evaluate("(function() { while(true); })");
}
void startEval() {
timer.start(1000);
qDebug() << "is callable?" << fun.isFunction();
QScriptContext *context = engine.pushContext();
QScriptValue v = context->activationObject();
v.setProperty("fun", fun);
engine.evaluate("fun()");
engine.popContext();

}
private slots:
void abort() {
bool ev = engine.isEvaluating();
qDebug() << "isEvaluating?" << ev;
qDebug() << "Aborting evaluation";
engine.abortEvaluation();
}
private:
// ...
QScriptValue fun;
//...
};

This calls the function and aborts it after one second. QScriptValue::call() itself doesn't seem abortable as it is executed using different JavaScriptCore method than evaluate(). If you want more flexibility you can probably make your function the activation object itself, set needed arguments and trigger the context (somehow).

lukass
29th February 2012, 18:51
This calls the function and aborts it after one second. QScriptValue::call() itself doesn't seem abortable as it is executed using different JavaScriptCore method than evaluate(). If you want more flexibility you can probably make your function the activation object itself, set needed arguments and trigger the context (somehow).
Thanks, this works!

However I not fully understand what for is these 4 lines:

QScriptContext *context = engine.pushContext();
QScriptValue v = context->activationObject();
v.setProperty("fun", fun);

engine.popContext();
engine.evaluate("fun()"); is enough for me.

wysota
29th February 2012, 20:13
However I not fully understand what for is these 4 lines:

QScriptContext *context = engine.pushContext();
QScriptValue v = context->activationObject();
v.setProperty("fun", fun);

engine.popContext();
Here I register "fun" to be a local property so that when I pop the context, the property is gone. this is to avoid having to register a function I want to call in the global context (as you do). This helps keep the engine clean.


engine.evaluate("fun()"); is enough for me.
Yes, because you are calling a function that is a property of the global object. Notice the difference between your code creating the function and mine. In my code the function is anonymous.