threading: faster interrupt safe frame/binding stack manipulations

Only use one memory barrier for pushing in the frame/binding stack
    and ensure interrupt safety by saving/restoring the stack frame in
    which writes could happen during an interrupt.
This commit is contained in:
Marius Gerbershagen 2018-08-04 17:21:16 +02:00
parent 319b7ef79b
commit a7353369ea
3 changed files with 33 additions and 47 deletions

View file

@ -322,15 +322,10 @@ ecl_bds_bind(cl_env_ptr env, cl_object s, cl_object v)
}
location = env->thread_local_bindings + index;
slot = env->bds_top+1;
if (slot >= env->bds_limit){
slot = ecl_bds_overflow();
slot->symbol = ECL_DUMMY_TAG;
} else {
slot->symbol = ECL_DUMMY_TAG;
AO_nop_full();
++env->bds_top;
}
if (slot >= env->bds_limit) slot = ecl_bds_overflow();
slot->symbol = ECL_DUMMY_TAG;
AO_nop_full();
++env->bds_top;
ecl_disable_interrupts_env(env);
slot->symbol = s;
slot->value = *location;
@ -359,15 +354,10 @@ ecl_bds_push(cl_env_ptr env, cl_object s)
}
location = env->thread_local_bindings + index;
slot = env->bds_top+1;
if (slot >= env->bds_limit){
slot = ecl_bds_overflow();
slot->symbol = ECL_DUMMY_TAG;
} else {
slot->symbol = ECL_DUMMY_TAG;
AO_nop_full();
++env->bds_top;
}
if (slot >= env->bds_limit) slot = ecl_bds_overflow();
slot->symbol = ECL_DUMMY_TAG;
AO_nop_full();
++env->bds_top;
ecl_disable_interrupts_env(env);
slot->symbol = s;
slot->value = *location;
@ -565,16 +555,13 @@ _ecl_frs_push(register cl_env_ptr env)
* needed to ensure that the CPU doesn't reorder the memory
* stores. */
ecl_frame_ptr output = env->frs_top+1;
if (output >= env->frs_limit){
if (output >= env->frs_limit) {
frs_overflow();
output = env->frs_top;
output->frs_val = ECL_DUMMY_TAG;
} else {
output->frs_val = ECL_DUMMY_TAG;
AO_nop_full();
++env->frs_top;
output = env->frs_top+1;
}
output->frs_val = ECL_DUMMY_TAG;
AO_nop_full();
++env->frs_top;
output->frs_bds_top_index = env->bds_top - env->bds_org;
output->frs_ihs = env->ihs_top;
output->frs_sp = ECL_STACK_INDEX(env);

View file

@ -378,8 +378,7 @@ handle_all_queued_interrupt_safe(cl_env_ptr env)
/* We have to save and later restore thread-local variables to
* ensure that they don't get overwritten by the interrupting
* code */
/* INV: - Frame, Binding and IHS stack manipulations are
* interrupt safe
/* INV: - IHS stack manipulations are interrupt safe
* - The rest of the thread local variables are
* guaranteed to be used in an interrupt safe way. This
* is not true for the compiler environment and ffi
@ -397,9 +396,19 @@ handle_all_queued_interrupt_safe(cl_env_ptr env)
* stack. Increasing env->stack_top ensures that we don't
* overwrite the topmost stack value. */
env->stack_top++;
/* Finally we can handle the queued signals */
/* We also need to save and restore the (top+1)'th frame and
* binding stack value to prevent overwriting it.
* INV: Due to the stack safety areas we don't need to check
* for env->frs/bds_limit */
struct ecl_frame top_frame;
memcpy(&top_frame, env->frs_top+1, sizeof(struct ecl_frame));
struct ecl_bds_frame top_binding;
memcpy(&top_binding, env->bds_top+1, sizeof(struct ecl_bds_frame));
/* Finally we can handle the queued signals ... */
handle_all_queued(env);
/* And restore thread local variables again */
/* ... and restore everything again */
memcpy(env->bds_top+1, &top_binding, sizeof(struct ecl_bds_frame));
memcpy(env->frs_top+1, &top_frame, sizeof(struct ecl_frame));
env->stack_top--;
env->packages_to_be_created_p = packages_to_be_created_p;
env->packages_to_be_created = packages_to_be_created;

View file

@ -59,7 +59,7 @@ extern "C" {
* handler. This can be achieved by either increasing the stack
* pointer temporarily during the execution of the interrupt code
* or by saving/restoring the topmost stack value. However due to
* the second requirement, this simple method is only possible
* the second requirement, this simple method is sufficient only
* for the arguments stack.
* The second requirement requires the stack to be in a consistent
* state during the interrupt. The easiest solution would be to
@ -117,18 +117,13 @@ static inline void ecl_bds_bind_inl(cl_env_ptr env, cl_object s, cl_object v)
} else {
location = env->thread_local_bindings + index;
slot = env->bds_top+1;
if (slot >= env->bds_limit){
slot = ecl_bds_overflow();
slot->symbol = ECL_DUMMY_TAG;
} else {
/* First, we push a dummy symbol in the stack to
* prevent segfaults when we are interrupted with a
* call to ecl_bds_unwind. */
slot->symbol = ECL_DUMMY_TAG;
AO_nop_full();
++env->bds_top;
}
if (slot >= env->bds_limit) slot = ecl_bds_overflow();
/* First, we push a dummy symbol in the stack to
* prevent segfaults when we are interrupted with a
* call to ecl_bds_unwind. */
slot->symbol = ECL_DUMMY_TAG;
AO_nop_full();
++env->bds_top;
/* Then we disable interrupts to ensure that
* ecl_bds_unwind doesn't overwrite the symbol with
* some random value. */
@ -160,15 +155,10 @@ static inline void ecl_bds_push_inl(cl_env_ptr env, cl_object s)
} else {
location = env->thread_local_bindings + index;
slot = env->bds_top+1;
if (slot >= env->bds_limit){
slot = ecl_bds_overflow();
slot->symbol = ECL_DUMMY_TAG;
} else {
slot->symbol = ECL_DUMMY_TAG;
AO_nop_full();
++env->bds_top;
}
if (slot >= env->bds_limit) slot = ecl_bds_overflow();
slot->symbol = ECL_DUMMY_TAG;
AO_nop_full();
++env->bds_top;
ecl_disable_interrupts_env(env);
slot->symbol = s;
slot->value = *location;