-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
Implement synchronous init failure for gen processes #6843
Implement synchronous init failure for gen processes #6843
Conversation
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.
Beautiful. 💯
I propose a function
Interesting questionWe maybe should take the opportunity to change the behaviour of purging Example:
There is no way for the parent calling
If I understood the code, that is... I think that a logical behaviour would be to never leave an Changing this would be subtly backwards incompatible, though. Would it be worth it to do that in OTP-26.0? |
220ac3b
to
fe36d8e
Compare
CT Test Results 3 files 142 suites 1h 38m 36s ⏱️ For more details on these failures, see this check. Results for commit 381648e. ♻️ This comment has been updated with latest results. To speed up review, make sure that you have read Contributing to Erlang/OTP and that all checks pass. See the TESTING and DEVELOPMENT HowTo guides for details about how to run test locally. Artifacts// Erlang/OTP Github Action Bot |
You could argue for both cases.
In other words, I think it will always be unpredictable? I would say that never removing EXIT from the message queue would be the predictable thing to do but it is also annoying and it causes the caller to do more work if you leave them there. |
A crash during Current error replies ( A server that returns an error via We can achieve a point where servers that are not broken (use |
It does not necessarily need to run but it may want to do some clean up that is expensive and therefore does not want to block the caller. Would that be a possibility? |
Then the caller cannot know when the server can be restarted, and the link may fire any time after returning the error. Wouldn't this be a special case of server start so you should return something like |
I see, thanks! So in that case I agree that we could always guarantee EXIT to be removed and also make it predictable (with less work on the caller). 👍 |
proc_lib:init_ack(Starter, {ok, self()}), | ||
loop( | ||
Parent, Name, State, CbCache, infinity, | ||
HibernateAfterTimeout, Debug); | ||
{ok, {ok, State, TimeoutOrHibernate}} | ||
when ?is_timeout(TimeoutOrHibernate); | ||
TimeoutOrHibernate =:= hibernate -> | ||
proc_lib:init_ack(Starter, {ok, self()}), | ||
loop(Parent, Name, State, CbCache, TimeoutOrHibernate, HibernateAfterTimeout, Debug); | ||
proc_lib:init_ack(Starter, {ok, self()}), | ||
loop( | ||
Parent, Name, State, CbCache, TimeoutOrHibernate, | ||
HibernateAfterTimeout, Debug); | ||
{ok, {ok, State, {continue, _}=Continue}} -> | ||
proc_lib:init_ack(Starter, {ok, self()}), | ||
loop(Parent, Name, State, CbCache, Continue, HibernateAfterTimeout, Debug); | ||
proc_lib:init_ack(Starter, {ok, self()}), | ||
loop( | ||
Parent, Name, State, CbCache, Continue, | ||
HibernateAfterTimeout, Debug); |
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.
What is the difference? I can only see changes in indentation and line breaks.
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.
Yes. Those are only indentation and line break changes.
There were trailing whitespaces and lines longer than 80, which annoyed me...
To summarize how the
Currently
Proposed change
|
fe36d8e
to
a6e8664
Compare
Sorry for my brainfarting, but I am trying to clarify this to me and for an OTP Technical Board soon to be, where I want to discuss if we dare to clean up the exit message behaviour for the major release OTP-26.0. Summary of the summaryA The second commit (first after the one that introduces test cases) changes how synchronous the start functions are:
These changes, I think, can be regarded as bug fixes, so we should simply do them. The third commit changes when
The change is to always consume the |
88611ab
to
e1124c9
Compare
Added documentation |
e1124c9
to
a372035
Compare
* Implement a function `proc_lib:init_fail/2,3` to call from `proc_lib` started processes when they fail to initialize instead of `proc_lib:init_ack/1,2`. The new function sends a `{nack,,}` to the spawner that then uses a process monitor on the spawned process to await the monitor `'DOWN'` message before returning a failure to the spawner. This avoids a race when using `proc_lib:init_ack/1,2` which allows the spawner to get and return the failure return value, and restart a new server instance while the old instance not yet has been scheduled for exit and cleanup. The old server instance may hold a VM resource such as a named ETS table that the new server instance fails to create. * Use the new init fail function in Kernel and Stdlib. * Fix a bug when `proc_lib:start_link/*` is called from a process that does _not_ trap exits and the `init/1` function simply exits with reason normal without calling `proc_lib:init_fail/2,3 or `proc_lib:init_ack/1,2`. Then the caller would wait for the start `Timeout` which could be `infinity`. This is solved by using a process monitor also for `proc_lib:start_link/*`.
a372035
to
28241ad
Compare
Rebased to a later 'master' |
This is the documentation that I have updated:
These applications seem they should be updated to use |
After fixing how proc_lib:start_link/* handles exit(normal) it is, from an init/1 function, equivalent to call exit(Reason) instead of proc_lib:init_fail({error, Reason}, {exit, Reason}).
972f88e
to
6eea2eb
Compare
"Simplify error propagation" and "Clarify documentation" are new, the others are rebased. |
See GitHub PR#6843. When `init()` function exits, return `{error, EXIT_reason}`. A link, of course, still may kill the caller. Never leave an {`EXIT',_,_} message lingering after an error return.
6eea2eb
to
2f5efa0
Compare
Documented the new behaviour of consuming |
7d81867
to
897bd2e
Compare
|
||
<func> | ||
<name since="OTP-26.0">init_fail(Ret, Exception) -> no_return()</name> | ||
<name since="OTP-26.0">init_fail(Parent, Ret, Exception) -> no_return()</name> |
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.
Do we really need to be able to raise every possible exception there is here? Wouldn't raising an exit exception with an exit Reason
as last argument instead of Exception
be enough?
Using the class throw
here seems really fishy. I would actually say it is plain wrong.
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.
We probably do not need to raise every possible exception here, but; this function should be called from a process started with the very general proc_lib:start{_link,monitor}/3,4,5
, which expects the started process to run to an end in any manner; any value or exception.
So if there is user code that today, for some reason throws some exception, say it wants the logging in the top level catch in proc_lib:init_p/3,5
; to make it possible for that code to use the new proc_lib:init_fail/2,3
and raise any exception, I think it should handle also the the really fishy in this context class throw
.
Should a process unconditionally exit by, for example process_flag(trap_exit, false), exit(self(), suicide)
, it will circumvent the top level catch in init_p()
. Should it catch the exception from init_fail()
it would mess up the exception it itself requested. Both cases, I think, are misuses that we cannot guard against. The proc_lib:start
process simply has to run to an end.
Implement a function
proc_lib:init_fail/2,3
to call fromproc_lib
started processes when they fail to initialize instead ofproc_lib:init_ack/1,2
. The new function sends a{nack,,}
to the spawner that then uses a process monitor on the spawned process to await the monitor'DOWN'
message before returning a failure to the spawner.This avoids a race when using
proc_lib:init_ack/1,2
whichallows the spawner to get and return the failure return value, and restart a new server instance while the old instance not yet has been scheduled for exit and cleanup. The old server instance may hold a VM resource such as a named ETS table that the new server instance fails to create.
Use the new init fail function in Kernel and Stdlib.