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

Ensure that restore_terminal is called on sigint #151

Merged
merged 4 commits into from Aug 10, 2021

Conversation

outkine
Copy link
Contributor

@outkine outkine commented Aug 3, 2021

re #93

@ulrikstrid
Copy link
Collaborator

Do we really want exit 1?

@aantron
Copy link
Owner

aantron commented Aug 3, 2021

Do we really want exit 1?

@ulrikstrid Can you be more precise with your objection? Why wouldn't we want exit 1? What's the alternative (or alternatives)?

Dream.run is used for the top level of relatively simple Dream programs. Most of these will have SIGINT set to Signal_default, which will terminate the process with some non-zero exit code (130 on my platform). How is exit 1 substantially different from this?

The only thing I would suggest, perhaps for a separate PR, is to use Sys.signal, store the previous behavior in a ref (because the API is awkward and you can only get the previous behavior after you have already set the new one), and then trigger the previous behavior by dereferencing the ref. If the previous behavior was Signal_handle, call the function. If it was Signal_ignore, do nothing. If it was Signal_default, call exit 1. @outkine would you mind making this change during this PR? It would extend the usefulness of Dream.run for people that want to set a custom signal handler — we would no longer blindly override it, as done by this PR (though it is an improvement w.r.t. #93).

@aantron
Copy link
Owner

aantron commented Aug 3, 2021

The only other thing I can think of is that 1 might be a bad choice for a Ctrl+C exit code. @ulrikstrid or @outkine, if you are on Mac, what do you get as the exit code for Ctrl+C from just any other OCaml program, or a Dream example without this PR yet applied?

@aantron
Copy link
Owner

aantron commented Aug 3, 2021

Actually, if it works, a better approach might be to use at_exit rather than a signal handler and the normal-exit code that is already in Dream.run. However, the documentation does not mention that this function runs in response to uncaught signals, so that would have to be checked.

@outkine
Copy link
Contributor Author

outkine commented Aug 4, 2021

at_exit does not appear to react to ctrl-c. I don't have a Mac so I'm not sure about the exit code, but I'll make the Sys.signal change.

@outkine
Copy link
Contributor Author

outkine commented Aug 4, 2021

Would it make sense to handle sigkill as well?

@aantron
Copy link
Owner

aantron commented Aug 4, 2021

Would it make sense to handle sigkill as well?

Yes, please.

src/http/http.ml Outdated
@@ -837,6 +837,16 @@ let run
ignore
in

let prev_signal_behavior = ref Sys.Signal_default in
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try combining this with the next line with

let rec prev_signal_behavior := Sys.signal ...

Not sure if it will work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, that's a syntax error

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, I meant

let rec prev_signal_behavior = ref (Sys.signal ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, but is that possible with the new structure?

let prev_signal_behavior = ref Sys.Signal_default in

let handler () = (Sys.Signal_handle (fun signal ->
  restore_terminal ();
  match !prev_signal_behavior with
    | Sys.Signal_default -> exit 1
    | Sys.Signal_handle f -> f signal
    | Sys.Signal_ignore -> ignore ()
)) in

prev_signal_behavior := Sys.signal Sys.sigint @@ handler ();
prev_signal_behavior := Sys.signal Sys.sigterm @@ handler ();

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new structure is incorrect AFAIK, as the derferencing of prev_signal_behavior is suspended inside closures (fun signal -> ...), which are only called upon actual reception of signals. By then, the setup code will have assigned prev_signal_behavior twice, the second time for SIGTERM, completely losing the SIGINT previous behavior, and making both signals behave as the previous behavior of SIGTERM.

By far the easiest way to get this code right (and to make it immediately legible for readers) is to use a local reference for each signal, and you will probably be able to use the rec in that case, too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not exactly sure what you mean by using "a local reference for each signal". It seems to me that the ref has to be passed as an argument to some sort of create_handler function, no?

let create_handler prev_signal_behavior = (Sys.Signal_handle (fun signal ->
  restore_terminal ();
  match !prev_signal_behavior with
    | Sys.Signal_default -> exit 1
    | Sys.Signal_handle f -> f signal
    | Sys.Signal_ignore -> ignore ()
)) in

let rec sigint_behavior = ref (Sys.signal Sys.sigint @@ handler sigint_behavior);

Except it looks like this rec is also not possible, I'm getting a compilation error:

This kind of expression is not allowed as right-hand side of `let rec'ocamllsp

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the ref can and really should be created inside the create_handler function, which guarantees that it will be local to that handler. create_handler should take a signal number, not a ref to a signal behavior.

@outkine
Copy link
Contributor Author

outkine commented Aug 4, 2021

Would it make sense to handle sigkill as well?

Yes, please.

Actually, it looks like it's not possible. Setting any handler for Sys.sigkill throws:

Fatal error: exception Sys_error("Invalid argument")

@aantron
Copy link
Owner

aantron commented Aug 4, 2021

Would it make sense to handle sigkill as well?

Yes, please.

Actually, it looks like it's not possible. Setting any handler for Sys.sigkill throws:

Fatal error: exception Sys_error("Invalid argument")

Try SIGTERM instead. One or both of us may have confused the signals. SIGKILL is, looking again, the uncatchable one.

@aantron aantron merged commit 2714e1c into aantron:master Aug 10, 2021
@aantron
Copy link
Owner

aantron commented Aug 10, 2021

Looks good now, thanks!

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.

None yet

3 participants