Skip to content

Commit e0ebd72

Browse files
Merge pull request #993 from talex5/forward-signals
Make signal handling work on OCaml 5 and with Eio
2 parents 90e0d6e + ed81ff9 commit e0ebd72

File tree

6 files changed

+64
-13
lines changed

6 files changed

+64
-13
lines changed

CHANGES

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,21 @@
2323

2424
* Lwt_preemptive.run_in_main_dont_wait to run a function in the main preemptive thread but without waiting for the result. (Kate Deplaix, #960)
2525

26+
* Lwt_unix.handle_signal and Lwt_engine.forwards_signal to allow other IO libraries (such as Eio) to share signal handlers. (Thomas Leonard, #993, #991)
27+
2628
====== Build ======
2729

2830
* Remove unused dependency in dune file. (#969, Kate Deplaix)
2931

3032
* Fix some compilation warnings for C stubs with OCaml 5. (#976, Antonin Décimo)
3133

34+
====== Fixes ======
35+
36+
* Use SA_ONSTACK on OCaml5 to avoid SIGSEGV. (Thomas Leonard, #993, #981)
37+
38+
* Fix race in worker loop. (Thomas Leonard, #993, #994)
39+
40+
3241
===== 5.6.1 =====
3342

3443
====== Fixes ======

src/unix/lwt_engine.ml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ class virtual abstract = object(self)
108108
method timer_count = Lwt_sequence.length timers
109109

110110
method fork = ()
111+
112+
method forwards_signal (_signum:int) = false
111113
end
112114

113115
class type t = object
@@ -433,6 +435,7 @@ let readable_count () = !current#readable_count
433435
let writable_count () = !current#writable_count
434436
let timer_count () = !current#timer_count
435437
let fork () = !current#fork
438+
let forwards_signal n = !current#forwards_signal n
436439

437440
module Versioned =
438441
struct

src/unix/lwt_engine.mli

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ val fake_io : Unix.file_descr -> unit
5353
val fork : unit -> unit
5454
(** Called internally by Lwt_unix.fork to make sure we don't get strange behaviour *)
5555

56+
val forwards_signal : int -> bool
57+
(** [forwards_signal signum] is [true] if the engine will call {!Lwt_unix.handle_signal}
58+
when signal [signum] occurs. In this case, Lwt will not install its own signal handler.
59+
60+
Normally, this just returns [false], but when Lwt is used in combination
61+
with other IO libraries, this allows sharing e.g. the SIGCHLD handler. *)
62+
5663
(** {2 Engines} *)
5764

5865
(** An engine represents a set of functions used to register different
@@ -82,6 +89,7 @@ class virtual abstract : object
8289
method readable_count : int
8390
method writable_count : int
8491
method timer_count : int
92+
method forwards_signal : int -> bool
8593

8694
(** {2 Backend methods} *)
8795

src/unix/lwt_unix.cppo.ml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2227,12 +2227,19 @@ let event_notifications = ref (Lwt_engine.on_readable (init_notification ()) han
22272227
| Signals |
22282228
+-----------------------------------------------------------------+ *)
22292229

2230-
external set_signal : int -> int -> unit = "lwt_unix_set_signal"
2231-
external remove_signal : int -> unit = "lwt_unix_remove_signal"
2230+
external set_signal : int -> int -> bool -> unit = "lwt_unix_set_signal"
2231+
external remove_signal : int -> bool -> unit = "lwt_unix_remove_signal"
22322232
external init_signals : unit -> unit = "lwt_unix_init_signals"
2233+
external handle_signal : int -> unit = "lwt_unix_handle_signal"
22332234

22342235
let () = init_signals ()
22352236

2237+
let set_signal signum notification =
2238+
set_signal signum notification (Lwt_engine.forwards_signal signum)
2239+
2240+
let remove_signal signum =
2241+
remove_signal signum (Lwt_engine.forwards_signal signum)
2242+
22362243
module Signal_map = Map.Make(struct type t = int let compare a b = a - b end)
22372244

22382245
type signal_handler = {

src/unix/lwt_unix.cppo.mli

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,14 @@ val reinstall_signal_handler : int -> unit
854854
signal handler (with [Sys.set_signal]). This is useful in case
855855
another part of the program install another signal handler. *)
856856

857+
val handle_signal : int -> unit
858+
(** [handle_signal signum] acts as if Lwt had received the [signum] signal.
859+
This allows another IO library to install the handler, perform its own
860+
handling, but still notify Lwt. It is particularly useful for SIGCHLD,
861+
where several IO libraries may be spawning sub-processes.
862+
863+
This function is thread-safe. *)
864+
857865
(** {2 Sockets} *)
858866

859867
type inet_addr = Unix.inet_addr

src/unix/lwt_unix_stubs.c

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -169,16 +169,25 @@ value lwt_unix_system_byte_order() {
169169
int lwt_unix_launch_thread(void *(*start)(void *), void *data) {
170170
pthread_t thread;
171171
pthread_attr_t attr;
172+
sigset_t mask, old_mask;
172173

173174
pthread_attr_init(&attr);
174175

175176
/* The thread is created in detached state so we do not have to join
176177
it when it terminates: */
177178
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
178179

180+
/* Block all signals, otherwise ocaml handlers defined with the
181+
module Sys may be executed in the new thread, oops... */
182+
sigfillset(&mask);
183+
pthread_sigmask(SIG_SETMASK, &mask, &old_mask);
184+
179185
int zero_if_created_otherwise_errno =
180186
pthread_create(&thread, &attr, start, data);
181187

188+
/* Restore the signal mask for the calling thread. */
189+
pthread_sigmask(SIG_SETMASK, &old_mask, NULL);
190+
182191
pthread_attr_destroy(&attr);
183192

184193
return zero_if_created_otherwise_errno;
@@ -801,6 +810,11 @@ static void handle_signal(int signum) {
801810
}
802811
}
803812

813+
CAMLprim value lwt_unix_handle_signal(value val_signum) {
814+
handle_signal(caml_convert_signal_number(Int_val(val_signum)));
815+
return Val_unit;
816+
}
817+
804818
#if defined(LWT_ON_WINDOWS)
805819
/* Handle Ctrl+C on windows. */
806820
static BOOL WINAPI handle_break(DWORD event) {
@@ -813,7 +827,7 @@ static BOOL WINAPI handle_break(DWORD event) {
813827
#endif
814828

815829
/* Install a signal handler. */
816-
CAMLprim value lwt_unix_set_signal(value val_signum, value val_notification) {
830+
CAMLprim value lwt_unix_set_signal(value val_signum, value val_notification, value val_forwarded) {
817831
#if !defined(LWT_ON_WINDOWS)
818832
struct sigaction sa;
819833
#endif
@@ -825,6 +839,8 @@ CAMLprim value lwt_unix_set_signal(value val_signum, value val_notification) {
825839

826840
signal_notifications[signum] = notification;
827841

842+
if (Bool_val(val_forwarded)) return Val_unit;
843+
828844
#if defined(LWT_ON_WINDOWS)
829845
if (signum == SIGINT) {
830846
if (!SetConsoleCtrlHandler(handle_break, TRUE)) {
@@ -840,7 +856,11 @@ CAMLprim value lwt_unix_set_signal(value val_signum, value val_notification) {
840856
}
841857
#else
842858
sa.sa_handler = handle_signal;
859+
#if OCAML_VERSION >= 50000
860+
sa.sa_flags = SA_ONSTACK;
861+
#else
843862
sa.sa_flags = 0;
863+
#endif
844864
sigemptyset(&sa.sa_mask);
845865
if (sigaction(signum, &sa, NULL) == -1) {
846866
signal_notifications[signum] = -1;
@@ -851,14 +871,17 @@ CAMLprim value lwt_unix_set_signal(value val_signum, value val_notification) {
851871
}
852872

853873
/* Remove a signal handler. */
854-
CAMLprim value lwt_unix_remove_signal(value val_signum) {
874+
CAMLprim value lwt_unix_remove_signal(value val_signum, value val_forwarded) {
855875
#if !defined(LWT_ON_WINDOWS)
856876
struct sigaction sa;
857877
#endif
858878
/* The signal number is valid here since it was when we did the
859879
set_signal. */
860880
int signum = caml_convert_signal_number(Int_val(val_signum));
861881
signal_notifications[signum] = -1;
882+
883+
if (Bool_val(val_forwarded)) return Val_unit;
884+
862885
#if defined(LWT_ON_WINDOWS)
863886
if (signum == SIGINT)
864887
SetConsoleCtrlHandler(NULL, FALSE);
@@ -964,18 +987,11 @@ void initialize_threading() {
964987
| Worker loop |
965988
+-----------------------------------------------------------------+ */
966989

967-
/* Function executed by threads of the pool. */
990+
/* Function executed by threads of the pool.
991+
* Note: all signals are masked for this thread. */
968992
static void *worker_loop(void *data) {
969993
lwt_unix_job job = (lwt_unix_job)data;
970994

971-
#if defined(HAVE_PTHREAD)
972-
/* Block all signals, otherwise ocaml handlers defined with the
973-
module Sys may be executed in this thread, oops... */
974-
sigset_t mask;
975-
sigfillset(&mask);
976-
pthread_sigmask(SIG_SETMASK, &mask, NULL);
977-
#endif
978-
979995
/* Execute the initial job if any. */
980996
if (job != NULL) execute_job(job);
981997

0 commit comments

Comments
 (0)