diff --git a/CHANGELOG b/CHANGELOG index 9e86cc779..eb5918a07 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -29,6 +29,16 @@ * Pending changes since 16.1.3 ** Enhancements +- support for atomic operations added with an interface similar to SBCL. + Currently (SBCL 1.4.12), it differs in the following points: + - the set of supported places is slightly different + - ~atomic-incf/decf~ always implement modular arithmetic with respect to + the width of a fixnum, instead of a width of 64 bits as for some places + in SBCL. + - Functions such as ~(cas foo)~ analogous to ~(setf foo)~ are not allowed + in ECL. + - The arguments of ~defcas~ are consistent with the short form of + ~defsetf~ in contrast to SBCL, where they are different. - improvements to C backtrace interface by Marius Gerbershagen. Compile with CFLAGS+="-rdynamic" ./configure ... (Unix) or nmake ECL_USE_DBGHELP=yes ... (MSVC) to take full advantage of this feature. diff --git a/src/doc/new-doc/extensions/mp.txi b/src/doc/new-doc/extensions/mp.txi index 0c7f4e5d5..766be83a0 100644 --- a/src/doc/new-doc/extensions/mp.txi +++ b/src/doc/new-doc/extensions/mp.txi @@ -16,6 +16,8 @@ * Condition variables dictionary:: * Semaphores:: * Semaphores dictionary:: +* Atomic operations:: +* Atomic operations dictionary:: @end menu @node Introduction to native threads @@ -56,7 +58,7 @@ the next section. @include extensions/mp_ref_rwlock.txi @include extensions/mp_ref_cv.txi @include extensions/mp_ref_sem.txi +@include extensions/mp_ref_atomic.txi @c @include extensions/mp_ref_barrier.txi @c @include extensions/mp_ref_mailbox.txi -@c @include extensions/mp_ref_atomic.txi diff --git a/src/doc/new-doc/extensions/mp_ref_atomic.txi b/src/doc/new-doc/extensions/mp_ref_atomic.txi index bd9687eee..eb91570db 100644 --- a/src/doc/new-doc/extensions/mp_ref_atomic.txi +++ b/src/doc/new-doc/extensions/mp_ref_atomic.txi @@ -1,9 +1,233 @@ @node Atomic operations -@subsubsection Atomic operations -/* threads/atomic.c */ +@subsection Atomic operations +ECL supports both compare-and-swap and fetch-and-add (which may be +faster on some processors) atomic operations on a number of different +places. The compare-and-swap macro is user extensible with a protocol +similar to @code{setf}. -cl_object ecl_atomic_get(cl_object *slot); -void ecl_atomic_push(cl_object *slot, cl_object o); -void ecl_atomic_nconc(cl_object l, cl_object *slot); -cl_object ecl_atomic_pop(cl_object *slot); -cl_index ecl_atomic_index_incf(cl_index *slot); +@node Atomic operations dictionary +@subsection Atomic operations dictionary + +@subsubheading C Reference +@cppindex ecl_compare_and_swap +@deftypefun cl_object ecl_compare_and_swap(cl_object *slot, cl_object old, cl_object new) + +Perform an atomic compare and swap operation on @var{slot} and return +the previous value stored in @var{slot}. If the return value is equal +to @var{old} (comparison by @code{==}), the operation has succeeded. +@end deftypefun + +@cppindex ecl_atomic_incf +@deftypefun cl_object ecl_atomic_incf(cl_object *slot, cl_object increment) +@end deftypefun +@cppindex ecl_atomic_incf_by_fixnum +@deftypefun cl_object ecl_atomic_incf_by_fixnum(cl_object *slot, cl_fixnum increment) + +Atomically increment @var{slot} by the given increment and return the +previous value stored in @var{slot}. The consequences are undefined if +the value of @var{slot} is not of type @code{fixnum}. +@code{ecl_atomic_incf} signals an error if @var{increment} is not of +type @code{fixnum}. . +@end deftypefun + +@cppindex ecl_atomic_index_incf +@deftypefun cl_index ecl_atomic_index_incf(cl_index *slot); + +Atomically increment @var{slot} by 1 and return the new value stored +in @var{slot}. +@end deftypefun + +@cppindex ecl_atomic_get +@deftypefun cl_object ecl_atomic_get(cl_object *slot) + +Perform a volatile load of the object in @var{slot} and then +atomically set @var{slot} to @code{ECL_NIL}. Returns the value +previously stored in @var{slot}. +@end deftypefun + +@cppindex ecl_atomic_push +@deftypefun void ecl_atomic_push(cl_object *slot, cl_object o) +@end deftypefun +@cppindex ecl_atomic_pop +@deftypefun cl_object ecl_atomic_pop(cl_object *slot) + +Like push/pop but atomic. +@end deftypefun + +@subsubheading Lisp Reference +@lspindex mp:atomic-incf +@defmac mp:atomic-incf place &optional (increment 1) +@end defmac +@lspindex mp:atomic-decf +@defmac mp:atomic-decf place &optional (increment 1) + +Atomically increments/decrements the fixnum stored in @var{place} by +the given @var{increment} and returns the value of @var{place} before +the increment. Incrementing and decrementing is done using modular +arithmetic, so that @code{mp:atomic-incf} of a place whose value is +@code{most-positive-fixnum} by 1 results in +@code{most-negative-fixnum} stored in place. + +Currently the following places are supported: + +@code{car}, @code{cdr}, @code{first}, @code{rest}, @code{svref}, +@code{symbol-value}, @code{slot-value}, +@code{clos:standard-instance-access}, +@code{clos:funcallable-standard-instance-access}. + +For @code{slot-value}, the object should have no applicable methods +defined for @code{slot-value-using-class} or @code{(setf +slot-value-using-class)}. + +The consequences are undefined if the value of @var{place} is not of +type @code{fixnum}. +@end defmac + +@lspindex mp:compare-and-swap +@defmac mp:compare-and-swap place old new + +Atomically stores @var{new} in @var{place} if @var{old} is @code{eq} +to the current value of @var{place}. Returns the previous value of +@var{place}: if the returned value is @code{eq} to @var{old}, the swap +was carried out. + +Currently, the following places are supported: + +@code{car}, @code{cdr}, @code{first}, @code{rest}, @code{svref}, +@code{symbol-plist}, @code{symbol-value}, @code{slot-value}, +@code{clos:standard-instance-access}, +@code{clos:funcallable-standard-instance-access}, a struct accessor +defined by @code{defstruct} with the @code{:atomic-accessors} option +enabled or any other place for which a compare-and-swap expansion was +defined by @code{mp:defcas} or @code{mp:define-cas-expander}. + +For @code{slot-value}, @code{slot-unbound} is called if the slot is +unbound unless @var{old} is @code{eq} to @code{si:unbound}, in which +case @var{old} is returned and @var{new} is assigned to the slot. +Additionally, the object should have no applicable methods defined for +@code{slot-value-using-class} or @code{(setf slot-value-using-class)}. +@end defmac + +@lspindex mp:atomic-update +@defmac mp:atomic-update place update-fn &rest arguments + +Atomically updates the CAS-able @var{place} to the value returned by +calling @var{update-fn} with @var{arguments} and the old value of +@var{place}. @var{update-fn} must be a function accepting @code{(1+ +(length arguments))} arguments. Returns the new value which was stored +in @var{place}. + +@var{place} may be read and @var{update-fn} may be called more than +once if multiple threads are trying to write to @var{place} at the +same time. + +@exindex Atomic update of a structure slot +Example: + +Atomic update of a structure slot. If the update would not be atomic, +the result would be unpredictable. +@lisp +(defstruct (test-struct :atomic-accessors) + (slot1 0)) +(let ((struct (make-test-struct))) + (mapc #'mp:process-join + (loop repeat 100 + collect (mp:process-run-function + "" + (lambda () + (loop repeat 1000 do + (mp:atomic-update (test-struct-slot1 struct) #'1+) + (sleep 0.00001)))))) + (slot-value object 'slot1)) +=> 100000 +@end lisp +@end defmac + +@lspindex mp:atomic-push +@defmac mp:atomic-push obj place +@end defmac +@lspindex mp:atomic-pop +@defmac mp:atomic-pop place + +Like @code{push}/@code{pop}, but atomic. @var{place} must be CAS-able +and may be read multiple times before the update succeeds. +@end defmac + +@lspindex mp:define-cas-expander +@defmac mp:define-cas-expander accessor lambda-list &body body + +Define a compare-and-swap expander similar to +@code{define-setf-expander}. Defines the compare-and-swap-expander for +generalized-variables @code{(accessor ...)}. When a form +@code{(mp:compare-and-swap (accessor arg1 ... argn) old new)} is +evaluated, the forms given in the body of +@code{mp:define-cas-expander} are evaluated in order with the +parameters in @code{lambda-list} bound to @code{arg1 ... argn}. The +body must return six values +@lisp +(var1 ... vark) +(form1 ... formk) +old-var +new-var +compare-and-swap-form +volatile-access-form +@end lisp +in order (Note that @code{old-var} and @code{new-var} are single +variables, unlike in @code{define-setf-expander}). The whole +@code{compare-and-swap} form is then expanded into +@lisp +(let* ((var1 from1) ... (vark formk) + (old-var old-form) + (new-var new-form)) + compare-and-swap-form). +@end lisp +Note that it is up to the user of this macro to ensure atomicity for +the resulting compare-and-swap expansions. + +@exindex CAS expansion definition +Example: + +CAS expansion for @code{mp:process-name}. A process is defined as +follows in C: +@example +@verbatim +struct ecl_process { + _ECL_HDR; + cl_object name; + cl_object function; + ... +}; +@end verbatim +@end example + +Hence we can define a CAS expander as (omitting type checks): +@lisp +(mp:define-cas-expander mp:process-name (x) + (let ((old (gensym)) (new (gensym))) + (values nil nil old new + `(ffi:c-inline (,x ,old ,new) (:object :object :object) :object + "ecl_compare_and_swap(&(#0)->process.name,#1,#2)" + :one-liner t) + `(mp:process-name ,x)))) +@end lisp +@end defmac + +@lspindex mp:defcas +@defmac mp:defcas accessor cas-fun &optional documentation + +Define a compare-and-swap expansion similar to the short form +of @code{defsetf}. Defines an expansion +@lisp +(compare-and-swap (accessor arg1 ... argn) old new) +=> (cas-fun arg1 ... argn old new) +@end lisp +Note that it is up to the user of this macro to ensure atomicity for +the resulting compare-and-swap expansions. +@end defmac + +@lspindex mp:get-cas-expansion +@defun mp:get-cas-expansion place &optional environment + +Returns the compare-and-swap expansion forms and variables as +defined in @code{mp:define-cas-expander} for @var{place} as six values. +@end defun diff --git a/src/doc/new-doc/extensions/mp_ref_process.txi b/src/doc/new-doc/extensions/mp_ref_process.txi index 061c159cc..82b8bb3a2 100644 --- a/src/doc/new-doc/extensions/mp_ref_process.txi +++ b/src/doc/new-doc/extensions/mp_ref_process.txi @@ -299,7 +299,7 @@ See @code{mp:interrupt-process}. @deftypefun cl_object mp_restore_signals (cl_object sigmask) @end deftypefun -@defun mp:restor-signals sigmask +@defun mp:restore-signals sigmask Enables the interrupts from @code{sigmask}. See @code{mp:interrupt-process}. @@ -318,7 +318,7 @@ interrupts arriving during execution of the @code{body} take effect after @code{body} has been executed. Deferrable interrupts include most blockable POSIX signals, and -@code{mp:interrupt-thread}. Does not interfere with garbage collection, +@code{mp:interrupt-process}. Does not interfere with garbage collection, and unlike in many traditional Lisps using userspace threads, in ECL @code{mp:without-interrupts} does not inhibit scheduling of other threads. diff --git a/src/doc/new-doc/standards/numbers.txi b/src/doc/new-doc/standards/numbers.txi index 82cccf275..f15eb464a 100644 --- a/src/doc/new-doc/standards/numbers.txi +++ b/src/doc/new-doc/standards/numbers.txi @@ -161,11 +161,11 @@ Unchecked conversion from Lisp types to C numbers @subsubheading Functions @deftypefun cl_fixnum ecl_fixnum (cl_object @var{n}) @end deftypefun -@deftypefun float ecl_fixnum (cl_object @var{n}) +@deftypefun float ecl_single_float (cl_object @var{n}) @end deftypefun -@deftypefun double ecl_fixnum (cl_object @var{n}) +@deftypefun double ecl_double_float (cl_object @var{n}) @end deftypefun -@deftypefun {long double} ecl_fixnum (cl_object @var{n}) +@deftypefun {long double} ecl_long_float (cl_object @var{n}) @end deftypefun @subsubheading Description @@ -180,15 +180,13 @@ Checked conversion from Lisp types to C numbers @end deftypefun @deftypefun cl_index ecl_to_unsigned_integer (cl_object @var{n}); @end deftypefun -@deftypefun flaot ecl_to_float (cl_object @var{n}); +@deftypefun float ecl_to_float (cl_object @var{n}); @end deftypefun @deftypefun double ecl_to_double (cl_object @var{n}); @end deftypefun @deftypefun {long double} ecl_to_long_double (cl_object @var{n}); @end deftypefun -@deftypefun float ecl_to_float (cl_object @var{n}); -@end deftypefun -@deftypefun int8_t ecl_to_uint8_t (cl_object @var{n}); +@deftypefun uint8_t ecl_to_uint8_t (cl_object @var{n}); @end deftypefun @deftypefun int8_t ecl_to_int8_t (cl_object @var{n}); @end deftypefun diff --git a/src/h/external.h b/src/h/external.h index ca2f4749b..d44666dfa 100755 --- a/src/h/external.h +++ b/src/h/external.h @@ -1796,7 +1796,6 @@ extern ECL_API cl_object mp_mailbox_try_send(cl_object mailbox, cl_object msg); extern ECL_API cl_object ecl_atomic_get(cl_object *slot); extern ECL_API void ecl_atomic_push(cl_object *slot, cl_object o); -extern ECL_API void ecl_atomic_nconc(cl_object l, cl_object *slot); extern ECL_API cl_object ecl_atomic_pop(cl_object *slot); extern ECL_API cl_index ecl_atomic_index_incf(cl_index *slot); diff --git a/src/lsp/mp.lsp b/src/lsp/mp.lsp index 5d655cac8..b4c907e97 100644 --- a/src/lsp/mp.lsp +++ b/src/lsp/mp.lsp @@ -30,7 +30,7 @@ interrupts arriving during execution of the BODY take effect after BODY has been executed. Deferrable interrupts include most blockable POSIX signals, and -MP:INTERRUPT-THREAD. Does not interfere with garbage collection, and +MP:INTERRUPT-PROCESS. Does not interfere with garbage collection, and unlike in many traditional Lisps using userspace threads, in ECL WITHOUT-INTERRUPTS does not inhibit scheduling of other threads. @@ -210,9 +210,9 @@ the resulting COMPARE-AND-SWAP expansions." #+threads (defmacro defcas (accessor cas-fun &optional documentation) "Define a COMPARE-AND-SWAP expansion similar to the short form of DEFSETF. -Syntax: (defsetf symbol cas-fun) +Syntax: (defsetf accessor cas-fun) Defines an expansion - (compare-and-swap (SYMBOL arg1 ... argn) old new) + (compare-and-swap (ACCESSOR arg1 ... argn) old new) => (CAS-FUN arg1 ... argn old new) Note that it is up to the user of this macro to ensure atomicity for the resulting COMPARE-AND-SWAP expansions." @@ -225,8 +225,7 @@ the resulting COMPARE-AND-SWAP expansions." #+threads (defun get-cas-expansion (place &optional environment &aux f) - "Args: (form) -Returns the COMPARE-AND-SWAP expansion forms and variables as defined + "Returns the COMPARE-AND-SWAP expansion forms and variables as defined in DEFINE-CAS-EXPANDER for PLACE as six values." (cond ((setq f (si:get-sysprop (first place) 'CAS-EXPANDER)) (apply f environment (rest place)))