]> Cypherpunks repositories - gostls13.git/commitdiff
runtime, sync, sync/atomic: document happens-before guarantees
authorRuss Cox <rsc@golang.org>
Wed, 26 Jan 2022 21:53:50 +0000 (16:53 -0500)
committerGopher Robot <gobot@golang.org>
Mon, 6 Jun 2022 20:48:03 +0000 (20:48 +0000)
A few of these are copied from the memory model doc.
Many are entirely new, following discussion on #47141.
See https://research.swtch.com/gomm for background.

The rule we are establishing is that each type that is meant
to help synchronize a Go program should document its
happens-before guarantees.

For #50859.

Change-Id: I947c40639b263abe67499fa74f68711a97873a39
Reviewed-on: https://go-review.googlesource.com/c/go/+/381316
Auto-Submit: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
src/runtime/mfinal.go
src/sync/atomic/doc.go
src/sync/cond.go
src/sync/map.go
src/sync/mutex.go
src/sync/once.go
src/sync/pool.go
src/sync/rwmutex.go
src/sync/waitgroup.go

index 44174913defe6921c2ab13885857d28c74f8afde..f3f3a79fa58c76305f53340f8ad7cace1ba5fda8 100644 (file)
@@ -321,11 +321,23 @@ func runfinq() {
 // closing p.d, causing syscall.Write to fail because it is writing to
 // a closed file descriptor (or, worse, to an entirely different
 // file descriptor opened by a different goroutine). To avoid this problem,
-// call runtime.KeepAlive(p) after the call to syscall.Write.
+// call KeepAlive(p) after the call to syscall.Write.
 //
 // A single goroutine runs all finalizers for a program, sequentially.
 // If a finalizer must run for a long time, it should do so by starting
 // a new goroutine.
+//
+// In the terminology of the Go memory model, a call
+// SetFinalizer(x, f) “synchronizes before” the finalization call f(x).
+// However, there is no guarantee that KeepAlive(x) or any other use of x
+// “synchronizes before” f(x), so in general a finalizer should use a mutex
+// or other synchronization mechanism if it needs to access mutable state in x.
+// For example, consider a finalizer that inspects a mutable field in x
+// that is modified from time to time in the main program before x
+// becomes unreachable and the finalizer is invoked.
+// The modifications in the main program and the inspection in the finalizer
+// need to use appropriate synchronization, such as mutexes or atomic updates,
+// to avoid read-write races.
 func SetFinalizer(obj any, finalizer any) {
        if debug.sbrk != 0 {
                // debug.sbrk never frees memory, so no finalizers run
index bb3b8f673ec781050b86b04bc6ced434a108e9bb..4d426826da330ea5773b16cdd6153004ddcf8762 100644 (file)
 // The load and store operations, implemented by the LoadT and StoreT
 // functions, are the atomic equivalents of "return *addr" and
 // "*addr = val".
+//
+// In the terminology of the Go memory model, if the effect of
+// an atomic operation A is observed by atomic operation B,
+// then A “synchronizes before” B.
+// Additionally, all the atomic operations executed in a program
+// behave as though executed in some sequentially consistent order.
+// This definition provides the same semantics as
+// C++'s sequentially consistent atomics and Java's volatile variables.
 package atomic
 
 import (
index 841be96896e11fa316dc6ca403479bd1436bf00f..19f986e478407f4451fbf366a97a7e3500213e0b 100644 (file)
@@ -18,6 +18,10 @@ import (
 // when calling the Wait method.
 //
 // A Cond must not be copied after first use.
+//
+// In the terminology of the Go memory model, Cond arranges that
+// a call to Broadcast or Signal “synchronizes before” any Wait call
+// that it unblocks.
 type Cond struct {
        noCopy noCopy
 
@@ -85,11 +89,13 @@ func (c *copyChecker) check() {
        }
 }
 
-// noCopy may be embedded into structs which must not be copied
+// noCopy may be added to structs which must not be copied
 // after the first use.
 //
 // See https://golang.org/issues/8005#issuecomment-190753527
 // for details.
+//
+// Note that it must not be embedded, due to the Lock and Unlock methods.
 type noCopy struct{}
 
 // Lock is a no-op used by -copylocks checker from `go vet`.
index 2fa3253429114f457986f80b44770e2ef53099c1..ec529e056bf3ece2428b31f8cdd2d246d13067d4 100644 (file)
@@ -24,6 +24,13 @@ import (
 // contention compared to a Go map paired with a separate Mutex or RWMutex.
 //
 // The zero Map is empty and ready for use. A Map must not be copied after first use.
+//
+// In the terminology of the Go memory model, Map arranges that a write operation
+// “synchronizes before” any read operation that observes the effect of the write, where
+// read and write operations are defined as follows.
+// Load, LoadAndDelete, LoadOrStore are read operations;
+// Delete, LoadAndDelete, and Store are write operations;
+// and LoadOrStore is a write operation when it returns loaded set to false.
 type Map struct {
        mu Mutex
 
index 80bb827054a81fdee4c9a048e1697f312557ac1d..2ea024e5856440eb404a07c022b43f3c9d939d2b 100644 (file)
@@ -24,6 +24,13 @@ func fatal(string)
 // The zero value for a Mutex is an unlocked mutex.
 //
 // A Mutex must not be copied after first use.
+//
+// In the terminology of the Go memory model,
+// the n'th call to Unlock “synchronizes before” the m'th call to Lock
+// for any n < m.
+// A successful call to TryLock is equivalent to a call to Lock.
+// A failed call to TryLock does not establish any “synchronizes before”
+// relation at all.
 type Mutex struct {
        state int32
        sema  uint32
index 38373160b9ffc2ad33fe2db97bb05c92ee885e1e..b6399cfc3d100a657f545000cf651e4a95ccd737 100644 (file)
@@ -11,6 +11,10 @@ import (
 // Once is an object that will perform exactly one action.
 //
 // A Once must not be copied after first use.
+//
+// In the terminology of the Go memory model,
+// the return from f “synchronizes before”
+// the return from any call of once.Do(f).
 type Once struct {
        // done indicates whether the action has been performed.
        // It is first in the struct because it is used in the hot path.
index ea142bb181796de4dea421e471c7cf3c0b96244e..cf01e2e1891a10d3feabf547c8721fb3d6ab3bec 100644 (file)
@@ -41,6 +41,11 @@ import (
 // free list.
 //
 // A Pool must not be copied after first use.
+//
+// In the terminology of the Go memory model, a call to Put(x) “synchronizes before”
+// a call to Get returning that same value x.
+// Similarly, a call to New returning x “synchronizes before”
+// a call to Get returning that same value x.
 type Pool struct {
        noCopy noCopy
 
index 7b10808ec4ffb65e45775fe1ec4a98b6f47db993..e914f3eba0a2689b262b98fc3b5c6cadc0c1e7b1 100644 (file)
@@ -25,6 +25,14 @@ import (
 // recursive read locking. This is to ensure that the lock eventually becomes
 // available; a blocked Lock call excludes new readers from acquiring the
 // lock.
+//
+// In the terminology of the Go memory model,
+// the n'th call to Unlock “synchronizes before” the m'th call to Lock
+// for any n < m, just as for Mutex.
+// For any call to RLock, there exists an n such that
+// the n'th call to Unlock “synchronizes before” that call to RLock,
+// and the corresponding call to RUnlock “synchronizes before”
+// the n+1'th call to Lock.
 type RWMutex struct {
        w           Mutex  // held if there are pending writers
        writerSem   uint32 // semaphore for writers to wait for completing readers
index 9c6662d04be08f6eedbb62db97b1dfef82bfcf66..9f26ae106cb9de2a18c747b5a226c50f58305285 100644 (file)
@@ -17,6 +17,9 @@ import (
 // Wait can be used to block until all goroutines have finished.
 //
 // A WaitGroup must not be copied after first use.
+//
+// In the terminology of the Go memory model, a call to Done
+// “synchronizes before” the return of any Wait call that it unblocks.
 type WaitGroup struct {
        noCopy noCopy