While Loop in Binding does not stop the Script when removed

While experimenting with While loops, I've concluded that I should avoid using them. However, during my usage of While loops, I believe I've identified a bug in Property Binding Scripts.

If I unintentionally create an infinite while loop within a Property Binding and then remove it, the script continues to run even after the code is removed. Furthermore, if I create an infinite loop and then hit the apply button, I end up with two threads running even after removing the while loop and clicking apply. I've only allowed these threads to run for a few minutes, so I'm unsure if they will eventually terminate. Is this behavior normal for Property Binding Scripts, or is something unusual happening?

I can duplicate this every time by just creating a new property binding and adding an infinite while loop then removing the loop from the script.


Not a bug in Ignition. It is a bug in your code. An infinite loop (or a very long duration loop) within an event is pathological in an event-driven system. If you can't make your events run in a few milliseconds, then you should be using timers and state machines. (And, no, that doesn't mean using any form of .sleep(). The event routine must exit.)

And no, they won't eventually end. They will burn CPU time until you restart the gateway.

So even after I correct my mistake in the script I will still need to restart the gateway to free it back up?

Yes, editing an event script doesn't stop threads already running it. (This is true everywhere in Ignition.)

1 Like

Thanks

I thought I had been able to kill someone else's infinite loop scripts using the gateway's Status | Diagnostics | Running Scripts | Cancel. This didn't require a gateway restart as far as I can remember.

1 Like

I second this, I was able to stop an accidental infinite loop by cancelling it using the Running Scripts tab (clarifying that I am not an expert on how Java works, so not totally sure if it's necessary to restart the gateway :sweat_smile:).

1 Like

I wouldn't do it on a production server. The JVM can become unstable, supposedly. Killing threads is a legacy java feature that comes with big warnings.

2 Likes

The 'cancel' mechanism hooks into Jython to interrupt the next stack frame/call/etc performed.
So, it's not using the same thread death mechanism Java explicitly warns you against... it also won't stop, in theory, a totally pointless busy loop, but as long as there's some statement inside the loop (which there (always?) should be, it will stop just that executing thread.

The example in this topic did not have any such statement. Just pass. (Must be I/O or synchronization/locking operations to be interruptible, IIRC.)

Still works. We're adding a TraceFunction to the interpreter thread, and the pass is hit on traceLine, even though it's not actually a 'real' statement, so we're still able to stop there (by simply throwing an unexpected exception).

https://www.javadoc.io/static/org.python/jython-standalone/2.7.1/org/python/core/TraceFunction.html

com.inductiveautomation.ignition.common.script.ScriptManager$ScriptCanceledError: Script canceled: 
	at com.inductiveautomation.ignition.common.script.ScriptManager$BreakTraceFunction.doBreak(ScriptManager.java:1043)
	at com.inductiveautomation.ignition.common.script.ScriptManager$BreakTraceFunction.traceException(ScriptManager.java:1066)
	at org.python.core.PyException.<init>(PyException.java:55)
	at org.python.core.PyException.<init>(PyException.java:43)
	at org.python.core.Py.JavaError(Py.java:547)
	at org.python.core.PyTableCode.call(PyTableCode.java:182)
	at org.python.core.PyBaseCode.call(PyBaseCode.java:306)
	at org.python.core.PyFunction.function___call__(PyFunction.java:474)
	at org.python.core.PyFunction.__call__(PyFunction.java:469)
	at org.python.core.PyFunction.__call__(PyFunction.java:464)
	at com.inductiveautomation.ignition.common.script.ScriptManager.runFunction(ScriptManager.java:846)
	at com.inductiveautomation.ignition.client.script.DesignerSystemUtilities.lambda$_invokeAsyncImpl$1(DesignerSystemUtilities.java:140)
	at java.base/java.lang.Thread.run(Thread.java:829)

Ignition v(Dev Version)
Java: Azul Systems, Inc. 11.0.17

Sneaky. Wonder what the performance consequences are.

Jython lets you swap in a trace function 'live', so no implication until we try to interrupt.

2 Likes