mp_ref_atomic.txi 7.94 KB
Newer Older
1
@node Atomic operations
2 3 4 5 6 7 8 9 10 11 12
@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
13
@deftypefun cl_object ecl_compare_and_swap (cl_object *slot, cl_object old, cl_object new)
14 15 16 17

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.
18
This is a inline-only function defined in ``ecl/ecl_atomics.h''.
19 20 21
@end deftypefun

@cppindex ecl_atomic_incf
22
@deftypefun cl_object ecl_atomic_incf (cl_object *slot, cl_object increment)
23 24
@end deftypefun
@cppindex ecl_atomic_incf_by_fixnum
25
@deftypefun cl_object ecl_atomic_incf_by_fixnum (cl_object *slot, cl_fixnum increment)
26 27 28 29 30

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
31 32
type @code{fixnum}. This is a inline-only function defined in
``ecl/ecl_atomics.h''.
33 34 35
@end deftypefun

@cppindex ecl_atomic_index_incf
36
@deftypefun cl_index ecl_atomic_index_incf (cl_index *slot);
37 38 39 40 41 42

Atomically increment @var{slot} by 1 and return the new value stored
in @var{slot}.
@end deftypefun

@cppindex ecl_atomic_get
43
@deftypefun cl_object ecl_atomic_get (cl_object *slot)
44 45 46 47 48 49 50

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
51
@deftypefun void ecl_atomic_push (cl_object *slot, cl_object o)
52 53
@end deftypefun
@cppindex ecl_atomic_pop
54
@deftypefun cl_object ecl_atomic_pop (cl_object *slot)
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142

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))))))
143
  (test-struct-slot1 struct))
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
=> 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