From e75c210832727f00cd3deb6eb24e7cd19780938a Mon Sep 17 00:00:00 2001 From: Chris O'Hara Date: Mon, 25 Sep 2023 17:03:15 -0700 Subject: [PATCH] Move gls code to internal package --- coroutine.go | 4 +++- coroutine_durable.go | 8 +++++--- coroutine_volatile.go | 12 ++++++++---- getg.go => internal/gls/getg.go | 2 +- getg_386.s => internal/gls/getg_386.s | 0 getg_amd64.s => internal/gls/getg_amd64.s | 0 getg_arm64.s => internal/gls/getg_arm64.s | 0 gls.go => internal/gls/gls.go | 24 +++++++++++++++++------ gls_test.go => internal/gls/gls_test.go | 17 +++++++--------- 9 files changed, 42 insertions(+), 25 deletions(-) rename getg.go => internal/gls/getg.go (93%) rename getg_386.s => internal/gls/getg_386.s (100%) rename getg_amd64.s => internal/gls/getg_amd64.s (100%) rename getg_arm64.s => internal/gls/getg_arm64.s (100%) rename gls.go => internal/gls/gls.go (73%) rename gls_test.go => internal/gls/gls_test.go (82%) diff --git a/coroutine.go b/coroutine.go index 001a169..197f92c 100644 --- a/coroutine.go +++ b/coroutine.go @@ -2,6 +2,8 @@ package coroutine import ( "errors" + + "github.com/stealthrocket/coroutine/internal/gls" ) // Coroutine instances expose APIs allowing the program to drive the execution @@ -94,7 +96,7 @@ func Yield[R, S any](v R) S { // The function panics when called on a stack where no active coroutine exists, // or if the type parameters do not match those of the coroutine. func LoadContext[R, S any]() *Context[R, S] { - switch c := loadContext(getg()).(type) { + switch c := gls.Context().Load().(type) { case *Context[R, S]: return c case nil: diff --git a/coroutine_durable.go b/coroutine_durable.go index 4c8fb59..c268c37 100644 --- a/coroutine_durable.go +++ b/coroutine_durable.go @@ -6,6 +6,7 @@ import ( "slices" "strconv" + "github.com/stealthrocket/coroutine/internal/gls" "github.com/stealthrocket/coroutine/internal/serde" ) @@ -27,11 +28,12 @@ func (c Coroutine[R, S]) Next() (hasNext bool) { return false } - g := getg() - storeContext(g, c.ctx) + g := gls.Context() + + g.Store(c.ctx) defer func() { - clearContext(g) + g.Clear() switch err := recover(); err { case nil: diff --git a/coroutine_volatile.go b/coroutine_volatile.go index aa2b240..ed41e73 100644 --- a/coroutine_volatile.go +++ b/coroutine_volatile.go @@ -2,7 +2,11 @@ package coroutine -import "runtime" +import ( + "runtime" + + "github.com/stealthrocket/coroutine/internal/gls" +) // New creates a new coroutine which executes f as entry point. func New[R, S any](f func()) Coroutine[R, S] { @@ -13,13 +17,13 @@ func New[R, S any](f func()) Coroutine[R, S] { } go func() { - g := getg() - storeContext(g, c) + g := gls.Context() + g.Store(c) defer func() { c.done = true close(c.next) - clearContext(g) + g.Clear() }() <-c.next diff --git a/getg.go b/internal/gls/getg.go similarity index 93% rename from getg.go rename to internal/gls/getg.go index fc92a3b..f4424d7 100644 --- a/getg.go +++ b/internal/gls/getg.go @@ -1,4 +1,4 @@ -package coroutine +package gls // getg is like the compiler intrisinc runtime.getg which retrieves the current // goroutine object. diff --git a/getg_386.s b/internal/gls/getg_386.s similarity index 100% rename from getg_386.s rename to internal/gls/getg_386.s diff --git a/getg_amd64.s b/internal/gls/getg_amd64.s similarity index 100% rename from getg_amd64.s rename to internal/gls/getg_amd64.s diff --git a/getg_arm64.s b/internal/gls/getg_arm64.s similarity index 100% rename from getg_arm64.s rename to internal/gls/getg_arm64.s diff --git a/gls.go b/internal/gls/gls.go similarity index 73% rename from gls.go rename to internal/gls/gls.go index 2e78b5d..8c01f9f 100644 --- a/gls.go +++ b/internal/gls/gls.go @@ -1,4 +1,4 @@ -package coroutine +package gls import "sync" @@ -23,26 +23,38 @@ import "sync" // simple memory loads. var ( gmutex sync.RWMutex - gstate map[uintptr]any + gstate map[G]any ) -func loadContext(g uintptr) any { +// G is a reference to a goroutine, and provides a way +// to load, store and clear a goroutine local context. +type G uintptr + +// Context retrieves the goroutine local storage for contexts. +func Context() G { + return G(getg()) +} + +// Load loads the goroutine local context. +func (g G) Load() any { gmutex.RLock() v := gstate[g] gmutex.RUnlock() return v } -func storeContext(g uintptr, c any) { +// Store stores the goroutine local context. +func (g G) Store(c any) { gmutex.Lock() if gstate == nil { - gstate = make(map[uintptr]any) + gstate = make(map[G]any) } gstate[g] = c gmutex.Unlock() } -func clearContext(g uintptr) { +// Clear clears the goroutine local context. +func (g G) Clear() { gmutex.Lock() delete(gstate, g) gmutex.Unlock() diff --git a/gls_test.go b/internal/gls/gls_test.go similarity index 82% rename from gls_test.go rename to internal/gls/gls_test.go index 50916b4..2558e07 100644 --- a/gls_test.go +++ b/internal/gls/gls_test.go @@ -1,4 +1,4 @@ -package coroutine +package gls import "testing" @@ -7,15 +7,15 @@ func TestGLS(t *testing.T) { f := func(n int) { defer close(c) - storeContext(getg(), n) + Context().Store(n) load := func() int { - v, _ := loadContext(getg()).(int) + v, _ := Context().Load().(int) return v } c <- load() - clearContext(getg()) + Context().Clear() c <- load() } @@ -43,27 +43,24 @@ func BenchmarkGLS(b *testing.B) { b.Run("loadContext", func(b *testing.B) { b.RunParallel(func(pb *testing.PB) { - g := getg() for pb.Next() { - _ = loadContext(g) + _ = Context().Load() } }) }) b.Run("storeContext", func(b *testing.B) { b.RunParallel(func(pb *testing.PB) { - g := getg() for pb.Next() { - storeContext(g, 42) + Context().Store(42) } }) }) b.Run("clearContext", func(b *testing.B) { b.RunParallel(func(pb *testing.PB) { - g := getg() for pb.Next() { - clearContext(g) + Context().Clear() } }) })