aboutsummaryrefslogtreecommitdiff
path: root/tx.go
diff options
context:
space:
mode:
Diffstat (limited to 'tx.go')
-rw-r--r--tx.go74
1 files changed, 74 insertions, 0 deletions
diff --git a/tx.go b/tx.go
new file mode 100644
index 0000000..f63de76
--- /dev/null
+++ b/tx.go
@@ -0,0 +1,74 @@
+package stm
+
+// A Tx represents an atomic transaction.
+type Tx struct {
+ reads map[*Var]uint64
+ writes map[*Var]interface{}
+}
+
+// verify checks that none of the logged values have changed since the
+// transaction began.
+// TODO: is pointer equality good enough? probably not, without immutable data
+func (tx *Tx) verify() bool {
+ for v, version := range tx.reads {
+ v.mu.Lock()
+ changed := v.version != version
+ v.mu.Unlock()
+ if changed {
+ return false
+ }
+ }
+ return true
+}
+
+// commit 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()
+ }
+}
+
+// wait blocks until another transaction modifies any of the Vars read by tx.
+func (tx *Tx) wait() {
+ globalCond.L.Lock()
+ for tx.verify() {
+ globalCond.Wait()
+ }
+ globalCond.L.Unlock()
+}
+
+// Get returns the value of v as of the start of the transaction.
+func (tx *Tx) Get(v *Var) interface{} {
+ // If we previously wrote to v, it will be in the write log.
+ if val, ok := tx.writes[v]; ok {
+ return val
+ }
+ v.mu.Lock()
+ defer v.mu.Unlock()
+ // If we haven't previously read v, record its version
+ if _, ok := tx.reads[v]; !ok {
+ tx.reads[v] = v.version
+ }
+ return v.val
+}
+
+// Set sets the value of a Var for the lifetime of the transaction.
+func (tx *Tx) Set(v *Var, val interface{}) {
+ tx.writes[v] = val
+}
+
+// Retry aborts the transaction and retries it when a Var changes.
+func (tx *Tx) Retry() {
+ panic(Retry)
+}
+
+// Assert is a helper function that retries a transaction if the condition is
+// not satisfied.
+func (tx *Tx) Assert(p bool) {
+ if !p {
+ tx.Retry()
+ }
+}