diff --git a/csug/threads.stex b/csug/threads.stex index e964a78f3d..c495006b7f 100644 --- a/csug/threads.stex +++ b/csug/threads.stex @@ -568,6 +568,36 @@ allocation of the containing object and decremented upon freeing of the containing object. + +\section{Memory Consistency\label{SECTSMGMTMEMMODEL}} + +Scheme threads can expose the memory-consistency model of the +underlying processor, except to the degree that it would interefere +with the memory safety of Scheme programs. For example, if two threads +share a vector, then a \scheme{vector-set!} in one thread will not +allow the other thread to read the vector and see a partially +constructed primitive object installed into the vector; the Scheme +system includes a memory fence around operations and on platforms as +needed to preserve safety. There's no guarantee, for example, that +assigning to multiple slots in an fxvector will become visible in the +same order to other threads that share the vector, because no such +ordering is required to preserve memory safety. + +%---------------------------------------------------------------------------- +\entryheader +\formdef{memory-order-acquire}{\categoryprocedure}{(memory-order-acquire)} +\formdef{memory-order-release}{\categoryprocedure}{(memory-order-release)} +\returns unspecified +\listlibraries +\endnoskipentryheader + +These procedures fence memory operations in a way that is consistent +with acquire--release patterns. Specifically, +\scheme{memory-order-acquire} ensures at least a load--load and +load--store fence, and \scheme{memory-order-release} ensures at least +a store--store and store--load fence. + + \section{Thread Parameters\label{SECTTHREADPARAMETERS}} %---------------------------------------------------------------------------- diff --git a/makefiles/Mf-install.in b/makefiles/Mf-install.in index 9ce6714100..71bb66cee5 100644 --- a/makefiles/Mf-install.in +++ b/makefiles/Mf-install.in @@ -62,7 +62,7 @@ InstallLZ4Target= # no changes should be needed below this point # ############################################################################### -Version=csv9.5.3.31 +Version=csv9.5.3.32 Include=boot/$m PetiteBoot=boot/$m/petite.boot SchemeBoot=boot/$m/scheme.boot diff --git a/mats/thread.ms b/mats/thread.ms index 9c361330d7..64d9e403d0 100644 --- a/mats/thread.ms +++ b/mats/thread.ms @@ -1568,6 +1568,51 @@ (set! done? #t) (condition-broadcast c)) (equal? gc-ids (list (get-thread-id))))) + ) + +(mat memory-consistency + (equal? (memory-order-acquire) (void)) + (equal? (memory-order-release) (void)) + ;; Try to make a thread see a partially constructed box + (let ([ids '(one two three four)]) + (let ([m (make-mutex)] + [c (make-condition)] + [ok? #t] + [running (length ids)] + [v (make-vector 1000 (box (car ids)))]) + (let loop ([i running]) + (unless (= i 0) + (fork-thread (lambda () + (let ([id (list-ref ids (sub1 i))] + [failed? #f]) + (let loop ([j 10000]) + (cond + [(fx= j 0) + (mutex-acquire m) + (set! running (sub1 running)) + (condition-signal c) + (set! ok? (and ok? (not failed?))) + (mutex-release m)] + [else + (let loop ([i 0]) + (unless (fx= i (vector-length v)) + (let ([b (vector-ref v i)]) + (unless (and (box? b) + (memq (unbox b) ids)) + (set! failed? #t))) + (vector-set! v i (box id)) + (loop (fx+ i 1)))) + (loop (fx- j 1))]))))) + (loop (sub1 i)))) + (mutex-acquire m) + (let loop () + (cond + [(not (zero? running)) + (condition-wait c m) + (loop)] + [else + (mutex-release m)])) + ok?)) ) (mat wait-for-threads @@ -1581,6 +1626,6 @@ (sleep (make-time 'time-duration 10000 0)) (loop)))) #t) -) +) ) diff --git a/s/arm32.ss b/s/arm32.ss index a2d61ee040..53321e3d36 100644 --- a/s/arm32.ss +++ b/s/arm32.ss @@ -985,9 +985,17 @@ `(set! ,(make-live-info) ,u2 (asm ,null-info ,asm-kill)) `(asm ,info ,asm-cas ,r ,old ,new ,u1 ,u2)))))])) - (define-instruction effect (write-write-fence) + (define-instruction effect (store-store-fence) [(op) - `(asm ,info ,asm-write-write-fence)]) + `(asm ,info ,(asm-fence 'store-store))]) + + (define-instruction effect (acquire-fence) + [(op) + `(asm ,info ,(asm-fence 'acquire))]) + + (define-instruction effect (release-fence) + [(op) + `(asm ,info ,(asm-fence 'release))]) (define-instruction effect (pause) ; NB: user sqrt or something like that? @@ -1033,7 +1041,7 @@ asm-rp-header asm-rp-compact-header asm-indirect-call asm-condition-code asm-fpmove-single asm-fl-cvt asm-fpt asm-fpmove asm-fpcastto asm-fpcastfrom asm-fptrunc - asm-lock asm-lock+/- asm-cas asm-write-write-fence + asm-lock asm-lock+/- asm-cas asm-fence asm-fpop-2 asm-fpsqrt asm-c-simple-call asm-save-flrv asm-restore-flrv asm-return asm-c-return asm-size asm-enter asm-foreign-call asm-foreign-callable @@ -1186,7 +1194,9 @@ (define-op ldrex ldrex-op #b00011001) (define-op strex strex-op #b00011000) - (define-op dmbst dmb-op #b1110) + (define-op dmbst dmb-op #b1110) + (define-op dmbish dmb-op #b1011) + (define-op dmbishst dmb-op #b1010) (define-op bnei branch-imm-op (ax-cond 'ne)) (define-op brai branch-imm-op (ax-cond 'al)) @@ -2182,9 +2192,15 @@ (emit cmpi tmp2 0 code*)))))))) - (define asm-write-write-fence - (lambda (code*) - (emit dmbst code*))) + ;; Based in part on https://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html + (define-who asm-fence + (lambda (kind) + (lambda (code*) + (case kind + [(store-store) (emit dmbishst code*)] + [(acquire) (emit dmbish code*)] + [(release) (emit dmbish code*)] + [else (sorry! who "unexpected kind ~s" kind)])))) (define asm-fp-relop (lambda (info) diff --git a/s/arm64.ss b/s/arm64.ss index bcf4341c40..119ab1944f 100644 --- a/s/arm64.ss +++ b/s/arm64.ss @@ -822,9 +822,17 @@ `(set! ,(make-live-info) ,u2 (asm ,null-info ,asm-kill)) `(asm ,info ,asm-cas ,r ,old ,new ,u1 ,u2)))))])) - (define-instruction effect (write-write-fence) + (define-instruction effect (store-store-fence) [(op) - `(asm ,info ,asm-write-write-fence)]) + `(asm ,info ,(asm-fence 'store-store))]) + + (define-instruction effect (acquire-fence) + [(op) + `(asm ,info ,(asm-fence 'acquire))]) + + (define-instruction effect (release-fence) + [(op) + `(asm ,info ,(asm-fence 'release))]) (define-instruction effect (pause) ;; NB: use sqrt or something like that? @@ -870,7 +878,7 @@ asm-rp-header asm-rp-compact-header asm-indirect-call asm-condition-code asm-fpmove-single asm-fl-cvt asm-fpt asm-fpmove asm-fpcastto asm-fpcastfrom asm-fptrunc - asm-lock asm-lock+/- asm-cas asm-write-write-fence + asm-lock asm-lock+/- asm-cas asm-fence asm-fpop-2 asm-fpsqrt asm-c-simple-call asm-return asm-c-return asm-size asm-enter asm-foreign-call asm-foreign-callable @@ -1062,7 +1070,10 @@ (define-op ldxr ldxr-op #b1 `(reg . ,%real-zero)) (define-op stxr ldxr-op #b0) - (define-op dmbst dmb-op #b1110) + (define-op dmbst dmb-op #b1110) + (define-op dmbish dmb-op #b1011) + (define-op dmbishld dmb-op #b1001) + (define-op dmbishst dmb-op #b1010) (define-op bnei branch-imm-op (ax-cond 'ne)) (define-op beqi branch-imm-op (ax-cond 'eq)) @@ -2105,9 +2116,15 @@ (emit cmpi tmp2 0 code*)))))))) - (define asm-write-write-fence - (lambda (code*) - (emit dmbst code*))) + ;; Based in part on https://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html + (define-who asm-fence + (lambda (kind) + (lambda (code*) + (case kind + [(store-store) (emit dmbishst code*)] + [(acquire) (emit dmbishld code*)] + [(release) (emit dmbish code*)] + [else (sorry! who "unexpected kind ~s" kind)])))) (define asm-fp-relop (lambda (info) diff --git a/s/cmacros.ss b/s/cmacros.ss index ef99a6e9e1..b720830810 100644 --- a/s/cmacros.ss +++ b/s/cmacros.ss @@ -348,7 +348,7 @@ ;; --------------------------------------------------------------------- ;; Version and machine types: -(define-constant scheme-version #x0905031F) +(define-constant scheme-version #x09050320) (define-syntax define-machine-types (lambda (x) diff --git a/s/cpnanopass.ss b/s/cpnanopass.ss index d451d3f8e6..ea88226b37 100644 --- a/s/cpnanopass.ss +++ b/s/cpnanopass.ss @@ -3702,11 +3702,13 @@ ,(%inline sll ,e (immediate ,(fx- (constant char-data-offset) (constant fixnum-offset)))) ,(%constant type-char)))) - (define add-write-fence + (define add-store-fence + ;; A store--store fence should be good enough for safety on a platform that + ;; orders load dependencies (which is anything except Alpha) (lambda (e) (if-feature pthreads (constant-case architecture - [(arm32 arm64) `(seq ,(%inline write-write-fence) ,e)] + [(arm32 arm64) `(seq ,(%inline store-store-fence) ,e)] [else e]) e))) (define build-dirty-store @@ -3714,8 +3716,8 @@ [(base offset e) (build-dirty-store base %zero offset e)] [(base index offset e) (build-dirty-store base index offset e (lambda (base index offset e) `(set! ,(%mref ,base ,index ,offset) ,e)) - (lambda (s r) (add-write-fence `(seq ,s ,r))))] - [(base index offset e build-assign build-seq) + (lambda (s r) (add-store-fence `(seq ,s ,r))))] + [(base index offset e build-assign build-barrier-seq) (if (nanopass-case (L7 Expr) e [(quote ,d) (ptr->imm d)] [else #f]) @@ -3731,13 +3733,13 @@ (bind #f ([e e]) ; eval a second so the address is not live across any calls (bind #t ([a a]) - (build-seq + (build-barrier-seq (build-assign a %zero 0 e) (%inline remember ,a)))) (bind #t ([e e]) ; eval a second so the address is not live across any calls (bind #t ([a a]) - (build-seq + (build-barrier-seq (build-assign a %zero 0 e) `(if ,(%type-check mask-fixnum type-fixnum ,e) ,(%constant svoid) @@ -3750,7 +3752,7 @@ (inline ,(make-info-condition-code 'eq? #f #t) ,%condition-code))))) (define build-cas-seq (lambda (cas remember) - (add-write-fence + (add-store-fence `(if ,cas (seq ,remember ,(%constant strue)) ,(%constant sfalse))))) @@ -6121,6 +6123,18 @@ (define-inline 3 $set-symbol-hash! ; no need for dirty store---e2 should be a fixnum [(e1 e2) `(set! ,(%mref ,e1 ,(constant symbol-hash-disp)) ,e2)]) + (define-inline 2 memory-order-acquire + [() (if-feature pthreads + (constant-case architecture + [(arm32 arm64) (%seq ,(%inline acquire-fence) (quote ,(void)))] + [else `(quote ,(void))]) + `(quote ,(void)))]) + (define-inline 2 memory-order-release + [() (if-feature pthreads + (constant-case architecture + [(arm32 arm64) (%seq ,(%inline release-fence) (quote ,(void)))] + [else `(quote ,(void))]) + `(quote ,(void)))]) (let () (define-syntax define-tlc-parameter (syntax-rules () diff --git a/s/np-languages.ss b/s/np-languages.ss index 80a50a59b0..169371e7d0 100644 --- a/s/np-languages.ss +++ b/s/np-languages.ss @@ -563,7 +563,9 @@ (declare-primitive push-fpmultiple effect #f) ; arm64 (declare-primitive pop-fpmultiple effect #f) ; arm64 (declare-primitive cas effect #f) - (declare-primitive write-write-fence effect #f) + (declare-primitive store-store-fence effect #f) + (declare-primitive acquire-fence effect #f) + (declare-primitive release-fence effect #f) (declare-primitive < pred #t) (declare-primitive <= pred #t) diff --git a/s/primdata.ss b/s/primdata.ss index bd187df1fe..182cde33fa 100644 --- a/s/primdata.ss +++ b/s/primdata.ss @@ -1504,6 +1504,8 @@ (maybe-compile-file [sig [(pathname) (pathname pathname) -> (void)]] [flags true]) (maybe-compile-library [sig [(pathname) (pathname pathname) -> (void)]] [flags true]) (maybe-compile-program [sig [(pathname) (pathname pathname) -> (void)]] [flags true]) + (memory-order-acquire [sig [() -> (void)]] [flags true]) + (memory-order-release [sig [() -> (void)]] [flags true]) (merge [sig [(procedure list list) -> (list)]] [flags true]) (merge! [sig [(procedure list list) -> (list)]] [flags true]) (mkdir [sig [(pathname) (pathname sub-uint) -> (void)]] [flags]) diff --git a/s/prims.ss b/s/prims.ss index 856f5853bd..2cf077a0e7 100644 --- a/s/prims.ss +++ b/s/prims.ss @@ -1167,6 +1167,14 @@ ($top-level-bound? s) ($oops '$top-level-bound? "~s is not a symbol" s)))) +(define memory-order-acquire + (lambda () + (memory-order-acquire))) + +(define memory-order-release + (lambda () + (memory-order-release))) + (define-who $bignum-length (lambda (n) (unless (bignum? n) ($oops who "~s is not a bignum" n))