Skip to content

Commit 3a72aab

Browse files
committed
Implement Caml_state->mask_async_callbacks
This is a flag to control the delaying of asynchronous callbacks (signal handlers, finalisers and memprof callbacks). We distinguish two kinds of asynchronous actions: - unrestricted asynchronous callbacks: possibly-raising signal handlers, memprof callbacks and finalisers (including GC alarms). - non-raising actions, such as non-raising signal handlers (e.g. systhread's yield). Masking temporarily delays the execution of asynchronous callbacks. New functions caml_mask and caml_unmask to control which kind of asynchronous callbacks are delayed.
1 parent da1633e commit 3a72aab

File tree

11 files changed

+245
-79
lines changed

11 files changed

+245
-79
lines changed

Changes

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@ Working version
6565
- #9675: Remove the caml_static_{alloc,free,resize} primitives, now unused.
6666
(Xavier Leroy, review by Gabriel Scherer)
6767

68+
- #8961: Switch to disable/enable the processing of asynchronous
69+
callbacks (finalisers, signal handlers, memprof callbacks) on a
70+
per-thread basis. New C functions caml_mask_async_callbacks
71+
and caml_unmask_async_callbacks.
72+
(Guillaume Munch-Maccagnoni, suggestion and advice by Stephen
73+
Dolan, review by TODO)
74+
6875
### Code generation and optimizations:
6976

7077
- #9620: Limit the number of parameters for an uncurried or untupled

otherlibs/systhreads/st_stubs.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ struct caml_thread_struct {
102102
backtrace_slot * backtrace_buffer; /* Saved Caml_state->backtrace_buffer */
103103
value backtrace_last_exn; /* Saved Caml_state->backtrace_last_exn (root) */
104104
struct caml_memprof_th_ctx memprof_ctx;
105+
caml_mask_kind mask_async_callbacks; /* Caml_state->mask_async_callbacks */
105106
};
106107

107108
typedef struct caml_thread_struct * caml_thread_t;
@@ -199,6 +200,7 @@ Caml_inline void caml_thread_save_runtime_state(void)
199200
curr_thread->backtrace_buffer = Caml_state->backtrace_buffer;
200201
curr_thread->backtrace_last_exn = Caml_state->backtrace_last_exn;
201202
caml_memprof_save_th_ctx(&curr_thread->memprof_ctx);
203+
curr_thread->mask_async_callbacks = Caml_state->mask_async_callbacks;
202204
}
203205

204206
Caml_inline void caml_thread_restore_runtime_state(void)
@@ -227,6 +229,7 @@ Caml_inline void caml_thread_restore_runtime_state(void)
227229
Caml_state->backtrace_pos = curr_thread->backtrace_pos;
228230
Caml_state->backtrace_buffer = curr_thread->backtrace_buffer;
229231
Caml_state->backtrace_last_exn = curr_thread->backtrace_last_exn;
232+
Caml_state->mask_async_callbacks = curr_thread->mask_async_callbacks;
230233
caml_memprof_restore_th_ctx(&curr_thread->memprof_ctx);
231234
}
232235

@@ -381,6 +384,7 @@ static caml_thread_t caml_thread_new_info(void)
381384
th->backtrace_buffer = NULL;
382385
th->backtrace_last_exn = Val_unit;
383386
caml_memprof_init_th_ctx(&th->memprof_ctx);
387+
th->mask_async_callbacks = CAML_MASK_NONE;
384388
return th;
385389
}
386390

@@ -751,12 +755,12 @@ CAMLprim value caml_thread_yield(value unit) /* ML */
751755
our blocking section doesn't contain anything interesting, don't bother
752756
with saving errno.)
753757
*/
754-
caml_raise_if_exception(caml_process_pending_signals_exn());
758+
caml_raise_if_exception(caml_do_pending_actions_exn());
755759
caml_thread_save_runtime_state();
756760
st_thread_yield(&caml_master_lock);
757761
curr_thread = st_tls_get(thread_descriptor_key);
758762
caml_thread_restore_runtime_state();
759-
caml_raise_if_exception(caml_process_pending_signals_exn());
763+
caml_raise_if_exception(caml_do_pending_actions_exn());
760764

761765
return Val_unit;
762766
}

runtime/caml/domain_state.tbl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ DOMAIN_STATE(intnat, requested_major_slice)
6464
DOMAIN_STATE(intnat, requested_minor_gc)
6565
DOMAIN_STATE(struct caml__roots_block *, local_roots)
6666

67+
DOMAIN_STATE(caml_mask_kind, mask_async_callbacks)
68+
/* See signals.c */
69+
6770
DOMAIN_STATE(double, stat_minor_words)
6871
DOMAIN_STATE(double, stat_promoted_words)
6972
DOMAIN_STATE(double, stat_major_words)

runtime/caml/misc.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,13 @@ extern double caml_log1p(double);
317317

318318
#endif /* _WIN32 */
319319

320+
/* For signal.h. This is needed in domain_state.h. */
321+
322+
typedef enum {
323+
CAML_MASK_NONE,
324+
CAML_MASK_INTERRUPTIBLE,
325+
CAML_MASK_UNINTERRUPTIBLE
326+
} caml_mask_kind;
320327

321328
/* Data structures */
322329

runtime/caml/signals.h

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,85 @@ extern "C" {
3333
CAMLextern void caml_enter_blocking_section (void); //raises
3434
CAMLextern void caml_leave_blocking_section (void); //raises
3535

36+
/* Asynchronous actions
37+
38+
We distinguish two kinds of asynchronous actions:
39+
40+
- unrestricted asynchronous callbacks: possibly-raising signal
41+
handlers, memprof callbacks and finalisers (including GC alarms).
42+
43+
- non-raising actions, such as non-raising signal handlers (e.g.
44+
systhread's yield).
45+
46+
Signal handlers in particular are defined as one of two kinds,
47+
non-raising and raising. Non-raising handlers must guarantee that
48+
they do not raise or fail, but also that they are brief (typically,
49+
systhread's yield()), because themselves cannot be interrupted.
50+
51+
In contrast, asynchronous callbacks of the second kind are allowed
52+
to raise an exception (typically, when handling SIGINT or SIGALRM),
53+
are not limited in what they can express (typically, finalisers and
54+
memprof callbacks), and can themselves be interrupted by
55+
asynchronous callbacks.
56+
57+
Masking temporarily delays the execution of asynchronous callbacks.
58+
There are two kinds of masking:
59+
60+
- _interruptible masks_ delay the execution of unrestricted
61+
asynchronous callbacks, whereas
62+
63+
- _uninterruptible masks_ delay execution of all asynchronous
64+
callbacks.
65+
66+
This gives three possible values for masking: CAML_MASK_NONE,
67+
CAML_MASK_INTERRUPTIBLE and CAML_MASK_UNINTERRUPTIBLE.
68+
69+
In particular:
70+
71+
- a program in an interruptible mask will not be interrupted by
72+
asynchronous exceptions;
73+
74+
- with systhreads, a program in an interruptible mask will yield to
75+
other threads, while one in an uninterruptible mask will not.
76+
*/
77+
78+
CAMLextern caml_mask_kind caml_mask (caml_mask_kind new_mask);
79+
CAMLextern void caml_unmask (caml_mask_kind old_mask);
80+
/* The caml_mask functions set an interruptible or uninterruptible
81+
mask and return the previous mask. The caml_unmask function sets
82+
the previous mask back. Assumes that the runtime lock is held.
83+
84+
The caml_mask function never undoes a previous mask, that is,
85+
calling caml_mask(CAML_MASK_INTERRUPTIBLE) has no effect inside an
86+
uninterruptible mask. Similarly, caml_mask(CAML_MASK_NONE) has no
87+
effect at all, it thus can be used to query the current mask.
88+
89+
Calls to caml_mask and caml_unmask must be correctly bracketed and
90+
caml_unmask must be supplied value returned from the corresponding
91+
call to caml_mask (the previous mask). It is incorrect to call
92+
caml_unmask with a different mask, so as to undo a mask one does
93+
not own.
94+
95+
Example:
96+
97+
caml_mask_kind old_mask = caml_mask(CAML_MASK_INTERRUPTIBLE);
98+
...(no raising of exception)...
99+
caml_unmask(old_mask);
100+
*/
101+
36102
CAMLextern void caml_process_pending_actions (void);
37-
/* Checks for pending actions and executes them. This includes pending
38-
minor and major collections, signal handlers, finalisers, and
39-
Memprof callbacks. Assumes that the runtime lock is held. Can raise
40-
exceptions asynchronously into OCaml code. */
103+
/* Checks for pending actions and executes them, depending on the
104+
current mask. Assumes that the runtime lock is held.
105+
106+
Can raise exceptions asynchronously into OCaml code if the current
107+
mask is CAML_MASK_NONE.
108+
*/
41109

42110
CAMLextern value caml_process_pending_actions_exn (void);
43111
/* Same as [caml_process_pending_actions], but returns the encoded
44112
exception if any (otherwise returns [Val_unit]). */
45113

114+
46115
#ifdef CAML_INTERNALS
47116
CAMLextern intnat volatile caml_pending_signals[];
48117

@@ -58,9 +127,9 @@ CAMLextern int caml_convert_signal_number (int);
58127
CAMLextern int caml_rev_convert_signal_number (int);
59128
value caml_execute_signal_exn(int signal_number, int in_signal_handler);
60129
void caml_record_signal(int signal_number);
61-
value caml_process_pending_signals_exn(void);
130+
void caml_handle_signal(int signal_number);
62131

63-
int caml_has_action_pending (void);
132+
int caml_has_action_to_do (void);
64133
void caml_set_action_pending (void);
65134
void caml_notify_action (void);
66135
value caml_do_pending_actions_exn (void);

runtime/domain.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ void caml_init_domain ()
8282
Caml_state->requested_major_slice = 0;
8383
Caml_state->requested_minor_gc = 0;
8484

85+
Caml_state->mask_async_callbacks = CAML_MASK_NONE;
86+
8587
Caml_state->eventlog_enabled = 0;
8688
Caml_state->eventlog_paused = 0;
8789
Caml_state->eventlog_startup_pid = 0;

runtime/interp.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -858,7 +858,7 @@ value caml_interprete(code_t prog, asize_t prog_size)
858858
Next;
859859

860860
Instruct(POPTRAP):
861-
if (caml_has_action_pending()) {
861+
if (caml_has_action_to_do()) {
862862
/* We must check here so that if a signal is pending and its
863863
handler triggers an exception, the exception is trapped
864864
by the current try...with, not the enclosing one. */
@@ -917,7 +917,7 @@ value caml_interprete(code_t prog, asize_t prog_size)
917917
/* Signal handling */
918918

919919
Instruct(CHECK_SIGNALS): /* accu not preserved */
920-
if (caml_has_action_pending()) goto process_actions;
920+
if (caml_has_action_to_do()) goto process_actions;
921921
Next;
922922

923923
process_actions:

0 commit comments

Comments
 (0)