This is how I see this, and that Iâve been trying to point out:
Once upon a time, JSR223 was taken in use in OH where engine instances were kept separate. It might have been by mistake/lack of overview, or it might have been for good reason that either doesnât apply anymore, or that still does (although I have no idea what that reason could be):
Google says:
JSR-223 (Scripting for the Java⢠Platform) enables multithreading by allowing a single engine instance to support concurrent evaluations by multiple threads, though the exact behavior depends on the engineâs implementation. JSR-223-compatible engines, such as Groovy in Apache JMeter, are designed to perform well under multithreaded conditions, contrasting with less efficient languages like BeanShell for high-load scenarios.
How JSR-223 Handles Multithreading
- Concurrent Execution: The JSR-223 specification allows a single script engine to be evaluated by multiple threads simultaneously.
- Engine Dependency: The specific level of multithreading support, such as thread-safe operations and caching, is an implementation detail of the particular script engine (e.g., Groovy, JavaScript).
- Performance: Languages like Groovy are generally more performant and have less overhead in multithreaded environments compared to languages like BeanShell, making them a better choice for high-load testing scenarios in tools like JMeter.
Later, when Graal came along, it was probably found to be too inefficient to keep recreating the engine, so the âhackâ of mapping the âengineâ to a context object was introduced.
This means that Graal based scripting languages donât suffer as much, but JSR 223 languages does.
But, this also complicates things, since in Core, both these two very different âconceptsâ must be treated using the same code, where under Graal, the context is âimpersonating an engine instanceâ. The more functionality and performance you try to get into this solution, the harder it will get, because youâre actually dealing with two very different things, and one set of ârulesâ that work well for both doesnât actually exist.
My suggestion is therefore that, assuming that we can verify that a JSR223 engine instance actually behaves as I believe, and as I find documented, the âhackâ is removed, and engine means engine and context means context throughout their life in Core.
Whether this requires a new âlayer of interfacesâ or not, Iâm not sure of. If needed, there must be âparallel pathsâ for Graal and JSR223 based languages, if not, they can keep coexisting âas JSR223â.
My claim is that this will âsolve some knotsâ by itself, some things that are âtricky to balanceâ or leads to restrictions/things that canât be done today, will just solve themselves, because they are a result of the mixing of instance types/concepts. Further, Iâm convinced that it will improve performance for JSR223, and that it will make it easier to avoid bugs in the future, because anyone touching the code donât have to know all the details of whatâs âactually happeningâ despite appearances.
I get that it might be a bit of work to do all the changes in the corresponding places, but I think that the longer it is postponed, the harder it gets, and the more complicated things become in the meanwhile.
edit: My confidence in the above just got a serious dent. I read some JavaDocs in the ScriptEngine
(JSR223) interface:
/**
* Causes the immediate execution of the script whose source is the String
* passed as the first argument. The script may be reparsed or recompiled before
* execution. State left in the engine from previous executions, including
* variable values and compiled procedures may be visible during this execution.
*
* @param script The script to be executed by the script engine.
*
* @param context A <code>ScriptContext</code> exposing sets of attributes in
* different scopes. The meanings of the scopes <code>ScriptContext.GLOBAL_SCOPE</code>,
* and <code>ScriptContext.ENGINE_SCOPE</code> are defined in the specification.
* <br><br>
* The <code>ENGINE_SCOPE</code> <code>Bindings</code> of the <code>ScriptContext</code> contains the
* bindings of scripting variables to application objects to be used during this
* script execution.
*
*
* @return The value returned from the execution of the script.
*
* @throws ScriptException if an error occurs in script. ScriptEngines should create and throw
* <code>ScriptException</code> wrappers for checked Exceptions thrown by underlying scripting
* implementations.
* @throws NullPointerException if either argument is null.
*/
public Object eval(String script, ScriptContext context) throws ScriptException;
Iâm thinking of âState left in the engine from previous executions, including variable values and compiled procedures may be visible during this execution.â in particular. Also, the âengine globalâ context values:
/**
* Sets a key/value pair in the state of the ScriptEngine that may either create
* a Java Language Binding to be used in the execution of scripts or be used in some
* other way, depending on whether the key is reserved. Must have the same effect as
* <code>getBindings(ScriptContext.ENGINE_SCOPE).put</code>.
*
* @param key The name of named value to add
* @param value The value of named value to add.
*
* @throws NullPointerException if key is null.
* @throws IllegalArgumentException if key is empty.
*/
public void put(String key, Object value);
Without proper isolation between executions, an engine canât âsafelyâ be reused for different purposes, obviously. I guess this explains a lot of the things Iâve found âstrangeâ.
I still think that there might be room for âscript engineâ and âcontextâ concepts in OH that aligns more with Graal, that would make things simpler and âcleanerâ to handle. Maybe a JSR223 script engine should âpose as a contextâ instead of the other way around..?
edit2: ScriptEngineFactory
further confirms this:
/**
* Returns an instance of the <code>ScriptEngine</code> associated with this
* <code>ScriptEngineFactory</code>. A new ScriptEngine is generally
* returned, but implementations may pool, share or reuse engines.
*
* @return A new <code>ScriptEngine</code> instance.
*/
public ScriptEngine getScriptEngine();
So, I have probably âmade noiseâ for no good reason. Let me just add that this was a really stupid design (by JSR223).