blob: 1b99a33d01a8c5d8ec8f4c99412f86e15563b873 [file]
// IGNORE_NATIVE: optimizationMode=DEBUG
// IGNORE_NATIVE: optimizationMode=NO
@file:OptIn(kotlin.native.runtime.NativeRuntimeApi::class)
import kotlin.native.internal.isStack
import kotlin.native.runtime.GC
open class Node {
var next: Node? = null
}
class Middle : Node() {
var next2: Node? = null
var next3: Node? = null
var next4: Node? = null
var next5: Node? = null
}
var count = 0
fun left(depth: Int) {
if (depth <= 0) return
// Schedule the GC often enough to find the problem sooner:
if (count++ % 500 == 0) GC.schedule()
val head = Node()
check(head.isStack())
// Use many stack objects to increase the probability of hitting one of them with zeroes:
val tail1 = Node()
val tail2 = Node()
val tail3 = Node()
val tail4 = Node()
val tail5 = Node()
repeat(1) {
val middle = Middle()
check(!middle.isStack())
head.next = middle
middle.next = tail1
middle.next2 = tail2
middle.next3 = tail3
middle.next4 = tail4
middle.next5 = tail5
}
// The bug is that `middle` is marked as a normal heap object, not as a local object.
// So, we have a heap object referencing a stack object.
// In CMS, when it marks concurrently, by the time it scans `middle`,
// the referred stack objects might be overwritten.
// That's exactly what this test tries to achieve.
//
// `left` and `right` are similar, but the former allocates such stack objects,
// while the latter zeroes the stack.
// So, if the stack trace changes between scanning stack roots (in STW)
// and marking `middle` (concurrently), then the deepest frame changes
// from `left` to `right`, so left's objects are overwritten by right's zeroing.
left(depth - 1)
right(depth - 1)
}
fun right(depth: Int) {
if (depth <= 0) return
val zeroes = IntArray(16)
check(zeroes.isStack()) // Zeroes the stack.
left(depth - 1)
right(depth - 1)
}
fun box(): String {
left(20)
// Wait till the scheduled GC finishes:
GC.collect()
return "OK"
}