Created
September 14, 2011 15:03
-
-
Save tux21b/1216808 to your computer and use it in GitHub Desktop.
Shared vs. Isolated Mutability
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Fictional accounting example #1 - "Shared Mutability" | |
package main | |
import ( | |
"fmt" | |
"sync" | |
) | |
type Account struct { | |
id string | |
balance uint | |
mu sync.RWMutex | |
} | |
const ( | |
MinBalance = 0 | |
MaxBalance = 10000 | |
) | |
func NewAccount(id string) *Account { | |
return &Account{id: id} | |
} | |
func (a *Account) Id() string { | |
return a.id | |
} | |
func (a *Account) Deposit(amount uint) bool { | |
a.mu.Lock() | |
defer a.mu.Unlock() | |
if a.balance+amount <= MaxBalance { | |
a.balance += amount | |
return true | |
} | |
return false | |
} | |
func (a *Account) Withdraw(amount uint) bool { | |
a.mu.Lock() | |
defer a.mu.Unlock() | |
if a.balance >= MinBalance+amount { | |
a.balance -= amount | |
return true | |
} | |
return false | |
} | |
func (a *Account) Balance() uint { | |
a.mu.RLock() | |
defer a.mu.RUnlock() | |
return a.balance | |
} | |
func Transfer(src, dst *Account, amount uint) bool { | |
// Always lock accounts in the same order to avoid deadlocks | |
if src.Id() < dst.Id() { | |
src.mu.Lock() | |
defer src.mu.Unlock() | |
dst.mu.Lock() | |
defer dst.mu.Unlock() | |
} else if dst.Id() < src.Id() { | |
dst.mu.Lock() | |
defer dst.mu.Unlock() | |
src.mu.Lock() | |
defer src.mu.Unlock() | |
} else { | |
panic("invalid transfer") | |
} | |
if src.balance-amount >= 0 { | |
src.balance -= amount | |
dst.balance += amount | |
return true | |
} | |
return false | |
} | |
func main() { | |
acc1 := NewAccount("Account1") | |
acc2 := NewAccount("Account2") | |
acc1.Deposit(100) | |
acc2.Deposit(20) | |
fmt.Printf("Balance of %s: %d\n", acc1.Id(), acc1.Balance()) | |
fmt.Printf("Balance of %s: %d\n", acc2.Id(), acc2.Balance()) | |
fmt.Println("Transfering money...") | |
Transfer(acc1, acc2, 50) | |
fmt.Printf("Balance of %s: %d\n", acc1.Id(), acc1.Balance()) | |
fmt.Printf("Balance of %s: %d\n", acc2.Id(), acc2.Balance()) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Fictional accounting example #2 - "Isolated Mutability" | |
package main | |
import ( | |
"fmt" | |
) | |
type Account struct { | |
id string | |
balance int | |
qry chan accQuery | |
} | |
const ( | |
MinBalance = 0 | |
MaxBalance = 10000 | |
) | |
type accQuery struct { | |
amount int | |
reply chan accReply | |
} | |
type accReply struct { | |
ok bool | |
balance int | |
} | |
func NewAccount(id string) *Account { | |
a := &Account{id: id, qry: make(chan accQuery)} | |
go func() { | |
for msg := range a.qry { | |
if a.balance+msg.amount <= MaxBalance && | |
a.balance+msg.amount >= MinBalance { | |
a.balance += msg.amount | |
msg.reply <- accReply{true, a.balance} | |
} else { | |
msg.reply <- accReply{false, a.balance} | |
} | |
} | |
}() | |
return a | |
} | |
func (a *Account) Id() string { | |
return a.id | |
} | |
func (a *Account) Deposit(amount int) bool { | |
reply := make(chan accReply) | |
a.qry <- accQuery{amount, reply} | |
return (<-reply).ok | |
} | |
func (a *Account) Withdraw(amount int) bool { | |
reply := make(chan accReply) | |
a.qry <- accQuery{-amount, reply} | |
return (<-reply).ok | |
} | |
func (a *Account) Balance() int { | |
reply := make(chan accReply) | |
a.qry <- accQuery{0, reply} | |
return (<-reply).balance | |
} | |
func Transfer(src, dst *Account, amount int) bool { | |
// TODO: Coordinate the transaction and make it atomic... How? :) | |
src.Withdraw(amount) | |
dst.Deposit(amount) | |
return false | |
} | |
func main() { | |
acc1 := NewAccount("Account1") | |
acc2 := NewAccount("Account2") | |
acc1.Deposit(100) | |
acc2.Deposit(20) | |
fmt.Printf("Balance of %s: %d\n", acc1.Id(), acc1.Balance()) | |
fmt.Printf("Balance of %s: %d\n", acc2.Id(), acc2.Balance()) | |
fmt.Println("Transfering money...") | |
Transfer(acc1, acc2, 50) | |
fmt.Printf("Balance of %s: %d\n", acc1.Id(), acc1.Balance()) | |
fmt.Printf("Balance of %s: %d\n", acc2.Id(), acc2.Balance()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment