Skip to content

Commit

Permalink
flambda-backend: Improve the semantics of asynchronous exceptions (ne…
Browse files Browse the repository at this point in the history
…w simpler version) (ocaml-flambda#802)

Co-authored-by: Stephen Dolan <[email protected]>
  • Loading branch information
mshinwell and stedolan authored Oct 12, 2022
1 parent d9e4dd0 commit 9469765
Show file tree
Hide file tree
Showing 34 changed files with 714 additions and 112 deletions.
16 changes: 14 additions & 2 deletions otherlibs/systhreads/st_stubs.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ struct caml_thread_struct {
uintnat last_retaddr; /* Saved value of Caml_state->last_return_address */
value * gc_regs; /* Saved value of Caml_state->gc_regs */
char * exception_pointer; /* Saved value of Caml_state->exception_pointer */
char * async_exception_pointer;
/* Saved value of Caml_state->async_exception_pointer */
struct caml__roots_block * local_roots; /* Saved value of local_roots */
struct caml_local_arenas * local_arenas;
struct longjmp_buffer * exit_buf; /* For thread exit */
Expand All @@ -90,6 +92,8 @@ struct caml_thread_struct {
/* Saved value of Caml_state->local_roots */
struct caml__roots_block * local_roots;
struct longjmp_buffer * external_raise; /* Saved Caml_state->external_raise */
struct longjmp_buffer * external_raise_async;
/* Saved Caml_state->external_raise_async */
#endif
int backtrace_pos; /* Saved Caml_state->backtrace_pos */
backtrace_slot * backtrace_buffer; /* Saved Caml_state->backtrace_buffer */
Expand Down Expand Up @@ -185,6 +189,7 @@ Caml_inline void caml_thread_save_runtime_state(void)
curr_thread->last_retaddr = Caml_state->last_return_address;
curr_thread->gc_regs = Caml_state->gc_regs;
curr_thread->exception_pointer = Caml_state->exception_pointer;
curr_thread->async_exception_pointer = Caml_state->async_exception_pointer;
curr_thread->local_arenas = caml_get_local_arenas();
#else
curr_thread->stack_low = Caml_state->stack_low;
Expand All @@ -193,6 +198,7 @@ Caml_inline void caml_thread_save_runtime_state(void)
curr_thread->sp = Caml_state->extern_sp;
curr_thread->trapsp = Caml_state->trapsp;
curr_thread->external_raise = Caml_state->external_raise;
curr_thread->external_raise_async = Caml_state->external_raise_async;
#endif
curr_thread->local_roots = Caml_state->local_roots;
curr_thread->backtrace_pos = Caml_state->backtrace_pos;
Expand All @@ -209,6 +215,7 @@ Caml_inline void caml_thread_restore_runtime_state(void)
Caml_state->last_return_address = curr_thread->last_retaddr;
Caml_state->gc_regs = curr_thread->gc_regs;
Caml_state->exception_pointer = curr_thread->exception_pointer;
Caml_state->async_exception_pointer = curr_thread->async_exception_pointer;
caml_set_local_arenas(curr_thread->local_arenas);
#else
Caml_state->stack_low = curr_thread->stack_low;
Expand All @@ -217,6 +224,7 @@ Caml_inline void caml_thread_restore_runtime_state(void)
Caml_state->extern_sp = curr_thread->sp;
Caml_state->trapsp = curr_thread->trapsp;
Caml_state->external_raise = curr_thread->external_raise;
Caml_state->external_raise_async = curr_thread->external_raise_async;
#endif
Caml_state->local_roots = curr_thread->local_roots;
Caml_state->backtrace_pos = curr_thread->backtrace_pos;
Expand Down Expand Up @@ -337,6 +345,7 @@ static caml_thread_t caml_thread_new_info(void)
th->top_of_stack = NULL;
th->last_retaddr = 1;
th->exception_pointer = NULL;
th->async_exception_pointer = NULL;
th->local_roots = NULL;
th->local_arenas = NULL;
th->exit_buf = NULL;
Expand All @@ -349,6 +358,7 @@ static caml_thread_t caml_thread_new_info(void)
th->trapsp = th->stack_high;
th->local_roots = NULL;
th->external_raise = NULL;
th->external_raise_async = NULL;
#endif
th->backtrace_pos = 0;
th->backtrace_buffer = NULL;
Expand Down Expand Up @@ -751,12 +761,14 @@ CAMLprim value caml_thread_yield(value unit) /* ML */
our blocking section doesn't contain anything interesting, don't bother
with saving errno.)
*/
caml_raise_if_exception(caml_process_pending_signals_exn());
caml_raise_async_if_exception(caml_process_pending_signals_exn(),
"signal handler");
caml_thread_save_runtime_state();
st_thread_yield(&caml_master_lock);
curr_thread = st_tls_get(thread_descriptor_key);
caml_thread_restore_runtime_state();
caml_raise_if_exception(caml_process_pending_signals_exn());
caml_raise_async_if_exception(caml_process_pending_signals_exn(),
"signal handler");

return Val_unit;
}
Expand Down
8 changes: 5 additions & 3 deletions runtime/amd64.S
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,8 @@ FUNCTION(G(caml_start_program))
/* Common code for caml_start_program and caml_callback* */
LBL(caml_start_program):
/* Build a callback link */
subq $8, %rsp; CFI_ADJUST (8) /* stack 16-aligned */
pushq Caml_state(async_exception_pointer); CFI_ADJUST (8)
/* Stack is 16-aligned at this point */
pushq Caml_state(gc_regs); CFI_ADJUST(8)
pushq Caml_state(last_return_address); CFI_ADJUST(8)
pushq Caml_state(bottom_of_stack); CFI_ADJUST(8)
Expand All @@ -587,6 +588,7 @@ LBL(caml_start_program):
pushq %r13; CFI_ADJUST(8)
pushq Caml_state(exception_pointer); CFI_ADJUST(8)
movq %rsp, Caml_state(exception_pointer)
movq %rsp, Caml_state(async_exception_pointer)
/* Call the OCaml code */
call *%r12
LBL(107):
Expand All @@ -600,7 +602,7 @@ LBL(109):
popq Caml_state(bottom_of_stack); CFI_ADJUST(-8)
popq Caml_state(last_return_address); CFI_ADJUST(-8)
popq Caml_state(gc_regs); CFI_ADJUST(-8)
addq $8, %rsp; CFI_ADJUST (-8);
popq Caml_state(async_exception_pointer); CFI_ADJUST(-8)
/* Restore callee-save registers. */
POP_CALLEE_SAVE_REGS
/* Return to caller. */
Expand Down Expand Up @@ -696,7 +698,7 @@ ENDFUNCTION(G(caml_raise_exception))
FUNCTION(G(caml_stack_overflow))
movq C_ARG_1, %r14 /* Caml_state */
LEA_VAR(caml_exn_Stack_overflow, %rax)
movq Caml_state(exception_pointer), %rsp /* cut the stack */
movq Caml_state(async_exception_pointer), %rsp /* cut the stack */
/* Recover previous exn handler */
popq Caml_state(exception_pointer)
ret /* jump to handler's code */
Expand Down
148 changes: 126 additions & 22 deletions runtime/callback.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@
#include "caml/memory.h"
#include "caml/mlvalues.h"

static value raise_if_exception(value res)
{
if (Is_exception_result(res)) {
if (Caml_state->raising_async_exn) {
Caml_state->raising_async_exn = 0;
caml_raise_async(Extract_exception(res));
} else {
caml_raise(Extract_exception(res));
}
}
return res;
}

#ifndef NATIVE_CODE

/* Bytecode callbacks */
Expand Down Expand Up @@ -51,7 +64,9 @@ static void init_callback_code(void)
callback_code_inited = 1;
}

CAMLexport value caml_callbackN_exn(value closure, int narg, value args[])
/* Functions that return all exceptions, including asynchronous ones */

static value caml_callbackN_exn0(value closure, int narg, value args[])
{
int i;
value res;
Expand All @@ -72,29 +87,75 @@ CAMLexport value caml_callbackN_exn(value closure, int narg, value args[])
return res;
}

CAMLexport value caml_callbackN_exn(value closure, int narg, value args[])
{
value res = caml_callbackN_exn0(closure, narg, args);
Caml_state->raising_async_exn = 0;
return res;
}

CAMLexport value caml_callback_exn(value closure, value arg1)
{
value arg[1];
value res, arg[1];
arg[0] = arg1;
return caml_callbackN_exn(closure, 1, arg);
res = caml_callbackN_exn0(closure, 1, arg);
Caml_state->raising_async_exn = 0;
return res;
}

CAMLexport value caml_callback2_exn(value closure, value arg1, value arg2)
{
value arg[2];
value res, arg[2];
arg[0] = arg1;
arg[1] = arg2;
return caml_callbackN_exn(closure, 2, arg);
res = caml_callbackN_exn0(closure, 2, arg);
Caml_state->raising_async_exn = 0;
return res;
}

CAMLexport value caml_callback3_exn(value closure,
value arg1, value arg2, value arg3)
value arg1, value arg2, value arg3)
{
value res, arg[3];
arg[0] = arg1;
arg[1] = arg2;
arg[2] = arg3;
res = caml_callbackN_exn0(closure, 3, arg);
Caml_state->raising_async_exn = 0;
return res;
}

/* Functions that propagate all exceptions, with any asynchronous exceptions
also being propagated asynchronously. */

CAMLexport value caml_callbackN(value closure, int narg, value args[])
{
return raise_if_exception(caml_callbackN_exn0(closure, narg, args));
}

CAMLexport value caml_callback(value closure, value arg1)
{
value arg[1];
arg[0] = arg1;
return caml_callbackN(closure, 1, arg);
}

CAMLexport value caml_callback2(value closure, value arg1, value arg2)
{
value arg[2];
arg[0] = arg1;
arg[1] = arg2;
return caml_callbackN(closure, 2, arg);
}

CAMLexport value caml_callback3(value closure,
value arg1, value arg2, value arg3)
{
value arg[3];
arg[0] = arg1;
arg[1] = arg2;
arg[2] = arg3;
return caml_callbackN_exn(closure, 3, arg);
return caml_callbackN(closure, 3, arg);
}

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

callback_stub caml_callback_asm, caml_callback2_asm, caml_callback3_asm;

CAMLexport value caml_callback_exn(value closure, value arg)
static value callback(value closure, value arg)
{
return caml_callback_asm(Caml_state, closure, &arg);
}

CAMLexport value caml_callback2_exn(value closure, value arg1, value arg2)
static value callback2(value closure, value arg1, value arg2)
{
value args[] = {arg1, arg2};
return caml_callback2_asm(Caml_state, closure, args);
}

CAMLexport value caml_callback3_exn(value closure,
value arg1, value arg2, value arg3)
static value callback3(value closure, value arg1, value arg2, value arg3)
{
value args[] = {arg1, arg2, arg3};
return caml_callback3_asm(Caml_state, closure, args);
}


CAMLexport value caml_callbackN_exn(value closure, int narg, value args[])
static value callbackN(value closure, int narg, value args[])
{
CAMLparam1 (closure);
CAMLxparamN (args, narg);
Expand All @@ -137,17 +196,17 @@ CAMLexport value caml_callbackN_exn(value closure, int narg, value args[])
/* Pass as many arguments as possible */
switch (narg - i) {
case 1:
res = caml_callback_exn(res, args[i]);
res = callback(res, args[i]);
if (Is_exception_result(res)) CAMLreturn (res);
i += 1;
break;
case 2:
res = caml_callback2_exn(res, args[i], args[i + 1]);
res = callback2(res, args[i], args[i + 1]);
if (Is_exception_result(res)) CAMLreturn (res);
i += 2;
break;
default:
res = caml_callback3_exn(res, args[i], args[i + 1], args[i + 2]);
res = callback3(res, args[i], args[i + 1], args[i + 2]);
if (Is_exception_result(res)) CAMLreturn (res);
i += 3;
break;
Expand All @@ -156,31 +215,76 @@ CAMLexport value caml_callbackN_exn(value closure, int narg, value args[])
CAMLreturn (res);
}

#endif
/* Functions that return all exceptions, including asynchronous ones */

CAMLexport value caml_callback_exn(value closure, value arg)
{
value res = callback(closure, arg);
Caml_state->raising_async_exn = 0;
return res;
}

CAMLexport value caml_callback2_exn(value closure, value arg1, value arg2)
{
value res = callback2(closure, arg1, arg2);
Caml_state->raising_async_exn = 0;
return res;
}

/* Exception-propagating variants of the above */
CAMLexport value caml_callback3_exn(value closure, value arg1, value arg2,
value arg3)
{
value res = callback3(closure, arg1, arg2, arg3);
Caml_state->raising_async_exn = 0;
return res;
}

CAMLexport value caml_callbackN_exn(value closure, int narg, value args[])
{
value res = callbackN(closure, narg, args);
Caml_state->raising_async_exn = 0;
return res;
}

/* Functions that propagate all exceptions, with any asynchronous exceptions
also being propagated asynchronously. */

CAMLexport value caml_callback (value closure, value arg)
{
return caml_raise_if_exception(caml_callback_exn(closure, arg));
return raise_if_exception(callback(closure, arg));
}

CAMLexport value caml_callback2 (value closure, value arg1, value arg2)
{
return caml_raise_if_exception(caml_callback2_exn(closure, arg1, arg2));
return raise_if_exception(callback2(closure, arg1, arg2));
}

CAMLexport value caml_callback3 (value closure, value arg1, value arg2,
value arg3)
{
return caml_raise_if_exception(caml_callback3_exn(closure, arg1, arg2, arg3));
return raise_if_exception(callback3(closure, arg1, arg2, arg3));
}

CAMLexport value caml_callbackN (value closure, int narg, value args[])
{
return caml_raise_if_exception(caml_callbackN_exn(closure, narg, args));
return raise_if_exception(callbackN(closure, narg, args));
}

#endif

CAMLprim value caml_with_async_exns(value body_callback)
{
value res;
res = caml_callback_exn(body_callback, Val_unit);

/* raised as a normal exn, even if it was asynchronous */
if (Is_exception_result(res))
caml_raise(Extract_exception(res));

return res;
}


/* Naming of OCaml values */

struct named_value {
Expand Down
7 changes: 7 additions & 0 deletions runtime/caml/domain_state.tbl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ DOMAIN_STATE(value*, young_limit)
DOMAIN_STATE(char*, exception_pointer)
/* Exception pointer that points into the current stack */

DOMAIN_STATE(char*, async_exception_pointer)
/* Async exception pointer that points into the current stack */

DOMAIN_STATE(void*, young_base)
DOMAIN_STATE(value*, young_start)
DOMAIN_STATE(value*, young_end)
Expand Down Expand Up @@ -51,6 +54,7 @@ DOMAIN_STATE(value*, extern_sp)
DOMAIN_STATE(value*, trapsp)
DOMAIN_STATE(value*, trap_barrier)
DOMAIN_STATE(struct longjmp_buffer*, external_raise)
DOMAIN_STATE(struct longjmp_buffer*, external_raise_async)
DOMAIN_STATE(value, exn_bucket)
/* See interp.c */

Expand All @@ -60,6 +64,9 @@ DOMAIN_STATE(uintnat, last_return_address)
DOMAIN_STATE(value*, gc_regs)
/* See roots_nat.c */

DOMAIN_STATE(intnat, raising_async_exn)
/* Set when an async exn is raised, cleared when caught */

DOMAIN_STATE(intnat, backtrace_active)
DOMAIN_STATE(intnat, backtrace_pos)
DOMAIN_STATE(backtrace_slot*, backtrace_buffer)
Expand Down
Loading

0 comments on commit 9469765

Please sign in to comment.