From 79c71a4770f0041579f37541561ea8895241e96f Mon Sep 17 00:00:00 2001 From: Dmitriy Dovnar Date: Wed, 4 Oct 2023 15:22:47 +0300 Subject: [PATCH] add shared memory between SubInterpreters --- src/main/java/jep/Jep.java | 7 +- src/main/java/jep/JepConfig.java | 13 +++ src/main/java/jep/SubInterpreter.java | 6 ++ .../TestSharedMemoryInSubInterpreter.java | 79 +++++++++++++++++++ 4 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 src/test/java/jep/test/TestSharedMemoryInSubInterpreter.java diff --git a/src/main/java/jep/Jep.java b/src/main/java/jep/Jep.java index f0866f77..08094fd2 100644 --- a/src/main/java/jep/Jep.java +++ b/src/main/java/jep/Jep.java @@ -130,8 +130,13 @@ public Jep(JepConfig config) throws JepException { protected Jep(JepConfig config, boolean useSubInterpreter, MemoryManager memoryManager) throws JepException { + this(config, useSubInterpreter, memoryManager, false); + } + + protected Jep(JepConfig config, boolean useSubInterpreter, MemoryManager memoryManager, boolean useSharedMemory) + throws JepException { MainInterpreter mainInterpreter = MainInterpreter.getMainInterpreter(); - if (threadUsed.get()) { + if (!useSharedMemory && threadUsed.get()) { Thread current = Thread.currentThread(); StringBuilder warning = new StringBuilder(THREAD_WARN) .append("Unsafe reuse of thread ").append(current.getName()) diff --git a/src/main/java/jep/JepConfig.java b/src/main/java/jep/JepConfig.java index 8b4e8009..667b95a7 100644 --- a/src/main/java/jep/JepConfig.java +++ b/src/main/java/jep/JepConfig.java @@ -24,6 +24,8 @@ */ package jep; +import jep.python.MemoryManager; + import java.io.File; import java.io.OutputStream; import java.util.Collections; @@ -58,6 +60,8 @@ public class JepConfig { protected Set sharedModules = null; + protected MemoryManager sharedMemoryManager = null; + /** * Sets a path of directories separated by File.pathSeparator that will be * appended to the sub-intepreter's sys.path @@ -201,4 +205,13 @@ public SubInterpreter createSubInterpreter() throws JepException { return new SubInterpreter(this); } + public SubInterpreter createSubInterpreterSharedMemory() { + if (sharedMemoryManager == null) { + SubInterpreter interpreter = new SubInterpreter(this); + sharedMemoryManager = interpreter.getMemoryManager(); + return interpreter; + } + return new SubInterpreter(this, sharedMemoryManager, true); + } + } diff --git a/src/main/java/jep/SubInterpreter.java b/src/main/java/jep/SubInterpreter.java index 49bda56f..28ce95d7 100644 --- a/src/main/java/jep/SubInterpreter.java +++ b/src/main/java/jep/SubInterpreter.java @@ -24,6 +24,8 @@ */ package jep; +import jep.python.MemoryManager; + /** * Class for creating instances of Interpreters which are sandboxed from other * Interpreters. Sub-interpreters isolate different SubInterpreter instances, @@ -63,4 +65,8 @@ public SubInterpreter(JepConfig config) throws JepException { super(config); } + public SubInterpreter(JepConfig config, MemoryManager memoryManager, boolean useSharedMemory) throws JepException { + super(config, true, memoryManager, useSharedMemory); + } + } diff --git a/src/test/java/jep/test/TestSharedMemoryInSubInterpreter.java b/src/test/java/jep/test/TestSharedMemoryInSubInterpreter.java new file mode 100644 index 00000000..b61c313c --- /dev/null +++ b/src/test/java/jep/test/TestSharedMemoryInSubInterpreter.java @@ -0,0 +1,79 @@ +package jep.test; + +import jep.Interpreter; +import jep.JepConfig; +import jep.JepException; + +import java.util.Random; + +public class TestSharedMemoryInSubInterpreter { + private JepConfig config; + private Interpreter mainIterp; + private Object pythonObject; + + public static void main(String[] args) { + TestSharedMemoryInSubInterpreter app = new TestSharedMemoryInSubInterpreter(); + app.init(); + + // test in single thread + int v = app.callMethodAdd(100, 50); + System.out.println("Add (100, 50) = " + v); + + v = app.callMethodAdd(10, 10); + System.out.println("Sub (10, 10) = " + v); + + // test with separate threads + Random random = new Random(); + + for (int i = 0; i < 3; i++) { + Thread thread = new Thread(() -> { + System.out.println(Thread.currentThread().getName() + ": started"); + int a = random.nextInt(100); + int b = random.nextInt(100); + + int r = app.callMethodAdd(a, b); + String result = a + " + " + b + " = " + r; + + try { + Thread.sleep(1000L); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println(Thread.currentThread().getName() + ": result(" + result + ")"); + System.out.println(Thread.currentThread().getName() + ": finish"); + }); + thread.start(); + } + } + + private void init() throws JepException { + String initScript = + "class SimpleObject:\n" + + " def add(self, a, b):\n" + + " return a + b\n" + + " def sub(self, a, b):\n" + + " return a - b\n" + + "myobj = SimpleObject()"; + + // config need for sharing memory + config = new JepConfig(); + + Interpreter interp = config.createSubInterpreterSharedMemory(); + interp.exec(initScript); + pythonObject = interp.getValue("myobj", Object.class); + // store main interpreter with shared memory + mainIterp = interp; + } + + private int callMethodAdd(int a, int b) throws JepException { + // this interpreter accesses the object created in the main interpreter + try (Interpreter interp = config.createSubInterpreterSharedMemory()) { + interp.set("obj", pythonObject); + interp.set("a", a); + interp.set("b", b); + interp.exec("result = obj.add(a, b)"); + return interp.getValue("result", Integer.class); + } + } + +}