Skip to content

Commit a77f0dd

Browse files
mshinwellstedolan
andauthored
Improve the semantics of asynchronous exceptions (new simpler version) (#802)
Co-authored-by: Stephen Dolan <[email protected]>
1 parent 8b9cf09 commit a77f0dd

36 files changed

+718
-116
lines changed

backend/cfg/cfg.ml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,8 @@ let can_raise_operation : operation -> bool = function
390390
391391
let can_raise_basic : basic -> bool = function
392392
| Op op -> can_raise_operation op
393-
| Call _ -> true
393+
| Call (P (Alloc _)) -> false
394+
| Call (P (External _ | Checkbound _)) | Call (F _) -> true
394395
| Reloadretaddr -> false
395396
| Pushtrap _ -> false
396397
| Poptrap -> false

backend/mach.ml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,7 @@ let operation_can_raise op =
208208
match op with
209209
| Icall_ind | Icall_imm _ | Iextcall _
210210
| Iintop (Icheckbound) | Iintop_imm (Icheckbound, _)
211-
| Iprobe _
212-
| Ialloc _ -> true
211+
| Iprobe _ -> true
213212
| Ispecific sop -> Arch.operation_can_raise sop
214213
| Iintop_imm((Iadd | Isub | Imul | Imulh _ | Idiv | Imod | Iand | Ior | Ixor
215214
| Ilsl | Ilsr | Iasr | Ipopcnt | Iclz _|Ictz _|Icomp _), _)
@@ -222,7 +221,7 @@ let operation_can_raise op =
222221
| Istackoffset _ | Istore _ | Iload (_, _, _) | Iname_for_debugger _
223222
| Itailcall_imm _ | Itailcall_ind
224223
| Iopaque | Ibeginregion | Iendregion
225-
| Iprobe_is_enabled _
224+
| Iprobe_is_enabled _ | Ialloc _
226225
-> false
227226

228227
let free_conts_for_handlers fundecl =

ocaml/otherlibs/systhreads/st_stubs.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ struct caml_thread_struct {
7878
uintnat last_retaddr; /* Saved value of Caml_state->last_return_address */
7979
value * gc_regs; /* Saved value of Caml_state->gc_regs */
8080
char * exception_pointer; /* Saved value of Caml_state->exception_pointer */
81+
char * async_exception_pointer;
82+
/* Saved value of Caml_state->async_exception_pointer */
8183
struct caml__roots_block * local_roots; /* Saved value of local_roots */
8284
struct caml_local_arenas * local_arenas;
8385
struct longjmp_buffer * exit_buf; /* For thread exit */
@@ -90,6 +92,8 @@ struct caml_thread_struct {
9092
/* Saved value of Caml_state->local_roots */
9193
struct caml__roots_block * local_roots;
9294
struct longjmp_buffer * external_raise; /* Saved Caml_state->external_raise */
95+
struct longjmp_buffer * external_raise_async;
96+
/* Saved Caml_state->external_raise_async */
9397
#endif
9498
int backtrace_pos; /* Saved Caml_state->backtrace_pos */
9599
backtrace_slot * backtrace_buffer; /* Saved Caml_state->backtrace_buffer */
@@ -185,6 +189,7 @@ Caml_inline void caml_thread_save_runtime_state(void)
185189
curr_thread->last_retaddr = Caml_state->last_return_address;
186190
curr_thread->gc_regs = Caml_state->gc_regs;
187191
curr_thread->exception_pointer = Caml_state->exception_pointer;
192+
curr_thread->async_exception_pointer = Caml_state->async_exception_pointer;
188193
curr_thread->local_arenas = caml_get_local_arenas();
189194
#else
190195
curr_thread->stack_low = Caml_state->stack_low;
@@ -193,6 +198,7 @@ Caml_inline void caml_thread_save_runtime_state(void)
193198
curr_thread->sp = Caml_state->extern_sp;
194199
curr_thread->trapsp = Caml_state->trapsp;
195200
curr_thread->external_raise = Caml_state->external_raise;
201+
curr_thread->external_raise_async = Caml_state->external_raise_async;
196202
#endif
197203
curr_thread->local_roots = Caml_state->local_roots;
198204
curr_thread->backtrace_pos = Caml_state->backtrace_pos;
@@ -209,6 +215,7 @@ Caml_inline void caml_thread_restore_runtime_state(void)
209215
Caml_state->last_return_address = curr_thread->last_retaddr;
210216
Caml_state->gc_regs = curr_thread->gc_regs;
211217
Caml_state->exception_pointer = curr_thread->exception_pointer;
218+
Caml_state->async_exception_pointer = curr_thread->async_exception_pointer;
212219
caml_set_local_arenas(curr_thread->local_arenas);
213220
#else
214221
Caml_state->stack_low = curr_thread->stack_low;
@@ -217,6 +224,7 @@ Caml_inline void caml_thread_restore_runtime_state(void)
217224
Caml_state->extern_sp = curr_thread->sp;
218225
Caml_state->trapsp = curr_thread->trapsp;
219226
Caml_state->external_raise = curr_thread->external_raise;
227+
Caml_state->external_raise_async = curr_thread->external_raise_async;
220228
#endif
221229
Caml_state->local_roots = curr_thread->local_roots;
222230
Caml_state->backtrace_pos = curr_thread->backtrace_pos;
@@ -337,6 +345,7 @@ static caml_thread_t caml_thread_new_info(void)
337345
th->top_of_stack = NULL;
338346
th->last_retaddr = 1;
339347
th->exception_pointer = NULL;
348+
th->async_exception_pointer = NULL;
340349
th->local_roots = NULL;
341350
th->local_arenas = NULL;
342351
th->exit_buf = NULL;
@@ -349,6 +358,7 @@ static caml_thread_t caml_thread_new_info(void)
349358
th->trapsp = th->stack_high;
350359
th->local_roots = NULL;
351360
th->external_raise = NULL;
361+
th->external_raise_async = NULL;
352362
#endif
353363
th->backtrace_pos = 0;
354364
th->backtrace_buffer = NULL;
@@ -751,12 +761,14 @@ CAMLprim value caml_thread_yield(value unit) /* ML */
751761
our blocking section doesn't contain anything interesting, don't bother
752762
with saving errno.)
753763
*/
754-
caml_raise_if_exception(caml_process_pending_signals_exn());
764+
caml_raise_async_if_exception(caml_process_pending_signals_exn(),
765+
"signal handler");
755766
caml_thread_save_runtime_state();
756767
st_thread_yield(&caml_master_lock);
757768
curr_thread = st_tls_get(thread_descriptor_key);
758769
caml_thread_restore_runtime_state();
759-
caml_raise_if_exception(caml_process_pending_signals_exn());
770+
caml_raise_async_if_exception(caml_process_pending_signals_exn(),
771+
"signal handler");
760772

761773
return Val_unit;
762774
}

ocaml/runtime/amd64.S

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,8 @@ FUNCTION(G(caml_start_program))
576576
/* Common code for caml_start_program and caml_callback* */
577577
LBL(caml_start_program):
578578
/* Build a callback link */
579-
subq $8, %rsp; CFI_ADJUST (8) /* stack 16-aligned */
579+
pushq Caml_state(async_exception_pointer); CFI_ADJUST (8)
580+
/* Stack is 16-aligned at this point */
580581
pushq Caml_state(gc_regs); CFI_ADJUST(8)
581582
pushq Caml_state(last_return_address); CFI_ADJUST(8)
582583
pushq Caml_state(bottom_of_stack); CFI_ADJUST(8)
@@ -587,6 +588,7 @@ LBL(caml_start_program):
587588
pushq %r13; CFI_ADJUST(8)
588589
pushq Caml_state(exception_pointer); CFI_ADJUST(8)
589590
movq %rsp, Caml_state(exception_pointer)
591+
movq %rsp, Caml_state(async_exception_pointer)
590592
/* Call the OCaml code */
591593
call *%r12
592594
LBL(107):
@@ -600,7 +602,7 @@ LBL(109):
600602
popq Caml_state(bottom_of_stack); CFI_ADJUST(-8)
601603
popq Caml_state(last_return_address); CFI_ADJUST(-8)
602604
popq Caml_state(gc_regs); CFI_ADJUST(-8)
603-
addq $8, %rsp; CFI_ADJUST (-8);
605+
popq Caml_state(async_exception_pointer); CFI_ADJUST(-8)
604606
/* Restore callee-save registers. */
605607
POP_CALLEE_SAVE_REGS
606608
/* Return to caller. */
@@ -696,7 +698,7 @@ ENDFUNCTION(G(caml_raise_exception))
696698
FUNCTION(G(caml_stack_overflow))
697699
movq C_ARG_1, %r14 /* Caml_state */
698700
LEA_VAR(caml_exn_Stack_overflow, %rax)
699-
movq Caml_state(exception_pointer), %rsp /* cut the stack */
701+
movq Caml_state(async_exception_pointer), %rsp /* cut the stack */
700702
/* Recover previous exn handler */
701703
popq Caml_state(exception_pointer)
702704
ret /* jump to handler's code */

ocaml/runtime/callback.c

Lines changed: 126 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,19 @@
2424
#include "caml/memory.h"
2525
#include "caml/mlvalues.h"
2626

27+
static value raise_if_exception(value res)
28+
{
29+
if (Is_exception_result(res)) {
30+
if (Caml_state->raising_async_exn) {
31+
Caml_state->raising_async_exn = 0;
32+
caml_raise_async(Extract_exception(res));
33+
} else {
34+
caml_raise(Extract_exception(res));
35+
}
36+
}
37+
return res;
38+
}
39+
2740
#ifndef NATIVE_CODE
2841

2942
/* Bytecode callbacks */
@@ -51,7 +64,9 @@ static void init_callback_code(void)
5164
callback_code_inited = 1;
5265
}
5366

54-
CAMLexport value caml_callbackN_exn(value closure, int narg, value args[])
67+
/* Functions that return all exceptions, including asynchronous ones */
68+
69+
static value caml_callbackN_exn0(value closure, int narg, value args[])
5570
{
5671
int i;
5772
value res;
@@ -72,29 +87,75 @@ CAMLexport value caml_callbackN_exn(value closure, int narg, value args[])
7287
return res;
7388
}
7489

90+
CAMLexport value caml_callbackN_exn(value closure, int narg, value args[])
91+
{
92+
value res = caml_callbackN_exn0(closure, narg, args);
93+
Caml_state->raising_async_exn = 0;
94+
return res;
95+
}
96+
7597
CAMLexport value caml_callback_exn(value closure, value arg1)
7698
{
77-
value arg[1];
99+
value res, arg[1];
78100
arg[0] = arg1;
79-
return caml_callbackN_exn(closure, 1, arg);
101+
res = caml_callbackN_exn0(closure, 1, arg);
102+
Caml_state->raising_async_exn = 0;
103+
return res;
80104
}
81105

82106
CAMLexport value caml_callback2_exn(value closure, value arg1, value arg2)
83107
{
84-
value arg[2];
108+
value res, arg[2];
85109
arg[0] = arg1;
86110
arg[1] = arg2;
87-
return caml_callbackN_exn(closure, 2, arg);
111+
res = caml_callbackN_exn0(closure, 2, arg);
112+
Caml_state->raising_async_exn = 0;
113+
return res;
88114
}
89115

90116
CAMLexport value caml_callback3_exn(value closure,
91-
value arg1, value arg2, value arg3)
117+
value arg1, value arg2, value arg3)
118+
{
119+
value res, arg[3];
120+
arg[0] = arg1;
121+
arg[1] = arg2;
122+
arg[2] = arg3;
123+
res = caml_callbackN_exn0(closure, 3, arg);
124+
Caml_state->raising_async_exn = 0;
125+
return res;
126+
}
127+
128+
/* Functions that propagate all exceptions, with any asynchronous exceptions
129+
also being propagated asynchronously. */
130+
131+
CAMLexport value caml_callbackN(value closure, int narg, value args[])
132+
{
133+
return raise_if_exception(caml_callbackN_exn0(closure, narg, args));
134+
}
135+
136+
CAMLexport value caml_callback(value closure, value arg1)
137+
{
138+
value arg[1];
139+
arg[0] = arg1;
140+
return caml_callbackN(closure, 1, arg);
141+
}
142+
143+
CAMLexport value caml_callback2(value closure, value arg1, value arg2)
144+
{
145+
value arg[2];
146+
arg[0] = arg1;
147+
arg[1] = arg2;
148+
return caml_callbackN(closure, 2, arg);
149+
}
150+
151+
CAMLexport value caml_callback3(value closure,
152+
value arg1, value arg2, value arg3)
92153
{
93154
value arg[3];
94155
arg[0] = arg1;
95156
arg[1] = arg2;
96157
arg[2] = arg3;
97-
return caml_callbackN_exn(closure, 3, arg);
158+
return caml_callbackN(closure, 3, arg);
98159
}
99160

100161
#else
@@ -106,26 +167,24 @@ typedef value (callback_stub)(caml_domain_state* state, value closure,
106167

107168
callback_stub caml_callback_asm, caml_callback2_asm, caml_callback3_asm;
108169

109-
CAMLexport value caml_callback_exn(value closure, value arg)
170+
static value callback(value closure, value arg)
110171
{
111172
return caml_callback_asm(Caml_state, closure, &arg);
112173
}
113174

114-
CAMLexport value caml_callback2_exn(value closure, value arg1, value arg2)
175+
static value callback2(value closure, value arg1, value arg2)
115176
{
116177
value args[] = {arg1, arg2};
117178
return caml_callback2_asm(Caml_state, closure, args);
118179
}
119180

120-
CAMLexport value caml_callback3_exn(value closure,
121-
value arg1, value arg2, value arg3)
181+
static value callback3(value closure, value arg1, value arg2, value arg3)
122182
{
123183
value args[] = {arg1, arg2, arg3};
124184
return caml_callback3_asm(Caml_state, closure, args);
125185
}
126186

127-
128-
CAMLexport value caml_callbackN_exn(value closure, int narg, value args[])
187+
static value callbackN(value closure, int narg, value args[])
129188
{
130189
CAMLparam1 (closure);
131190
CAMLxparamN (args, narg);
@@ -137,17 +196,17 @@ CAMLexport value caml_callbackN_exn(value closure, int narg, value args[])
137196
/* Pass as many arguments as possible */
138197
switch (narg - i) {
139198
case 1:
140-
res = caml_callback_exn(res, args[i]);
199+
res = callback(res, args[i]);
141200
if (Is_exception_result(res)) CAMLreturn (res);
142201
i += 1;
143202
break;
144203
case 2:
145-
res = caml_callback2_exn(res, args[i], args[i + 1]);
204+
res = callback2(res, args[i], args[i + 1]);
146205
if (Is_exception_result(res)) CAMLreturn (res);
147206
i += 2;
148207
break;
149208
default:
150-
res = caml_callback3_exn(res, args[i], args[i + 1], args[i + 2]);
209+
res = callback3(res, args[i], args[i + 1], args[i + 2]);
151210
if (Is_exception_result(res)) CAMLreturn (res);
152211
i += 3;
153212
break;
@@ -156,31 +215,76 @@ CAMLexport value caml_callbackN_exn(value closure, int narg, value args[])
156215
CAMLreturn (res);
157216
}
158217

159-
#endif
218+
/* Functions that return all exceptions, including asynchronous ones */
219+
220+
CAMLexport value caml_callback_exn(value closure, value arg)
221+
{
222+
value res = callback(closure, arg);
223+
Caml_state->raising_async_exn = 0;
224+
return res;
225+
}
226+
227+
CAMLexport value caml_callback2_exn(value closure, value arg1, value arg2)
228+
{
229+
value res = callback2(closure, arg1, arg2);
230+
Caml_state->raising_async_exn = 0;
231+
return res;
232+
}
160233

161-
/* Exception-propagating variants of the above */
234+
CAMLexport value caml_callback3_exn(value closure, value arg1, value arg2,
235+
value arg3)
236+
{
237+
value res = callback3(closure, arg1, arg2, arg3);
238+
Caml_state->raising_async_exn = 0;
239+
return res;
240+
}
241+
242+
CAMLexport value caml_callbackN_exn(value closure, int narg, value args[])
243+
{
244+
value res = callbackN(closure, narg, args);
245+
Caml_state->raising_async_exn = 0;
246+
return res;
247+
}
248+
249+
/* Functions that propagate all exceptions, with any asynchronous exceptions
250+
also being propagated asynchronously. */
162251

163252
CAMLexport value caml_callback (value closure, value arg)
164253
{
165-
return caml_raise_if_exception(caml_callback_exn(closure, arg));
254+
return raise_if_exception(callback(closure, arg));
166255
}
167256

168257
CAMLexport value caml_callback2 (value closure, value arg1, value arg2)
169258
{
170-
return caml_raise_if_exception(caml_callback2_exn(closure, arg1, arg2));
259+
return raise_if_exception(callback2(closure, arg1, arg2));
171260
}
172261

173262
CAMLexport value caml_callback3 (value closure, value arg1, value arg2,
174263
value arg3)
175264
{
176-
return caml_raise_if_exception(caml_callback3_exn(closure, arg1, arg2, arg3));
265+
return raise_if_exception(callback3(closure, arg1, arg2, arg3));
177266
}
178267

179268
CAMLexport value caml_callbackN (value closure, int narg, value args[])
180269
{
181-
return caml_raise_if_exception(caml_callbackN_exn(closure, narg, args));
270+
return raise_if_exception(callbackN(closure, narg, args));
182271
}
183272

273+
#endif
274+
275+
CAMLprim value caml_with_async_exns(value body_callback)
276+
{
277+
value res;
278+
res = caml_callback_exn(body_callback, Val_unit);
279+
280+
/* raised as a normal exn, even if it was asynchronous */
281+
if (Is_exception_result(res))
282+
caml_raise(Extract_exception(res));
283+
284+
return res;
285+
}
286+
287+
184288
/* Naming of OCaml values */
185289

186290
struct named_value {

0 commit comments

Comments
 (0)