Commit c45098e1 authored by Marius Gerbershagen's avatar Marius Gerbershagen
Browse files

doc: add documentation for atomic operations

parent 239a2aa4
......@@ -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.
......
......@@ -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
@node Atomic operations
@subsubsection Atomic operations
/* threads/atomic.c */
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);
@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}.
@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
......@@ -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.
......
......@@ -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
......
......@@ -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);
......
......@@ -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)))
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment