1
0
mirror of https://github.com/golang/go synced 2024-11-22 02:34:40 -07:00

sync: use sync/atomic

Remove references to custom assembly routines.

R=r, r2
CC=golang-dev
https://golang.org/cl/4241043
This commit is contained in:
Russ Cox 2011-02-25 14:29:47 -05:00
parent 22eab1f5c7
commit 12b7875bf2
11 changed files with 92 additions and 176 deletions

View File

@ -12,21 +12,4 @@ GOFILES=\
rwmutex.go\
waitgroup.go\
# 386-specific object files
OFILES_386=\
asm_386.$O\
# amd64-specific object files
OFILES_amd64=\
asm_amd64.$O\
GOARM?=6
# arm-specific object files
OFILES_arm=\
asm_arm$(GOARM).$O\
OFILES=\
$(OFILES_$(GOARCH))\
include ../../Make.pkg

View File

@ -1,23 +0,0 @@
// Copyright 2009 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.
// func cas(val *int32, old, new int32) bool
// Atomically:
// if *val == old {
// *val = new;
// return true;
// }else
// return false;
TEXT ·cas(SB), 7, $0
MOVL 4(SP), BX
MOVL 8(SP), AX
MOVL 12(SP), CX
LOCK
CMPXCHGL CX, 0(BX)
JZ ok
MOVL $0, 16(SP)
RET
ok:
MOVL $1, 16(SP)
RET

View File

@ -1,23 +0,0 @@
// Copyright 2009 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.
// func cas(val *int32, old, new int32) bool
// Atomically:
// if *val == old {
// *val = new;
// return true;
// }else
// return false;
TEXT ·cas(SB), 7, $0
MOVQ 8(SP), BX
MOVL 16(SP), AX
MOVL 20(SP), CX
LOCK
CMPXCHGL CX, 0(BX)
JZ ok
MOVL $0, 24(SP)
RET
ok:
MOVL $1, 24(SP)
RET

View File

@ -1,40 +0,0 @@
// Copyright 2009 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.
// This version works on pre v6 architectures
// func cas(val *int32, old, new int32) bool
// Atomically:
// if *val == old {
// *val = new;
// return true;
// }else
// return false;
TEXT ·cas(SB),7,$0
MOVW 0(FP), R0 // *val
MOVW 4(FP), R1 // old
MOVW 8(FP), R2 // new
MOVW $1, R3
MOVW $runtime·cas_mutex(SB), R4
l:
SWPW (R4), R3 // acquire mutex
CMP $0, R3
BNE fail0
MOVW (R0), R5
CMP R1, R5
BNE fail1
MOVW R2, (R0)
MOVW R3, (R4) // release mutex
MOVW $1, R0
MOVW R0, 16(SP)
RET
fail1:
MOVW R3, (R4) // release mutex
fail0:
MOVW $0, R0
MOVW R0, 16(SP)
RET

View File

@ -1,30 +0,0 @@
// Copyright 2009 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.
// func cas(val *int32, old, new int32) bool
// Atomically:
// if *val == old {
// *val = new;
// return true;
// }else
// return false;
TEXT ·cas(SB),7,$0
MOVW 0(FP), R1 // *val
MOVW 4(FP), R2 // old
MOVW 8(FP), R3 // new
l:
LDREX (R1), R0
CMP R0, R2
BNE fail
STREX R3, (R1), R0
CMP $0, R0
BNE l
MOVW $1, R0
MOVW R0, 16(SP)
RET
fail:
MOVW $0, R0
MOVW R0, 16(SP)
RET

View File

@ -0,0 +1,68 @@
// 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.
// Linux/ARM atomic operations.
// Because there is so much variation in ARM devices,
// the Linux kernel provides an appropriate compare-and-swap
// implementation at address 0xffff0fc0. Caller sets:
// R0 = old value
// R1 = new value
// R2 = valptr
// LR = return address
// The function returns with CS true if the swap happened.
// http://lxr.linux.no/linux+v2.6.37.2/arch/arm/kernel/entry-armv.S#L850
TEXT cas<>(SB),7,$0
MOVW $0xffff0fc0, PC
TEXT ·CompareAndSwapInt32(SB),7,$0
B ·CompareAndSwapUint32(SB)
// Implement using kernel cas for portability.
TEXT ·CompareAndSwapUint32(SB),7,$0
MOVW valptr+0(FP), R2
MOVW old+4(FP), R0
MOVW new+8(FP), R1
BL cas<>(SB)
MOVW $0, R0
MOVW.CS $1, R0
MOVW R0, ret+12(FP)
RET
TEXT ·CompareAndSwapUintptr(SB),7,$0
B ·CompareAndSwapUint32(SB)
TEXT ·AddInt32(SB),7,$0
B ·AddUint32(SB)
// Implement using kernel cas for portability.
TEXT ·AddUint32(SB),7,$0
MOVW valptr+0(FP), R2
MOVW delta+4(FP), R4
addloop1:
MOVW 0(R2), R0
MOVW R0, R1
ADD R4, R1
BL cas<>(SB)
BCC addloop1
MOVW R1, ret+8(FP)
RET
TEXT ·AddUintptr(SB),7,$0
B ·AddUint32(SB)
// The kernel provides no 64-bit compare-and-swap,
// so use native ARM instructions, which will only work on
// ARM 11 and later devices.
TEXT ·CompareAndSwapInt64(SB),7,$0
B ·armCompareAndSwapUint64(SB)
TEXT ·CompareAndSwapUint64(SB),7,$0
B ·armCompareAndSwapUint64(SB)
TEXT ·AddInt64(SB),7,$0
B ·armAddUint64(SB)
TEXT ·AddUint64(SB),7,$0
B ·armAddUint64(SB)

View File

@ -11,7 +11,7 @@ import (
func TestCondSignal(t *testing.T) {
var m Mutex
c := NewCond(&m)
n := 1000
n := 2
running := make(chan bool, n)
awake := make(chan bool, n)
for i := 0; i < n; i++ {

View File

@ -9,15 +9,16 @@
// done via channels and communication.
package sync
import "runtime"
func cas(val *uint32, old, new uint32) bool
import (
"runtime"
"sync/atomic"
)
// A Mutex is a mutual exclusion lock.
// Mutexes can be created as part of other structures;
// the zero value for a Mutex is an unlocked mutex.
type Mutex struct {
key uint32
key int32
sema uint32
}
@ -27,25 +28,11 @@ type Locker interface {
Unlock()
}
// Add delta to *val, and return the new *val in a thread-safe way. If multiple
// goroutines call xadd on the same val concurrently, the changes will be
// serialized, and all the deltas will be added in an undefined order.
func xadd(val *uint32, delta int32) (new uint32) {
for {
v := *val
nv := v + uint32(delta)
if cas(val, v, nv) {
return nv
}
}
panic("unreached")
}
// Lock locks m.
// If the lock is already in use, the calling goroutine
// blocks until the mutex is available.
func (m *Mutex) Lock() {
if xadd(&m.key, 1) == 1 {
if atomic.AddInt32(&m.key, 1) == 1 {
// changed from 0 to 1; we hold lock
return
}
@ -59,11 +46,11 @@ func (m *Mutex) Lock() {
// It is allowed for one goroutine to lock a Mutex and then
// arrange for another goroutine to unlock it.
func (m *Mutex) Unlock() {
switch v := xadd(&m.key, -1); {
switch v := atomic.AddInt32(&m.key, -1); {
case v == 0:
// changed from 1 to 0; no contention
return
case int32(v) == -1:
case v == -1:
// changed from 0 to -1: wasn't locked
// (or there are 4 billion goroutines waiting)
panic("sync: unlock of unlocked mutex")

View File

@ -4,6 +4,8 @@
package sync
import "sync/atomic"
// An RWMutex is a reader/writer mutual exclusion lock.
// The lock can be held by an arbitrary number of readers
// or a single writer.
@ -14,9 +16,9 @@ package sync
// Writers take priority over Readers: no new RLocks
// are granted while a blocked Lock call is waiting.
type RWMutex struct {
w Mutex // held if there are pending readers or writers
r Mutex // held if the w is being rd
readerCount uint32 // number of pending readers
w Mutex // held if there are pending readers or writers
r Mutex // held if the w is being rd
readerCount int32 // number of pending readers
}
// RLock locks rw for reading.
@ -33,7 +35,7 @@ func (rw *RWMutex) RLock() {
// B: rw.RUnlock()
// ... (new readers come and go indefinitely, W is starving)
rw.r.Lock()
if xadd(&rw.readerCount, 1) == 1 {
if atomic.AddInt32(&rw.readerCount, 1) == 1 {
// The first reader locks rw.w, so writers will be blocked
// while the readers have the RLock.
rw.w.Lock()
@ -46,7 +48,7 @@ func (rw *RWMutex) RLock() {
// It is a run-time error if rw is not locked for reading
// on entry to RUnlock.
func (rw *RWMutex) RUnlock() {
if xadd(&rw.readerCount, -1) == 0 {
if atomic.AddInt32(&rw.readerCount, -1) == 0 {
// last reader finished, enable writers
rw.w.Unlock()
}

View File

@ -10,6 +10,7 @@ import (
"fmt"
"runtime"
. "sync"
"sync/atomic"
"testing"
)
@ -49,31 +50,31 @@ func TestParallelReaders(t *testing.T) {
doTestParallelReaders(4, 2)
}
func reader(rwm *RWMutex, num_iterations int, activity *uint32, cdone chan bool) {
func reader(rwm *RWMutex, num_iterations int, activity *int32, cdone chan bool) {
for i := 0; i < num_iterations; i++ {
rwm.RLock()
n := Xadd(activity, 1)
n := atomic.AddInt32(activity, 1)
if n < 1 || n >= 10000 {
panic(fmt.Sprintf("wlock(%d)\n", n))
}
for i := 0; i < 100; i++ {
}
Xadd(activity, -1)
atomic.AddInt32(activity, -1)
rwm.RUnlock()
}
cdone <- true
}
func writer(rwm *RWMutex, num_iterations int, activity *uint32, cdone chan bool) {
func writer(rwm *RWMutex, num_iterations int, activity *int32, cdone chan bool) {
for i := 0; i < num_iterations; i++ {
rwm.Lock()
n := Xadd(activity, 10000)
n := atomic.AddInt32(activity, 10000)
if n != 10000 {
panic(fmt.Sprintf("wlock(%d)\n", n))
}
for i := 0; i < 100; i++ {
}
Xadd(activity, -10000)
atomic.AddInt32(activity, -10000)
rwm.Unlock()
}
cdone <- true
@ -82,7 +83,7 @@ func writer(rwm *RWMutex, num_iterations int, activity *uint32, cdone chan bool)
func HammerRWMutex(gomaxprocs, numReaders, num_iterations int) {
runtime.GOMAXPROCS(gomaxprocs)
// Number of active readers + 10000 * number of active writers.
var activity uint32
var activity int32
var rwm RWMutex
cdone := make(chan bool)
go writer(&rwm, num_iterations, &activity, cdone)

View File

@ -1,9 +0,0 @@
// Copyright 2009 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.
package sync
func Xadd(val *uint32, delta int32) (new uint32) {
return xadd(val, delta)
}