Skip to content
This repository was archived by the owner on May 16, 2024. It is now read-only.

Commit e76a322

Browse files
authored
Implement SetFinalizer for latest tinygo dev (#5)
1 parent 0823f16 commit e76a322

File tree

4 files changed

+117
-6
lines changed

4 files changed

+117
-6
lines changed

.github/workflows/ci.yaml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,9 @@ jobs:
4040
- run: go run mage.go check
4141
build-tinygodev:
4242
runs-on: ubuntu-22.04
43-
container: ghcr.io/tinygo-org/tinygo/tinygo-dev:sha-e0a5fc255522933f651a89c071ebd531ad634e7b
43+
container: ghcr.io/tinygo-org/tinygo/tinygo-dev:sha-f6df2761187f1975e35eb43461d735d6e325df85
4444
steps:
4545
- uses: actions/checkout@v3
46-
- uses: actions/setup-go@v3
47-
with:
48-
go-version: '^1.20.0'
49-
cache: true
5046

5147
- run: |
5248
go install github.com/wasilibs/tools/cmd/wasmtime@c93d2e477ab3c1eb7f5303c66a35c84a21d06dbd

finalizer.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//go:build nottinygc_finalizer
2+
3+
package nottinygc
4+
5+
/*
6+
void GC_register_finalizer(void* obj, void* fn, void* cd, void** ofn, void** ocn);
7+
void onFinalizer(void* obj, void* fn);
8+
void* malloc(unsigned int long);
9+
void free(void* ptr);
10+
*/
11+
import "C"
12+
import "unsafe"
13+
14+
var finalizers = map[uintptr]interface{}{}
15+
16+
//go:linkname SetFinalizer runtime.SetFinalizer
17+
func SetFinalizer(obj interface{}, finalizer interface{}) {
18+
finKey := uintptr((*_interface)(unsafe.Pointer(&finalizer)).value)
19+
finalizers[finKey] = finalizer
20+
21+
in := (*_interface)(unsafe.Pointer(&obj))
22+
23+
rf := (*registeredFinalizer)(C.malloc(C.ulong(unsafe.Sizeof(registeredFinalizer{}))))
24+
rf.typecode = in.typecode
25+
rf.finKey = finKey
26+
27+
C.GC_register_finalizer(in.value, C.onFinalizer, unsafe.Pointer(rf), nil, nil)
28+
}
29+
30+
//export onFinalizer
31+
func onFinalizer(obj unsafe.Pointer, data unsafe.Pointer) {
32+
defer C.free(data)
33+
34+
rf := (*registeredFinalizer)(data)
35+
finalizer := finalizers[rf.finKey]
36+
delete(finalizers, rf.finKey)
37+
38+
var in interface{}
39+
inFields := (*_interface)(unsafe.Pointer(&in))
40+
inFields.typecode = rf.typecode
41+
inFields.value = obj
42+
43+
switch f := finalizer.(type) {
44+
case func(interface{}):
45+
f(in)
46+
default:
47+
panic("currently only finalizers of the form func(interface{}) are supported")
48+
}
49+
}
50+
51+
type _interface struct {
52+
typecode uintptr
53+
value unsafe.Pointer
54+
}
55+
56+
type registeredFinalizer struct {
57+
typecode uintptr
58+
finKey uintptr
59+
}

finalizer_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//go:build nottinygc_finalizer
2+
3+
package nottinygc
4+
5+
import (
6+
"runtime"
7+
"testing"
8+
)
9+
10+
func TestFinalizer(t *testing.T) {
11+
finalized := 0
12+
allocFinalized(10, func() {
13+
finalized++
14+
})
15+
16+
runtime.GC()
17+
18+
if finalized == 0 {
19+
t.Errorf("finalizer not called")
20+
}
21+
}
22+
23+
//go:noinline
24+
func allocFinalized(num int, cb func()) {
25+
f := &finalized{
26+
a: 100,
27+
b: "foo",
28+
}
29+
30+
runtime.SetFinalizer(f, func(f interface{}) {
31+
cb()
32+
})
33+
34+
// Recurse to create some more stack frames or else the shadow stack
35+
// may still not have been reset, causing GC to find the inaccessible
36+
// pointers.
37+
if num > 1 {
38+
allocFinalized(num-1, cb)
39+
}
40+
}
41+
42+
type finalized struct {
43+
a int
44+
b string
45+
}

magefiles/magefile.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,25 @@ import (
44
"fmt"
55
"os"
66
"path/filepath"
7+
"strings"
78

89
"github.com/magefile/mage/mg"
910
"github.com/magefile/mage/sh"
1011
)
1112

1213
// Test runs unit tests
1314
func Test() error {
14-
return sh.RunV("tinygo", "test", "-gc=custom", "-tags=custommalloc", "-target=wasi", "-v", "-scheduler=none", "./...")
15+
v, err := sh.Output("tinygo", "version")
16+
if err != nil {
17+
return fmt.Errorf("invoking tinygo: %w", err)
18+
}
19+
20+
tags := []string{"custommalloc"}
21+
if strings.HasSuffix(v, "tinygo version 0.28.") {
22+
tags = append(tags, "nottinygc_finalizer")
23+
}
24+
25+
return sh.RunV("tinygo", "test", "-gc=custom", fmt.Sprintf("-tags='%s'", strings.Join(tags, " ")), "-target=wasi", "-v", "-scheduler=none", "./...")
1526
}
1627

1728
func Format() error {

0 commit comments

Comments
 (0)