How do I kill a currently running xojoScript? I’ve tried .reset, which appears to actually create another instance of the script which is already running… then restarts that from the beginning, leaving both running. I want a way out if a user (who can edit their own scripts) puts one in an infinite loop, and causes my server to hang.
You could artificially insert a context method calls which would allow you to stop the script.
You’ll need to insert before: Next, Continue, Loop and Wend.
Your context function could be something like:
Return StopFlag
And then the function you insert would be something like:
If CheckStopFlag() Then Return
Bah, why didn’t I think of that!? I tried something similar, but wrapped the entire script in the check… such as:
if killScript = false then
[script here]
end if
hoping that when the timeout timer reset the script, it would bypass everything and end it. What I found was that it did end that script, but the initial script was still running. Your solution should work… appreciate it, Greg.
Out of curiosity, are your scripts running in a thread?
I created a generic class object, and added a property named ‘scriptCore’ as a data type ‘xojoScript’. There is also a property added named ‘timeOut’ as a data type ‘thread’. Another property named ‘killScript’ which is boolean. Constant ‘TIME_OUT’ added and set to ‘5120’. Added a method to that class labeled ‘runScript’ which contains:
[code] scriptCore = new xojoScript
timeOut = new thread
killScript = false
addHandler timeOut.run, addressOf invokeTimeOut
addHandler scriptCore.print, addressOf scriptPrint
scriptCore.source = prepareSource(scriptSource)
scriptCore.context = me
if scriptCore.state = xojoScript.states.ready then
timeOut.run
scriptCore.run
end if[/code]
The method ‘prepareSource’ in the class contains:
[code] dim sourceBuffer as string
dim killCode as string
killCode = “if thread(-1) then return”
sourceBuffer = replaceAll(lowercase(scriptSource), “loop”, killCode + endOfLine + “loop”)
sourceBuffer = replaceAll(sourceBuffer, “next”, killCode + endOfLine + “next”)
sourceBuffer = replaceAll(sourceBuffer, “wend”, killCode + endOfLine + “wend”)
sourceBuffer = replaceAll(sourceBuffer, “continue”, killCode + endOfLine + “continue”)
return sourceBuffer[/code]
And the ‘invokeTimeout’ contains:
[code] targetObject.sleep(TIME_OUT)
if scriptCore.state = xojoScript.states.running then
killScript = true
end if
targetObject.kill[/code]
Method ‘thread’ is:
[code] app.doEvents(milliSeconds)
return killScript[/code]
So, not sure if this is ‘threaded’. Doubt it. How would I run a script specifically within a thread? And if so, I’m assuming killing that thread would kill the running script as well?
To run a script in a thread, simply make a normal RB thread, and in its Run event, run the Script.
Then you can kill the thread and the script along with it.
Alright… added a ‘thread’ control, a ‘xojoScript’ control, and a method to ‘Window1’ that just says “app.doevents()” and called it ‘blah’.
In the ‘thread’ run event, I have:
[code]xojoscript1.source = textarea1.text
xojoscript1.context = window1
xojoscript1.run
me.sleep(5000)
me.kill[/code]
For the ‘xojoScript’ source I have:
[code]dim a as integer
for a = 1 to 3000
blah
next a
print str(a)[/code]
In the ‘xojoScript.print’, I have:
msgbox msg
Change the ‘for a = 1 to 200’ and the message box pops up after about 2 seconds (fine). Change the ‘for a = 1 to 3000’ and the message box pops up after about 12 seconds (not fine, as after 5000ms the thread ended and killed itself). So unless I’m doing something wrong, it isn’t killing as you stated it would.
Eric,
You seem to be confused about the use of threads.
If you run something in one thread, then you need to control it (e.g. stop it) from a separate thread!
What you currently do, however, is that you start the script in your thread, so the script runs until it finishes its for loop, then calls print. At this point, MsgBox gets called. Once you OK the msgbox, the script ends, and returns from the xojoscript1.run call. Then your me.sleep(5000) and me.kill statements are executed.
That’s now what you wanted, though.
So, do this: In the Thread’s Run event, only run the script and nothing else.
In your main thread, i.e. the one where you start the thread, you can then do the sleep and the kill. That way, your main thread starts the thead which in turn starts the script, and while the script is running, your main thread runs as well, doing the sleep and eventually the kill.
Note, however, that you probably cannot kill the thead while it’s showing a Msgbox - that might crash your app, even.
You can solve this, though: In yout script’s Print event handler, do not invoke MsgBox, but make your own dialog window, which then shows the message in a textfield. Show it from the Print event with TheDialogWindow.ShowModal, and have it close itself when its OK button is pressed. Also, when you start showing this dialog, set a boolean variable to true, name it “DialogIsShowing” (you can make it global or part of your main window, or even of your script subclass), and set DialogIsShowing= false in the dialog’s Close event.
That way, other code can tell that the dialog is showing by checking DialogIsShowing.
Now, when you decide to stop your script, do this:
Check DialogIsShowing first. If it’s true, close the dialog first.
Only then kill the thread.
Got it?
Awesome, Thomas… appreciate the help! You’re right, I was (still am, but not as much) confused and unknowledged with threads. Trial and error, and I’m learning… with help from you guys. Appreciate it. Will try as you suggested.
I tried, but that does not appear to work. While the script is running in a thread, the thread will not yield execution to the main thread untill the script run ends. So you cannot kill or sleep a thread that is running a script by a command in another thread.
Of course, Gerard. Xojo’s Threads are not pre-emptive, so as long as a thread runs and doesn’t give time to other threads, others cannot do something such as kill another thread. The discussion here was involving the script invoking code in your app, e.g. via Print or, alternatively, by invoking other functions your code provides to the script via the XojoScript’s Context.
One could also argue that Xojoscripts should automatically yield whenever the script re-enters a loop, but since your code has control over the script it feeds to XojoScript, it could also insert such callouts itself.
On a related note: can anybody explain what XojoScript.Reset is supposed to do when a script is running?
The state changes from running to ready, but as far as I can tell it continues executing anyway…
You can just not use reset
It wont stop the current script from executing etc
What is it you’re needing ?
Ending a script.
I thought I needed reset to be able to clear the context (as I obviously can’t do that when it runs), but I just found out I can do it when the scripts’ state is aborted after calling thread.kill.
Just to be sure: there’s no other way to abort a script then to kill a thread that hosts the script object right?
or put a property on the context that your script can read to see if it should quit
then you can set this from other code (outside the script)
But the script can only quit itself by doing a return right? So that would be the solution @Greg O’Lone was talking about?
Yes.
Thanks guys!