davsclaus commented on code in PR #26311:
URL: https://github.com/apache/camel/pull/26311#discussion_r3989772222


##########
components/camel-python/src/main/java/org/apache/camel/language/python/PythonLanguage.java:
##########
@@ -75,19 +75,22 @@ public <T> T evaluate(String script, Map<String, Object> 
bindings, Class<T> resu
             }
         }
 
-        try {
-            if (bindings != null) {
-                bindings.forEach(compiler::set);
-            }
-            PyObject out = compiler.eval(code);
-            if (out != null) {
-                String value = out.toString();
-                return 
getCamelContext().getTypeConverter().convertTo(resultType, value);
+        // the interpreter is shared by every caller of this method: bind, run 
and clean up under one lock
+        synchronized (compiler) {

Review Comment:
   **The compile a few lines above this is on the same shared interpreter, but 
outside the lock.**
   
   `PythonInterpreter.compile(Reader, String)` is not a pure function of its 
argument — it reads `this.cflags`, passes that same mutable `CompilerFlags` 
instance into `ParserFacade.parseExpressionOrModule(...)` (which writes back 
any `from __future__` flags it finds), and then calls `setSystemState()`, which 
installs this interpreter's `PySystemState` as the *thread's* current one.
   
   So two threads missing the cache on different scripts mutate one 
`CompilerFlags`, and a compiling thread races the `setSystemState()`/`eval` of 
a thread already inside this `synchronized` block. Same 
shared-mutable-interpreter race the PR fixes on the evaluation path, just on 
the compile path.
   
   Pulling the cache miss inside the lock closes it and also makes the 
double-checked lookup honest:
   
   ```java
           synchronized (compiler) {
               PyCode code = getCompiledScriptFromCache(script);
               if (code == null) {
                   try {
                       code = compiler.compile(script);
                       addCompiledScriptToCache(script, code);
                   } catch (Exception e) {
                       throw new ExpressionIllegalSyntaxException(script, e);
                   }
               }
               try {
                   ...
   ```
   
   (`PythonExpression`'s constructor compiles against its own private 
interpreter before the expression is published, so that one is fine as-is.)



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to