Source file
src/runtime/mcleanup_test.go
1
2
3
4
5 package runtime_test
6
7 import (
8 "internal/runtime/atomic"
9 "runtime"
10 "sync"
11 "testing"
12 "time"
13 "unsafe"
14 )
15
16 func TestCleanup(t *testing.T) {
17 ch := make(chan bool, 1)
18 done := make(chan bool, 1)
19 want := 97531
20 go func() {
21
22
23
24 type T struct {
25 v int
26 p unsafe.Pointer
27 }
28 v := &new(T).v
29 *v = 97531
30 cleanup := func(x int) {
31 if x != want {
32 t.Errorf("cleanup %d, want %d", x, want)
33 }
34 ch <- true
35 }
36 runtime.AddCleanup(v, cleanup, 97531)
37 v = nil
38 done <- true
39 }()
40 <-done
41 runtime.GC()
42 <-ch
43 }
44
45 func TestCleanupMultiple(t *testing.T) {
46 ch := make(chan bool, 3)
47 done := make(chan bool, 1)
48 want := 97531
49 go func() {
50
51
52
53 type T struct {
54 v int
55 p unsafe.Pointer
56 }
57 v := &new(T).v
58 *v = 97531
59 cleanup := func(x int) {
60 if x != want {
61 t.Errorf("cleanup %d, want %d", x, want)
62 }
63 ch <- true
64 }
65 runtime.AddCleanup(v, cleanup, 97531)
66 runtime.AddCleanup(v, cleanup, 97531)
67 runtime.AddCleanup(v, cleanup, 97531)
68 v = nil
69 done <- true
70 }()
71 <-done
72 runtime.GC()
73 <-ch
74 <-ch
75 <-ch
76 }
77
78 func TestCleanupZeroSizedStruct(t *testing.T) {
79 type Z struct{}
80 z := new(Z)
81 runtime.AddCleanup(z, func(s string) {}, "foo")
82 }
83
84 func TestCleanupAfterFinalizer(t *testing.T) {
85 ch := make(chan int, 2)
86 done := make(chan bool, 1)
87 want := 97531
88 go func() {
89
90
91
92 type T struct {
93 v int
94 p unsafe.Pointer
95 }
96 v := &new(T).v
97 *v = 97531
98 finalizer := func(x *int) {
99 ch <- 1
100 }
101 cleanup := func(x int) {
102 if x != want {
103 t.Errorf("cleanup %d, want %d", x, want)
104 }
105 ch <- 2
106 }
107 runtime.AddCleanup(v, cleanup, 97531)
108 runtime.SetFinalizer(v, finalizer)
109 v = nil
110 done <- true
111 }()
112 <-done
113 runtime.GC()
114 var result int
115 result = <-ch
116 if result != 1 {
117 t.Errorf("result %d, want 1", result)
118 }
119 runtime.GC()
120 result = <-ch
121 if result != 2 {
122 t.Errorf("result %d, want 2", result)
123 }
124 }
125
126 func TestCleanupInteriorPointer(t *testing.T) {
127 ch := make(chan bool, 3)
128 done := make(chan bool, 1)
129 want := 97531
130 go func() {
131
132
133
134 type T struct {
135 p unsafe.Pointer
136 i int
137 a int
138 b int
139 c int
140 }
141 ts := new(T)
142 ts.a = 97531
143 ts.b = 97531
144 ts.c = 97531
145 cleanup := func(x int) {
146 if x != want {
147 t.Errorf("cleanup %d, want %d", x, want)
148 }
149 ch <- true
150 }
151 runtime.AddCleanup(&ts.a, cleanup, 97531)
152 runtime.AddCleanup(&ts.b, cleanup, 97531)
153 runtime.AddCleanup(&ts.c, cleanup, 97531)
154 ts = nil
155 done <- true
156 }()
157 <-done
158 runtime.GC()
159 <-ch
160 <-ch
161 <-ch
162 }
163
164 func TestCleanupStop(t *testing.T) {
165 done := make(chan bool, 1)
166 go func() {
167
168
169
170 type T struct {
171 v int
172 p unsafe.Pointer
173 }
174 v := &new(T).v
175 *v = 97531
176 cleanup := func(x int) {
177 t.Error("cleanup called, want no cleanup called")
178 }
179 c := runtime.AddCleanup(v, cleanup, 97531)
180 c.Stop()
181 v = nil
182 done <- true
183 }()
184 <-done
185 runtime.GC()
186 }
187
188 func TestCleanupStopMultiple(t *testing.T) {
189 done := make(chan bool, 1)
190 go func() {
191
192
193
194 type T struct {
195 v int
196 p unsafe.Pointer
197 }
198 v := &new(T).v
199 *v = 97531
200 cleanup := func(x int) {
201 t.Error("cleanup called, want no cleanup called")
202 }
203 c := runtime.AddCleanup(v, cleanup, 97531)
204 c.Stop()
205 c.Stop()
206 c.Stop()
207 v = nil
208 done <- true
209 }()
210 <-done
211 runtime.GC()
212 }
213
214 func TestCleanupStopinterleavedMultiple(t *testing.T) {
215 ch := make(chan bool, 3)
216 done := make(chan bool, 1)
217 go func() {
218
219
220
221 type T struct {
222 v int
223 p unsafe.Pointer
224 }
225 v := &new(T).v
226 *v = 97531
227 cleanup := func(x int) {
228 if x != 1 {
229 t.Error("cleanup called, want no cleanup called")
230 }
231 ch <- true
232 }
233 runtime.AddCleanup(v, cleanup, 1)
234 runtime.AddCleanup(v, cleanup, 2).Stop()
235 runtime.AddCleanup(v, cleanup, 1)
236 runtime.AddCleanup(v, cleanup, 2).Stop()
237 runtime.AddCleanup(v, cleanup, 1)
238 v = nil
239 done <- true
240 }()
241 <-done
242 runtime.GC()
243 <-ch
244 <-ch
245 <-ch
246 }
247
248 func TestCleanupStopAfterCleanupRuns(t *testing.T) {
249 ch := make(chan bool, 1)
250 done := make(chan bool, 1)
251 var stop func()
252 go func() {
253
254
255
256 type T struct {
257 v int
258 p unsafe.Pointer
259 }
260 v := &new(T).v
261 *v = 97531
262 cleanup := func(x int) {
263 ch <- true
264 }
265 cl := runtime.AddCleanup(v, cleanup, 97531)
266 v = nil
267 stop = cl.Stop
268 done <- true
269 }()
270 <-done
271 runtime.GC()
272 <-ch
273 stop()
274 }
275
276 func TestCleanupPointerEqualsArg(t *testing.T) {
277
278 defer func() {
279 want := "runtime.AddCleanup: ptr is equal to arg, cleanup will never run"
280 if r := recover(); r == nil {
281 t.Error("want panic, test did not panic")
282 } else if r == want {
283
284 } else {
285 t.Errorf("wrong panic: want=%q, got=%q", want, r)
286 }
287 }()
288
289
290
291
292 type T struct {
293 v int
294 p unsafe.Pointer
295 }
296 v := &new(T).v
297 *v = 97531
298 runtime.AddCleanup(v, func(x *int) {}, v)
299 v = nil
300 runtime.GC()
301 }
302
303
304 func TestCleanupLost(t *testing.T) {
305 type T struct {
306 v int
307 p unsafe.Pointer
308 }
309
310 cleanups := 10_000
311 if testing.Short() {
312 cleanups = 100
313 }
314 n := runtime.GOMAXPROCS(-1)
315 want := n * cleanups
316 var got atomic.Uint64
317 var wg sync.WaitGroup
318 for i := range n {
319 wg.Add(1)
320 go func(i int) {
321 defer wg.Done()
322
323 for range cleanups {
324 v := &new(T).v
325 *v = 97531
326 runtime.AddCleanup(v, func(_ int) {
327 got.Add(1)
328 }, 97531)
329 }
330 }(i)
331 }
332 wg.Wait()
333 runtime.GC()
334 runtime.BlockUntilEmptyCleanupQueue(int64(10 * time.Second))
335 if got := int(got.Load()); got != want {
336 t.Errorf("expected %d cleanups to be executed, got %d", got, want)
337 }
338 }
339
View as plain text