Skip to content

Instantly share code, notes, and snippets.

@bachue
Created June 8, 2020 08:52
Show Gist options
  • Save bachue/8e0a0fac9f22efdc5745a22f7e0a6d2e to your computer and use it in GitHub Desktop.
Save bachue/8e0a0fac9f22efdc5745a22f7e0a6d2e to your computer and use it in GitHub Desktop.
How to make lock-free go struct thread-safe
package main
import (
"github.com/AlekSi/pointer"
log "github.com/sirupsen/logrus"
"sync"
"sync/atomic"
"unsafe"
)
type A struct {
B int64
C string
D *string
}
var globalA unsafe.Pointer
func main() {
globalA = unsafe.Pointer(&A{
B: 0,
C: "12",
D: pointer.ToString("12.E"),
})
var wg sync.WaitGroup
wg.Add(100)
for i := 0; i < 100; i++ {
go worker(i, &wg)
}
wg.Wait()
}
func worker(workerID int, wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 100; i++ {
for j := 0; ; j++ {
original := globalA
copied := copyA((*A)(original))
log.WithField("workerID", workerID).WithField("iter", i).WithField("retry", j).WithField("A.B", copied.B).Infof("Copying A")
copied.B += 1
if atomic.CompareAndSwapPointer(&globalA, original, unsafe.Pointer(&copied)) {
log.WithField("workerID", workerID).WithField("iter", i).WithField("retry", j).WithField("A.B", copied.B).Infof("OK")
break
} else {
log.WithField("workerID", workerID).WithField("iter", i).WithField("retry", j).WithField("A.B", copied.B).Infof("Fail, Will retry")
}
}
}
}
func copyA(a *A) A {
return A{
B: a.B,
C: a.C,
D: pointer.ToString(pointer.GetString(a.D)),
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment