Skip to content
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

Remove Lazy from native and bytecode modes: not thread safe and sometimes SIGSEGV #229

Merged
merged 1 commit into from
Sep 11, 2024

Conversation

edwintorok
Copy link
Contributor

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

@edwintorok
Copy link
Contributor Author

edwintorok commented Sep 11, 2024

The failures in the OCaml-CI are probably due to missing bounds on opam packages, would be nice to fix them, although as a separate PR.
The Github CI confirms that the unit tests still pass.

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 <[email protected]>
@seliopou seliopou merged commit 69e166a into inhabitedtype:master Sep 11, 2024
1 check failed
edwintorok added a commit to edwintorok/xs-opam that referenced this pull request Sep 13, 2024
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 <[email protected]>
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