-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Reimplement Thread.exit using an exception #10935
Conversation
Previously, Thread.exit tried to behave exactly like pthread_exit(): it stops the current thread immediately, but doesn't stop the whole program as long as other threads keep running. As discussed in ocaml#9071, this behavior is a problem for resource management ("finally" functions are not called). As discussed during the Multicore merge review and reported in ocaml#10927, this behavior is a problem in the presence of multiple domains. As first proposed in ocaml#9100, this PR reimplements Thread.exit by simply raising a dedicated Thread.Exit exception. This is good for resource management ("finally" functions are properly called) and simplifies the implementation tremendously. It does introduce incompatibilities with OCaml 4. In particular, if `Thread.exit` is called from the main program or from a domain but not from a thread started by `Thread.create`, the exception `Thread.Exit` is not automatically caught and will abort the program / the domain, unless explicitly handled by the program.
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.
LGTM
As an aside, it would be nice to know why we don't do the check_memprof_cb
dance when a thread exits by uncaught exception.
I'm done re-reading the discussion of #9701, here is a summary of what I understand.
There are basically two options:
This PR goes for option (1), which seems to have the support of @xavierleroy, @damiendoligez and @jhjourdan. I guess that both options are probably fine. We dearly need a document (manual chapter?) that documents the known hurdles when moving to the Multicore runtime, that people porting their code can check. Changes such as this one should be documented, and clearly we are going to forget about some of them. |
How about both:
? In multicore, the worse consequences of However, this does not suddenly make the exception-raising behaviour a useful design. Thread.exit was explicitly changed from raising an exception to directly exiting the thread a long time ago because this did not work in practice. So I suggest to deprecate it anyway. When raising an exception really does what the programmer intents, how about letting them just raise an exception instead? |
In that scenario, the |
To add some historical background:
Right now, I feel I made the wrong decision back then: the exception-raising behavior is useful, and it's never too late to amend this. |
I am now convinced that an exception The breaking change needed for multicore is an orthogonal issue. What about:
? The point is that nobody here is making an argument that the change preserves the meaning of programs, only a belief that this has no impact most of the time. So even if it happens to have no impact on a program, someone had to review it to make sure. Jacques-Henri's review on the sample that I provided showed that even well-written modern code can be affected (in the case of Async), but before that it also showed that one had to look at the code (and moreover: 1- that it was not always obvious, 2- that it sometimes revealed bugs—I am sorry but if there is a bug in my program by my fault then I want to have a look at it and see for myself how it should be fixed, not have the language developer "fix" it for me). Another point which was not discussed enough is compatibility with OCaml 4. What will you do:
Instead, the above shows that it is possible to implement the standard warning-in-advance before a breaking change. Regarding catch-alls, I think this is a red herring since any way of looking at it, it is a breaking change, but the situation is more complicated in general. This was already reflected in the quote below from the 2003 issue. It shows that even at the time of the From #8110:
|
@gadmm the issue with the solution you are proposing is that it becomes impossible to have a piece of code which both work with OCaml 4.13 (only |
To be clear the proposed solution is to introduce |
I believe that what Jacques-Henri has in mind is to (re)write the program as if
With this approach, if |
Let me detail my previous message because Guillaume tells me that it was unclear. I think that the strategy Jacques-Henri proposes is to:
Unlike the proposal of Guillaume above, this lets people write code that works for both past versions of OCaml (with the old |
My "strategy" was not as clear in my mind as what you explained, @gasche, but I fully agree with what you propose. |
Clearer, thanks. The bit that confused me was "use an attribute to disable the deprecation warning" but I do not think it is a crucial part for what you have in mind. Happy to see that we agree on the big picture regarding the deprecation mechanism. There are two differences in the details which should be easy to sort out:
I proposed that Thread.exit raises Hence my final proposal adapting Gabriel's suggestion:
|
I don't think this is a correct use of deprecation warnings. Deprecation means intent to remove (at some point) so the only appropriate action from users is to stop using the deprecated thing. I have the impression we're over-thinking the Thread.exit issue, but here some more comments, for what they are worth.
|
Following the discussion at ocaml#10935 .
In an attempt to make progress, I added a "deprecated" warning to |
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.
The outlined plan sounds good to me. I agree that it is better to not even deprecate in 4.14. Minor nitpicks on the documentation.
Edit: I meant to approve, but it is not showing up.
|
||
To make it clear that an exception is raised and will trigger | ||
finalizers and catch-all exception handlers, it is recommended | ||
to write [raise {!Thread.Exit}] instead of [{!Thread.exit} ()]. |
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 do not find this explanation for writing directly raise Thread.Exit
convincing. You can just say that the previous behaviour was incompatible which justifies deprecating, and point out that converting to the raising form might need to review and adapt the program. In particular it might be good to point out that it is no longer guaranteed that a call to Thread.exit terminates the thread (I find the wording "unless the thread function handles the exception itself" unclear about accidental "handlings").
I took a look at the implementation and removal of the termination buffer bits and it looks good to me. Edit: |
You mean: if the user-provided uncaught exception handler raises |
Fixes: ocaml#9071 Closes: ocaml#9100
Changes
Outdated
causes `Fun.finally` finalizers to be run and catch-all exception | ||
handlers to abort termination. | ||
(Jacques-Henri Jourdan and Xavier Leroy, review by Damien Doligez, | ||
Guillaume Munch-Maccagnoni, and Enguerrand Decorne) |
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.
Let's credit Gabriel who contributed to the discussion.
First mention that the main addition is the new exception Thread.Exit
?
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.
My cunning plan is to mention the addition of Thread.Exit
in the 4.14 section of Changes. But it means I must sneak it into 4.14 first before the Changes file can be completed.
Fixes: ocaml#9071 Closes: ocaml#9100
356c6cd
to
e08c5f9
Compare
[ Resurrecting #9100 because we need to do something for OCaml 5.00 ]
Previously,
Thread.exit
tried to behave exactly likepthread_exit()
: it stops the current thread immediately, but doesn't stop the whole program as long as other threads keep running.As discussed in #9071, this behavior is a problem for resource management ("finally" functions are not called).
As discussed during the Multicore merge review and reported in #10927, this behavior is a problem in the presence of multiple domains.
As first proposed in #9100, this PR reimplements
Thread.exit
by simply raising a dedicatedThread.Exit
exception. This is good for resource management ("finally" functions are properly called) and simplifies the implementation tremendously.It does introduce incompatibilities with OCaml 4. In particular, if
Thread.exit
is called from the main program or from a domain but not from a thread started byThread.create
, the exceptionThread.Exit
is not automatically caught and will abort the program / the domain, unless explicitly handled by the program.My feeling is that with proper documentation of the incompatibilities, this reimplementation of
Thread.exit
is more useful than just removing the function altogether.