Skip to content

Instantly share code, notes, and snippets.

@catharinejm
Last active May 26, 2017 14:33
Show Gist options
  • Save catharinejm/3e95e988aace96eb350ca7c68a8fe097 to your computer and use it in GitHub Desktop.
Save catharinejm/3e95e988aace96eb350ca7c68a8fe097 to your computer and use it in GitHub Desktop.
A re-entrant lock with sync wrapper
package main
import (
"fmt"
"sync"
"sync/atomic"
"unsafe"
)
type Owner unsafe.Pointer
type Syncable interface {
DoSync(claimant Owner, fn func())
}
type syncImpl struct {
lock *sync.Mutex
owner unsafe.Pointer
}
func (s *syncImpl) DoSync(claimant Owner, fn func()) {
ownerP := unsafe.Pointer(claimant)
if atomic.CompareAndSwapPointer(&s.owner, ownerP, ownerP) {
fmt.Printf("CLAIMANT: %016x - BEFORE - Already has lock\n", uintptr(claimant))
fn()
fmt.Printf("CLAIMANT: %016x - AFTER - Still has lock\n", uintptr(claimant))
} else {
s.lock.Lock()
fmt.Printf("CLAIMANT: %016x - BEFORE - Acquired lock\n", uintptr(claimant))
atomic.SwapPointer(&s.owner, ownerP)
defer func() {
atomic.SwapPointer(&s.owner, unsafe.Pointer(nil))
fmt.Printf("CLAIMANT: %016x - AFTER - Releasing lock\n", uintptr(claimant))
s.lock.Unlock()
}()
fn()
}
}
func doit(sync Syncable, id int, done chan<- bool) {
answer := 42
handle := Owner(&answer)
// fmt.Printf("THREAD %2d: Outside sync\n", id)
sync.DoSync(handle, func() {
// fmt.Printf("THREAD %2d: First sync\n", id)
sync.DoSync(handle, func() {
// fmt.Printf("THREAD %2d: Second sync\n", id)
done <- true
})
})
}
func main() {
threads := 20
done := make(chan bool, threads)
sync := &syncImpl{new(sync.Mutex), unsafe.Pointer(nil)}
for x := 0; x < threads; x += 1 {
go doit(sync, x, done)
}
i := 0
Loop:
for {
select {
case <-done:
i += 1
fmt.Printf("%2d / %2d complete\n", i, threads)
if i == threads {
break Loop
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment