I’m running user-supplied JavaScript and I need a way to defend against infinite loops, intentional or otherwise. I’d like to implement a time limit, but to do that, I need a way to kill a preemptive thread on Mac and Linux. Considering the thread class does not have a handle property, I imagine no, there’s not a declare or something I could use. But I figured I’d ask.
Yeah, this is a tough one. There’s no way from within the script to force an exit using setTimeout
or similar. You don’t say how you’re executing the JavaScript, may be something there.
It’s hard to abstract your actors, sequences and inter-dependencies to think ways to solve your problem. But let’s imagine that process p1() contains thread t_dep() and t_dep fires a task t_js() and you wish that if t_js() hangs you wish to kill t_js() and ALSO t_dep(). Someone should be monitoring both, lets call it t_mon(); t_mon could be the bridge to fire and control a pair t_dep() and t_js(). t_mon can check time passed, flags, messages… t_mon can kill them too using thread.stop() if necessary.
But not as long as the other Thread is stuck within an infinite loop, or?
The current t.stop() substituted the now deprecated t.kill(), so it should end it. The thread loop should be suspended, stack unwinded, destructors for local objects to be called, and so forth.
No, Thread.Stop does not work for this. The calling thread will become locked while waiting for the thread to end.
As for how I’m executing the code, it currently just looks like this. Approximately.
(
user code
)();
And really, the closure isn’t strictly necessary.
I’ve tried a few things like Promise.race with a timeout and clearTimeout, but they won’t stop the thread.
What I really need is a worker. In JavaScript, not Xojo. But this is JavaScriptCore (JSContextMBS) so it’s single-threaded.
Using a Xojo worker isn’t going to work, because I need too much access to the rest of the code. A plugin that can’t communicate with the host is kind of useless. Plus the startup overheard wouldn’t be desirable.
So it’s sounding like I’m stuck.
The MBS plugins also contain the DukTape open source JavaScript engine. If this supports enough of the JavaScript language you could maybe ask Christian if he could implement the DUK_USE_EXEC_TIMEOUT_CHECK feature.
Create a preemptive killer thread, low priority, fire and forget. Its only task is killing another thread, in background, so if it takes 3 minutes it’s not a problem, your app still be running, the wait is in the background.
Class Killer1 As Thread
property killed As boolean = false
Func Run()
self.killed = false
anotherThread.stop()
self.killed = true
End
End
But then I just have two stuck threads instead of one…
So you mean that you have a completely unresponsive thread unable even to be killed by a thread.stop() ? Well… As you said, you will need a separate process and kill the process.
Correct. Thread.Stop is only a graceful stop of Xojo code.
What does the script do?
I’ve been doing a lot of work in my language parser/compiler recently and one thought that occurs to me is to parse the script and add in code at loop boundaries (for example) that would give your thread a chance to kill the script if it is taking too long. I don’t know enough about JavaScript to say how practical this is, though.
User plugins, it could be anything.
But I’ve been reaching the same conclusion. I think I’m going to write a tool using node and babel to parse and analyze submissions automatically. They can pass tentative approval, but if run too long will get pushed to a manual review.
DukTape is terrible. It doesn’t support any of the improvements made in the last decade. I can’t tell users I support JavaScript, but not any of the syntax they are used to.
I’ve also tried Wren and Lua. Both had showstopper problems that I don’t remember. Python would require an external interpreter, so it’s not as foolproof as I’d be satisfied with, as I’m not in the business of troubleshooting people’s Python installs. And XojoScript, besides being limited and unfamiliar to my users, won’t work in preemptive threads.
JavaScriptCore is the closest I’ve got to something… good.
I must admit i’ve never used DukTape so I don’t know.
A quick Google seems to indicate that there is a private WebKit function named JSContextGroupSetExecutionTimeLimit. Maybe there is a way for Christian to hook into that.
An alternative idea could be to run the JavaScript via a hidden HTML Browser control. The idea would be that you could load a different page into the control when a timeout occurred.
In theory, you could also inject a timer which triggered a navigate to a different page
The trouble is that would make any use of JavaScriptCore an automatic rejection from the app store. Not just for me - I don’t care about that personally - but for anybody using it. That’s an automatic no-go in my book. Never use private functions, even without the threat of an app store rejection. You never know when something will change.
I think I can add the limit.
Since I would load this on demand, we should not trigger any automatic scan.
If you add it, and there is a way you can do this, I’d love a way to know that the timeout was hit. Such as firing an exception or something.
There will be an event for you to decide.
But you can just set a limit:
Var c As New JSContextMBS
Var e As JSValueMBS
// allow 5 seconds
c.SetExecutionTimeLimit 5
System.DebugLog DateTime.Now.ToString
Var v As JSValueMBS = c.EvaluateScript("while(true) {}", "", Nil, e)
System.DebugLog DateTime.Now.ToString
In the ShouldTerminate event, you can set a flag to know.
Technically you could also just do a yield to other threads and make this more friendly for multithreading.
Can someone try this with pr7 and let me know wether it works?
And potential upload the app to Apple (TestFlight) to verify that the one the fly loading of the function works well?