-
Notifications
You must be signed in to change notification settings - Fork 323
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
Stateless parser API #11147
Stateless parser API #11147
Conversation
ebd4caf
to
234a2c4
Compare
9c0a0fe
to
b67db01
Compare
Stateless (static) parser interface. Buffer-reuse optimization is now hidden behind JNI FFI implementation. Fixes #11121 and prevents similar bugs.
e10fb43
to
ce95e4e
Compare
f856b49
to
285e2e6
Compare
engine/runtime-parser/src/main/java/org/enso/compiler/core/EnsoParser.java
Show resolved
Hide resolved
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.
Allowing use of the Rust parser from multiple threads at (some small cost) will simplify its (clueless) usage from JVM.
engine/runtime-parser/src/main/java/org/enso/compiler/core/EnsoParser.java
Show resolved
Hide resolved
...time-instrument-common/src/main/scala/org/enso/interpreter/instrument/ChangesetBuilder.scala
Show resolved
Hide resolved
engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/CompilerTest.scala
Show resolved
Hide resolved
lib/rust/parser/generate-java/java/org/enso/syntax2/Parser.java
Outdated
Show resolved
Hide resolved
lib/rust/parser/generate-java/java/org/enso/syntax2/Parser.java
Outdated
Show resolved
Hide resolved
lib/rust/parser/generate-java/java/org/enso/syntax2/Parser.java
Outdated
Show resolved
Hide resolved
This reverts commit 285e2e6.
I moved the thread-local buffer-recycling logic to the Java bindings, to keep the Rust code threading-agnostic in case we would like to compile it to WASM in the future. |
lib/rust/parser/generate-java/java/org/enso/syntax2/Parser.java
Outdated
Show resolved
Hide resolved
engine/runtime-parser/src/test/java/org/enso/compiler/core/EnsoParserMultiThreadedTest.java
Show resolved
Hide resolved
@@ -69,7 +70,6 @@ class Compiler( | |||
if (config.outputRedirect.isDefined) | |||
new PrintStream(config.outputRedirect.get) | |||
else context.getOut | |||
private lazy val ensoCompiler: EnsoParser = new EnsoParser() |
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.
Looks like the original code was buggy. The Compiler
should have implemented Closable
interface and call ensoCompiler.close()
on the EnsoParser
. I am not sure what's the overhead of a single EnsoParser
instance and its natively allocated memory, but this must have leaked a lot during unit test execution!
@hubertp did reduce the memory, but the Rust parser native part might now even have been visible in the JVM heap dump - only in RSS...
} catch (URISyntaxException ex) { | ||
root = new File(".").getAbsoluteFile(); | ||
private static final FinalizationManager finalizationManager = new FinalizationManager(); | ||
private static final Thread finalizationThread = new Thread(finalizationManager.createRunner()); |
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.
This thread is unfortunate overhead. These are some options to avoid it:
- check the
FinalizationManager
queue before parsing starts - used by WeakHashMap.exprungeStaleEntries() - use Cleaner
- leave the cleanup up to the user - e.g. continue to provide instances and
close()
operations
Own Thread
adds overhead. The Cleaner
might be better, but there seems to be a thread per each Cleaner
anyway. exprungingStaledParser
might be good, but may leave some of the latest parser(s) pending for cleanup. We do get a shutdown callback in the interpreter - e.g. we can explicitly request cleanup of EnsoContext.getCompiler()
parser workers...
Best Option? Exprunging stale & Close
Having exprungeStaleParsers()
check and a way to trigger it (or request shutdown of all existing) when EnsoContext.shutdown()
would do the trick too without need for any special Thread
or decisions of GC.
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.
Can we name it? I often look into thread dump and hunting down unnamed ones is a real PITA.
@@ -700,7 +696,7 @@ class Compiler( | |||
* @return A Tree representation of `source` | |||
*/ | |||
def parseInline(source: CharSequence): Tree = | |||
ensoCompiler.parse(source) | |||
Parser.parse(source) |
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.
Having EnsoParser
a few lines above and here Parser
it really begs a question: "What's the difference?"/"Which one should I use?" Wouldn't it better for EnsoParser.parse
to simply forward to Parser.parse
to hide that?
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.
I prefer not to expose two different levels of abstraction from EnsoParser
, so that EnsoParser
methods output IR
but not Tree
. Backend parser-users work with IR
exclusively, except the single caller of this parseInline
method. The caller uses the Tree
API to validate whether an input is inline. If we were to rewrite those few lines of code to use the IR
API, we would be able to treat the entire Tree
API as an internal implementation detail of the parser.
...time-instrument-common/src/main/scala/org/enso/interpreter/instrument/ChangesetBuilder.scala
Show resolved
Hide resolved
@@ -1351,6 +1351,17 @@ class RuntimeVisualizationsTest extends AnyFlatSpec with Matchers { | |||
) | |||
) | |||
|
|||
val attachVisualizationResponses = |
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.
This test is failing in CI. It looks to me like it is a nondeterministic failure related to sequentialExecution = false
command delays, and not related to this changeset. The observed failure mode is for all execution updates to show the result of executing the post-modification module. I think this would occur if delaying is applied to the attachVisualization
commands, and not to the textEdit
command, so that the edit is performed before the visualizations are attached.
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.
CCing @4e6
lib/rust/parser/generate-java/java/org/enso/syntax2/Parser.java
Outdated
Show resolved
Hide resolved
lib/rust/parser/generate-java/java/org/enso/syntax2/FinalizationManager.java
Show resolved
Hide resolved
lib/rust/parser/generate-java/java/org/enso/syntax2/Parser.java
Outdated
Show resolved
Hide resolved
private static native ByteBuffer parseTreeLazy(long state, ByteBuffer input); | ||
@Override | ||
public void run() { | ||
freeState(state.getAndSet(0)); |
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.
I see. This guarantees each Rust pointer is cleaned only once.
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.
I'd call runPendingFinalizers()
more frequently. Anyway overall it looks fine.
What happens when there is a Rust code working on a buffer and some other thread drops it? |
The parsing thread "checks out" the So that particular parsing thread's buffer won't be dropped by the
|
Pull Request Description
Stateless (static) parser interface. Buffer-reuse optimization is now hidden within
Parser
implementation. Fixes #11121 and prevents similar bugs.Important Notes
EnsoParser
API, exposing only a higher-level interface.Checklist
Please ensure that the following checklist has been satisfied before submitting the PR:
Scala,
Java,
TypeScript,
and
Rust
style guides. In case you are using a language not listed above, follow the Rust style guide.