]> Cypherpunks repositories - gostls13.git/commitdiff
runtime/race: better handling of atomic operations
authorDmitriy Vyukov <dvyukov@google.com>
Mon, 1 Sep 2014 12:04:33 +0000 (08:04 -0400)
committerDmitriy Vyukov <dvyukov@google.com>
Mon, 1 Sep 2014 12:04:33 +0000 (08:04 -0400)
This change fixes the last known false negative of the race detector --
detection of races between mutating atomic operations and non-atomic operations.
Race runtime already has functions for precise modelling of various atomic operations,
so this change just forwards all atomic ops to race runtime
instead of poor man modeling in sync/atomic package.
Performance is also improved -- full sync/atomic tests run in 60s instead of 85s now.

LGTM=khr
R=golang-codereviews, khr
CC=golang-codereviews, rsc
https://golang.org/cl/111310044

src/pkg/runtime/race.c
src/pkg/runtime/race/README
src/pkg/runtime/race/race_darwin_amd64.syso
src/pkg/runtime/race/race_freebsd_amd64.syso
src/pkg/runtime/race/race_linux_amd64.syso
src/pkg/runtime/race/race_windows_amd64.syso
src/pkg/runtime/race/testdata/atomic_test.go
src/pkg/runtime/race_amd64.s
src/pkg/sync/atomic/doc.go
src/pkg/sync/atomic/race.go [deleted file]
src/pkg/sync/atomic/race.s [new file with mode: 0644]

index 07623be1aa71b5412857f5ec27c327d2e461a6d4..a773addb07c5f4ba04cfd93a21c50c5410d79fc8 100644 (file)
@@ -24,6 +24,8 @@ void __tsan_malloc(void);
 void __tsan_acquire(void);
 void __tsan_release(void);
 void __tsan_release_merge(void);
+void __tsan_go_ignore_sync_begin(void);
+void __tsan_go_ignore_sync_end(void);
 
 // Mimic what cmd/cgo would do.
 #pragma cgo_import_static __tsan_init
@@ -36,6 +38,8 @@ void __tsan_release_merge(void);
 #pragma cgo_import_static __tsan_acquire
 #pragma cgo_import_static __tsan_release
 #pragma cgo_import_static __tsan_release_merge
+#pragma cgo_import_static __tsan_go_ignore_sync_begin
+#pragma cgo_import_static __tsan_go_ignore_sync_end
 
 // These are called from race_amd64.s.
 #pragma cgo_import_static __tsan_read
@@ -47,6 +51,17 @@ void __tsan_release_merge(void);
 #pragma cgo_import_static __tsan_func_enter
 #pragma cgo_import_static __tsan_func_exit
 
+#pragma cgo_import_static __tsan_go_atomic32_load
+#pragma cgo_import_static __tsan_go_atomic64_load
+#pragma cgo_import_static __tsan_go_atomic32_store
+#pragma cgo_import_static __tsan_go_atomic64_store
+#pragma cgo_import_static __tsan_go_atomic32_exchange
+#pragma cgo_import_static __tsan_go_atomic64_exchange
+#pragma cgo_import_static __tsan_go_atomic32_fetch_add
+#pragma cgo_import_static __tsan_go_atomic64_fetch_add
+#pragma cgo_import_static __tsan_go_atomic32_compare_exchange
+#pragma cgo_import_static __tsan_go_atomic64_compare_exchange
+
 extern byte runtime·noptrdata[];
 extern byte runtime·enoptrbss[];
   
@@ -250,32 +265,20 @@ runtime·RaceReleaseMerge(void *addr)
        runtime·racereleasemerge(addr);
 }
 
-// func RaceSemacquire(s *uint32)
-void
-runtime·RaceSemacquire(uint32 *s)
-{
-       runtime·semacquire(s, false);
-}
-
-// func RaceSemrelease(s *uint32)
-void
-runtime·RaceSemrelease(uint32 *s)
-{
-       runtime·semrelease(s);
-}
-
 // func RaceDisable()
 void
 runtime·RaceDisable(void)
 {
-       g->raceignore++;
+       if(g->raceignore++ == 0)
+               runtime·racecall(__tsan_go_ignore_sync_begin, g->racectx);
 }
 
 // func RaceEnable()
 void
 runtime·RaceEnable(void)
 {
-       g->raceignore--;
+       if(--g->raceignore == 0)
+               runtime·racecall(__tsan_go_ignore_sync_end, g->racectx);
 }
 
 typedef struct SymbolizeContext SymbolizeContext;
index 6a4259141efc368dbb086f07d96d75e8140ca355..7f185359fe768d06fba98fb619c6a93a34cef9ce 100644 (file)
@@ -9,4 +9,4 @@ $ ./buildgo.sh
 
 Tested with gcc 4.6.1 and 4.7.0.  On Windows it's built with 64-bit MinGW.
 
-Current runtime is built on rev 210365.
+Current runtime is built on rev 215000.
index 9061ce0aa119f99a9694e8629c600ae6a68cdc2e..81b48c6c9471cfd8adbb9c7bb725b5ee75e00488 100644 (file)
Binary files a/src/pkg/runtime/race/race_darwin_amd64.syso and b/src/pkg/runtime/race/race_darwin_amd64.syso differ
index b25d868f48ef01c4705db3e1bc179ebd8afe4de7..5bbe322299d94b0e9a5cded83a96237a8e742644 100644 (file)
Binary files a/src/pkg/runtime/race/race_freebsd_amd64.syso and b/src/pkg/runtime/race/race_freebsd_amd64.syso differ
index 8fd77ae58b3d810b98c4177e47419344fab7cecd..49bf08ef38f9377b13c8fe88def10f96baf295d6 100644 (file)
Binary files a/src/pkg/runtime/race/race_linux_amd64.syso and b/src/pkg/runtime/race/race_linux_amd64.syso differ
index 3ea80a6657f5f83cd1e50ea78508f5481da13426..a4eae9bdd9161e755c1f919def408c8f11c03c80 100644 (file)
Binary files a/src/pkg/runtime/race/race_windows_amd64.syso and b/src/pkg/runtime/race/race_windows_amd64.syso differ
index fc569b96cbca3cef9f92f0ad0c34162f996a8c1c..232744b3dd04f24a01e9f8d1903e96c4b8fb5f9a 100644 (file)
@@ -225,8 +225,7 @@ func TestNoRaceAtomicStoreCASUint64(t *testing.T) {
        x = 1
 }
 
-// Races with non-atomic loads are not detected.
-func TestRaceFailingAtomicStoreLoad(t *testing.T) {
+func TestRaceAtomicStoreLoad(t *testing.T) {
        c := make(chan bool)
        var a uint64
        go func() {
@@ -248,8 +247,7 @@ func TestRaceAtomicLoadStore(t *testing.T) {
        <-c
 }
 
-// Races with non-atomic loads are not detected.
-func TestRaceFailingAtomicAddLoad(t *testing.T) {
+func TestRaceAtomicAddLoad(t *testing.T) {
        c := make(chan bool)
        var a uint64
        go func() {
index f4e72386d8d25831911e492b9ae4161c64a4c726..a4a9dfeb55f49e6120ec5ff700dbaced1965a704 100644 (file)
@@ -79,7 +79,7 @@ TEXT  runtime·RaceWrite(SB), NOSPLIT, $0-8
 TEXT   runtime·racewritepc(SB), NOSPLIT, $0-24
        MOVQ    addr+0(FP), RARG1
        MOVQ    callpc+8(FP), RARG2
-       MOVQ    cp+16(FP), RARG3
+       MOVQ    pc+16(FP), RARG3
        // void __tsan_write_pc(ThreadState *thr, void *addr, void *callpc, void *pc);
        MOVQ    $__tsan_write_pc(SB), AX
        JMP     racecalladdr<>(SB)
@@ -180,6 +180,141 @@ TEXT      runtime·racefuncexit(SB), NOSPLIT, $0-0
        MOVQ    $__tsan_func_exit(SB), AX
        JMP     racecall<>(SB)
 
+// Atomic operations for sync/atomic package.
+
+// Load
+TEXT   sync∕atomic·LoadInt32(SB), NOSPLIT, $0-0
+       MOVQ    $__tsan_go_atomic32_load(SB), AX
+       CALL    racecallatomic<>(SB)
+       RET
+
+TEXT   sync∕atomic·LoadInt64(SB), NOSPLIT, $0-0
+       MOVQ    $__tsan_go_atomic64_load(SB), AX
+       CALL    racecallatomic<>(SB)
+       RET
+
+TEXT   sync∕atomic·LoadUint32(SB), NOSPLIT, $0-0
+       JMP     sync∕atomic·LoadInt32(SB)
+
+TEXT   sync∕atomic·LoadUint64(SB), NOSPLIT, $0-0
+       JMP     sync∕atomic·LoadInt64(SB)
+
+TEXT   sync∕atomic·LoadUintptr(SB), NOSPLIT, $0-0
+       JMP     sync∕atomic·LoadInt64(SB)
+
+TEXT   sync∕atomic·LoadPointer(SB), NOSPLIT, $0-0
+       JMP     sync∕atomic·LoadInt64(SB)
+
+// Store
+TEXT   sync∕atomic·StoreInt32(SB), NOSPLIT, $0-0
+       MOVQ    $__tsan_go_atomic32_store(SB), AX
+       CALL    racecallatomic<>(SB)
+       RET
+
+TEXT   sync∕atomic·StoreInt64(SB), NOSPLIT, $0-0
+       MOVQ    $__tsan_go_atomic64_store(SB), AX
+       CALL    racecallatomic<>(SB)
+       RET
+
+TEXT   sync∕atomic·StoreUint32(SB), NOSPLIT, $0-0
+       JMP     sync∕atomic·StoreInt32(SB)
+
+TEXT   sync∕atomic·StoreUint64(SB), NOSPLIT, $0-0
+       JMP     sync∕atomic·StoreInt64(SB)
+
+TEXT   sync∕atomic·StoreUintptr(SB), NOSPLIT, $0-0
+       JMP     sync∕atomic·StoreInt64(SB)
+
+TEXT   sync∕atomic·StorePointer(SB), NOSPLIT, $0-0
+       JMP     sync∕atomic·StoreInt64(SB)
+
+// Swap
+TEXT   sync∕atomic·SwapInt32(SB), NOSPLIT, $0-0
+       MOVQ    $__tsan_go_atomic32_exchange(SB), AX
+       CALL    racecallatomic<>(SB)
+       RET
+
+TEXT   sync∕atomic·SwapInt64(SB), NOSPLIT, $0-0
+       MOVQ    $__tsan_go_atomic64_exchange(SB), AX
+       CALL    racecallatomic<>(SB)
+       RET
+
+TEXT   sync∕atomic·SwapUint32(SB), NOSPLIT, $0-0
+       JMP     sync∕atomic·SwapInt32(SB)
+
+TEXT   sync∕atomic·SwapUint64(SB), NOSPLIT, $0-0
+       JMP     sync∕atomic·SwapInt64(SB)
+
+TEXT   sync∕atomic·SwapUintptr(SB), NOSPLIT, $0-0
+       JMP     sync∕atomic·SwapInt64(SB)
+
+TEXT   sync∕atomic·SwapPointer(SB), NOSPLIT, $0-0
+       JMP     sync∕atomic·SwapInt64(SB)
+
+// Add
+TEXT   sync∕atomic·AddInt32(SB), NOSPLIT, $0-0
+       MOVQ    $__tsan_go_atomic32_fetch_add(SB), AX
+       CALL    racecallatomic<>(SB)
+       MOVL    add+8(FP), AX   // convert fetch_add to add_fetch
+       ADDL    AX, ret+16(FP)
+       RET
+
+TEXT   sync∕atomic·AddInt64(SB), NOSPLIT, $0-0
+       MOVQ    $__tsan_go_atomic64_fetch_add(SB), AX
+       CALL    racecallatomic<>(SB)
+       MOVQ    add+8(FP), AX   // convert fetch_add to add_fetch
+       ADDQ    AX, ret+16(FP)
+       RET
+
+TEXT   sync∕atomic·AddUint32(SB), NOSPLIT, $0-0
+       JMP     sync∕atomic·AddInt32(SB)
+
+TEXT   sync∕atomic·AddUint64(SB), NOSPLIT, $0-0
+       JMP     sync∕atomic·AddInt64(SB)
+
+TEXT   sync∕atomic·AddUintptr(SB), NOSPLIT, $0-0
+       JMP     sync∕atomic·AddInt64(SB)
+
+TEXT   sync∕atomic·AddPointer(SB), NOSPLIT, $0-0
+       JMP     sync∕atomic·AddInt64(SB)
+
+// CompareAndSwap
+TEXT   sync∕atomic·CompareAndSwapInt32(SB), NOSPLIT, $0-0
+       MOVQ    $__tsan_go_atomic32_compare_exchange(SB), AX
+       CALL    racecallatomic<>(SB)
+       RET
+
+TEXT   sync∕atomic·CompareAndSwapInt64(SB), NOSPLIT, $0-0
+       MOVQ    $__tsan_go_atomic64_compare_exchange(SB), AX
+       CALL    racecallatomic<>(SB)
+       RET
+
+TEXT   sync∕atomic·CompareAndSwapUint32(SB), NOSPLIT, $0-0
+       JMP     sync∕atomic·CompareAndSwapInt32(SB)
+
+TEXT   sync∕atomic·CompareAndSwapUint64(SB), NOSPLIT, $0-0
+       JMP     sync∕atomic·CompareAndSwapInt64(SB)
+
+TEXT   sync∕atomic·CompareAndSwapUintptr(SB), NOSPLIT, $0-0
+       JMP     sync∕atomic·CompareAndSwapInt64(SB)
+
+TEXT   sync∕atomic·CompareAndSwapPointer(SB), NOSPLIT, $0-0
+       JMP     sync∕atomic·CompareAndSwapInt64(SB)
+
+// Generic atomic operation implementation.
+// AX already contains target function.
+TEXT   racecallatomic<>(SB), NOSPLIT, $0-0
+       // Trigger SIGSEGV early.
+       MOVQ    16(SP), R12
+       MOVL    (R12), R12
+       get_tls(R12)
+       MOVQ    g(R12), R14
+       MOVQ    g_racectx(R14), RARG0   // goroutine context
+       MOVQ    8(SP), RARG1    // caller pc
+       MOVQ    (SP), RARG2     // pc
+       LEAQ    16(SP), RARG3   // arguments
+       JMP     racecall<>(SB)
+
 // void runtime·racecall(void(*f)(...), ...)
 // Calls C function f from race runtime and passes up to 4 arguments to it.
 // The arguments are never heap-object-preserving pointers, so we pretend there are no arguments.
index 17ba72fa17187aa4fa31d4ffaba3c6eab9f4b14a..10fb8c9177c7ec31251a7665007bf28cc5bfd25a 100644 (file)
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !race
-
 // Package atomic provides low-level atomic memory primitives
 // useful for implementing synchronization algorithms.
 //
diff --git a/src/pkg/sync/atomic/race.go b/src/pkg/sync/atomic/race.go
deleted file mode 100644 (file)
index 6cbbf12..0000000
+++ /dev/null
@@ -1,276 +0,0 @@
-// Copyright 2011 The Go Authors.  All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build race
-
-package atomic
-
-import (
-       "runtime"
-       "unsafe"
-)
-
-// We use runtime.RaceRead() inside of atomic operations to catch races
-// between atomic and non-atomic operations.  It will also catch races
-// between Mutex.Lock() and mutex overwrite (mu = Mutex{}).  Since we use
-// only RaceRead() we won't catch races with non-atomic loads.
-// Otherwise (if we use RaceWrite()) we will report races
-// between atomic operations (false positives).
-
-var mtx uint32 = 1 // same for all
-
-func SwapInt32(addr *int32, new int32) (old int32) {
-       return int32(SwapUint32((*uint32)(unsafe.Pointer(addr)), uint32(new)))
-}
-
-func SwapUint32(addr *uint32, new uint32) (old uint32) {
-       _ = *addr
-       runtime.RaceSemacquire(&mtx)
-       runtime.RaceRead(unsafe.Pointer(addr))
-       runtime.RaceAcquire(unsafe.Pointer(addr))
-       old = *addr
-       *addr = new
-       runtime.RaceReleaseMerge(unsafe.Pointer(addr))
-       runtime.RaceSemrelease(&mtx)
-       return
-}
-
-func SwapInt64(addr *int64, new int64) (old int64) {
-       return int64(SwapUint64((*uint64)(unsafe.Pointer(addr)), uint64(new)))
-}
-
-func SwapUint64(addr *uint64, new uint64) (old uint64) {
-       _ = *addr
-       runtime.RaceSemacquire(&mtx)
-       runtime.RaceRead(unsafe.Pointer(addr))
-       runtime.RaceAcquire(unsafe.Pointer(addr))
-       old = *addr
-       *addr = new
-       runtime.RaceReleaseMerge(unsafe.Pointer(addr))
-       runtime.RaceSemrelease(&mtx)
-       return
-}
-
-func SwapUintptr(addr *uintptr, new uintptr) (old uintptr) {
-       return uintptr(SwapPointer((*unsafe.Pointer)(unsafe.Pointer(addr)), unsafe.Pointer(new)))
-}
-
-func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer) {
-       _ = *addr
-       runtime.RaceSemacquire(&mtx)
-       runtime.RaceRead(unsafe.Pointer(addr))
-       runtime.RaceAcquire(unsafe.Pointer(addr))
-       old = *addr
-       *addr = new
-       runtime.RaceReleaseMerge(unsafe.Pointer(addr))
-       runtime.RaceSemrelease(&mtx)
-       return
-}
-
-func CompareAndSwapInt32(val *int32, old, new int32) bool {
-       return CompareAndSwapUint32((*uint32)(unsafe.Pointer(val)), uint32(old), uint32(new))
-}
-
-func CompareAndSwapUint32(val *uint32, old, new uint32) (swapped bool) {
-       _ = *val
-       swapped = false
-       runtime.RaceSemacquire(&mtx)
-       runtime.RaceRead(unsafe.Pointer(val))
-       runtime.RaceAcquire(unsafe.Pointer(val))
-       if *val == old {
-               *val = new
-               swapped = true
-               runtime.RaceReleaseMerge(unsafe.Pointer(val))
-       }
-       runtime.RaceSemrelease(&mtx)
-       return
-}
-
-func CompareAndSwapInt64(val *int64, old, new int64) bool {
-       return CompareAndSwapUint64((*uint64)(unsafe.Pointer(val)), uint64(old), uint64(new))
-}
-
-func CompareAndSwapUint64(val *uint64, old, new uint64) (swapped bool) {
-       _ = *val
-       swapped = false
-       runtime.RaceSemacquire(&mtx)
-       runtime.RaceRead(unsafe.Pointer(val))
-       runtime.RaceAcquire(unsafe.Pointer(val))
-       if *val == old {
-               *val = new
-               swapped = true
-               runtime.RaceReleaseMerge(unsafe.Pointer(val))
-       }
-       runtime.RaceSemrelease(&mtx)
-       return
-}
-
-func CompareAndSwapPointer(val *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool) {
-       _ = *val
-       swapped = false
-       runtime.RaceSemacquire(&mtx)
-       runtime.RaceRead(unsafe.Pointer(val))
-       runtime.RaceAcquire(unsafe.Pointer(val))
-       if *val == old {
-               *val = new
-               swapped = true
-               runtime.RaceReleaseMerge(unsafe.Pointer(val))
-       }
-       runtime.RaceSemrelease(&mtx)
-       return
-}
-
-func CompareAndSwapUintptr(val *uintptr, old, new uintptr) (swapped bool) {
-       _ = *val
-       swapped = false
-       runtime.RaceSemacquire(&mtx)
-       runtime.RaceRead(unsafe.Pointer(val))
-       runtime.RaceAcquire(unsafe.Pointer(val))
-       if *val == old {
-               *val = new
-               swapped = true
-               runtime.RaceReleaseMerge(unsafe.Pointer(val))
-       }
-       runtime.RaceSemrelease(&mtx)
-       return
-}
-
-func AddInt32(val *int32, delta int32) int32 {
-       return int32(AddUint32((*uint32)(unsafe.Pointer(val)), uint32(delta)))
-}
-
-func AddUint32(val *uint32, delta uint32) (new uint32) {
-       _ = *val
-       runtime.RaceSemacquire(&mtx)
-       runtime.RaceRead(unsafe.Pointer(val))
-       runtime.RaceAcquire(unsafe.Pointer(val))
-       *val = *val + delta
-       new = *val
-       runtime.RaceReleaseMerge(unsafe.Pointer(val))
-       runtime.RaceSemrelease(&mtx)
-
-       return
-}
-
-func AddInt64(val *int64, delta int64) int64 {
-       return int64(AddUint64((*uint64)(unsafe.Pointer(val)), uint64(delta)))
-}
-
-func AddUint64(val *uint64, delta uint64) (new uint64) {
-       _ = *val
-       runtime.RaceSemacquire(&mtx)
-       runtime.RaceRead(unsafe.Pointer(val))
-       runtime.RaceAcquire(unsafe.Pointer(val))
-       *val = *val + delta
-       new = *val
-       runtime.RaceReleaseMerge(unsafe.Pointer(val))
-       runtime.RaceSemrelease(&mtx)
-
-       return
-}
-
-func AddUintptr(val *uintptr, delta uintptr) (new uintptr) {
-       _ = *val
-       runtime.RaceSemacquire(&mtx)
-       runtime.RaceRead(unsafe.Pointer(val))
-       runtime.RaceAcquire(unsafe.Pointer(val))
-       *val = *val + delta
-       new = *val
-       runtime.RaceReleaseMerge(unsafe.Pointer(val))
-       runtime.RaceSemrelease(&mtx)
-
-       return
-}
-
-func LoadInt32(addr *int32) int32 {
-       return int32(LoadUint32((*uint32)(unsafe.Pointer(addr))))
-}
-
-func LoadUint32(addr *uint32) (val uint32) {
-       _ = *addr
-       runtime.RaceSemacquire(&mtx)
-       runtime.RaceRead(unsafe.Pointer(addr))
-       runtime.RaceAcquire(unsafe.Pointer(addr))
-       val = *addr
-       runtime.RaceSemrelease(&mtx)
-       return
-}
-
-func LoadInt64(addr *int64) int64 {
-       return int64(LoadUint64((*uint64)(unsafe.Pointer(addr))))
-}
-
-func LoadUint64(addr *uint64) (val uint64) {
-       _ = *addr
-       runtime.RaceSemacquire(&mtx)
-       runtime.RaceRead(unsafe.Pointer(addr))
-       runtime.RaceAcquire(unsafe.Pointer(addr))
-       val = *addr
-       runtime.RaceSemrelease(&mtx)
-       return
-}
-
-func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer) {
-       _ = *addr
-       runtime.RaceSemacquire(&mtx)
-       runtime.RaceRead(unsafe.Pointer(addr))
-       runtime.RaceAcquire(unsafe.Pointer(addr))
-       val = *addr
-       runtime.RaceSemrelease(&mtx)
-       return
-}
-
-func LoadUintptr(addr *uintptr) (val uintptr) {
-       _ = *addr
-       runtime.RaceSemacquire(&mtx)
-       runtime.RaceRead(unsafe.Pointer(addr))
-       runtime.RaceAcquire(unsafe.Pointer(addr))
-       val = *addr
-       runtime.RaceSemrelease(&mtx)
-       return
-}
-
-func StoreInt32(addr *int32, val int32) {
-       StoreUint32((*uint32)(unsafe.Pointer(addr)), uint32(val))
-}
-
-func StoreUint32(addr *uint32, val uint32) {
-       _ = *addr
-       runtime.RaceSemacquire(&mtx)
-       runtime.RaceRead(unsafe.Pointer(addr))
-       *addr = val
-       runtime.RaceRelease(unsafe.Pointer(addr))
-       runtime.RaceSemrelease(&mtx)
-}
-
-func StoreInt64(addr *int64, val int64) {
-       StoreUint64((*uint64)(unsafe.Pointer(addr)), uint64(val))
-}
-
-func StoreUint64(addr *uint64, val uint64) {
-       _ = *addr
-       runtime.RaceSemacquire(&mtx)
-       runtime.RaceRead(unsafe.Pointer(addr))
-       *addr = val
-       runtime.RaceRelease(unsafe.Pointer(addr))
-       runtime.RaceSemrelease(&mtx)
-}
-
-func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer) {
-       _ = *addr
-       runtime.RaceSemacquire(&mtx)
-       runtime.RaceRead(unsafe.Pointer(addr))
-       *addr = val
-       runtime.RaceRelease(unsafe.Pointer(addr))
-       runtime.RaceSemrelease(&mtx)
-}
-
-func StoreUintptr(addr *uintptr, val uintptr) {
-       _ = *addr
-       runtime.RaceSemacquire(&mtx)
-       runtime.RaceRead(unsafe.Pointer(addr))
-       *addr = val
-       runtime.RaceRelease(unsafe.Pointer(addr))
-       runtime.RaceSemrelease(&mtx)
-}
diff --git a/src/pkg/sync/atomic/race.s b/src/pkg/sync/atomic/race.s
new file mode 100644 (file)
index 0000000..4dadc9e
--- /dev/null
@@ -0,0 +1,8 @@
+// Copyright 2014 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build race
+
+// This file is here only to allow external functions.
+// The operations are implemented in src/pkg/runtime/race_amd64.s