X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2Fcode%2Flate-extensions.lisp;h=72288977d3e53ca6436ac4de5e25db92dd9f7f6b;hb=d720bc359f03734ccb9baf66cb45dc01d623f369;hp=d18939be21377e8f46c8ae003b33eb846fc40d82;hpb=8a33054f6541596c61b091e2b77118deda1511e2;p=sbcl.git diff --git a/src/code/late-extensions.lisp b/src/code/late-extensions.lisp index d18939b..7228897 100644 --- a/src/code/late-extensions.lisp +++ b/src/code/late-extensions.lisp @@ -365,3 +365,43 @@ returns NIL each time." ,(/ 1.0 internal-time-units-per-second)) 0))))) ,@body)))) + +(defmacro atomic-update (place update-fn &rest arguments &environment env) + #!+sb-doc + "Updates PLACE atomically to the value returned by calling function +designated by UPDATE-FN with ARGUMENTS and the previous value of PLACE. + +PLACE may be read and UPDATE-FN evaluated and called multiple times before the +update succeeds: atomicity in this context means that value of place did not +change between the time it was read, and the time it was replaced with the +computed value. + +PLACE can be any place supported by SB-EXT:COMPARE-AND-SWAP. + +Examples: + + ;;; Conses T to the head of FOO-LIST. + (defstruct foo list) + (defvar *foo* (make-foo)) + (atomic-update (foo-list *foo*) #'cons t) + + (let ((x (cons :count 0))) + (mapc #'sb-thread:join-thread + (loop repeat 1000 + collect (sb-thread:make-thread + (lambda () + (loop repeat 1000 + do (atomic-update (cdr x) #'1+) + (sleep 0.00001)))))) + ;; Guaranteed to be (:COUNT . 1000000) -- if you replace + ;; atomic update with (INCF (CDR X)) above, the result becomes + ;; unpredictable. + x) +" + (multiple-value-bind (vars vals old new cas-form read-form) + (get-cas-expansion place env) + `(let* (,@(mapcar 'list vars vals) + (,old ,read-form)) + (loop for ,new = (funcall ,update-fn ,@arguments ,old) + until (eq ,old (setf ,old ,cas-form)) + finally (return ,new)))))