Skip to content

Commit

Permalink
refactor: supports go1.23
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaost committed Aug 1, 2024
1 parent a489639 commit 61c8a7b
Show file tree
Hide file tree
Showing 10 changed files with 262 additions and 156 deletions.
4 changes: 2 additions & 2 deletions internal/atm/pgen/pgen_gcwb_go121_121_amd64.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//go:build go1.21 && !go1.23
// +build go1.21,!go1.23
//go:build go1.21 && !go1.24
// +build go1.21,!go1.24

/*
* Copyright 2022 CloudWeGo Authors
Expand Down
41 changes: 16 additions & 25 deletions internal/loader/funcdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,15 @@ const (

//go:linkname lastmoduledatap runtime.lastmoduledatap
//goland:noinspection GoUnusedGlobalVariable
var lastmoduledatap *_ModuleData
var lastmoduledatap unsafe.Pointer

//go:linkname moduledataverify1 runtime.moduledataverify1
func moduledataverify1(_ *_ModuleData)

var (
/* retains local reference of all modules to bypass gc */
modList = utils.ListNode{}
modList0 = utils.ListNode{} // all frugal _ModuleData
modList1 = utils.ListNode{} // all runtime.moduledata
)

func toZigzag(v int) int {
Expand Down Expand Up @@ -74,33 +75,23 @@ func encodeVariant(v int) []byte {
return r
}

func registerModule(mod *_ModuleData) {
modList.Prepend(unsafe.Pointer(mod))
registerModuleLockFree(&lastmoduledatap, mod)
func registerModule(p *_ModuleData) {
mod := asRuntimeModuleData(p)
modList0.Prepend(unsafe.Pointer(p))
modList1.Prepend(mod)
registerModuleLockFree(&lastmoduledatap, mod, rtModuleDataFields["next"].off)
}

func registerModuleLockFree(tail **_ModuleData, mod *_ModuleData) {
func registerModuleLockFree(tail *unsafe.Pointer, mod unsafe.Pointer, nextoff uintptr) {
// oldmod := tail
// tail = mod
// oldmod.next = mod
for {
oldTail := loadModule(tail)
if casModule(tail, oldTail, mod) {
storeModule(&oldTail.next, mod)
oldmod := atomic.LoadPointer(tail)
if atomic.CompareAndSwapPointer(tail, oldmod, mod) {
p := unsafe.Add(oldmod, nextoff) // &oldmod.next
atomic.StorePointer((*unsafe.Pointer)(p), mod)
break
}
}
}

func loadModule(p **_ModuleData) *_ModuleData {
return (*_ModuleData)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p))))
}

func storeModule(p **_ModuleData, value *_ModuleData) {
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(value))
}

func casModule(p **_ModuleData, oldValue *_ModuleData, newValue *_ModuleData) bool {
return atomic.CompareAndSwapPointer(
(*unsafe.Pointer)(unsafe.Pointer(p)),
unsafe.Pointer(oldValue),
unsafe.Pointer(newValue),
)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//go:build go1.18 && !go1.23
// +build go1.18,!go1.23
//go:build go1.18 && !go1.24
// +build go1.18,!go1.24

/*
* Copyright 2022 CloudWeGo Authors
Expand Down
8 changes: 4 additions & 4 deletions internal/loader/funcdata_invalid.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//go:build !go1.16 || go1.23
// +build !go1.16 go1.23
//go:build !go1.16 || go1.24
// +build !go1.16 go1.24

/*
* Copyright 2022 CloudWeGo Authors
Expand All @@ -25,9 +25,9 @@ import (

// triggers a compilation error
const (
_ = panic("Unsupported Go version. Supported versions are 1.17 ~ 1.22")
_ = panic("Unsupported Go version.")
)

func registerFunction(_ string, _ uintptr, _ uintptr, _ rt.Frame) {
panic("Unsupported Go version. Supported versions are 1.17 ~ 1.22")
panic("Unsupported Go version.")
}
53 changes: 28 additions & 25 deletions internal/loader/funcdata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,35 @@
package loader

import (
"testing"
"sync"
"reflect"
"sync"
"testing"
"unsafe"
)

func Test_registerModuleLockFree(t *testing.T) {
n, parallel := 1000, 8
head := _ModuleData{}
tail := &head
wg := sync.WaitGroup{}
wg.Add(parallel)
filler := func(n int) {
defer wg.Done()
for i := 0; i < n; i++ {
m := &_ModuleData{}
registerModuleLockFree(&tail, m)
}
}
for i := 0; i < parallel; i++ {
go filler(n)
}
wg.Wait()
i := 0
for p := head.next; p != nil; p = p.next {
i += 1
}
if i != parallel * n {
t.Errorf("got %v, expected %v", i, parallel * n)
}
fnext, _ := reflect.TypeOf(_ModuleData{}).FieldByName("next")
n, parallel := 1000, 8
head := _ModuleData{}
tail := &head
wg := sync.WaitGroup{}
wg.Add(parallel)
filler := func(n int) {
defer wg.Done()
for i := 0; i < n; i++ {
m := &_ModuleData{}
registerModuleLockFree((*unsafe.Pointer)(unsafe.Pointer(&tail)), unsafe.Pointer(m), fnext.Offset)
}
}
for i := 0; i < parallel; i++ {
go filler(n)
}
wg.Wait()
i := 0
for p := head.next; p != nil; p = p.next {
i += 1
}
if i != parallel*n {
t.Errorf("got %v, expected %v", i, parallel*n)
}
}
107 changes: 107 additions & 0 deletions internal/loader/moduledata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package loader

import (
"fmt"
"reflect"
"runtime"
"unsafe"
)

type mdFieldInfo struct {
off uintptr
sz uintptr
}

var (
moduleDataType reflect.Type // runtime.moduledata

rtModuleDataFields map[string]mdFieldInfo
fgModuleDataFields map[string]mdFieldInfo
)

func moduledataPanic(reason string) {
panic(" moduledata compatibility issue found: " + reason)
}

func searchStructForModuleData(t reflect.Type, depth int) bool {
if depth == 0 {
return false
}
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
if t.Kind() != reflect.Struct {
return false
}
if t.Name() == "moduledata" && t.PkgPath() == "runtime" {
moduleDataType = t
return true
}
for i := 0; i < t.NumField(); i++ {
if searchStructForModuleData(t.Field(i).Type, depth-1) {
return true
}
}
return false
}

func init() {
// extract reflect.Type of runtime.moduledata from runtime.Frame
t := reflect.TypeOf(runtime.Frame{})
if !searchStructForModuleData(t, 3) {
moduledataPanic("not found runtime.moduledata in runtime.Frame{}")
}

t = moduleDataType
rtModuleDataFields = map[string]mdFieldInfo{}
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
rtModuleDataFields[f.Name] = mdFieldInfo{off: f.Offset, sz: f.Type.Size()}
}

t = reflect.TypeOf(_ModuleData{})
fgModuleDataFields = map[string]mdFieldInfo{}
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
fgModuleDataFields[f.Name] = mdFieldInfo{off: f.Offset, sz: f.Type.Size()}
}

for name, fi := range fgModuleDataFields {
fi0, ok := rtModuleDataFields[name]
if !ok {
moduledataPanic(fmt.Sprintf("runtime.moduledata field %q gone?", name))
}
if fi0.sz != fi.sz {
moduledataPanic(fmt.Sprintf("runtime.moduledata field %q type size mismatch.", name))
}
}
}

func getModuleDataType() reflect.Type {

Check failure on line 80 in internal/loader/moduledata.go

View workflow job for this annotation

GitHub Actions / lint

func `getModuleDataType` is unused (unused)
// extract reflect.Type of runtime.moduledata from runtime.Frame
t := reflect.TypeOf(runtime.Frame{})
f, ok := t.FieldByName("funcInfo")
if !ok {
moduledataPanic("missing runtime.Frame{}.funcInfo")
}
t = f.Type
f, ok = t.FieldByName("datap")
if !ok {
moduledataPanic("missing runtime.Frame{}.funcInfo.datap")
}
t = f.Type
return t.Elem() // *runtime.moduledata -> runtime.moduledata
}

func asRuntimeModuleData(m *_ModuleData) unsafe.Pointer {
b := make([]byte, moduleDataType.Size())
dst := unsafe.Pointer(&b[0])
src := unsafe.Pointer(m)
for name, fsrc := range fgModuleDataFields {
fdst, _ := rtModuleDataFields[name]

Check failure on line 101 in internal/loader/moduledata.go

View workflow job for this annotation

GitHub Actions / lint

S1005: unnecessary assignment to the blank identifier (gosimple)
for i := uintptr(0); i < fsrc.sz; i++ { // copy by byte
*(*byte)(unsafe.Add(dst, fdst.off+i)) = *(*byte)(unsafe.Add(src, fsrc.off+i))
}
}
return dst
}
88 changes: 0 additions & 88 deletions internal/loader/moduledata_go121_122.go

This file was deleted.

Loading

0 comments on commit 61c8a7b

Please sign in to comment.