Skip to content

Fix for the "compiler mirror not found" error when using dynamic execution + improvements made along the way #91

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

Merged
merged 8 commits into from
Apr 18, 2025

Conversation

jjudd
Copy link

@jjudd jjudd commented Apr 18, 2025

The primary problem this PR fixes is described below. I made a few other improvements along the way and included them on this PR as well.

Fix race condition in ScalaCompile by selectively interrupting futures with Thread.interrupt

Prior to this change there was a bug when ScalaCompile actions were used with dynamic execution. The bug would cause builds to fail with the error message "compiler mirror not found". It is my understanding this message typically indicates a problem loading the Scala Library jar during compilation.

The error would always occur in the local multiplex worker's logs and not the remote logs.

This commit fixes this error by not using Thread.interrupt when cancelling the future running the ScalaCompile action.

My hypothesis on why this error occurs is as follows: imagine one of the classloaders in the shared ScalaInstance is in use on Thread A. That thread gets cancelled and interrupted via Thread.interrupt. This causes some kind of persistent error inside either the classloader or another part of Zinc or the Scala compiler. We correctly ignore the error on Thread A because the request it was handling was cancelled. Another thread is working a non-cancelled request, it uses that same ScalaInstance, hit that persistent error, and fails.

By not using Thread.interrupt we don't trigger the persistent error and thus avoid the bug.

Copy link

@jnowjack-lucidchart jnowjack-lucidchart left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did my best to look through and things look good to me. Would probably still be good to get a second pair of eyes to review.

@@ -76,6 +76,10 @@ object ZincRunnerWorkerConfig {
*/
object ZincRunner extends WorkerMain[ZincRunnerWorkerConfig] {

// Interrupting concurrent Zinc/Scala compilation with a shared ScalaInstance (and thus shared classloaders)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be worth elaborating here on your hypothesis for why these errors are happening.

jjudd added 4 commits April 18, 2025 10:07
We were copying a file used by multiple threads directly to its
destination. Problem is that copying is not an atomic action, so we
could end up in states where the file wasn't correct when it was used.

This should avoid that issue by first copying the file to a temp file
and then using an atomic move to move the file to the destination used
by other threads.
@jjudd jjudd force-pushed the jjudd-scala-compiler-mirror-error-fix branch 2 times, most recently from 3f63f80 to 815b291 Compare April 18, 2025 18:31
jjudd added 3 commits April 18, 2025 12:41
…s with Thread.interrupt

Prior to this change there was a bug when ScalaCompile actions were used
with dynamic execution. The bug would cause builds to fail with the
error message "compiler mirror not found". It is my understanding this
message typically indicates a problem loading the Scala Library jar
during compilation.

The error would always occur in the local multiplex worker's logs and
not the remote logs.

This commit fixes this error by not using Thread.interrupt when
cancelling the future running the ScalaCompile action.

My hypothesis on why this error occurs is as follows: imagine one of the
classloaders in the shared ScalaInstance is in use on Thread A. That
thread gets cancelled and interrupted via Thread.interrupt. This causes
some kind of persistent error inside either the classloader or another
part of Zinc or the Scala compiler. We correctly ignore the error on
Thread A because the request it was handling was cancelled. Another
thread is working a non-cancelled request, it uses that same
ScalaInstance, hit that persistent error, and fails.

By not using Thread.interrupt we don't trigger the persistent error and
thus avoid the bug.
@jjudd jjudd force-pushed the jjudd-scala-compiler-mirror-error-fix branch from 815b291 to 9534999 Compare April 18, 2025 18:42
@jjudd jjudd merged commit 6b13254 into lucid-master Apr 18, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants