In both bytecmp and c compiler we use si:function-boundary and
si:unwind-protect-boundary where appropriate. Previously bytecmp used an ad-hoc
special variable for function-boundary and didn't mark unwind-protect at all.
Remove recently-introduced ECI package (maybe we will reintroduce it later when
we'll have a common frontend for compilers).
If ecl_unwind is interrupted with another call to ecl_unwind
before it has decremented env->frs_top, the second call of
ecl_unwind may stop too early with its unwinding, leading to
potential segfaults.
bc-compile is more conforming now (we validate definition and name, also our
closure compilation is a bit better, still broken though).
Also improve some error messages and add periods at the end.
We have created empty closure for instance when flet was in null lexenv. Check
for Null in close_over and create t_bclosure only for non-null lexical
environments.
We don't need to save/restore outside of signal handlers. Also,
bignum_registers were not saved. Allocation of the values array
has been changed to heap allocation, since this array is quite
large and we may overflow the C stack, if we allocate it there.
If ecl_bds_push or ecl_bds_bind were interrupted by a call to
ecl_bds_unwind, segementation faults could occur, because
env->bds_top->symbol may not have pointed to a valid symbol.
Also, memory corruption was possible if the functions were
interrupted after setting slot->symbol but before setting
slot->value.
Interrupting a thread during setjmp with a call to ecl_unwind
leads to segmentation faults, since we try to call longjmp
before the corresponding setjmp has finished. Thus, we also need
to wait until setjmp has finished before we can set frs_val of
the frame.
Code was previously written with an assumption, that we know whenever function
crosses lexical for closure boundaries before it is compiled (and env-mark for
such boundries was LB and CB appropriately). Later it has changed, but code was
ready to work with LB and CB marks.
Fix these parts of code and replace it with a single mark ECI:FUNCTION. Also
replace CL:UNWIND-PROTECT boundry mark with ECI:UNWIND-PROTECT so we are less
dependent on use-ing CL package. Adjust comments to have this change.
If by chance env->frs_top->frs_val has the value ECL_PROTECT_TAG,
ecl_unwind will stop and call longjmp. However, at this point
setjmp has not yet been called, leading to a segmentation fault.
We have dummy variable for that, so we refere to
(var-ref-ccb (tag-var blk))
(var-ref-clb (tag-var blk))
Brokeness comes from the fact that closures are computed after function
compilation pass-1 (and tagbody is inside). Analogous change to the previous
commit in cmpblock.
Also improve comments in cmptypes to make it clear that these parts are not
used. Further refactor could make tag inherit from variable - then we wouldn't
have a dummy variable and unnecessary fields whatsoever.
We have dummy variable for that, so we refere to
(var-ref-ccb (blk-var blk))
(var-ref-clb (blk-var blk))
Brokeness comes from the fact that closures are computed after function
compilation pass-1 (and block is inside). Fixes#374.
Also improve comments in cmptypes to make it clear that these parts are not
used. Further refactor could make blk inherit from variable - then we wouldn't
have a dummy variable and unnecessary fields whatsoever.
This optimization doesn't buy us much but it doesn't cost us anything
either. Note that it emits c1form in case of expression what means that
expression won't be promoted to top-level form after the reduction.
We have to make sure that the stack pointers always point to a
valid object. This means that we have to increase env->stack_top
before we change things in the stack.
If a thread is interrupted directly after a call to
ecl_function_dispatch, env->function may be overwritten before
it is used. Thus we need to save and restore when we
execute queued signals.
The logic im mp_barrier_wait is wrong. decrement_counter returns
the value of the counter __before__ it is decremented. Before
the fix, the counter decremented until it reached 0 and then the
next arriving thread would get stuck in decrement_counter. Also,
interrupts were not reenabled in all cases.
If mp_process_enable is interrupted after pthread_create, but
before its exit code is examined, the cleanup code may be run
even when pthread_create did not fail, so we need to disable
interrupts in this region.
If a thread is killed while it holds a spinlock, the lock will
never be released, leading to deadlocks. Hence we have to clean
up spinlocks in ECL_WITH_SPINLOCK_END. In mp_process_enable,
other cleanup (deallocating the environment, unlisting the
process) has to performed too.
This is important to prevent race conditions. If interrupts are
left disabled, the environment may be wrongly write protected by
an interrupting thread and completely harmless writes in the
environment can lead to segmentation faults.
If a process, that has already unwound its whole frame stack
(after ECL_CATCH_ALL_END in thread_entry_point) is interrupted by
a call to mp_exit_process, ECL will crash with a segmentation
fault. We thus need to aquire the start_stop_spinlock before we
unwind the frame stack.
If a thread is interrupted while interrupts are disabled by C,
then the signal is queued and the environment is write protected
by mprotect. If another thread then calls queue_signal, it will
try to write in the protected environment, leading to a
segmentation fault. Since mprotect can only protect whole memory
pages, we need to allocate the pending interrupts and the signal
queue in a separate struct.