-
Notifications
You must be signed in to change notification settings - Fork 56
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
Uri is not thread/domain safe #178
Comments
I was able to reproduce the let () =
let barrier = Atomic.make 2 in
let parse () =
Atomic.decr barrier;
while Atomic.get barrier <> 0 do
Domain.cpu_relax ()
done;
Uri.of_string "http://localhost:8080/" |> ignore
in
let d1 = Domain.spawn parse and d2 = Domain.spawn parse in
Domain.join d1 |> ignore;
Domain.join d2 |> ignore;
() NOTE: It may take a few retries! EDIT: I updated the above to use a different URI. |
Interestingly Uri-re isn't safe either:
fails after a couple of tries with:
|
Uri_legacy uses Lazy too internally :( |
Here is a slightly tweaked testcase that crashes on OCaml 4.14.2 too (with a segfault too sometimes :( ):
FWIW I couldn't get the equivalent uri-re code to crash/fail on OCaml 4 yet, although that doesn't mean it is thread safe there. |
See [issue in Uri](mirage/ocaml-uri#178).
Potential workaround is to do this in Angstrom:
Or this:
There might be other ways of fixing it too |
It's somewhat worrying to see that test case segfaulting. Do you have a backtrace for that, @edwintorok? |
See [issue in Uri](mirage/ocaml-uri#178).
OCaml 4.14.2, uri 4.4.0 and angstrom 0.16.0
Disabling compaction doesn't help and the debug runtime doesn't give any particularly useful clues either. Perhaps we should move debugging this part to an ocaml/ocaml issue on 4.14 if it is not meant to SIGSEGV (which I asume is not, it is meant to raise the exception). |
See [issue in Uri](mirage/ocaml-uri#178).
Angstrom (and as a consequence Uri.of_string) is not thread-safe. On OCaml 4 this actually leads to a SIGSEGV sometimes, on OCaml 5 it raises CamlinternalLazy.Undefined. This could be fixed by adding a 'Lazy.force p |> ignore' so that the Lazy value is never observable from a concurrent/parallel context, but I prefer to remove Lazy completely, at least until the reason for the OCaml 4 segfault is understood. Using a ref here is safe, because the ref is only changed once before 'fix_direct' returns, so it is never changed from a concurrent/parallel context. And it still preserves the optimization that Lazy did: the fixpoint is only computed once. This avoids the crash noticed in mirage/ocaml-uri#178 Signed-off-by: Edwin Török <edwin.torok@cloud.com>
Angstrom (and as a consequence Uri.of_string) is not thread-safe. On OCaml 4 this actually leads to a SIGSEGV sometimes, on OCaml 5 it raises CamlinternalLazy.Undefined. This could be fixed by adding a 'Lazy.force p |> ignore' so that the Lazy value is never observable from a concurrent/parallel context, but I prefer to remove Lazy completely, at least until the reason for the OCaml 4 segfault is understood. Using a ref here is safe, because the ref is only changed once before 'fix_direct' returns, so it is never changed from a concurrent/parallel context. And it still preserves the optimization that Lazy did: the fixpoint is only computed once. This avoids the crash noticed in mirage/ocaml-uri#178 Use @gasche's suggestion to avoid an option by storing a function that would raise initially (this function never gets invoked, because the ref is immediately updated) Signed-off-by: Edwin Török <edwin.torok@cloud.com>
Angstrom (and as a consequence Uri.of_string) is not thread-safe. On OCaml 4 this actually leads to a SIGSEGV sometimes, on OCaml 5 it raises `CamlinternalLazy.Undefined`. This could be fixed by adding a `Lazy.force p |> ignore` so that the Lazy value is never observable from a concurrent/parallel context, but I prefer to remove Lazy completely, at least until the reason for the OCaml 4 segfault is understood. Using a ref here is safe, because the ref is only changed once before 'fix_direct' returns. And it still preserves the optimization that Lazy did: the fixpoint is only computed once. While the fixpoint is being calculated the ref is set to a closure that raises an exception. This is compatible with the previous behaviour, where `f` calling its argument in a non-delayed way would've raised `CamlinternalLazy.Undefined`. When used correctly the changing `ref` is not observable from concurrent/parallel context, and even if it would be it wouldn't lead to the program crashing. This avoids the crash noticed in mirage/ocaml-uri#178 Use @gasche's suggestion to avoid an option by storing a function that would raise initially Signed-off-by: Edwin Török <edwin.torok@cloud.com>
Thank you Edwin for the fixes to Angstrom! Now released into opam repo as 0.16.1 |
Using Lazy from a multithreaded program should raise Lazy.Undefined. However due to bugs in the OCaml runtime this was actually crashing. Update to angstrom 0.16.1 which no longer uses Lazy and avoids this crash. This was initially discovered: * mirage/ocaml-uri#178 Fixed in Angstrom: * inhabitedtype/angstrom#229 Runtime bug reported with potential fix from maintainers: * ocaml/ocaml#13430 * ocaml/ocaml#13434 There are various other usages of Lazy, but I couldn't get those to crash yet, so lets fix the known crash for now, and audit/fix the rest next. Signed-off-by: Edwin Török <edwin.torok@cloud.com>
I've looked at the OCaml 5 failure with Uri_re (Uri_re is safe to use on OCaml 4, I wasn't able to find any bugs there). There are a lot of complaints from the thread sanitizer: This has been discussed before, although in the context of Re.Str not being thread-safe https://discuss.ocaml.org/t/re-str-is-not-thread-safe/9425/4?u=edwin, and I think we reasonably believed Re itself to be domain-safe though. Although ocaml/ocaml-re#287 (comment) claims that
|
It's about time to remove Uri_re -- it's been deprecated for some years now, and the older version will still be available in opam. |
We encountered the following:
Our program runs multiple domains in parallel.
I spent a brief moment to look through the Uri and Angstrom code bases. I strongly suspect the problem is that the
Uri.Parser
module uses the Angstromfix
combinator, which useslazy
internally. The problem with that is thatLazy
is not safe to use concurrently / in parallel — an attempt to do so may raise theUndefined
exception. As a workaround we will try to force the parser before spawning domains, but this should really be fixed properly.The text was updated successfully, but these errors were encountered: