in src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt [250:323]
fun visitCodeLocation(iThread: Int, codeLocation: Int): Decision {
check(currentThreadId == iThread) {
"The current thread id $currentThreadId is not equal to the one provided $iThread."
}
// Increase the total number of happened operations for live-lock detection
totalExecutionsCount++
// Ignore unknown code locations.
if (codeLocation == UNKNOWN_CODE_LOCATION_ID) {
return Decision.Idle
}
// Update code location visit counter
updateCodeLocationVisitCounter(codeLocation)
val count = currentThreadCodeLocationVisitCountMap.getOrDefault(codeLocation, 0)
// If we are in replay mode, check if the replay has lead to a deadlock
replayModeLoopDetectorHelper?.let {
return it.detectLivelock()
}
// In trace debugger mode, check whether the count exceeds
// the maximum number of repetitions for spin-loop detection.
// Check whether the count exceeds the maximum number of repetitions for loop/hang detection.
if (isInTraceDebuggerMode) {
return when {
// spin-loop detected - switch
count > currentHangingDetectionThreshold ->
Decision.LivelockThreadSwitch(currentHangingDetectionThreshold)
// live-lock detected - fail
totalExecutionsCount > ManagedCTestConfiguration.DEFAULT_LIVELOCK_EVENTS_THRESHOLD ->
Decision.EventsThresholdReached
// else - continue
else -> Decision.Idle
}
}
val detectedFirstTime = count > currentHangingDetectionThreshold
val detectedEarly = loopTrackingCursor.isInCycle
// detectedFirstTime and detectedEarly can both sometimes be true
// when we can't find a cycle period and can't switch to another thread.
if (detectedFirstTime && !detectedEarly) {
if (mode == Mode.DEFAULT) {
// Turn on parameters and read/write values and receivers tracking and request one more replay.
mode = Mode.CYCLE_PERIOD_CALCULATION
return Decision.LivelockReplayToDetectCycleRequired
}
registerCycle()
// Turn off parameters tracking and request one more replay to avoid side effects.
mode = Mode.DEFAULT
// Enormous operations count considered as total spin lock
if (totalExecutionsCount > ManagedCTestConfiguration.DEFAULT_LIVELOCK_EVENTS_THRESHOLD) {
return Decision.EventsThresholdReached
}
// Replay current interleaving to avoid side effects caused by multiple cycle executions
return Decision.LivelockReplayRequired
}
if (!detectedFirstTime && detectedEarly) {
totalExecutionsCount += currentHangingDetectionThreshold
val lastNode = currentInterleavingHistory.last()
// spinCyclePeriod may be not 0 only we tried to switch
// from the current thread but no available threads were available to switch
if (lastNode.spinCyclePeriod == 0) {
// transform current node to the state corresponding to early found cycle
val cyclePeriod = loopTrackingCursor.cyclePeriod
lastNode.executions -= cyclePeriod
lastNode.spinCyclePeriod = cyclePeriod
lastNode.executionHash = loopTrackingCursor.cycleLocationsHash
}
// Enormous operations count considered as total spin lock
if (totalExecutionsCount > ManagedCTestConfiguration.DEFAULT_LIVELOCK_EVENTS_THRESHOLD) {
return Decision.EventsThresholdReached
}
}
if (detectedFirstTime || detectedEarly) {
return Decision.LivelockThreadSwitch(replayModeCurrentCyclePeriod)
}
return Decision.Idle
}