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

Thread.delay blocks signal delivery in OCaml 4.07 #7903

Closed
vicuna opened this issue Jan 28, 2019 · 10 comments
Closed

Thread.delay blocks signal delivery in OCaml 4.07 #7903

vicuna opened this issue Jan 28, 2019 · 10 comments
Assignees
Milestone

Comments

@vicuna
Copy link

vicuna commented Jan 28, 2019

Original bug ID: 7903
Reporter: lindig
Assigned to: @xavierleroy
Status: resolved (set by @xavierleroy on 2019-03-11T09:40:14Z)
Resolution: fixed
Priority: normal
Severity: minor
Platform: Unix
OS: Linux
Version: 4.07.1
Target version: 4.09.0+dev
Fixed in version: 4.09.0+dev
Category: runtime system and C interface
Monitored by: robhoes @ygrek

Bug description

A thread in Thread.delay won't receive a signal like SIGTERM in OCaml 4.07 but it did in OCaml 4.06. This makes it difficult to end a process that is delayed in a thread. This might be a deliberate change but I would like to see it discussed.

Steps to reproduce

(* ocamlc -thread -o foo unix.cma threads.cma foo.ml
*)

let on_sigterm _ =
prerr_endline "Received signal";
flush stderr;
exit 0

let () =
Sys.set_signal Sys.sigterm Sys.(Signal_handle on_sigterm);
Sys.catch_break true;
while true; do
Thread.delay 300.
done

Control C does not end the thread while it is in Thread.delay.

@vicuna
Copy link
Author

vicuna commented Jan 28, 2019

Comment author: lindig

  • Preemption #1533: (a) The implementation of Thread.yield for system thread
    now uses nanosleep(1) for enabling better preemption.
    (b) Thread.delay is now an alias for Unix.sleepf.
    (Jacques-Henri Jourdan, review by Xavier Leroy and David Allsopp)
  • Protect Unix.sleep against interruptions by handled signals.
    Before, a handled signal could cause Unix.sleep to return early.
    Now, the sleep is restarted until the given time is elapsed.
    (Xavier Leroy)

@vicuna
Copy link
Author

vicuna commented Jan 28, 2019

Comment author: @edwintorok

It might be useful for the stdlib implementation to reacquire the ocaml runtime lock, run the equivalent of caml_process_signals, and release the runtime lock again (perhaps by tracking how long that took via gettimeofday).
This would allow processing SIGTERM in a timely manner, while still allowing for Thread.delay/sleep to return only after the correct time has elapsed in the common case. (the signal handler could still call exit of course)

@vicuna
Copy link
Author

vicuna commented Jan 28, 2019

Comment author: lindig

diff --git a/otherlibs/systhreads/thread.ml b/otherlibs/systhreads/thread.ml
index ec05f39..bf47d75 100644
--- a/otherlibs/systhreads/thread.ml
+++ b/otherlibs/systhreads/thread.ml
@@ -71,7 +71,7 @@ let () =

(* Wait functions *)

-let delay time = ignore(Unix.select [] [] [] time)
+let delay = Unix.sleepf

let wait_read fd = ()
let wait_write fd = ()

@vicuna
Copy link
Author

vicuna commented Jan 29, 2019

Comment author: lindig

In the case of EINTR the runtime system could check for signals and deliver them.

CAMLprim value unix_sleep(value duration)
{
double d = Double_val(duration);
if (d < 0.0) return Val_unit;
{
struct timespec t;
int ret;
caml_enter_blocking_section();
t.tv_sec = (time_t) d;
t.tv_nsec = (d - t.tv_sec) * 1e9;
do {
ret = nanosleep(&t, &t);
} while (ret == -1 && errno == EINTR);
caml_leave_blocking_section();
if (ret == -1) uerror("sleep", Nothing);
}

@vicuna
Copy link
Author

vicuna commented Feb 22, 2019

Comment author: lindig

Can we get an opinion on this? If the changed in behaviour in OCaml 4.07 is as intended, how should one terminate sleeping threads? it could mean that threads never should sleep long to guarantee that signals can be delivered.

@vicuna
Copy link
Author

vicuna commented Feb 25, 2019

Comment author: @jhjourdan

Can we get an opinion on this? If the changed in behaviour in OCaml 4.07 is as intended, how should one terminate sleeping threads? it could mean that threads never should sleep long to guarantee that signals can be delivered.

Sorry for the delay. I will have a look at some point during the week.

In the case of EINTR the runtime system could check for signals and deliver them.

Indeed, this seems possible. However, the signal handling system in the OCaml runtime is subtle, and we have to make sure this does not have unexpected implications.

@vicuna
Copy link
Author

vicuna commented Mar 9, 2019

Comment author: @xavierleroy

Sorry I didn't see this report earlier. Yes, the 4.07 behavior is not quite right and an unintended consequence.

We could enter/leave around each call to nanosleep:

do {
caml_enter_blocking_section();
ret = nanosleep(&t, &t);
caml_leave_blocking_section();
} while (ret == -1 && errno == EINTR);

I think this would call signal handlers in a timely manner. However, the extra leave / enter calls can take arbitrarily long time, so we can end up sleeping too long.

@vicuna
Copy link
Author

vicuna commented Mar 10, 2019

Comment author: @jhjourdan

However, the extra leave / enter calls can take arbitrarily long time, so we can end up sleeping too long.

Isn't that already the behavior the the libC? I mean, if you write a pure C program which contains time-consuming signal handlers, then the sleep-like functions are not a suitable way of pausing an accurate amount of time.

@vicuna
Copy link
Author

vicuna commented Mar 10, 2019

Comment author: @xavierleroy

Proposed fix at #2306

@vicuna
Copy link
Author

vicuna commented Mar 11, 2019

Comment author: @xavierleroy

Fixed in commit 187ceb6.

@vicuna vicuna closed this as completed Mar 11, 2019
@vicuna vicuna added the stdlib label Mar 14, 2019
@vicuna vicuna added this to the 4.09.0 milestone Mar 14, 2019
@vicuna vicuna added the bug label Mar 20, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants