add memory-order-{acquire,release}
This commit is contained in:
parent
100597f9bb
commit
40f07236b9
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
(define collection 'multi)
|
(define collection 'multi)
|
||||||
|
|
||||||
(define version "7.8.0.1")
|
(define version "7.8.0.2")
|
||||||
|
|
||||||
(define deps `("racket-lib"
|
(define deps `("racket-lib"
|
||||||
["racket" #:version ,version]))
|
["racket" #:version ,version]))
|
||||||
|
|
|
@ -19,3 +19,4 @@ support for parallelism to improve performance.
|
||||||
@include-section["futures.scrbl"]
|
@include-section["futures.scrbl"]
|
||||||
@include-section["places.scrbl"]
|
@include-section["places.scrbl"]
|
||||||
@include-section["engine.scrbl"]
|
@include-section["engine.scrbl"]
|
||||||
|
@include-section["memory-order.scrbl"]
|
||||||
|
|
|
@ -129,7 +129,7 @@ boxes that are not @tech{impersonators}.
|
||||||
@racket[box-cas!] is guaranteed to use a hardware @emph{compare and
|
@racket[box-cas!] is guaranteed to use a hardware @emph{compare and
|
||||||
set} operation. Uses of @racket[box-cas!] be performed safely in a
|
set} operation. Uses of @racket[box-cas!] be performed safely in a
|
||||||
@tech{future} (i.e., allowing the future thunk to continue in
|
@tech{future} (i.e., allowing the future thunk to continue in
|
||||||
parallel).}
|
parallel). See also @secref["memory-order"].}
|
||||||
|
|
||||||
@; ----------------------------------------------------------------------
|
@; ----------------------------------------------------------------------
|
||||||
@include-section["hashes.scrbl"]
|
@include-section["hashes.scrbl"]
|
||||||
|
|
|
@ -40,7 +40,8 @@ futures and threads. Furthermore, guarantees about the visibility of
|
||||||
effects and ordering are determined by the operating system and
|
effects and ordering are determined by the operating system and
|
||||||
hardware---which rarely support, for example, the guarantee of
|
hardware---which rarely support, for example, the guarantee of
|
||||||
sequential consistency that is provided for @racket[thread]-based
|
sequential consistency that is provided for @racket[thread]-based
|
||||||
concurrency. At the same time, operations that seem obviously safe may
|
concurrency; see also @secref["memory-order"]. At the same time, operations
|
||||||
|
that seem obviously safe may
|
||||||
have a complex enough implementation internally that they cannot run in
|
have a complex enough implementation internally that they cannot run in
|
||||||
parallel. See also @guidesecref["effective-futures"] in @|Guide|.
|
parallel. See also @guidesecref["effective-futures"] in @|Guide|.
|
||||||
|
|
||||||
|
@ -153,6 +154,13 @@ the futures may be @racket[touch]ed in any order.
|
||||||
are not safe to perform in parallel, and they therefore prevent
|
are not safe to perform in parallel, and they therefore prevent
|
||||||
a computation from continuing in parallel.
|
a computation from continuing in parallel.
|
||||||
|
|
||||||
|
Beware of trying to use an fsemaphore to implement a lock. A future
|
||||||
|
may run concurrently and in parallel to other futures, but a future
|
||||||
|
that is not demanded by a Racket thread can be suspended at any
|
||||||
|
time---such as just after it takes a lock and before it releases the
|
||||||
|
lock. If you must share mutable data among futures, lock-free data
|
||||||
|
structures are generally a better fit.
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(fsemaphore? [v any/c]) boolean?]{
|
@defproc[(fsemaphore? [v any/c]) boolean?]{
|
||||||
|
|
44
pkgs/racket-doc/scribblings/reference/memory-order.scrbl
Normal file
44
pkgs/racket-doc/scribblings/reference/memory-order.scrbl
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#lang scribble/doc
|
||||||
|
@(require "mz.rkt"
|
||||||
|
(for-label racket/unsafe/ops))
|
||||||
|
|
||||||
|
@title[#:tag "memory-order"]{Machine Memory Order}
|
||||||
|
|
||||||
|
Unlike Racket @tech{threads}, futures and places can expose the
|
||||||
|
underlying machine's memory model, including a weak memory ordering.
|
||||||
|
For example, when a future writes to multiple slots in a mutable
|
||||||
|
vector, it's possible on some platforms for another future to observe
|
||||||
|
the writes in a different order or not at all, unless the futures are
|
||||||
|
explicitly synchronized. Similarly, shared byte strings or
|
||||||
|
@tech{fxvectors} can expose the machine's memory model across places.
|
||||||
|
|
||||||
|
Racket ensures that a machine's memory model is not observed in a way
|
||||||
|
that unsafely exposes the implementation of primitive datatypes. For
|
||||||
|
example, it is not possible for one future to see a partially
|
||||||
|
constructed primitive value as a result of reading a vector that is
|
||||||
|
mutated by another future.
|
||||||
|
|
||||||
|
The @racket[box-cas!], @racket[vector-cas!],
|
||||||
|
@racket[unsafe-box*-cas!], @racket[unsafe-vector*-cas!], and
|
||||||
|
@racket[unsafe-struct*-cas!] operations all provide a machine-level
|
||||||
|
compare-and-set, so they can be used in ways that are specifically
|
||||||
|
supported by the a machine's memory model. The
|
||||||
|
@racket[(memory-order-acquire)] and @racket[(memory-order-release)]
|
||||||
|
operations similarly constrain machine-level stores and loads.
|
||||||
|
Synchronization operations such as place messages, future
|
||||||
|
@racket[touch]es, and @tech{future semaphores} imply suitable
|
||||||
|
machine-level acquire and release ordering.
|
||||||
|
|
||||||
|
@deftogether[(
|
||||||
|
@defproc[(memory-order-acquire) void?]
|
||||||
|
@defproc[(memory-order-release) void?]
|
||||||
|
)]{
|
||||||
|
|
||||||
|
Those operations implement a machine-level memory fence on platforms
|
||||||
|
where one is needed for synchronization. The
|
||||||
|
@racket[memory-order-acquire] operation ensures at least a load--load
|
||||||
|
and load--store fence at the machine level, and the
|
||||||
|
@racket[memory-order-release] operation ensures at least a
|
||||||
|
store--store and store--load fence at the machine level.
|
||||||
|
|
||||||
|
@history[#:added "7.7.0.11"]}
|
|
@ -6,6 +6,12 @@
|
||||||
;; demanded by a thread (so it must continue to
|
;; demanded by a thread (so it must continue to
|
||||||
;; run if it takes a lock)
|
;; run if it takes a lock)
|
||||||
|
|
||||||
|
(define (box-cas!* b old new)
|
||||||
|
(or (box-cas! b old new)
|
||||||
|
;; Try again if failure looks spurious:
|
||||||
|
(and (eq? (unbox b) old)
|
||||||
|
(box-cas!* b old new))))
|
||||||
|
|
||||||
(for ([N (in-range 4 20)])
|
(for ([N (in-range 4 20)])
|
||||||
(define f (make-fsemaphore 1))
|
(define f (make-fsemaphore 1))
|
||||||
(define working (box 'ok))
|
(define working (box 'ok))
|
||||||
|
@ -16,10 +22,10 @@
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(for ([i (in-range 5000)])
|
(for ([i (in-range 5000)])
|
||||||
(fsemaphore-wait f)
|
(fsemaphore-wait f)
|
||||||
(unless (box-cas! working 'ok 'not-ok)
|
(unless (box-cas!* working 'ok 'not-ok)
|
||||||
(printf "FAIL\n")
|
(printf "FAIL\n")
|
||||||
(exit 1))
|
(exit 1))
|
||||||
(unless (box-cas! working 'not-ok 'ok)
|
(unless (box-cas!* working 'not-ok 'ok)
|
||||||
(printf "FAIL\n")
|
(printf "FAIL\n")
|
||||||
(exit 1))
|
(exit 1))
|
||||||
(fsemaphore-post f)))))
|
(fsemaphore-post f)))))
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
;; Check to make we're using a build of Chez Scheme
|
;; Check to make we're using a build of Chez Scheme
|
||||||
;; that has all the features we need.
|
;; that has all the features we need.
|
||||||
(define-values (need-maj need-min need-sub need-dev)
|
(define-values (need-maj need-min need-sub need-dev)
|
||||||
(values 9 5 3 31))
|
(values 9 5 3 32))
|
||||||
|
|
||||||
(unless (guard (x [else #f]) (eval 'scheme-fork-version-number))
|
(unless (guard (x [else #f]) (eval 'scheme-fork-version-number))
|
||||||
(error 'compile-file
|
(error 'compile-file
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
#define USE_COMPILED_STARTUP 1
|
#define USE_COMPILED_STARTUP 1
|
||||||
|
|
||||||
#define EXPECTED_PRIM_COUNT 1469
|
#define EXPECTED_PRIM_COUNT 1471
|
||||||
|
|
||||||
#ifdef MZSCHEME_SOMETHING_OMITTED
|
#ifdef MZSCHEME_SOMETHING_OMITTED
|
||||||
# undef USE_COMPILED_STARTUP
|
# undef USE_COMPILED_STARTUP
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
#define MZSCHEME_VERSION_X 7
|
#define MZSCHEME_VERSION_X 7
|
||||||
#define MZSCHEME_VERSION_Y 8
|
#define MZSCHEME_VERSION_Y 8
|
||||||
#define MZSCHEME_VERSION_Z 0
|
#define MZSCHEME_VERSION_Z 0
|
||||||
#define MZSCHEME_VERSION_W 1
|
#define MZSCHEME_VERSION_W 2
|
||||||
|
|
||||||
/* A level of indirection makes `#` work as needed: */
|
/* A level of indirection makes `#` work as needed: */
|
||||||
#define AS_a_STR_HELPER(x) #x
|
#define AS_a_STR_HELPER(x) #x
|
||||||
|
|
|
@ -391,6 +391,8 @@ static Scheme_Object *will_executor_sema(Scheme_Object *w, int *repost);
|
||||||
|
|
||||||
static Scheme_Object *check_break_now(int argc, Scheme_Object *args[]);
|
static Scheme_Object *check_break_now(int argc, Scheme_Object *args[]);
|
||||||
|
|
||||||
|
static Scheme_Object *memory_order(int argc, Scheme_Object *args[]);
|
||||||
|
|
||||||
static Scheme_Object *unsafe_start_atomic(int argc, Scheme_Object **argv);
|
static Scheme_Object *unsafe_start_atomic(int argc, Scheme_Object **argv);
|
||||||
static Scheme_Object *unsafe_end_atomic(int argc, Scheme_Object **argv);
|
static Scheme_Object *unsafe_end_atomic(int argc, Scheme_Object **argv);
|
||||||
static Scheme_Object *unsafe_start_breakable_atomic(int argc, Scheme_Object **argv);
|
static Scheme_Object *unsafe_start_breakable_atomic(int argc, Scheme_Object **argv);
|
||||||
|
@ -609,6 +611,8 @@ void scheme_init_thread(Scheme_Startup_Env *env)
|
||||||
ADD_PRIM_W_ARITY("custodian-limit-memory" , custodian_limit_mem , 2, 3, env);
|
ADD_PRIM_W_ARITY("custodian-limit-memory" , custodian_limit_mem , 2, 3, env);
|
||||||
ADD_PRIM_W_ARITY("custodian-memory-accounting-available?", custodian_can_mem , 0, 0, env);
|
ADD_PRIM_W_ARITY("custodian-memory-accounting-available?", custodian_can_mem , 0, 0, env);
|
||||||
|
|
||||||
|
ADD_FOLDING_PRIM("memory-order-acquire", memory_order, 0, 0, 1, env);
|
||||||
|
ADD_FOLDING_PRIM("memory-order-release", memory_order, 0, 0, 1, env);
|
||||||
|
|
||||||
ADD_FOLDING_PRIM("evt?" , evt_p , 1, 1 , 1, env);
|
ADD_FOLDING_PRIM("evt?" , evt_p , 1, 1 , 1, env);
|
||||||
ADD_PRIM_W_ARITY2("sync" , sch_sync , 0, -1, 0, -1, env);
|
ADD_PRIM_W_ARITY2("sync" , sch_sync , 0, -1, 0, -1, env);
|
||||||
|
@ -8848,6 +8852,19 @@ static Scheme_Object *will_executor_sema(Scheme_Object *w, int *repost)
|
||||||
/* GC preparation and timing */
|
/* GC preparation and timing */
|
||||||
/*========================================================================*/
|
/*========================================================================*/
|
||||||
|
|
||||||
|
/* We don't currently support threads on a platform with a weaked
|
||||||
|
memory model than x86, and no memory-order operations are needed on
|
||||||
|
x86. */
|
||||||
|
|
||||||
|
static Scheme_Object *memory_order(int argc, Scheme_Object *args[])
|
||||||
|
{
|
||||||
|
return scheme_void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*========================================================================*/
|
||||||
|
/* GC preparation and timing */
|
||||||
|
/*========================================================================*/
|
||||||
|
|
||||||
typedef struct Scheme_GC_Pre_Post_Callback_Desc {
|
typedef struct Scheme_GC_Pre_Post_Callback_Desc {
|
||||||
/* All pointer fields => allocate with GC_malloc() */
|
/* All pointer fields => allocate with GC_malloc() */
|
||||||
Scheme_Object *boxed_key;
|
Scheme_Object *boxed_key;
|
||||||
|
|
|
@ -53,12 +53,14 @@
|
||||||
(define (lock-acquire lock)
|
(define (lock-acquire lock)
|
||||||
(start-future-uninterrupted)
|
(start-future-uninterrupted)
|
||||||
(let loop ()
|
(let loop ()
|
||||||
(unless (box-cas! lock 0 1)
|
(if (box-cas! lock 0 1)
|
||||||
|
(memory-order-acquire)
|
||||||
(loop))))
|
(loop))))
|
||||||
|
|
||||||
(define (lock-release lock)
|
(define (lock-release lock)
|
||||||
(cond
|
(cond
|
||||||
[(box-cas! lock 1 0)
|
[(box-cas! lock 1 0)
|
||||||
|
(memory-order-release)
|
||||||
(end-future-uninterrupted)]
|
(end-future-uninterrupted)]
|
||||||
[(eq? (unbox lock) 0)
|
[(eq? (unbox lock) 0)
|
||||||
;; not just a spurious failure...
|
;; not just a spurious failure...
|
||||||
|
|
Loading…
Reference in New Issue
Block a user