Previously we've cached the stack base and dereferenced from there, but when the
stack is resized, this reference is invalidated and there is no good fix it in
all frames (we don't store back references).
This commit replaces pointers with indexes, so the stack frame is always
displaced onto the current lisp stack.
When we don't use mprotect (nor guard page), we allocate the memory manually.
This simplifies some code and makes the booting process less intervened with GC.
This commit replaces capturing whole LEX with an explicit vector of closed
variables. We introduce a set of additional opcodes that deal with closed
entities. Locals are referred as lcl and closed variables as lex.
The function ecl_fdefinition checks also for lamdbas and whatnot, while all we
need is a lookup in the global namespace for the function name.
This commit also changes how we store SETF function definition -- instead of
maintaining them in a global environment, it is stored along with the symbol.
That saves us from taking a global lock repeatedly.
Previously we've registered the finalizer when a symbol was dynamically bound
for the first time; this commit changes that to remove a dependency on GC when
binding special variables.
A previous commit unconditionally configures Boehm GC to support fork.
This breaks the Windows MSVC build, producing an error dialog with the
message "Fatal error in GC: fork() handling unsupported".
This commit restricts the call to GC_set_handle_fork to non-Windows
hosts.
The local variables o and c were lost during the refactor in
bd723748d7, re-add them. Also,
the initialization in init_type_info() got moved to the wrong place in
the same refactor, this needs to be called later on.
The function GC_init_explicit_typing is internal to bdwgc and called
automatically in GC_make_descriptor. Therefore, we don't need to call
it during GC initialization.
- GBC_BOEHM_OWN_ALLOCATOR is dead for a long time
- undef alloc_object was used the function rename to ecl_alloc_object
- remove mark phase ignored by the preprocessor
Read-write locks are always provided; if no operating system
primitives exist, emulate them using ordinary locks. Also provide a
Windows implementation.
- Spinlocks have been replaced by ordinary locks. Without access to
the underyling scheduler, spinlocks provide no performace benefit
and may even be harmful in case of high contention.
- Synchronization of process creation and exiting has been simplified.
Instead of a spinlock, a barrier and atomic operations we now use
only a single lock protecting the shared process state and a
condition variable for implementing process joins.
- Some locks which were implemented using Lisp objects now directly
use a native mutex.
- Our own mutex implementation has been removed as it is now unused.
The old implementation was not race condition free. If two threads (A
and B) were writing at the same time while one thread (C) was reading,
the following could happen:
1. thread A increases the write pointer (but does not store the
message yet)
2. thread B increases the write pointer, stores the message and
signals thread C
3. thread C tries to read from the location that thread A has not yet
written to
The new implementation is a simple and obvious solution using a common
mutex and two condition variables for reading/writing. We don't bother
with a (complex) interrupt safe implementation.
Replace slow homegrown mutex implementation by standard OS functions.
We try our best to be interrupt safe, however a completely safe
implementation is impossible (unless one completely removes the ability
to interrupt a thread waiting on a mutex). There is always a window
after the OS specific function has returned, but before we can set
the owner, in which interrupts will see an inconsistent state of the
mutex with regards to owner and count.
Condition variables are now based on OS functions as well. Timed
waiting on condition variables has also been implemented.
Otherwise it can happen that a user-defined finalizer for some object
A storing a builtin object B registers another finalizer for A making
B reachable again while B has already been finalized.
We can't impose an ordering for user-defined finalizers in general
since these may include cyclic references. Therefore it is the duty of
the user to write the finalizers in a consistent way which is
independent of the order in which the finalizers are called. This
doesn't work for builtin objects since the user can't influence
the finalizers in this case.
We also fix a bug which lead to the removal of the standard finalizer
if a user-defined finalizer was registered and then removed.
Co-authored-by: Daniel Kochmański <daniel@turtleware.eu>
We have special-cased all immediate objects except ECL_T. Creating such link
leads to a segmentation fault when GC tries to dereference the pointer. Fixes#610.
We can't use the standard functions like cl_list for allocating the
wrapper object for the finalizer since these need a working environment
for disabling interrupts.
Slot definitions are no longer a signature, but they are still needed
to update obsolete instances. Reader function name is also changed to
SI:INSTANCE-SLOTDS. SI:INSTANCE-SIG-SET name does not change, because
it sets both SLOTDS and the STAMP.
We should call make-instances-obsolete from finalize-inheritance if we
want to be conforming, because user may have added their own auxiliary
methods.
This change while being last in a serie of commits was locally the
first change which solved problems. It will enable us to implement the
fast generic dispatch after the release.
Many parts of the source code were bent backward to support builds
without long floats which are always present given we depend expect
c99 compiler.
The corresponding C macros (ECL_LONG_FLOAT) and the *feature*
entry (:long-float) are marked as deprecated in the documentation.
Complex is a macro specified in complex.h for c99 to simplify using
_Complex type. This file also contains functions to work on complex
floats. To avoid conflicts we rename internal name complex to
gencomplex and update all references to it.
This function used writes in the thread local environment while
interrupts where disabled with the env->disable_interrupts
mechanism, which causes problems with the mprotect() mechanism for
fast interrupt dispatch.
The garbage collector can call stacks_scanner in a thread before
pthread_setspecific, leading to a wrong error message. The
solution is simply not to mark the environment, if
pthread_setspecific has not yet been called.
When mp_process_interrupt and thread_cleanup are called at the same
time, it is possible that the thread-local environment is deallocated
while ecl_interrupt_process tries to use it. This two methods thus
need to be protected with a lock.