1// Copyright 2015 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
5//go:build !plan9 && !windows
6// +build !plan9,!windows
7
8package main
9
10/*
11#include <pthread.h>
12
13void go_callback();
14
15static void *thr(void *arg) {
16    go_callback();
17    return 0;
18}
19
20static void foo() {
21    pthread_t th;
22    pthread_attr_t attr;
23    pthread_attr_init(&attr);
24    pthread_attr_setstacksize(&attr, 256 << 10);
25    pthread_create(&th, &attr, thr, 0);
26    pthread_join(th, 0);
27}
28*/
29import "C"
30
31import (
32	"fmt"
33	"os"
34	"runtime"
35	"sync/atomic"
36	_ "unsafe" // for go:linkname
37)
38
39func init() {
40	register("CgoCallbackGC", CgoCallbackGC)
41}
42
43//export go_callback
44func go_callback() {
45	if e := extraMInUse.Load(); e == 0 {
46		fmt.Printf("in callback extraMInUse got %d want >0\n", e)
47		os.Exit(1)
48	}
49
50	runtime.GC()
51	grow()
52	runtime.GC()
53}
54
55var cnt int
56
57func grow() {
58	x := 10000
59	sum := 0
60	if grow1(&x, &sum) == 0 {
61		panic("bad")
62	}
63}
64
65func grow1(x, sum *int) int {
66	if *x == 0 {
67		return *sum + 1
68	}
69	*x--
70	sum1 := *sum + *x
71	return grow1(x, &sum1)
72}
73
74func CgoCallbackGC() {
75	P := 100
76	if os.Getenv("RUNTIME_TEST_SHORT") != "" {
77		P = 10
78	}
79
80	if e := extraMInUse.Load(); e != 0 {
81		fmt.Printf("before testing extraMInUse got %d want 0\n", e)
82		os.Exit(1)
83	}
84
85	done := make(chan bool)
86	// allocate a bunch of stack frames and spray them with pointers
87	for i := 0; i < P; i++ {
88		go func() {
89			grow()
90			done <- true
91		}()
92	}
93	for i := 0; i < P; i++ {
94		<-done
95	}
96	// now give these stack frames to cgo callbacks
97	for i := 0; i < P; i++ {
98		go func() {
99			C.foo()
100			done <- true
101		}()
102	}
103	for i := 0; i < P; i++ {
104		<-done
105	}
106
107	if e := extraMInUse.Load(); e != 0 {
108		fmt.Printf("after testing extraMInUse got %d want 0\n", e)
109		os.Exit(1)
110	}
111
112	fmt.Printf("OK\n")
113}
114
115//go:linkname extraMInUse runtime.extraMInUse
116var extraMInUse atomic.Uint32
117