1// Copyright 2023 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package main
6
7// #cgo noescape annotations for a C function means its arguments won't escape to heap.
8
9// We assume that there won't be 100 new allocated heap objects in other places,
10// i.e. runtime.ReadMemStats or other runtime background works.
11// So, the tests are:
12// 1. at least 100 new allocated heap objects after invoking withoutNoEscape 100 times.
13// 2. less than 100 new allocated heap objects after invoking withoutNoEscape 100 times.
14
15/*
16// TODO(#56378): #cgo noescape runCWithNoEscape
17
18void runCWithNoEscape(void *p) {
19}
20void runCWithoutNoEscape(void *p) {
21}
22*/
23import "C"
24
25import (
26	"fmt"
27	"runtime"
28	"runtime/debug"
29	"unsafe"
30)
31
32const num = 100
33
34func init() {
35	register("CgoNoEscape", CgoNoEscape)
36}
37
38//go:noinline
39func withNoEscape() {
40	var str string
41	C.runCWithNoEscape(unsafe.Pointer(&str))
42}
43
44//go:noinline
45func withoutNoEscape() {
46	var str string
47	C.runCWithoutNoEscape(unsafe.Pointer(&str))
48}
49
50func CgoNoEscape() {
51	// make GC stop to see the heap objects allocated
52	debug.SetGCPercent(-1)
53
54	var stats runtime.MemStats
55	runtime.ReadMemStats(&stats)
56	preHeapObjects := stats.HeapObjects
57
58	for i := 0; i < num; i++ {
59		withNoEscape()
60	}
61
62	runtime.ReadMemStats(&stats)
63	nowHeapObjects := stats.HeapObjects
64
65	if nowHeapObjects-preHeapObjects >= num {
66		fmt.Printf("too many heap objects allocated, pre: %v, now: %v\n", preHeapObjects, nowHeapObjects)
67	}
68
69	runtime.ReadMemStats(&stats)
70	preHeapObjects = stats.HeapObjects
71
72	for i := 0; i < num; i++ {
73		withoutNoEscape()
74	}
75
76	runtime.ReadMemStats(&stats)
77	nowHeapObjects = stats.HeapObjects
78
79	if nowHeapObjects-preHeapObjects < num {
80		fmt.Printf("too few heap objects allocated, pre: %v, now: %v\n", preHeapObjects, nowHeapObjects)
81	}
82
83	fmt.Println("OK")
84}
85