-
Couldn't load subscription status.
- Fork 334
Channel connecting two JVM instances
#13206
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 12 commits
64fd1ac
2e3911c
5b42e1d
9dc5646
4d69228
1e753eb
d7d547b
879a092
a23cdd5
eeab6a9
cf02db1
32aa5b0
22de1c4
5e1a8e7
e28e6ea
7133830
3f16786
b04963d
d11a0cf
42d24f4
21fb777
bc9aab7
4531025
fa46a7c
0445962
93c6fd5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2141,13 +2141,10 @@ lazy val `persistance` = (project in file("lib/java/persistance")) | |
| Compile / javacOptions := ((Compile / javacOptions).value), | ||
| inConfig(Compile)(truffleRunOptionsSettings), | ||
| libraryDependencies ++= slf4jApi ++ Seq( | ||
| "org.netbeans.api" % "org-openide-util-lookup" % netbeansApiVersion, | ||
| "junit" % "junit" % junitVersion % Test, | ||
| "com.github.sbt" % "junit-interface" % junitIfVersion % Test | ||
| "junit" % "junit" % junitVersion % Test, | ||
| "com.github.sbt" % "junit-interface" % junitIfVersion % Test | ||
| ), | ||
| Compile / moduleDependencies ++= slf4jApi ++ Seq( | ||
| "org.netbeans.api" % "org-openide-util-lookup" % netbeansApiVersion | ||
| ) | ||
| Compile / moduleDependencies ++= slf4jApi | ||
| ) | ||
| .dependsOn(`persistance-dsl` % Test) | ||
|
|
||
|
|
@@ -3356,12 +3353,12 @@ lazy val `runtime-parser` = | |
| commands += WithDebugCommand.withDebug, | ||
| fork := true, | ||
| libraryDependencies ++= Seq( | ||
| "junit" % "junit" % junitVersion % Test, | ||
| "com.github.sbt" % "junit-interface" % junitIfVersion % Test, | ||
| "org.scalatest" %% "scalatest" % scalatestVersion % Test | ||
| "junit" % "junit" % junitVersion % Test, | ||
| "com.github.sbt" % "junit-interface" % junitIfVersion % Test, | ||
| "org.scalatest" %% "scalatest" % scalatestVersion % Test, | ||
| "org.netbeans.api" % "org-openide-util-lookup" % netbeansApiVersion % "provided" | ||
| ), | ||
| Compile / moduleDependencies ++= Seq( | ||
| "org.netbeans.api" % "org-openide-util-lookup" % netbeansApiVersion | ||
| ), | ||
| // Java compiler is not able to correctly find all the annotation processors, because | ||
| // one of them is on module-path. To overcome this, we explicitly list all of them here. | ||
|
|
@@ -3643,9 +3640,10 @@ lazy val `runtime-instrument-common` = | |
| "ENSO_TEST_DISABLE_IR_CACHE" -> "false" | ||
| ), | ||
| libraryDependencies ++= Seq( | ||
| "junit" % "junit" % junitVersion % Test, | ||
| "com.github.sbt" % "junit-interface" % junitIfVersion % Test, | ||
| "org.scalatest" %% "scalatest" % scalatestVersion % Test | ||
| "junit" % "junit" % junitVersion % Test, | ||
| "com.github.sbt" % "junit-interface" % junitIfVersion % Test, | ||
| "org.scalatest" %% "scalatest" % scalatestVersion % Test, | ||
| "org.netbeans.api" % "org-openide-util-lookup" % netbeansApiVersion % Test | ||
| ), | ||
| javaModuleName := "org.enso.runtime.instrument.common", | ||
| Compile / moduleDependencies ++= slf4jApi ++ Seq( | ||
|
|
@@ -3739,12 +3737,11 @@ lazy val `runtime-instrument-runtime-server` = | |
| Compile / forceModuleInfoCompilation := true, | ||
| instrumentationSettings, | ||
| Compile / moduleDependencies ++= Seq( | ||
| "org.graalvm.truffle" % "truffle-api" % graalMavenPackagesVersion, | ||
| "org.graalvm.polyglot" % "polyglot" % graalMavenPackagesVersion, | ||
| "org.graalvm.sdk" % "collections" % graalMavenPackagesVersion, | ||
| "org.graalvm.sdk" % "word" % graalMavenPackagesVersion, | ||
| "org.graalvm.sdk" % "nativeimage" % graalMavenPackagesVersion, | ||
| "org.netbeans.api" % "org-openide-util-lookup" % netbeansApiVersion | ||
| "org.graalvm.truffle" % "truffle-api" % graalMavenPackagesVersion, | ||
| "org.graalvm.polyglot" % "polyglot" % graalMavenPackagesVersion, | ||
| "org.graalvm.sdk" % "collections" % graalMavenPackagesVersion, | ||
| "org.graalvm.sdk" % "word" % graalMavenPackagesVersion, | ||
| "org.graalvm.sdk" % "nativeimage" % graalMavenPackagesVersion | ||
| ), | ||
| Compile / internalModuleDependencies := Seq( | ||
| (`runtime-instrument-common` / Compile / exportedModule).value, | ||
|
|
@@ -4006,6 +4003,7 @@ lazy val `engine-runner` = project | |
| "-H:+AddAllCharsets", | ||
| "-H:+IncludeAllLocales", | ||
| "-H:+RunReachabilityHandlersConcurrently", | ||
| "-H:+ForeignAPISupport", | ||
| "-R:-InstallSegfaultHandler", | ||
| // Workaround a problem with build-/runtime-initialization conflict | ||
| // by disabling this service provider | ||
|
|
@@ -4313,6 +4311,7 @@ lazy val `os-environment` = | |
| ), | ||
| Compile / internalModuleDependencies ++= Seq( | ||
| (`engine-common` / Compile / exportedModule).value, | ||
| (`persistance` / Compile / exportedModule).value, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| (`logging-utils` / Compile / exportedModule).value, | ||
| (`logging-config` / Compile / exportedModule).value | ||
| ), | ||
|
|
@@ -4331,6 +4330,7 @@ lazy val `os-environment` = | |
| additionalOptions = Seq( | ||
| "-ea", | ||
| "--features=org.enso.os.environment.TestCollectorFeature", | ||
| "-H:+ForeignAPISupport", | ||
| "-R:-InstallSegfaultHandler" | ||
| ) | ||
| ) | ||
|
|
@@ -4352,6 +4352,8 @@ lazy val `os-environment` = | |
| .value, | ||
| Test / fork := true | ||
| ) | ||
| .dependsOn(`persistance`) | ||
| .dependsOn(`persistance-dsl` % "provided") | ||
| .dependsOn(`engine-common`) | ||
|
|
||
| lazy val `bench-processor` = (project in file("lib/scala/bench-processor")) | ||
|
|
||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,143 @@ | ||
| package org.enso.os.environment.jni; | ||
|
|
||
| import java.lang.foreign.FunctionDescriptor; | ||
| import java.lang.foreign.Linker; | ||
| import java.lang.foreign.MemorySegment; | ||
| import java.lang.foreign.ValueLayout; | ||
| import org.enso.persist.Persistance; | ||
| import org.graalvm.nativeimage.CurrentIsolate; | ||
| import org.graalvm.nativeimage.ImageInfo; | ||
| import org.graalvm.nativeimage.StackValue; | ||
| import org.graalvm.nativeimage.c.type.CTypeConversion; | ||
|
|
||
| /** Channel connects two {@link JVM} instances. */ | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since e28e6ea the channels are allocated in pairs. There is always an SubstrateVM side and HotSpot JVM side of the channel. Right now these two sides are going to have the same ID stored in |
||
| public final class Channel { | ||
|
|
||
| /** persistance pool associated with this channel object */ | ||
| private final Persistance.Pool pool; | ||
|
|
||
| private final JNI.JNIEnv env; | ||
| private final long isolate; | ||
| private final long callbackFn; | ||
|
|
||
| /* private */ | ||
| Channel(Persistance.Pool pool, JNI.JNIEnv env) { | ||
| this.pool = pool; | ||
| this.env = env; | ||
| this.isolate = -1; | ||
| this.callbackFn = -1; | ||
| } | ||
|
|
||
| /* private */ | ||
| Channel(Persistance.Pool pool, long isolate, long callbackFn) { | ||
| if (ImageInfo.inImageCode()) { | ||
| throw new IllegalStateException("Only usable in HotSpot"); | ||
| } | ||
| this.pool = pool; | ||
| this.env = null; | ||
| this.isolate = isolate; | ||
| this.callbackFn = callbackFn; | ||
| } | ||
|
|
||
| /** | ||
| * <em>Executes a message</em> in the other JVM. The message is any subclass of {@link Message} | ||
| * registered for persistance via {@link Persistable @Persistable} annotation into the {@link | ||
| * Persistance.Pool pool associated with this JVM}. The result (which is of type {@code R}) also | ||
| * has to be registered for serde. | ||
| * | ||
| * @param msg the message that gets serialized, transfered into the other JVM, deserialized on the | ||
| * other side and {@link Message#evaluate() evaluated} there | ||
| * @param <R> the type of result we expect the message to return | ||
| * @return the value gets computed via {@link Message#evaluate()} in the other JVM and then it | ||
| * gets serialized and transfered back to us. Deserialized and the value is then returned from | ||
| * this method | ||
| */ | ||
| public final <R> R execute(Message<R> msg) { | ||
| if (this.isolate == -1) { | ||
| return JVM.executeImpl(pool, msg, memory -> toHotSpotMessage(env, memory)); | ||
| } else { | ||
| java.lang.foreign.MemorySegment fnCallbackAddress = MemorySegment.ofAddress(callbackFn); | ||
| java.lang.foreign.FunctionDescriptor fnDescriptor = | ||
| FunctionDescriptor.of( | ||
| ValueLayout.JAVA_LONG, | ||
| ValueLayout.ADDRESS, | ||
| ValueLayout.ADDRESS, | ||
| ValueLayout.JAVA_LONG); | ||
| java.lang.invoke.MethodHandle fnHandle = | ||
| Linker.nativeLinker().downcallHandle(fnCallbackAddress, fnDescriptor); | ||
| return JVM.executeImpl( | ||
| pool, | ||
| msg, | ||
| seg -> { | ||
| Object res = -1L; | ||
| try { | ||
| java.lang.foreign.MemorySegment isoRef = MemorySegment.ofAddress(isolate); | ||
| res = fnHandle.invoke(isoRef, seg, seg.byteSize()); | ||
| } catch (Throwable ex) { | ||
| ex.printStackTrace(); | ||
| } | ||
| return (long) res; | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| private static long toHotSpotMessage(JNI.JNIEnv e, MemorySegment segment) { | ||
| java.lang.String classNameWithSlashes = "org/enso/os/environment/jni/JVMPeer"; | ||
| java.lang.String methodName = "handle"; | ||
| try (org.graalvm.nativeimage.c.type.CTypeConversion.CCharPointerHolder classInC = | ||
| CTypeConversion.toCString(classNameWithSlashes); | ||
| org.graalvm.nativeimage.c.type.CTypeConversion.CCharPointerHolder methodInC = | ||
| CTypeConversion.toCString(methodName); | ||
| org.graalvm.nativeimage.c.type.CTypeConversion.CCharPointerHolder signatureInC = | ||
| CTypeConversion.toCString("(JJJJ)J")) { | ||
| org.enso.os.environment.jni.JNINativeInterface fn = e.getFunctions(); | ||
| org.enso.os.environment.jni.JNI.JClass clazz = fn.getFindClass().call(e, classInC.get()); | ||
| assert clazz.isNonNull() : "Class not found " + classNameWithSlashes; | ||
| org.enso.os.environment.jni.JNI.JMethodID method = | ||
| fn.getGetStaticMethodID().call(e, clazz, methodInC.get(), signatureInC.get()); | ||
| assert method.isNonNull() : "method not found in " + classNameWithSlashes; | ||
| long address = segment.address(); | ||
| assert address > 0 : "We need an address"; | ||
| org.enso.os.environment.jni.JNI.JValue arg = StackValue.get(4, JNI.JValue.class); | ||
| arg.addressOf(0).setLong(CurrentIsolate.getCurrentThread().rawValue()); | ||
| arg.addressOf(1).setLong(JVM.CALLBACK_FN.getFunctionPointer().rawValue()); | ||
| arg.addressOf(2).setLong(address); | ||
| arg.addressOf(3).setLong(segment.byteSize()); | ||
| long replySize = fn.getCallStaticLongMethodA().call(e, clazz, method, arg); | ||
| return replySize; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Subclasses of message denote a computational task to be performed in the "other {@link JVM}". | ||
| * | ||
| * @param <R> type of the return value | ||
| */ | ||
| public abstract static class Message<R> { | ||
|
|
||
| final Class<R> replyType; | ||
|
|
||
| /** | ||
| * Constructor for subclasses. Use it as {@code super(Integer.class)} to specify the reply type | ||
| * of the exception which is then returned from the {@link #evaluate} method. | ||
| * | ||
| * @param replyType the type of the reply | ||
| */ | ||
| protected Message(Class<R> replyType) { | ||
| this.replyType = replyType; | ||
| } | ||
|
|
||
| /** | ||
| * Handles evaluation of the exception. Use {@link Channel#execute} to pass this messages to the | ||
| * other {@link JVM}. After all the serde and transfer to the other {@link JVM} this method is | ||
| * executed to perform its operation. Then the result is passed back via serde again and | ||
| * returned from the {@link Channel#execute} method. | ||
| * | ||
| * @param channel allows sending messages to the other JVM | ||
| * @return the result of the evaluation or {@code null} | ||
| * @throws Throwable the computation may yield exceptions or errors which are then transferred | ||
| * back to the callee JVM | ||
| */ | ||
| protected abstract R evaluate(Channel channel) throws Throwable; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NetBeans Lookup shouldn't be used during runtime: 879a092