Skip to content

Instantly share code, notes, and snippets.

@lithdew
Last active June 28, 2021 22:07
Show Gist options
  • Save lithdew/4dc571141716f7e6fba26739d811f565 to your computer and use it in GitHub Desktop.
Save lithdew/4dc571141716f7e6fba26739d811f565 to your computer and use it in GitHub Desktop.
go: one shot event
$ go test -bench=. -run=NONE
goos: linux
goarch: amd64
cpu: Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
BenchmarkOneShotEvent-8 3904360 306.8 ns/op 8 B/op 1 allocs/op
BenchmarkChannel-8 3139602 381.7 ns/op 96 B/op 1 allocs/op
BenchmarkWaitGroup-8 2958838 408.5 ns/op 16 B/op 1 allocs/op
package unsafesync
import (
"sync/atomic"
"unsafe"
)
//go:noescape
//go:linkname gopark runtime.gopark
func gopark(
onSuspend func(unsafe.Pointer, unsafe.Pointer) bool,
argument unsafe.Pointer,
suspendReason uint8,
suspendTraceEvent byte,
traceSkip int,
)
//go:noescape
//go:linkname goready runtime.goready
func goready(
frame unsafe.Pointer,
traceSkip int,
)
type OneShotEvent struct {
frame unsafe.Pointer
}
func (self *OneShotEvent) Wait() {
gopark(doSuspendBlock, unsafe.Pointer(self), 0, 0, 1)
}
func doSuspendBlock(frame, event unsafe.Pointer) bool {
self := (*OneShotEvent)(event)
return atomic.CompareAndSwapPointer(&self.frame, nil, frame)
}
func (self *OneShotEvent) Set() {
frame := atomic.SwapPointer(&self.frame, unsafe.Pointer(self))
if frame != nil {
goready(frame, 1)
}
}
package unsafesync
import (
"fmt"
"sync"
"testing"
"time"
)
func TestOneShotEvent(t *testing.T) {
var event OneShotEvent
go func() {
fmt.Println("Sleep")
time.Sleep(1 * time.Second)
fmt.Println("Wake")
event.Set()
}()
fmt.Println("Wait")
event.Wait()
fmt.Println("Notified")
}
func BenchmarkOneShotEvent(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
var event OneShotEvent
go func() { event.Set() }()
event.Wait()
}
}
func BenchmarkChannel(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
ch := make(chan struct{})
go func() { close(ch) }()
<-ch
}
}
func BenchmarkWaitGroup(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
var wg sync.WaitGroup
wg.Add(1)
go func() { wg.Done() }()
wg.Wait()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment