Source file
src/runtime/mgcscavenge_test.go
1
2
3
4
5 package runtime_test
6
7 import (
8 "fmt"
9 "internal/goos"
10 "math/rand"
11 . "runtime"
12 "runtime/internal/atomic"
13 "testing"
14 "time"
15 )
16
17
18
19 func makePallocData(alloc, scavenged []BitRange) *PallocData {
20 b := new(PallocData)
21 for _, v := range alloc {
22 if v.N == 0 {
23
24
25 continue
26 }
27 b.AllocRange(v.I, v.N)
28 }
29 for _, v := range scavenged {
30 if v.N == 0 {
31
32 continue
33 }
34 b.ScavengedSetRange(v.I, v.N)
35 }
36 return b
37 }
38
39 func TestFillAligned(t *testing.T) {
40 fillAlignedSlow := func(x uint64, m uint) uint64 {
41 if m == 1 {
42 return x
43 }
44 out := uint64(0)
45 for i := uint(0); i < 64; i += m {
46 for j := uint(0); j < m; j++ {
47 if x&(uint64(1)<<(i+j)) != 0 {
48 out |= ((uint64(1) << m) - 1) << i
49 break
50 }
51 }
52 }
53 return out
54 }
55 check := func(x uint64, m uint) {
56 want := fillAlignedSlow(x, m)
57 if got := FillAligned(x, m); got != want {
58 t.Logf("got: %064b", got)
59 t.Logf("want: %064b", want)
60 t.Errorf("bad fillAligned(%016x, %d)", x, m)
61 }
62 }
63 for m := uint(1); m <= 64; m *= 2 {
64 tests := []uint64{
65 0x0000000000000000,
66 0x00000000ffffffff,
67 0xffffffff00000000,
68 0x8000000000000001,
69 0xf00000000000000f,
70 0xf00000010050000f,
71 0xffffffffffffffff,
72 0x0000000000000001,
73 0x0000000000000002,
74 0x0000000000000008,
75 uint64(1) << (m - 1),
76 uint64(1) << m,
77
78 0xb02b9effcf137016,
79 0x3975a076a9fbff18,
80 0x0f8c88ec3b81506e,
81 0x60f14d80ef2fa0e6,
82 }
83 for _, test := range tests {
84 check(test, m)
85 }
86 for i := 0; i < 1000; i++ {
87
88 check(rand.Uint64(), m)
89
90 if m > 1 {
91
92
93
94 val := uint64(0)
95 for n := uint(0); n < 64; n += m {
96
97
98
99 if rand.Uint64()%2 == 0 {
100 val |= (rand.Uint64() & ((1 << m) - 1)) << n
101 }
102 }
103 check(val, m)
104 }
105 }
106 }
107 }
108
109 func TestPallocDataFindScavengeCandidate(t *testing.T) {
110 type test struct {
111 alloc, scavenged []BitRange
112 min, max uintptr
113 want BitRange
114 }
115 tests := map[string]test{
116 "MixedMin1": {
117 alloc: []BitRange{{0, 40}, {42, PallocChunkPages - 42}},
118 scavenged: []BitRange{{0, 41}, {42, PallocChunkPages - 42}},
119 min: 1,
120 max: PallocChunkPages,
121 want: BitRange{41, 1},
122 },
123 "MultiMin1": {
124 alloc: []BitRange{{0, 63}, {65, 20}, {87, PallocChunkPages - 87}},
125 scavenged: []BitRange{{86, 1}},
126 min: 1,
127 max: PallocChunkPages,
128 want: BitRange{85, 1},
129 },
130 }
131
132 for m := uintptr(1); m <= 64; m *= 2 {
133 suffix := fmt.Sprintf("Min%d", m)
134 tests["AllFree"+suffix] = test{
135 min: m,
136 max: PallocChunkPages,
137 want: BitRange{0, PallocChunkPages},
138 }
139 tests["AllScavenged"+suffix] = test{
140 scavenged: []BitRange{{0, PallocChunkPages}},
141 min: m,
142 max: PallocChunkPages,
143 want: BitRange{0, 0},
144 }
145 tests["NoneFree"+suffix] = test{
146 alloc: []BitRange{{0, PallocChunkPages}},
147 scavenged: []BitRange{{PallocChunkPages / 2, PallocChunkPages / 2}},
148 min: m,
149 max: PallocChunkPages,
150 want: BitRange{0, 0},
151 }
152 tests["StartFree"+suffix] = test{
153 alloc: []BitRange{{uint(m), PallocChunkPages - uint(m)}},
154 min: m,
155 max: PallocChunkPages,
156 want: BitRange{0, uint(m)},
157 }
158 tests["EndFree"+suffix] = test{
159 alloc: []BitRange{{0, PallocChunkPages - uint(m)}},
160 min: m,
161 max: PallocChunkPages,
162 want: BitRange{PallocChunkPages - uint(m), uint(m)},
163 }
164 tests["Straddle64"+suffix] = test{
165 alloc: []BitRange{{0, 64 - uint(m)}, {64 + uint(m), PallocChunkPages - (64 + uint(m))}},
166 min: m,
167 max: 2 * m,
168 want: BitRange{64 - uint(m), 2 * uint(m)},
169 }
170 tests["BottomEdge64WithFull"+suffix] = test{
171 alloc: []BitRange{{64, 64}, {128 + 3*uint(m), PallocChunkPages - (128 + 3*uint(m))}},
172 scavenged: []BitRange{{1, 10}},
173 min: m,
174 max: 3 * m,
175 want: BitRange{128, 3 * uint(m)},
176 }
177 tests["BottomEdge64WithPocket"+suffix] = test{
178 alloc: []BitRange{{64, 62}, {127, 1}, {128 + 3*uint(m), PallocChunkPages - (128 + 3*uint(m))}},
179 scavenged: []BitRange{{1, 10}},
180 min: m,
181 max: 3 * m,
182 want: BitRange{128, 3 * uint(m)},
183 }
184 tests["Max0"+suffix] = test{
185 scavenged: []BitRange{{0, PallocChunkPages - uint(m)}},
186 min: m,
187 max: 0,
188 want: BitRange{PallocChunkPages - uint(m), uint(m)},
189 }
190 if m <= 8 {
191 tests["OneFree"] = test{
192 alloc: []BitRange{{0, 40}, {40 + uint(m), PallocChunkPages - (40 + uint(m))}},
193 min: m,
194 max: PallocChunkPages,
195 want: BitRange{40, uint(m)},
196 }
197 tests["OneScavenged"] = test{
198 alloc: []BitRange{{0, 40}, {40 + uint(m), PallocChunkPages - (40 + uint(m))}},
199 scavenged: []BitRange{{40, 1}},
200 min: m,
201 max: PallocChunkPages,
202 want: BitRange{0, 0},
203 }
204 }
205 if m > 1 {
206 tests["MaxUnaligned"+suffix] = test{
207 scavenged: []BitRange{{0, PallocChunkPages - uint(m*2-1)}},
208 min: m,
209 max: m - 2,
210 want: BitRange{PallocChunkPages - uint(m), uint(m)},
211 }
212 tests["SkipSmall"+suffix] = test{
213 alloc: []BitRange{{0, 64 - uint(m)}, {64, 5}, {70, 11}, {82, PallocChunkPages - 82}},
214 min: m,
215 max: m,
216 want: BitRange{64 - uint(m), uint(m)},
217 }
218 tests["SkipMisaligned"+suffix] = test{
219 alloc: []BitRange{{0, 64 - uint(m)}, {64, 63}, {127 + uint(m), PallocChunkPages - (127 + uint(m))}},
220 min: m,
221 max: m,
222 want: BitRange{64 - uint(m), uint(m)},
223 }
224 tests["MaxLessThan"+suffix] = test{
225 scavenged: []BitRange{{0, PallocChunkPages - uint(m)}},
226 min: m,
227 max: 1,
228 want: BitRange{PallocChunkPages - uint(m), uint(m)},
229 }
230 }
231 }
232 if PhysHugePageSize > uintptr(PageSize) {
233
234 bits := uint(PhysHugePageSize / uintptr(PageSize))
235 if bits < PallocChunkPages {
236 tests["PreserveHugePageBottom"] = test{
237 alloc: []BitRange{{bits + 2, PallocChunkPages - (bits + 2)}},
238 min: 1,
239 max: 3,
240 want: BitRange{0, bits + 2},
241 }
242 if 3*bits < PallocChunkPages {
243
244 tests["PreserveHugePageMiddle"] = test{
245 alloc: []BitRange{{0, bits - 10}, {2*bits + 10, PallocChunkPages - (2*bits + 10)}},
246 min: 1,
247 max: 12,
248 want: BitRange{bits, bits + 10},
249 }
250 }
251 tests["PreserveHugePageTop"] = test{
252 alloc: []BitRange{{0, PallocChunkPages - bits}},
253 min: 1,
254 max: 1,
255 want: BitRange{PallocChunkPages - bits, bits},
256 }
257 } else if bits == PallocChunkPages {
258 tests["PreserveHugePageAll"] = test{
259 min: 1,
260 max: 1,
261 want: BitRange{0, PallocChunkPages},
262 }
263 } else {
264
265
266
267 tests["PreserveHugePageNone"] = test{
268 min: 1,
269 max: 1,
270 want: BitRange{PallocChunkPages - 1, 1},
271 }
272 }
273 }
274 for name, v := range tests {
275 v := v
276 t.Run(name, func(t *testing.T) {
277 b := makePallocData(v.alloc, v.scavenged)
278 start, size := b.FindScavengeCandidate(PallocChunkPages-1, v.min, v.max)
279 got := BitRange{start, size}
280 if !(got.N == 0 && v.want.N == 0) && got != v.want {
281 t.Fatalf("candidate mismatch: got %v, want %v", got, v.want)
282 }
283 })
284 }
285 }
286
287
288 func TestPageAllocScavenge(t *testing.T) {
289 if GOOS == "openbsd" && testing.Short() {
290 t.Skip("skipping because virtual memory is limited; see #36210")
291 }
292 type test struct {
293 request, expect uintptr
294 }
295 minPages := PhysPageSize / PageSize
296 if minPages < 1 {
297 minPages = 1
298 }
299 type setup struct {
300 beforeAlloc map[ChunkIdx][]BitRange
301 beforeScav map[ChunkIdx][]BitRange
302 expect []test
303 afterScav map[ChunkIdx][]BitRange
304 }
305 tests := map[string]setup{
306 "AllFreeUnscavExhaust": {
307 beforeAlloc: map[ChunkIdx][]BitRange{
308 BaseChunkIdx: {},
309 BaseChunkIdx + 1: {},
310 BaseChunkIdx + 2: {},
311 },
312 beforeScav: map[ChunkIdx][]BitRange{
313 BaseChunkIdx: {},
314 BaseChunkIdx + 1: {},
315 BaseChunkIdx + 2: {},
316 },
317 expect: []test{
318 {^uintptr(0), 3 * PallocChunkPages * PageSize},
319 },
320 afterScav: map[ChunkIdx][]BitRange{
321 BaseChunkIdx: {{0, PallocChunkPages}},
322 BaseChunkIdx + 1: {{0, PallocChunkPages}},
323 BaseChunkIdx + 2: {{0, PallocChunkPages}},
324 },
325 },
326 "NoneFreeUnscavExhaust": {
327 beforeAlloc: map[ChunkIdx][]BitRange{
328 BaseChunkIdx: {{0, PallocChunkPages}},
329 BaseChunkIdx + 1: {},
330 BaseChunkIdx + 2: {{0, PallocChunkPages}},
331 },
332 beforeScav: map[ChunkIdx][]BitRange{
333 BaseChunkIdx: {},
334 BaseChunkIdx + 1: {{0, PallocChunkPages}},
335 BaseChunkIdx + 2: {},
336 },
337 expect: []test{
338 {^uintptr(0), 0},
339 },
340 afterScav: map[ChunkIdx][]BitRange{
341 BaseChunkIdx: {},
342 BaseChunkIdx + 1: {{0, PallocChunkPages}},
343 BaseChunkIdx + 2: {},
344 },
345 },
346 "ScavHighestPageFirst": {
347 beforeAlloc: map[ChunkIdx][]BitRange{
348 BaseChunkIdx: {},
349 },
350 beforeScav: map[ChunkIdx][]BitRange{
351 BaseChunkIdx: {{uint(minPages), PallocChunkPages - uint(2*minPages)}},
352 },
353 expect: []test{
354 {1, minPages * PageSize},
355 },
356 afterScav: map[ChunkIdx][]BitRange{
357 BaseChunkIdx: {{uint(minPages), PallocChunkPages - uint(minPages)}},
358 },
359 },
360 "ScavMultiple": {
361 beforeAlloc: map[ChunkIdx][]BitRange{
362 BaseChunkIdx: {},
363 },
364 beforeScav: map[ChunkIdx][]BitRange{
365 BaseChunkIdx: {{uint(minPages), PallocChunkPages - uint(2*minPages)}},
366 },
367 expect: []test{
368 {minPages * PageSize, minPages * PageSize},
369 {minPages * PageSize, minPages * PageSize},
370 },
371 afterScav: map[ChunkIdx][]BitRange{
372 BaseChunkIdx: {{0, PallocChunkPages}},
373 },
374 },
375 "ScavMultiple2": {
376 beforeAlloc: map[ChunkIdx][]BitRange{
377 BaseChunkIdx: {},
378 BaseChunkIdx + 1: {},
379 },
380 beforeScav: map[ChunkIdx][]BitRange{
381 BaseChunkIdx: {{uint(minPages), PallocChunkPages - uint(2*minPages)}},
382 BaseChunkIdx + 1: {{0, PallocChunkPages - uint(2*minPages)}},
383 },
384 expect: []test{
385 {2 * minPages * PageSize, 2 * minPages * PageSize},
386 {minPages * PageSize, minPages * PageSize},
387 {minPages * PageSize, minPages * PageSize},
388 },
389 afterScav: map[ChunkIdx][]BitRange{
390 BaseChunkIdx: {{0, PallocChunkPages}},
391 BaseChunkIdx + 1: {{0, PallocChunkPages}},
392 },
393 },
394 "ScavDiscontiguous": {
395 beforeAlloc: map[ChunkIdx][]BitRange{
396 BaseChunkIdx: {},
397 BaseChunkIdx + 0xe: {},
398 },
399 beforeScav: map[ChunkIdx][]BitRange{
400 BaseChunkIdx: {{uint(minPages), PallocChunkPages - uint(2*minPages)}},
401 BaseChunkIdx + 0xe: {{uint(2 * minPages), PallocChunkPages - uint(2*minPages)}},
402 },
403 expect: []test{
404 {2 * minPages * PageSize, 2 * minPages * PageSize},
405 {^uintptr(0), 2 * minPages * PageSize},
406 {^uintptr(0), 0},
407 },
408 afterScav: map[ChunkIdx][]BitRange{
409 BaseChunkIdx: {{0, PallocChunkPages}},
410 BaseChunkIdx + 0xe: {{0, PallocChunkPages}},
411 },
412 },
413 }
414
415
416 if PageAlloc64Bit != 0 && goos.IsIos == 0 {
417 tests["ScavAllVeryDiscontiguous"] = setup{
418 beforeAlloc: map[ChunkIdx][]BitRange{
419 BaseChunkIdx: {},
420 BaseChunkIdx + 0x1000: {},
421 },
422 beforeScav: map[ChunkIdx][]BitRange{
423 BaseChunkIdx: {},
424 BaseChunkIdx + 0x1000: {},
425 },
426 expect: []test{
427 {^uintptr(0), 2 * PallocChunkPages * PageSize},
428 {^uintptr(0), 0},
429 },
430 afterScav: map[ChunkIdx][]BitRange{
431 BaseChunkIdx: {{0, PallocChunkPages}},
432 BaseChunkIdx + 0x1000: {{0, PallocChunkPages}},
433 },
434 }
435 }
436 for name, v := range tests {
437 v := v
438 t.Run(name, func(t *testing.T) {
439 b := NewPageAlloc(v.beforeAlloc, v.beforeScav)
440 defer FreePageAlloc(b)
441
442 for iter, h := range v.expect {
443 if got := b.Scavenge(h.request); got != h.expect {
444 t.Fatalf("bad scavenge #%d: want %d, got %d", iter+1, h.expect, got)
445 }
446 }
447 want := NewPageAlloc(v.beforeAlloc, v.afterScav)
448 defer FreePageAlloc(want)
449
450 checkPageAlloc(t, want, b)
451 })
452 }
453 }
454
455 func TestScavenger(t *testing.T) {
456
457
458 workedTime := func(bytes uintptr) int64 {
459 return int64((bytes+4095)/4096) * int64(10*time.Microsecond)
460 }
461
462
463
464 totalWork := uint64(64<<20 - 3*PhysPageSize)
465 var totalSlept, totalWorked atomic.Int64
466 var availableWork atomic.Uint64
467 var stopAt atomic.Uint64
468
469
470 var s Scavenger
471 s.Sleep = func(ns int64) int64 {
472 totalSlept.Add(ns)
473 return ns
474 }
475 s.Scavenge = func(bytes uintptr) (uintptr, int64) {
476 avail := availableWork.Load()
477 if uint64(bytes) > avail {
478 bytes = uintptr(avail)
479 }
480 t := workedTime(bytes)
481 if bytes != 0 {
482 availableWork.Add(-int64(bytes))
483 totalWorked.Add(t)
484 }
485 return bytes, t
486 }
487 s.ShouldStop = func() bool {
488 if availableWork.Load() <= stopAt.Load() {
489 return true
490 }
491 return false
492 }
493 s.GoMaxProcs = func() int32 {
494 return 1
495 }
496
497
498 verifyScavengerState := func(t *testing.T, expWork uint64) {
499 t.Helper()
500
501
502 if workDone := uint64(s.Released()); workDone != expWork {
503 t.Errorf("want %d bytes of work done, got %d", expWork, workDone)
504 }
505
506 idealFraction := float64(ScavengePercent) / 100.0
507 cpuFraction := float64(totalWorked.Load()) / float64(totalWorked.Load()+totalSlept.Load())
508 if cpuFraction < idealFraction-0.005 || cpuFraction > idealFraction+0.005 {
509 t.Errorf("want %f CPU fraction, got %f", idealFraction, cpuFraction)
510 }
511 }
512
513
514 s.Start()
515
516
517 availableWork.Store(totalWork)
518 s.Wake()
519 if !s.BlockUntilParked(2e9 ) {
520 t.Fatal("timed out waiting for scavenger to run to completion")
521 }
522
523 verifyScavengerState(t, totalWork)
524
525
526
527 s.Wake()
528 if !s.BlockUntilParked(2e9 ) {
529 t.Fatal("timed out waiting for scavenger to run to completion")
530 }
531
532 verifyScavengerState(t, totalWork)
533
534
535
536 availableWork.Store(totalWork)
537 s.Wake()
538 if !s.BlockUntilParked(2e9 ) {
539 t.Fatal("timed out waiting for scavenger to run to completion")
540 }
541
542 verifyScavengerState(t, 2*totalWork)
543
544
545
546
547
548
549
550
551 availableWork.Store(totalWork)
552 stoppingPoint := uint64(1<<20 - 3*PhysPageSize)
553 stopAt.Store(stoppingPoint)
554 s.Wake()
555 if !s.BlockUntilParked(2e9 ) {
556 t.Fatal("timed out waiting for scavenger to run to completion")
557 }
558
559 verifyScavengerState(t, 2*totalWork+(totalWork-stoppingPoint))
560
561
562 s.Stop()
563 }
564
565 func TestScavengeIndex(t *testing.T) {
566 setup := func(t *testing.T) (func(ChunkIdx, uint), func(uintptr, uintptr)) {
567 t.Helper()
568
569
570 si := NewScavengeIndex(BaseChunkIdx, BaseChunkIdx+64)
571 find := func(want ChunkIdx, wantOffset uint) {
572 t.Helper()
573
574 got, gotOffset := si.Find()
575 if want != got {
576 t.Errorf("find: wanted chunk index %d, got %d", want, got)
577 }
578 if want != got {
579 t.Errorf("find: wanted page offset %d, got %d", wantOffset, gotOffset)
580 }
581 if t.Failed() {
582 t.FailNow()
583 }
584 si.Clear(got)
585 }
586 mark := func(base, limit uintptr) {
587 t.Helper()
588
589 si.Mark(base, limit)
590 }
591 return find, mark
592 }
593 t.Run("Uninitialized", func(t *testing.T) {
594 find, _ := setup(t)
595 find(0, 0)
596 })
597 t.Run("OnePage", func(t *testing.T) {
598 find, mark := setup(t)
599 mark(PageBase(BaseChunkIdx, 3), PageBase(BaseChunkIdx, 4))
600 find(BaseChunkIdx, 3)
601 find(0, 0)
602 })
603 t.Run("FirstPage", func(t *testing.T) {
604 find, mark := setup(t)
605 mark(PageBase(BaseChunkIdx, 0), PageBase(BaseChunkIdx, 1))
606 find(BaseChunkIdx, 0)
607 find(0, 0)
608 })
609 t.Run("SeveralPages", func(t *testing.T) {
610 find, mark := setup(t)
611 mark(PageBase(BaseChunkIdx, 9), PageBase(BaseChunkIdx, 14))
612 find(BaseChunkIdx, 13)
613 find(0, 0)
614 })
615 t.Run("WholeChunk", func(t *testing.T) {
616 find, mark := setup(t)
617 mark(PageBase(BaseChunkIdx, 0), PageBase(BaseChunkIdx+1, 0))
618 find(BaseChunkIdx, PallocChunkPages-1)
619 find(0, 0)
620 })
621 t.Run("LastPage", func(t *testing.T) {
622 find, mark := setup(t)
623 mark(PageBase(BaseChunkIdx, PallocChunkPages-1), PageBase(BaseChunkIdx+1, 0))
624 find(BaseChunkIdx, PallocChunkPages-1)
625 find(0, 0)
626 })
627 t.Run("TwoChunks", func(t *testing.T) {
628 find, mark := setup(t)
629 mark(PageBase(BaseChunkIdx, 128), PageBase(BaseChunkIdx+1, 128))
630 find(BaseChunkIdx+1, 127)
631 find(BaseChunkIdx, PallocChunkPages-1)
632 find(0, 0)
633 })
634 t.Run("TwoChunksOffset", func(t *testing.T) {
635 find, mark := setup(t)
636 mark(PageBase(BaseChunkIdx+7, 128), PageBase(BaseChunkIdx+8, 129))
637 find(BaseChunkIdx+8, 128)
638 find(BaseChunkIdx+7, PallocChunkPages-1)
639 find(0, 0)
640 })
641 t.Run("SevenChunksOffset", func(t *testing.T) {
642 find, mark := setup(t)
643 mark(PageBase(BaseChunkIdx+6, 11), PageBase(BaseChunkIdx+13, 15))
644 find(BaseChunkIdx+13, 14)
645 for i := BaseChunkIdx + 12; i >= BaseChunkIdx+6; i-- {
646 find(i, PallocChunkPages-1)
647 }
648 find(0, 0)
649 })
650 t.Run("ThirtyTwoChunks", func(t *testing.T) {
651 find, mark := setup(t)
652 mark(PageBase(BaseChunkIdx, 0), PageBase(BaseChunkIdx+32, 0))
653 for i := BaseChunkIdx + 31; i >= BaseChunkIdx; i-- {
654 find(i, PallocChunkPages-1)
655 }
656 find(0, 0)
657 })
658 t.Run("ThirtyTwoChunksOffset", func(t *testing.T) {
659 find, mark := setup(t)
660 mark(PageBase(BaseChunkIdx+3, 0), PageBase(BaseChunkIdx+35, 0))
661 for i := BaseChunkIdx + 34; i >= BaseChunkIdx+3; i-- {
662 find(i, PallocChunkPages-1)
663 }
664 find(0, 0)
665 })
666 t.Run("Mark", func(t *testing.T) {
667 find, mark := setup(t)
668 for i := BaseChunkIdx; i < BaseChunkIdx+32; i++ {
669 mark(PageBase(i, 0), PageBase(i+1, 0))
670 }
671 for i := BaseChunkIdx + 31; i >= BaseChunkIdx; i-- {
672 find(i, PallocChunkPages-1)
673 }
674 find(0, 0)
675 })
676 t.Run("MarkInterleaved", func(t *testing.T) {
677 find, mark := setup(t)
678 for i := BaseChunkIdx; i < BaseChunkIdx+32; i++ {
679 mark(PageBase(i, 0), PageBase(i+1, 0))
680 find(i, PallocChunkPages-1)
681 }
682 find(0, 0)
683 })
684 t.Run("MarkIdempotentOneChunk", func(t *testing.T) {
685 find, mark := setup(t)
686 mark(PageBase(BaseChunkIdx, 0), PageBase(BaseChunkIdx+1, 0))
687 mark(PageBase(BaseChunkIdx, 0), PageBase(BaseChunkIdx+1, 0))
688 find(BaseChunkIdx, PallocChunkPages-1)
689 find(0, 0)
690 })
691 t.Run("MarkIdempotentThirtyTwoChunks", func(t *testing.T) {
692 find, mark := setup(t)
693 mark(PageBase(BaseChunkIdx, 0), PageBase(BaseChunkIdx+32, 0))
694 mark(PageBase(BaseChunkIdx, 0), PageBase(BaseChunkIdx+32, 0))
695 for i := BaseChunkIdx + 31; i >= BaseChunkIdx; i-- {
696 find(i, PallocChunkPages-1)
697 }
698 find(0, 0)
699 })
700 t.Run("MarkIdempotentThirtyTwoChunksOffset", func(t *testing.T) {
701 find, mark := setup(t)
702 mark(PageBase(BaseChunkIdx+4, 0), PageBase(BaseChunkIdx+31, 0))
703 mark(PageBase(BaseChunkIdx+5, 0), PageBase(BaseChunkIdx+36, 0))
704 for i := BaseChunkIdx + 35; i >= BaseChunkIdx+4; i-- {
705 find(i, PallocChunkPages-1)
706 }
707 find(0, 0)
708 })
709 }
710
View as plain text