-
Notifications
You must be signed in to change notification settings - Fork 3.3k
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
Race condition over current working directory in parallel compiler #7699
Comments
Thanks @potatosalad for the fantastic report!
I would also like to point out that many projects actually rely on the |
I ran the same test against potatosalad/sentry_bug_232 using
Yeah, the more I've thought about it, I'm not sure if there is a "better" solution there at all. Short of changing the way Maybe resolving the paths prior to spawning the compilation workers would help prevent the exception from being raised from within the parallel compiler itself? Currently, if plataformatec/nimble_parsec had a module called |
Awesome @potatosalad. I will make sure that |
Closing in favor of #7833. |
@josevalim is it safe to use |
They are both private APIs, so none of them is recommended. :) The proper answer would be |
Is there a good way to get the version of the dep via |
|
@josevalim cool, thank you! |
Perform a test run, skipping all tests, before actually running the tests, in order to force compilation of all the *_test.exs files, in an attempt to workaround the parallel compiler issue in elixir-lang/elixir#7699 The parallel compiler has changed since Elixir 1.8, such that this workaround may no longer be necessary when using later versions.
Perform a test run, skipping all tests, before actually running the tests, in order to force compilation of all the *_test.exs files, in an attempt to workaround the parallel compiler issue in elixir-lang/elixir#7699 The parallel compiler has changed since Elixir 1.8, such that this workaround may no longer be necessary when using later versions.
Environment
Current behavior
A mix project with
sentry
as a dependency will occasionally fail to compile with the following exception (roughly ~5% of the time on Elixir master-6fd0cd7):TL;DR There's a race condition over the current working directory competing with the expected working directory as used by
Kernel.ParallelCompiler
. See "Examples" below.The compilation race condition while having getsentry/sentry-elixir as a dependency was found a while ago as reported in this issue: getsentry/sentry-elixir#232 (see also this forum post).
As partially pointed out by @michaelstalker in this comment, the race condition occurs between two separate processes fighting for control of the current working directory.
Sentry's problem is this call to
Mix.Dep.loaded/1
which causesFile.cd!/2
to be called for itsrebar
andrebar3
dependencies (seelib/mix/lib/mix/dep/loader.ex#L300
). Occasionally at the same time,Code.find_file/2
attempts to lookup a file based on the current working directory which may be wrong.I originally put together a demonstration mix project, but the race condition only occurred ~5% of the time and can be difficult to see what the actual problem is.
So, I put together potatosalad/elixir_uncompilable which has a much higher chance of exposing the race condition in
Kernel.ParallelCompiler
.More details are provided below in the examples.
Examples
Simplified non-concurrent example
Simplified concurrent example
Simplified mix project that is designed to force the race condition to occur
See potatosalad/elixir_uncompilable (example of failing build on travis).
The resulting exception differs slightly by version of Elixir.
For example, with Elixir 1.6.5:
Compared with
master
-ish (elixir-lang/elixir@6fd0cd7
):Demonstration mix project that first uncovered the race condition
See potatosalad/sentry_bug_232 (occurs roughly 5% of the time when schedulers are restricted to
2:2
).See
lib/sentry/event.ex#L42-L46
for the call-that-caused-it-all.Expected behavior
Possible solutions
First off, let's get the obvious one out of the way: change the
sentry
library to no longer callMix.Dep.loaded(env: Mix.env())
during compilation.However, I think this still exposes an interesting race condition that can and probably does occur with other libraries that either directly or indirectly cause the current working directory to change while in the middle of compilation.
The solution is a tricky one as some of the operations of mix and compilation are heavily reliant on
cwd
in general (which cannot be made thread-safe as far as I understand; seechdir(2)
).For example, OS-level processes could be improved by adding
{:cd, "/path/to/dir"}
as one of the options passed toPort.open/2
.Other solutions might involve performing a pre-compile path resolution check (which could also happen in parallel) before calling
Kernel.ParallelCompiler.compile/2
with the non-relative paths.There may be other solutions here that I'm not initially seeing, too. I primarily wanted to raise awareness of the existence of the race condition and supply documentation/examples on how to reproduce it.
The text was updated successfully, but these errors were encountered: