aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Joiner <anacrolix@gmail.com>2019-11-04 15:14:33 +1100
committerMatt Joiner <anacrolix@gmail.com>2019-11-04 15:14:33 +1100
commit80ec29f423aeb8f7de75c057b12b2e5cd8cb35ba (patch)
tree65b7923b152f9695a0a87dbc26a58bef1d10e29c
parentTransfer project stewardship to anacrolix (diff)
downloadstm-80ec29f423aeb8f7de75c057b12b2e5cd8cb35ba.tar.gz
stm-80ec29f423aeb8f7de75c057b12b2e5cd8cb35ba.tar.xz
Use atomic pointers for Var data
-rw-r--r--funcs.go8
-rw-r--r--tx.go16
-rw-r--r--var.go30
3 files changed, 31 insertions, 23 deletions
diff --git a/funcs.go b/funcs.go
index 47c6273..bd2cf20 100644
--- a/funcs.go
+++ b/funcs.go
@@ -58,13 +58,7 @@ retry:
// AtomicGet is a helper function that atomically reads a value.
func AtomicGet(v *Var) interface{} {
- // since we're only doing one operation, we don't need a full transaction
- globalLock.Lock()
- v.mu.Lock()
- val := v.val
- v.mu.Unlock()
- globalLock.Unlock()
- return val
+ return v.loadState().val
}
// AtomicSet is a helper function that atomically writes a value.
diff --git a/tx.go b/tx.go
index bae2006..f6d2fbf 100644
--- a/tx.go
+++ b/tx.go
@@ -14,9 +14,7 @@ type Tx struct {
// Check that none of the logged values have changed since the transaction began.
func (tx *Tx) verify() bool {
for v, version := range tx.reads {
- v.mu.Lock()
- changed := v.version != version
- v.mu.Unlock()
+ changed := v.loadState().version != version
if changed {
return false
}
@@ -27,10 +25,7 @@ func (tx *Tx) verify() bool {
// Writes the values in the transaction log to their respective Vars.
func (tx *Tx) commit() {
for v, val := range tx.writes {
- v.mu.Lock()
- v.val = val
- v.version++
- v.mu.Unlock()
+ v.changeValue(val)
for tx := range v.watchers {
tx.cond.Broadcast()
delete(v.watchers, tx)
@@ -59,13 +54,12 @@ func (tx *Tx) Get(v *Var) interface{} {
if val, ok := tx.writes[v]; ok {
return val
}
- v.mu.Lock()
- defer v.mu.Unlock()
+ state := v.loadState()
// If we haven't previously read v, record its version
if _, ok := tx.reads[v]; !ok {
- tx.reads[v] = v.version
+ tx.reads[v] = state.version
}
- return v.val
+ return state.val
}
// Set sets the value of a Var for the lifetime of the transaction.
diff --git a/var.go b/var.go
index bf48a41..2d0cb21 100644
--- a/var.go
+++ b/var.go
@@ -1,20 +1,40 @@
package stm
-import "sync"
+import (
+ "sync/atomic"
+ "unsafe"
+)
// Holds an STM variable.
type Var struct {
- mu sync.Mutex
+ state *varSnapshot
+ watchers map[*Tx]struct{}
+}
+
+func (v *Var) addr() *unsafe.Pointer {
+ return (*unsafe.Pointer)(unsafe.Pointer(&v.state))
+}
+
+func (v *Var) loadState() *varSnapshot {
+ return (*varSnapshot)(atomic.LoadPointer(v.addr()))
+}
+
+func (v *Var) changeValue(new interface{}) {
+ version := v.loadState().version
+ atomic.StorePointer(v.addr(), unsafe.Pointer(&varSnapshot{version: version + 1, val: new}))
+}
+
+type varSnapshot struct {
val interface{}
version uint64
-
- watchers map[*Tx]struct{}
}
// Returns a new STM variable.
func NewVar(val interface{}) *Var {
return &Var{
- val: val,
+ state: &varSnapshot{
+ val: val,
+ },
watchers: make(map[*Tx]struct{}),
}
}