-
Notifications
You must be signed in to change notification settings - Fork 326
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
Share worker thread among RuntimeServerInstrument and ydoc-polyfill #12528
base: develop
Are you sure you want to change the base?
Conversation
Manual TestingLet's use a simple script that connects to echo service at http://websocket.org to verify how well the support for var counter = 0;
function app() {
print("Connecting to websocket.org...");
let ws = new WebSocket('wss://echo.websocket.org');
function sayHi() {
ws.send(`Hi there! #${counter++}`);
}
ws.onopen = function(ev) {
print("Open: " + ev);
};
ws.onmessage = function(ev) {
print("Msg : " + Object.getOwnPropertyNames(ev));
print(" data: " + ev.data);
setTimeout(sayHi, 3000);
};
ws.onclose = function(ev) {
print("Close: " + Object.getOwnPropertyNames(ev));
};
ws.onerror = function(ev) {
print("Err: " + Object.getOwnPropertyNames(ev));
};
}
if (typeof insight === "undefined") {
globalThis.print = console.log;
if (typeof WebSocket === "undefined") {
let r = require("ws");
globalThis.WebSocket = r.WebSocket;
}
app();
} else {
app();
} The script is generic. You can use it in node.js as well as Enso IDE or command line interface. Enso GUIStore the above script as enso$ export ENSO_JVM_OPTS="-Denso.dev.insight=/tmp/wstest.js -Dpolyglot.enso.interpreter.jobParallelism=1"
enso$ sbt runProjectManagerDistribution
....
Connecting to websocket.org...
...
Msg :
data: Hi there! #2
... and in another terminal execute Verify with node.jsSave the above script into an empty directory
and so on. Every few seconds another "Hi there!" message with an increased counter shall appear. The same output shall be visible in the Enso GUI as well as from CLI. Enso CLITBD... |
@hubertp, @4e6, do you think following assumption:
Is supposed to be true? E.g. if I fix the violations and set |
log.warn( | ||
"Consider adding `-Dpolyglot.enso.interpreter.jobParallelism=1` to" | ||
+ " ENSO_JVM_OPTS to avoid this error. See #12528"); | ||
log.info(dumpStack()); |
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.
When Truffle system detects multi threaded access, chances are high we can inspect all thread stack traces and find the other thread that is also using JavaScript.
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.
The violator in the first accident is scala-execution-context-global-56
thread. This is the thread dump:
job-pool-1
java.lang.Thread.dumpThreads(Thread.java:-2)
java.lang.Thread.getAllStackTraces(Thread.java:2521)
org.enso.ydoc.polyfill.web.WebSocket.dumpStack(WebSocket.java:367)
org.enso.ydoc.polyfill.web.WebSocket$WebSocketConnection.lambda$handleCallback$8(WebSocket.java:356)
org.enso.ydoc.polyfill.web.WebSocket$WebSocketConnection$$Lambda/0x0000708124b85770.run(null:-1)
java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
java.util.concurrent.FutureTask.run(FutureTask.java:317)
java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
java.lang.Thread.runWith(Thread.java:1596)
java.lang.Thread.run(Thread.java:1583)
com.oracle.truffle.polyglot.PolyglotThread.access$001(PolyglotThread.java:53)
com.oracle.truffle.polyglot.PolyglotThread$1.execute(PolyglotThread.java:106)
com.oracle.truffle.polyglot.PolyglotThread$ThreadSpawnRootNode.executeImpl(PolyglotThread.java:140)
com.oracle.truffle.polyglot.PolyglotThread$ThreadSpawnRootNode.execute(PolyglotThread.java:131)
com.oracle.truffle.runtime.OptimizedCallTarget.executeRootNode(OptimizedCallTarget.java:745)
com.oracle.truffle.runtime.OptimizedCallTarget.profiledPERoot(OptimizedCallTarget.java:669)
com.oracle.truffle.runtime.OptimizedCallTarget.callBoundary(OptimizedCallTarget.java:602)
com.oracle.truffle.runtime.OptimizedCallTarget.doInvoke(OptimizedCallTarget.java:586)
com.oracle.truffle.runtime.OptimizedCallTarget.callIndirect(OptimizedCallTarget.java:519)
com.oracle.truffle.runtime.OptimizedCallTarget.call(OptimizedCallTarget.java:500)
com.oracle.truffle.polyglot.PolyglotThread.run(PolyglotThread.java:102)
job-pool-2
java.lang.Thread.dumpThreads(Thread.java:-2)
java.lang.Thread.getAllStackTraces(Thread.java:2521)
org.enso.ydoc.polyfill.web.WebSocket.dumpStack(WebSocket.java:367)
org.enso.ydoc.polyfill.web.WebSocket$WebSocketConnection.lambda$handleCallback$8(WebSocket.java:356)
org.enso.ydoc.polyfill.web.WebSocket$WebSocketConnection$$Lambda/0x0000708124b85770.run(null:-1)
java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
java.util.concurrent.FutureTask.run(FutureTask.java:317)
java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
java.lang.Thread.runWith(Thread.java:1596)
java.lang.Thread.run(Thread.java:1583)
com.oracle.truffle.polyglot.PolyglotThread.access$001(PolyglotThread.java:53)
com.oracle.truffle.polyglot.PolyglotThread$1.execute(PolyglotThread.java:106)
com.oracle.truffle.polyglot.PolyglotThread$ThreadSpawnRootNode.executeImpl(PolyglotThread.java:140)
com.oracle.truffle.polyglot.PolyglotThread$ThreadSpawnRootNode.execute(PolyglotThread.java:131)
com.oracle.truffle.runtime.OptimizedCallTarget.executeRootNode(OptimizedCallTarget.java:745)
com.oracle.truffle.runtime.OptimizedCallTarget.profiledPERoot(OptimizedCallTarget.java:669)
com.oracle.truffle.runtime.OptimizedCallTarget.callBoundary(OptimizedCallTarget.java:602)
com.oracle.truffle.runtime.OptimizedCallTarget.doInvoke(OptimizedCallTarget.java:586)
com.oracle.truffle.runtime.OptimizedCallTarget.callIndirect(OptimizedCallTarget.java:519)
com.oracle.truffle.runtime.OptimizedCallTarget.call(OptimizedCallTarget.java:500)
com.oracle.truffle.polyglot.PolyglotThread.run(PolyglotThread.java:102)
scala-execution-context-global-56
java.lang.ClassLoader.defineClass1(ClassLoader.java:-2)
java.lang.ClassLoader.defineClass(ClassLoader.java:1027)
java.lang.ClassLoader.defineClass(ClassLoader.java:1105)
java.security.SecureClassLoader.defineClass(SecureClassLoader.java:182)
jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:821)
jdk.internal.loader.BuiltinClassLoader.findClassInModuleOrNull(BuiltinClassLoader.java:741)
jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:665)
jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639)
jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
java.lang.ClassLoader.loadClass(ClassLoader.java:526)
com.oracle.truffle.host.HostObjectGen$InteropLibraryExports$Uncached.isString(HostObjectGen.java:9002)
com.oracle.truffle.api.interop.InteropLibraryGen$UncachedDispatch.isString(InteropLibraryGen.java:7076)
com.oracle.truffle.polyglot.PolyglotValueDispatch$InteropValue.isString(PolyglotValueDispatch.java:2729)
org.graalvm.polyglot.Value.isString(Value.java:1035)
org.enso.ydoc.polyfill.Arguments.lambda$toString$0(Arguments.java:13)
org.enso.ydoc.polyfill.Arguments.toString(Arguments.java:14)
org.enso.ydoc.polyfill.web.EventTarget.execute(EventTarget.java:38)
org.graalvm.polyglot.Engine$APIAccessImpl.callProxyExecutableExecute(Engine.java:1370)
com.oracle.truffle.host.GuestToHostCodeCache$1.executeImpl(GuestToHostCodeCache.java:126)
com.oracle.truffle.host.GuestToHostRootNode.execute(GuestToHostRootNode.java:80)
com.oracle.truffle.runtime.OptimizedCallTarget.executeRootNode(OptimizedCallTarget.java:745)
com.oracle.truffle.runtime.OptimizedCallTarget.callInlined(OptimizedCallTarget.java:550)
com.oracle.truffle.runtime.OptimizedRuntimeSupport.callInlined(OptimizedRuntimeSupport.java:250)
com.oracle.truffle.host.GuestToHostRootNode.guestToHostCall(GuestToHostRootNode.java:102)
com.oracle.truffle.host.HostProxy.execute(HostProxy.java:139)
com.oracle.truffle.host.HostProxyGen$InteropLibraryExports$Cached.executeNode_AndSpecialize(HostProxyGen.java:401)
com.oracle.truffle.host.HostProxyGen$InteropLibraryExports$Cached.execute(HostProxyGen.java:377)
com.oracle.truffle.api.interop.InteropLibraryGen$CachedDispatch.execute(InteropLibraryGen.java:7952)
com.oracle.truffle.js.nodes.function.JSFunctionCallNode$ForeignExecuteNode.executeCall(JSFunctionCallNode.java:1539)
...
com.oracle.truffle.js.runtime.objects.JSObject.invokeMember(JSObject.java:245)
com.oracle.truffle.js.runtime.objects.JSObjectGen$InteropLibraryExports$Uncached.invokeMember(JSObjectGen.java:1020)
com.oracle.truffle.api.interop.InteropLibraryGen.genericDispatch(InteropLibraryGen.java:329)
com.oracle.truffle.api.library.ReflectionLibraryDefault$Send.doSendGeneric(ReflectionLibrary.java:193)
com.oracle.truffle.api.library.ReflectionLibraryDefaultGen$ReflectionLibraryExports$Uncached.send(ReflectionLibraryDefaultGen.java:230)
com.oracle.truffle.polyglot.OtherContextGuestObject.sendImpl(OtherContextGuestObject.java:157)
com.oracle.truffle.polyglot.OtherContextGuestObject$Send.doSlowPath(OtherContextGuestObject.java:121)
com.oracle.truffle.polyglot.OtherContextGuestObjectGen$ReflectionLibraryExports$Cached.executeAndSpecialize(OtherContextGuestObjectGen.java:194)
com.oracle.truffle.polyglot.OtherContextGuestObjectGen$ReflectionLibraryExports$Cached.send(OtherContextGuestObjectGen.java:136)
com.oracle.truffle.api.interop.InteropLibraryGen$Proxy.invokeMember(InteropLibraryGen.java:2910)
org.enso.interpreter.epb.JsForeignNode.doExecute(JsForeignNode.java:40)
org.enso.interpreter.epb.JsForeignNodeGen.executeAndSpecialize(JsForeignNodeGen.java:77)
org.enso.interpreter.epb.JsForeignNodeGen.execute(JsForeignNodeGen.java:65)
org.enso.interpreter.epb.ForeignEvalNode.execute(ForeignEvalNode.java:107)
com.oracle.truffle.runtime.OptimizedCallTarget.executeRootNode(OptimizedCallTarget.java:745)
com.oracle.truffle.runtime.OptimizedCallTarget.profiledPERoot(OptimizedCallTarget.java:669)
com.oracle.truffle.runtime.OptimizedCallTarget.callBoundary(OptimizedCallTarget.java:602)
com.oracle.truffle.runtime.OptimizedCallTarget.doInvoke(OptimizedCallTarget.java:586)
com.oracle.truffle.runtime.OptimizedCallTarget.callIndirect(OptimizedCallTarget.java:519)
com.oracle.truffle.runtime.OptimizedCallTarget.call(OptimizedCallTarget.java:500)
com.oracle.truffle.tools.agentscript.impl.InsightPerSource.initializeAgent(InsightPerSource.java:120)
com.oracle.truffle.tools.agentscript.impl.InsightPerSource.onLanguageContextInitialized(InsightPerSource.java:190)
com.oracle.truffle.api.instrumentation.InstrumentationHandler.notifyLanguageContextInitialized(InstrumentationHandler.java:1049)
com.oracle.truffle.api.instrumentation.InstrumentAccessor$InstrumentImpl.notifyLanguageContextInitialized(InstrumentAccessor.java:269)
com.oracle.truffle.polyglot.PolyglotLanguageContext.ensureInitialized(PolyglotLanguageContext.java:796)
com.oracle.truffle.polyglot.PolyglotContextImpl.initializeLanguage(PolyglotContextImpl.java:1629)
com.oracle.truffle.polyglot.PolyglotContextImpl.initializeLanguage(PolyglotContextImpl.java:1638)
com.oracle.truffle.polyglot.PolyglotContextDispatch.initializeLanguage(PolyglotContextDispatch.java:55)
org.graalvm.polyglot.Context.initialize(Context.java:579)
org.enso.languageserver.boot.resource.TruffleContextInitialization.initComponent(TruffleContextInitialization.java:47)
org.enso.languageserver.boot.resource.LockedInitialization.lambda$init$0(LockedInitialization.java:37)
org.enso.languageserver.boot.resource.LockedInitialization$$Lambda/0x0000708124546ac8.run(null:-1)
java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804)
akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:49)
java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1423)
java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387)
java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312)
java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843)
java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808)
java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188)
I can fix it by doing the call to Context.initialize
at
org.enso.languageserver.boot.resource.TruffleContextInitialization.initComponent(TruffleContextInitialization.java:47)
in the ScheduledExecutorService
. Is it good idea, @hubertp?
Pull Request Description
Resolves #11477 by making callbacks from
ydoc-polyfill
WebSocket
emulation on the sameScheduledExecutorService
as used byRuntimeServerInstrument
. When the pool size is one (set via-Dpolyglot.enso.interpreter.jobParallelism=1
property) there should be no parallel executions (under the assumption that all Enso code is executing in the pool controlled by thejobParallelism
property).Important Notes
The
EpbLanguage
(which injects theWebSocket
emulation) locates theRuntimeServerInstrument
by its ID and obtains aSupplier<ScheduledExecutorService>
from it. Then it uses the obtained executor service for making callbacks into JavaScript whenever a websocket event arrives.Checklist
Please ensure that the following checklist has been satisfied before submitting the PR:
Scala,
Java,