diff options
| author | doujiang24 <doujiang24@gmail.com> | 2023-08-25 17:06:31 +0000 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2023-08-25 17:39:23 +0000 |
| commit | 24b9ef1a7366fe751880ab2098cff630144b8ac8 (patch) | |
| tree | 0f1de5cac722c84485bdfd9c1cc80f6422d7a037 /src/runtime/testdata | |
| parent | 1a01cb22f9ab07d55ee61c95a34e1e18e49596c0 (diff) | |
| download | go-24b9ef1a7366fe751880ab2098cff630144b8ac8.tar.xz | |
cmd/cgo: add #cgo noescape/nocallback annotations
When passing pointers of Go objects from Go to C, the cgo command generate _Cgo_use(pN) for the unsafe.Pointer type arguments, so that the Go compiler will escape these object to heap.
Since the C function may callback to Go, then the Go stack might grow/shrink, that means the pointers that the C function have will be invalid.
After adding the #cgo noescape annotation for a C function, the cgo command won't generate _Cgo_use(pN), and the Go compiler won't force the object escape to heap.
After adding the #cgo nocallback annotation for a C function, which means the C function won't callback to Go, if it do callback to Go, the Go process will crash.
Fixes #56378
Change-Id: Ifdca070584e0d349c7b12276270e50089e481f7a
GitHub-Last-Rev: f1a17b08b0590eca2670e404bbfedad5461df72f
GitHub-Pull-Request: golang/go#60399
Reviewed-on: https://go-review.googlesource.com/c/go/+/497837
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
Run-TryBot: Bryan Mills <bcmills@google.com>
Auto-Submit: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Diffstat (limited to 'src/runtime/testdata')
| -rw-r--r-- | src/runtime/testdata/testprogcgo/cgonocallback.c | 9 | ||||
| -rw-r--r-- | src/runtime/testdata/testprogcgo/cgonocallback.go | 32 | ||||
| -rw-r--r-- | src/runtime/testdata/testprogcgo/cgonoescape.go | 84 |
3 files changed, 125 insertions, 0 deletions
diff --git a/src/runtime/testdata/testprogcgo/cgonocallback.c b/src/runtime/testdata/testprogcgo/cgonocallback.c new file mode 100644 index 0000000000..465a484361 --- /dev/null +++ b/src/runtime/testdata/testprogcgo/cgonocallback.c @@ -0,0 +1,9 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "_cgo_export.h" + +void runCShouldNotCallback() { + CallbackToGo(); +} diff --git a/src/runtime/testdata/testprogcgo/cgonocallback.go b/src/runtime/testdata/testprogcgo/cgonocallback.go new file mode 100644 index 0000000000..8cbbfd1957 --- /dev/null +++ b/src/runtime/testdata/testprogcgo/cgonocallback.go @@ -0,0 +1,32 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// #cgo nocallback annotations for a C function means it should not callback to Go. +// But it do callback to go in this test, Go should crash here. + +/* +#cgo nocallback runCShouldNotCallback + +extern void runCShouldNotCallback(); +*/ +import "C" + +import ( + "fmt" +) + +func init() { + register("CgoNoCallback", CgoNoCallback) +} + +//export CallbackToGo +func CallbackToGo() { +} + +func CgoNoCallback() { + C.runCShouldNotCallback() + fmt.Println("OK") +} diff --git a/src/runtime/testdata/testprogcgo/cgonoescape.go b/src/runtime/testdata/testprogcgo/cgonoescape.go new file mode 100644 index 0000000000..056be44889 --- /dev/null +++ b/src/runtime/testdata/testprogcgo/cgonoescape.go @@ -0,0 +1,84 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// #cgo noescape annotations for a C function means its arguments won't escape to heap. + +// We assume that there won't be 100 new allocated heap objects in other places, +// i.e. runtime.ReadMemStats or other runtime background works. +// So, the tests are: +// 1. at least 100 new allocated heap objects after invoking withoutNoEscape 100 times. +// 2. less than 100 new allocated heap objects after invoking withoutNoEscape 100 times. + +/* +#cgo noescape runCWithNoEscape + +void runCWithNoEscape(void *p) { +} +void runCWithoutNoEscape(void *p) { +} +*/ +import "C" + +import ( + "fmt" + "runtime" + "runtime/debug" + "unsafe" +) + +const num = 100 + +func init() { + register("CgoNoEscape", CgoNoEscape) +} + +//go:noinline +func withNoEscape() { + var str string + C.runCWithNoEscape(unsafe.Pointer(&str)) +} + +//go:noinline +func withoutNoEscape() { + var str string + C.runCWithoutNoEscape(unsafe.Pointer(&str)) +} + +func CgoNoEscape() { + // make GC stop to see the heap objects allocated + debug.SetGCPercent(-1) + + var stats runtime.MemStats + runtime.ReadMemStats(&stats) + preHeapObjects := stats.HeapObjects + + for i := 0; i < num; i++ { + withNoEscape() + } + + runtime.ReadMemStats(&stats) + nowHeapObjects := stats.HeapObjects + + if nowHeapObjects-preHeapObjects >= num { + fmt.Printf("too many heap objects allocated, pre: %v, now: %v\n", preHeapObjects, nowHeapObjects) + } + + runtime.ReadMemStats(&stats) + preHeapObjects = stats.HeapObjects + + for i := 0; i < num; i++ { + withoutNoEscape() + } + + runtime.ReadMemStats(&stats) + nowHeapObjects = stats.HeapObjects + + if nowHeapObjects-preHeapObjects < num { + fmt.Printf("too few heap objects allocated, pre: %v, now: %v\n", preHeapObjects, nowHeapObjects) + } + + fmt.Println("OK") +} |
