Skip to content

Commit

Permalink
Add isLoopDetectorEnabled
Browse files Browse the repository at this point in the history
  • Loading branch information
zhelenskiy committed Jan 25, 2025
1 parent 2501a39 commit f6f8002
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
package org.jetbrains.kotlinx.lincheck

val TRACE_DEBUGGER_MODE = System.getProperty("lincheck.traceDebuggerMode", "false").toBoolean()
val isLoopDetectorEnabled get() = System.getProperty("lincheck.loopDetectorEnabled", "true").toBoolean()
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ abstract class ManagedStrategy(
protected val currentActorId = mutableThreadMapOf<Int>()

// Detector of loops or hangs (i.e. active locks).
internal val loopDetector: LoopDetector = LoopDetector(testCfg.hangingDetectionThreshold)
internal val loopDetector: LoopDetector? =
if (isLoopDetectorEnabled) LoopDetector(testCfg.hangingDetectionThreshold) else null

// Tracker of objects' allocations and object graph topology.
protected abstract val objectTracker: ObjectTracker
Expand Down Expand Up @@ -203,7 +204,7 @@ abstract class ManagedStrategy(
protected open fun initializeInvocation() {
traceCollector = if (collectTrace) TraceCollector() else null
suddenInvocationResult = null
loopDetector.initialize()
loopDetector?.initialize()
objectTracker.reset()
monitorTracker.reset()
parkingTracker.reset()
Expand Down Expand Up @@ -262,7 +263,7 @@ abstract class ManagedStrategy(
POST -> 0
VALIDATION -> 0
}
loopDetector.beforePart(nextThread)
loopDetector?.beforePart(nextThread)
threadScheduler.scheduleThread(nextThread)
}

Expand All @@ -284,7 +285,7 @@ abstract class ManagedStrategy(
}

collectTrace = true
loopDetector.enableReplayMode(
loopDetector?.enableReplayMode(
failDueToDeadlockInTheEnd =
result is ManagedDeadlockInvocationResult ||
result is ObstructionFreedomViolationInvocationResult
Expand Down Expand Up @@ -398,7 +399,7 @@ abstract class ManagedStrategy(
* In the considered example, we will retain that we will switch soon after
* the spin cycle in thread 1, so no bug will appear.
*/
loopDetector.replayModeEnabled ->
loopDetector?.replayModeEnabled == true ->
loopDetector.shouldSwitchInReplayMode()
/*
* In the regular mode, we use loop detector only to determine should we
Expand All @@ -409,13 +410,13 @@ abstract class ManagedStrategy(
(runner.currentExecutionPart == PARALLEL) && shouldSwitch(iThread)
}
// check if live-lock is detected
val decision = loopDetector.visitCodeLocation(iThread, codeLocation)
val decision = loopDetector?.visitCodeLocation(iThread, codeLocation)
// if we reached maximum number of events threshold, then fail immediately
if (decision == LoopDetector.Decision.EventsThresholdReached) {
failDueToDeadlock()
}
// if any kind of live-lock was detected, check for obstruction-freedom violation
if (decision.isLivelockDetected) {
if (decision?.isLivelockDetected == true) {
failIfObstructionFreedomIsRequired {
if (decision is LoopDetector.Decision.LivelockFailureDetected) {
// if failure is detected, add a special obstruction-freedom violation
Expand All @@ -435,14 +436,14 @@ abstract class ManagedStrategy(
}
// if live-lock was detected, and replay was requested,
// then abort current execution and start the replay
if (decision.isReplayRequired) {
if (decision?.isReplayRequired == true) {
abortWithSuddenInvocationResult(SpinCycleFoundAndReplayRequired)
}
// if the current thread in a live-lock, then try to switch to another thread
if (decision is LoopDetector.Decision.LivelockThreadSwitch) {
val switchHappened = switchCurrentThread(iThread, BlockingReason.LiveLocked, tracePoint)
if (switchHappened) {
loopDetector.initializeFirstCodeLocationAfterSwitch(codeLocation)
loopDetector?.initializeFirstCodeLocationAfterSwitch(codeLocation)
}
traceCollector?.passCodeLocation(tracePoint)
return
Expand All @@ -451,12 +452,12 @@ abstract class ManagedStrategy(
if (shouldSwitch) {
val switchHappened = switchCurrentThread(iThread, tracePoint = tracePoint)
if (switchHappened) {
loopDetector.initializeFirstCodeLocationAfterSwitch(codeLocation)
loopDetector?.initializeFirstCodeLocationAfterSwitch(codeLocation)
}
traceCollector?.passCodeLocation(tracePoint)
return
}
if (!loopDetector.replayModeEnabled) {
if (loopDetector != null && !loopDetector.replayModeEnabled) {
loopDetector.onNextExecutionPoint(codeLocation)
}
traceCollector?.passCodeLocation(tracePoint)
Expand Down Expand Up @@ -584,7 +585,7 @@ abstract class ManagedStrategy(
open fun onThreadFinish(iThread: Int) {
threadScheduler.awaitTurn(iThread)
threadScheduler.finishThread(iThread)
loopDetector.onThreadFinish(iThread)
loopDetector?.onThreadFinish(iThread)
traceCollector?.onThreadFinish()
unblockJoiningThreads(iThread)
val nextThread = chooseThreadSwitch(iThread, true)
Expand Down Expand Up @@ -617,7 +618,7 @@ abstract class ManagedStrategy(
currentActorId[iThread] = actorId
callStackTrace[iThread]!!.clear()
suspendedFunctionsStack[iThread]!!.clear()
loopDetector.onActorStart(iThread)
loopDetector?.onActorStart(iThread)
enterTestingCode()
}

Expand Down Expand Up @@ -711,7 +712,7 @@ abstract class ManagedStrategy(

@JvmName("setNextThread")
private fun setCurrentThread(nextThread: Int) {
loopDetector.onThreadSwitch(nextThread)
loopDetector?.onThreadSwitch(nextThread)
threadScheduler.scheduleThread(nextThread)
}

Expand Down Expand Up @@ -946,7 +947,7 @@ abstract class ManagedStrategy(
lastReadTracePoint[iThread] = tracePoint
}
newSwitchPoint(iThread, codeLocation, tracePoint)
loopDetector.beforeReadField(obj)
loopDetector?.beforeReadField(obj)
return@runInIgnoredSection true
}

Expand All @@ -973,7 +974,7 @@ abstract class ManagedStrategy(
lastReadTracePoint[iThread] = tracePoint
}
newSwitchPoint(iThread, codeLocation, tracePoint)
loopDetector.beforeReadArrayElement(array, index)
loopDetector?.beforeReadArrayElement(array, index)
true
}

Expand All @@ -983,7 +984,8 @@ abstract class ManagedStrategy(
lastReadTracePoint[iThread]?.initializeReadValue(adornedStringRepresentation(value))
lastReadTracePoint[iThread] = null
}
loopDetector.afterRead(value)
loopDetector?.afterRead(value)
Unit
}

override fun beforeWriteField(obj: Any?, className: String, fieldName: String, value: Any?, codeLocation: Int,
Expand Down Expand Up @@ -1013,7 +1015,7 @@ abstract class ManagedStrategy(
null
}
newSwitchPoint(iThread, codeLocation, tracePoint)
loopDetector.beforeWriteField(obj, value)
loopDetector?.beforeWriteField(obj, value)
return@runInIgnoredSection true
}

Expand All @@ -1039,7 +1041,7 @@ abstract class ManagedStrategy(
null
}
newSwitchPoint(iThread, codeLocation, tracePoint)
loopDetector.beforeWriteArrayElement(array, index, value)
loopDetector?.beforeWriteArrayElement(array, index, value)
true
}

Expand Down Expand Up @@ -1240,11 +1242,11 @@ abstract class ManagedStrategy(
if (guarantee == ManagedGuaranteeType.TREAT_AS_ATOMIC) {
// re-use last call trace point
newSwitchPoint(iThread, codeLocation, callStackTrace[iThread]!!.lastOrNull()?.tracePoint)
loopDetector.passParameters(params)
loopDetector?.passParameters(params)
}
// notify loop detector about the method call
if (guarantee == null) {
loopDetector.beforeMethodCall(codeLocation, params)
loopDetector?.beforeMethodCall(codeLocation, params)
}
// method's guarantee
guarantee
Expand All @@ -1261,7 +1263,7 @@ abstract class ManagedStrategy(

override fun onMethodCallReturn(result: Any?) {
runInIgnoredSection {
loopDetector.afterMethodCall()
loopDetector?.afterMethodCall()
if (collectTrace) {
val iThread = threadScheduler.getCurrentThreadId()
// this case is possible and can occur when we resume the coroutine,
Expand All @@ -1288,7 +1290,7 @@ abstract class ManagedStrategy(

override fun onMethodCallException(t: Throwable) {
runInIgnoredSection {
loopDetector.afterMethodCall()
loopDetector?.afterMethodCall()
}
if (collectTrace) {
runInIgnoredSection {
Expand Down Expand Up @@ -1798,6 +1800,7 @@ abstract class ManagedStrategy(
}

fun checkActiveLockDetected() {
if (loopDetector == null) return
val currentThreadId = threadScheduler.getCurrentThreadId()
if (!loopDetector.replayModeCurrentlyInSpinCycle) return
if (spinCycleStartAdded) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import org.jetbrains.kotlinx.lincheck.checkImpl
import org.jetbrains.kotlinx.lincheck.execution.ExecutionResult
import org.jetbrains.kotlinx.lincheck.execution.ExecutionScenario
import org.jetbrains.kotlinx.lincheck.execution.parallelResults
import org.jetbrains.kotlinx.lincheck.isLoopDetectorEnabled
import org.jetbrains.kotlinx.lincheck.strategy.managed.modelchecking.ModelCheckingOptions
import org.jetbrains.kotlinx.lincheck.verifier.Verifier
import org.junit.Test
Expand All @@ -34,6 +35,9 @@ abstract class AbstractNativeCallTest {
val myStdErr = PrintStream(stdErrOutputCollector)
System.setOut(myStdOut)
System.setErr(myStdErr)
val oldIsLoopDetectorEnabled = isLoopDetectorEnabled
val loopDetectorPropertyName = "lincheck.loopDetectorEnabled"
System.setProperty(loopDetectorPropertyName, "false")
try {
ModelCheckingOptions()
.actorsBefore(0)
Expand Down Expand Up @@ -62,6 +66,7 @@ abstract class AbstractNativeCallTest {
val stdErrOutput = stdErrOutputCollector.toString()
System.err.print(stdErrOutput)
require(!stdOutOutput.contains(forbiddenString) && !stdErrOutput.contains(forbiddenString))
System.setProperty(loopDetectorPropertyName, oldIsLoopDetectorEnabled.toString())
}
}

Expand Down

0 comments on commit f6f8002

Please sign in to comment.