From c7200b54b38f165d7231899eb8331454a045e9da Mon Sep 17 00:00:00 2001 From: "Aleksandr.Potapov" Date: Mon, 10 Jun 2024 02:15:31 +0200 Subject: [PATCH 01/13] Recursive spin lock detection support, spin lock detection related bug fixes. --- .../src/sun/nio/ch/lincheck/EventTracker.kt | 2 +- .../src/sun/nio/ch/lincheck/Injections.java | 4 +- .../org/jetbrains/kotlinx/lincheck/Utils.kt | 9 + .../InterleavingSequenceTrackableSet.kt | 31 +- .../lincheck/strategy/managed/LoopDetector.kt | 256 +++++++-- .../strategy/managed/ManagedStrategy.kt | 391 ++++++++++++-- .../lincheck/strategy/managed/TracePoint.kt | 10 +- .../strategy/managed/TraceReporter.kt | 179 ++++++- .../modelchecking/ModelCheckingStrategy.kt | 15 +- .../lincheck/transformation/CodeLocations.kt | 18 +- .../transformation/LincheckClassVisitor.kt | 3 + .../representation/RecursiveSpinLockTest.kt | 502 ++++++++++++++++++ .../representation/SpinlockEventsCutTests.kt | 200 ++++++- ...struction_freedom_violation_events_cut.txt | 20 +- ...eedom_violation_with_no_detected_cycle.txt | 20 +- .../broken-cas-2-recursive-live-lock.txt | 140 +++++ .../infinite_spin_loop_events_arrays.txt | 236 ++++++++ ...nite_spin_loop_events_arrays_receivers.txt | 136 +++++ .../infinite_spin_loop_events_cut.txt | 128 ++--- ...inite_spin_loop_events_no_cycle_params.txt | 140 +++++ .../infinite_spin_loop_events_read_write.txt | 144 +++++ .../infinite_spin_loop_events_receivers.txt | 140 +++++ .../recursive_spin_cycle_inner_events.txt | 95 ++++ .../recursive_spin_cycle_two_step.txt | 99 ++++ .../spin_lock/recursive_spin_lock.txt | 87 +++ .../recursive_spin_lock_param_dependent.txt | 39 ++ .../spin_lock/recursive_spin_lock_params.txt | 139 +++++ .../recursive_two_threads_spin_lock.txt | 49 ++ .../spin_lock_events_cut_inner_loop.txt | 148 ++++++ .../spin_lock_events_cut_long_cycle.txt | 140 +++++ ...in_lock_events_cut_single_action_cycle.txt | 128 +++++ ...spin_lock_events_cut_two_actions_cycle.txt | 142 +++++ ...spin_lock_in_incorrect_results_failure.txt | 56 +- .../spin_lock/spin_lock_nested_events.txt | 69 +++ .../spin_lock/spin_lock_top_level.txt | 31 ++ .../spin_lock_events_cut_inner_loop.txt | 148 ------ .../spin_lock_events_cut_long_cycle.txt | 140 ----- ...in_lock_events_cut_single_action_cycle.txt | 128 ----- ...spin_lock_events_cut_two_actions_cycle.txt | 130 ----- ..._the_middle_of_spin_cycle_causes_error.txt | 20 +- 40 files changed, 3715 insertions(+), 797 deletions(-) create mode 100644 src/jvm/test/org/jetbrains/kotlinx/lincheck_test/representation/RecursiveSpinLockTest.kt create mode 100644 src/jvm/test/resources/expected_logs/spin_lock/broken-cas-2-recursive-live-lock.txt create mode 100644 src/jvm/test/resources/expected_logs/spin_lock/infinite_spin_loop_events_arrays.txt create mode 100644 src/jvm/test/resources/expected_logs/spin_lock/infinite_spin_loop_events_arrays_receivers.txt rename src/jvm/test/resources/expected_logs/{ => spin_lock}/infinite_spin_loop_events_cut.txt (78%) create mode 100644 src/jvm/test/resources/expected_logs/spin_lock/infinite_spin_loop_events_no_cycle_params.txt create mode 100644 src/jvm/test/resources/expected_logs/spin_lock/infinite_spin_loop_events_read_write.txt create mode 100644 src/jvm/test/resources/expected_logs/spin_lock/infinite_spin_loop_events_receivers.txt create mode 100644 src/jvm/test/resources/expected_logs/spin_lock/recursive_spin_cycle_inner_events.txt create mode 100644 src/jvm/test/resources/expected_logs/spin_lock/recursive_spin_cycle_two_step.txt create mode 100644 src/jvm/test/resources/expected_logs/spin_lock/recursive_spin_lock.txt create mode 100644 src/jvm/test/resources/expected_logs/spin_lock/recursive_spin_lock_param_dependent.txt create mode 100644 src/jvm/test/resources/expected_logs/spin_lock/recursive_spin_lock_params.txt create mode 100644 src/jvm/test/resources/expected_logs/spin_lock/recursive_two_threads_spin_lock.txt create mode 100644 src/jvm/test/resources/expected_logs/spin_lock/spin_lock_events_cut_inner_loop.txt create mode 100644 src/jvm/test/resources/expected_logs/spin_lock/spin_lock_events_cut_long_cycle.txt create mode 100644 src/jvm/test/resources/expected_logs/spin_lock/spin_lock_events_cut_single_action_cycle.txt create mode 100644 src/jvm/test/resources/expected_logs/spin_lock/spin_lock_events_cut_two_actions_cycle.txt rename src/jvm/test/resources/expected_logs/{ => spin_lock}/spin_lock_in_incorrect_results_failure.txt (55%) create mode 100644 src/jvm/test/resources/expected_logs/spin_lock/spin_lock_nested_events.txt create mode 100644 src/jvm/test/resources/expected_logs/spin_lock/spin_lock_top_level.txt delete mode 100644 src/jvm/test/resources/expected_logs/spin_lock_events_cut_inner_loop.txt delete mode 100644 src/jvm/test/resources/expected_logs/spin_lock_events_cut_long_cycle.txt delete mode 100644 src/jvm/test/resources/expected_logs/spin_lock_events_cut_single_action_cycle.txt delete mode 100644 src/jvm/test/resources/expected_logs/spin_lock_events_cut_two_actions_cycle.txt diff --git a/bootstrap/src/sun/nio/ch/lincheck/EventTracker.kt b/bootstrap/src/sun/nio/ch/lincheck/EventTracker.kt index 8930ec8120..7a8269671e 100644 --- a/bootstrap/src/sun/nio/ch/lincheck/EventTracker.kt +++ b/bootstrap/src/sun/nio/ch/lincheck/EventTracker.kt @@ -39,7 +39,7 @@ interface EventTracker { fun beforeWriteArrayElement(array: Any, index: Int, value: Any?, codeLocation: Int): Boolean fun afterWrite() - fun beforeMethodCall(owner: Any?, className: String, methodName: String, codeLocation: Int, params: Array) + fun beforeMethodCall(owner: Any?, className: String, methodName: String, codeLocation: Int, methodId: Int, params: Array) fun beforeAtomicMethodCall(owner: Any?, className: String, methodName: String, codeLocation: Int, params: Array) fun onMethodCallFinishedSuccessfully(result: Any?) fun onMethodCallThrewException(t: Throwable) diff --git a/bootstrap/src/sun/nio/ch/lincheck/Injections.java b/bootstrap/src/sun/nio/ch/lincheck/Injections.java index 3db979c640..972c784820 100644 --- a/bootstrap/src/sun/nio/ch/lincheck/Injections.java +++ b/bootstrap/src/sun/nio/ch/lincheck/Injections.java @@ -253,8 +253,8 @@ public static void afterWrite() { * * @param owner is `null` for public static methods. */ - public static void beforeMethodCall(Object owner, String className, String methodName, int codeLocation, Object[] params) { - getEventTracker().beforeMethodCall(owner, className, methodName, codeLocation, params); + public static void beforeMethodCall(Object owner, String className, String methodName, int codeLocation, int methodId, Object[] params) { + getEventTracker().beforeMethodCall(owner, className, methodName, codeLocation, methodId, params); } /** diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt index c099ec410f..1484e8af48 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt @@ -87,6 +87,15 @@ private fun Class.getMethod(name: String, parameterTypes: Array value.hashCode() + else -> System.identityHashCode(value) +} + /** * Creates [Result] of corresponding type from any given value. * diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/InterleavingSequenceTrackableSet.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/InterleavingSequenceTrackableSet.kt index c1996b9d45..4093334283 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/InterleavingSequenceTrackableSet.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/InterleavingSequenceTrackableSet.kt @@ -241,9 +241,18 @@ internal class InterleavingSequenceTrackableSet { fun mergeBranch(newChain: List, startIndex: Int, executionsCountedEarlier: Int) { if (startIndex > newChain.lastIndex) return val firstNewNode = newChain[startIndex] - val firstNewNodeExecutions = (firstNewNode.executions + firstNewNode.spinCyclePeriod) - executionsCountedEarlier + var firstNewNodeExecutions = (firstNewNode.executions + firstNewNode.spinCyclePeriod) - executionsCountedEarlier check(firstNewNode.threadId == threadId) + // Some execution points may be added to the history after the spin-cycle is detected early + // even if we switched the execution using LoopDetector hint. In this case, the new branch may have + // more executions than the InterleavingSequenceSetNode which told switching the thread at this point. + // But these executions will be omitted in the next time, so we merge new branch taking execution count + // from the corresponding existing node. + if (cycleOccurred && firstNewNodeExecutions > executions) { + firstNewNodeExecutions = executions + } + when { executions == firstNewNodeExecutions -> mergeFurtherOrAddNewBranch(newChain, startIndex + 1, 0) @@ -295,8 +304,26 @@ internal class InterleavingSequenceTrackableSet { transition.mergeBranch(newChain, startIndex, executionsCountedEarlier) } } + + // Utility function to debug spin-locks related internals. + fun getTree(prefix: String = "", isTail: Boolean = true): String = buildString { + val tailSymbol = if (isTail) "\\-- " else "|-- " + appendLine("$prefix$tailSymbol$threadId : $executions : $cyclePeriod : ${if (cycleOccurred) "!" else "."}") + val children = transitions?.values?.toList() ?: emptyList() + for (i in children.indices) { + children[i].getTree(prefix + if (isTail) " " else "| ", i == children.size - 1) + } + } } + // Utility function to debug spin-locks related internals. + internal fun treeToString(): String = buildString { + rootTransitions.forEach { (threadId, node) -> + appendLine("$threadId:") + appendLine(node.getTree()) + appendLine() + } + } /** * Transforms a new chain @@ -329,7 +356,7 @@ internal class InterleavingSequenceTrackableSet { threadId = next.threadId, executions = next.executions + next.spinCyclePeriod, cyclePeriod = next.spinCyclePeriod, - cycleLocationsHash = first.executionHash, + cycleLocationsHash = next.executionHash, cycleOccurred = i == chain.lastIndex ) current.addTransition(next.threadId, nextNode) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt index 4b8522f026..6eaeb8554d 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt @@ -10,6 +10,7 @@ package org.jetbrains.kotlinx.lincheck.strategy.managed +import org.jetbrains.kotlinx.lincheck.primitiveHashCodeOrSystemHashCode import java.util.ArrayList /** @@ -19,7 +20,19 @@ import java.util.ArrayList * * The LoopDetector functions in two modes: default and replay mode. * - * In default mode: + * In the default mode: + * + * In the default mode, LoopDetector has two states. + * When a spin cycle is not detected yet, LoopDetector ignores method parameters and read/write values and receivers, + * operating only with switch points code locations. + * When a spin cycle is detected, LoopDetector requests the replay of the execution and starts tracking + * the parameters, etc., to detect a spin cycle period properly, taking into account all changes during the spin cycle. + * When we run into a spin cycle again, LoopDetector tries to find the period using [currentThreadCodeLocationsHistory]. + * Parameters etc. are converted to the negative integer representation using hashcode (see [paramToIntRepresentation]). + * But sometimes it's not possible, for example, in case of using random. In that case, LoopDetector omits parameters, + * read/written values, etc., and tries to find the period using only switch points code locations. + * Once it's found (or not) LoopDetector requests one more replay to avoid side effects, made by this spin cycle. + * * - The LoopDetector tracks code location executions (using [currentThreadCodeLocationsHistory]) performed by threads. * The history is stored for the current thread and is cleared during a thread switch. * - A map ([currentThreadCodeLocationVisitCountMap]) is maintained to track the number of times a thread visits a certain code location. @@ -34,7 +47,7 @@ import java.util.ArrayList * - If the counter exceeds the [ManagedCTestConfiguration.LIVELOCK_EVENTS_THRESHOLD], a total deadlock is assumed. * Due to the relative small size of scenarios generated by Lincheck, such a high number of executions indicates a lack of progress in the system. * - * In replay mode: + * In the replay mode: * - The number of allowable events to execute in each thread is determined using saved information from the last interleaving. * - For instance, if the [currentInterleavingHistory] is [0: 2], [1: 3], [0: 3], [1: 3], [0: 3], ..., [1: 3], [0: 3] and a deadlock is detected, * the cycle is identified as [1: 3], [0: 3]. @@ -45,7 +58,6 @@ import java.util.ArrayList * Note: An example of this behavior is detailed in the comments of the code itself. */ internal class LoopDetector( - private val nThreads: Int, private val hangingDetectionThreshold: Int ) { private var lastExecutedThread = -1 // no last thread @@ -65,14 +77,6 @@ internal class LoopDetector( */ private val currentInterleavingHistory = ArrayList() - /** - * When we're back to some thread, newSwitchPoint won't be called before the first event in the current - * thread part as it was called before the switch. So when we return to a thread that already was running, - * we have to start from 1 its executions counter. This set helps us to determine if some thread is running - * for the first time in an execution or not. - */ - private val threadsRan: BooleanArray = BooleanArray(nThreads) { false } - /** * Set of interleaving event sequences lead to loops. (A set of previously detected hangs) */ @@ -99,6 +103,17 @@ internal class LoopDetector( val replayModeEnabled: Boolean get() = replayModeLoopDetectorHelper != null + /** + * Indicates if we analyze method parameters to calculate the spin-cycle period. + */ + private var cycleCalculationPhase: Boolean = false + + /** + * Indicates that we are in a spin cycle iteration now. + * Should be called only in replay mode. + */ + val replayModeCurrentlyInSpinCycle: Boolean get() = replayModeLoopDetectorHelper!!.currentlyInSpinCycle + fun enableReplayMode(failDueToDeadlockInTheEnd: Boolean) { val contextSwitchesBeforeHalt = findMaxPrefixLengthWithNoCycleOnSuffix(currentInterleavingHistory)?.let { it.executionsBeforeCycle + it.cyclePeriod } @@ -138,18 +153,24 @@ internal class LoopDetector( data object EventsThresholdReached : Decision() /** - * This decision is returned when the live-lock is detected for the first time, - * in this case, a replay is required to cut the later spin-loop iterations. + * This decision is returned when the live-lock is detected and replay required to avoid potential side-effects. */ data object LivelockReplayRequired : Decision() + /** + * This decision is returned when the live-lock is detected for the first time, + * and we have to replay the execution analyzing method parameters additionally to + * calculate the spin cycle period. + */ + data object LivelockReplayToDetectCycleRequired : Decision() + /** * This decision is returned when global live-lock is detected decisively, * and the execution has to be aborted with the failure. * * @property cyclePeriod The period of the spin-loop cycle. */ - data class LivelockFailureDetected(val cyclePeriod: Int) : Decision() + data class LivelockFailureDetected(val cyclePeriod: Int) : Decision() /** * This decision is returned when a single thread enters a live-lock, @@ -157,7 +178,7 @@ internal class LoopDetector( * * @property cyclePeriod The period of the spin-loop cycle. */ - data class LivelockThreadSwitch(val cyclePeriod: Int) : Decision() + data class LivelockThreadSwitch(val cyclePeriod: Int) : Decision() } /** @@ -170,13 +191,12 @@ internal class LoopDetector( * @see LoopDetector.Decision */ fun visitCodeLocation(iThread: Int, codeLocation: Int): Decision { - threadsRan[iThread] = true replayModeLoopDetectorHelper?.let { return if (it.shouldSwitch()) it.detectLivelock() else Decision.Idle } // Increase the total number of happened operations for live-lock detection totalExecutionsCount++ - // Have the thread changed? Reset the counters in this case. + // Has the thread changed? Reset the counters in this case. check(lastExecutedThread == iThread) { "reset expected!" } // Ignore coroutine suspension code locations. if (codeLocation == COROUTINE_SUSPENSION_CODE_LOCATION) return Decision.Idle @@ -190,7 +210,14 @@ internal class LoopDetector( // when we can't find a cycle period and can't switch to another thread. // Check whether the count exceeds the maximum number of repetitions for loop/hang detection. if (detectedFirstTime && !detectedEarly) { + if (!cycleCalculationPhase) { + // Turn on parameters and read/write values and receivers tracking and request one more replay. + cycleCalculationPhase = true + return Decision.LivelockReplayToDetectCycleRequired + } registerCycle() + // Turn off parameters tracking and request one more replay to avoid side effects. + cycleCalculationPhase = false // Enormous operations count considered as total spin lock if (totalExecutionsCount > ManagedCTestConfiguration.LIVELOCK_EVENTS_THRESHOLD) { return Decision.EventsThresholdReached @@ -222,6 +249,68 @@ internal class LoopDetector( Decision.Idle } + /** + * Called before regular method calls. + */ + fun beforeRegularMethodCall(codeLocation: Int, params: Array) { + replayModeLoopDetectorHelper?.let { + it.onNextExecution() + return + } + currentThreadCodeLocationsHistory += codeLocation + passParameters(params) + val lastInterleavingHistoryNode = currentInterleavingHistory.last() + if (lastInterleavingHistoryNode.cycleOccurred) { + return /* If we already ran into cycle and haven't switched than no need to track executions */ + } + lastInterleavingHistoryNode.addExecution(codeLocation) + loopTrackingCursor.onNextExecutionPoint() + } + + /** + * Called after any method calls. + */ + fun afterRegularMethodCall() { + val afterMethodCallLocation = 0 + replayModeLoopDetectorHelper?.let { + it.onNextExecution() + return + } + currentThreadCodeLocationsHistory += afterMethodCallLocation + val lastInterleavingHistoryNode = currentInterleavingHistory.last() + if (lastInterleavingHistoryNode.cycleOccurred) { + return /* If we already ran into cycle and haven't switched than no need to track executions */ + } + lastInterleavingHistoryNode.addExecution(afterMethodCallLocation) + loopTrackingCursor.onNextExecutionPoint() + } + + /** + * Called when we pass some parameters before method call in the instrumented call. + * Used only if LoopDetector is in the cycle calculation mode. + * Otherwise, does nothing. + */ + fun passParameters(params: Array) { + if (!cycleCalculationPhase) return + params.forEach { param -> + currentThreadCodeLocationsHistory += paramToIntRepresentation(param) + } + } + + /** + * Called when we: + * - Read/write some value. + * - Use some object as a receiver (receiver is passed as a parameter [obj]). + * - Use an index to access an array cell (index is passed as a parameter [obj]). + * + * Used only if LoopDetector is in the cycle calculation mode. + * Otherwise, does nothing. + */ + fun passValue(obj: Any?) { + if (!cycleCalculationPhase) return + currentThreadCodeLocationsHistory += paramToIntRepresentation(obj) + } + fun onActorStart(iThread: Int) { check(iThread == lastExecutedThread) // if a thread has reached a new actor, then it means it has made some progress; @@ -248,31 +337,44 @@ internal class LoopDetector( in current thread part as it was called before switch. So, we're tracking that to maintain the number of performed operations correctly. */ - val threadRunningFirstTime = !threadsRan[nextThread] if (currentInterleavingHistory.isNotEmpty() && currentInterleavingHistory.last().threadId == nextThread) { return } currentInterleavingHistory.add( InterleavingHistoryNode( threadId = nextThread, - executions = if (threadRunningFirstTime) 0 else 1, + executions = 0, ) ) loopTrackingCursor.onNextSwitchPoint(nextThread) - if (!threadRunningFirstTime) { - loopTrackingCursor.onNextExecutionPoint() - } - replayModeLoopDetectorHelper?.onNextSwitch(threadRunningFirstTime) + replayModeLoopDetectorHelper?.onNextSwitch() } /** - * Is called after switch back to a thread + * Is called after switch back to a thread. + * Required because after we switch back to the thread no `visitCodeLocations` will be called before the next + * execution point, as it was called earlier. + * But we need to track that this point is going to + * be executed after the switch, so we pass it after the switch back, + * but before the instruction is actually executed. */ fun initializeFirstCodeLocationAfterSwitch(codeLocation: Int) { - val lastInterleavingHistoryNode = currentInterleavingHistory.last() - lastInterleavingHistoryNode.executionHash = lastInterleavingHistoryNode.executionHash xor codeLocation + replayModeLoopDetectorHelper?.let { helper -> + helper.onNextExecution() + return + } + onNextExecutionPoint(codeLocation) + // Increase the total number of happened operations for live-lock detection + totalExecutionsCount++ + // Increment the number of times the specified code location is visited. + val count = currentThreadCodeLocationVisitCountMap.getOrDefault(codeLocation, 0) + 1 + currentThreadCodeLocationVisitCountMap[codeLocation] = count + currentThreadCodeLocationsHistory += codeLocation } + /** + * Called only when replay mode is disabled. + */ fun onNextExecutionPoint(executionIdentity: Int) { val lastInterleavingHistoryNode = currentInterleavingHistory.last() if (lastInterleavingHistoryNode.cycleOccurred) { @@ -280,15 +382,15 @@ internal class LoopDetector( } lastInterleavingHistoryNode.addExecution(executionIdentity) loopTrackingCursor.onNextExecutionPoint() - replayModeLoopDetectorHelper?.onNextExecution() } private fun registerCycle() { - val cycleInfo = findMaxPrefixLengthWithNoCycleOnSuffix(currentThreadCodeLocationsHistory) + val (cycleInfo, switchPointsCodeLocationsHistory) = tryFindCycleWithParamsOrWithout() + if (cycleInfo == null) { val lastNode = currentInterleavingHistory.last() val cycleStateLastNode = lastNode.asNodeCorrespondingToCycle( - executionsBeforeCycle = currentThreadCodeLocationsHistory.size - 1, + executionsBeforeCycle = switchPointsCodeLocationsHistory.size - 1, cyclePeriod = 0, cycleExecutionsHash = lastNode.executionHash // corresponds to a cycle ) @@ -320,9 +422,9 @@ internal class LoopDetector( So we need to [threadId = 1, executions = 5] execution part to have a hash equals to next cycle nodes, because we will take only thread executions before cycle and the first cycle iteration. */ - var cycleExecutionLocationsHash = currentThreadCodeLocationsHistory[cycleInfo.executionsBeforeCycle] + var cycleExecutionLocationsHash = switchPointsCodeLocationsHistory[cycleInfo.executionsBeforeCycle] for (i in cycleInfo.executionsBeforeCycle + 1 until cycleInfo.executionsBeforeCycle + cycleInfo.cyclePeriod) { - cycleExecutionLocationsHash = cycleExecutionLocationsHash xor currentThreadCodeLocationsHistory[i] + cycleExecutionLocationsHash = cycleExecutionLocationsHash xor switchPointsCodeLocationsHistory[i] } val cycleStateLastNode = currentInterleavingHistory.last().asNodeCorrespondingToCycle( @@ -335,11 +437,60 @@ internal class LoopDetector( interleavingsLeadToSpinLockSet.addBranch(currentInterleavingHistory) } + /** + * Tries to find a spin cycle in [currentThreadCodeLocationsHistory] taking parameters + * and values into account, or, if it's not possible, without it, using just switch points code locations. + * + * @return a pair of a [CycleInfo], corresponding to the spin cycle in a code locations history + * **without** parameters and values, (i.e. considering only code locations) and a list of visited code + * locations **without** parameters and values, only potential switch points. + */ + private fun tryFindCycleWithParamsOrWithout(): Pair> { + // Get the code locations history of potential switch points, without parameters and values. + // Potential switch point code locations are >= 0. + val historyWithoutParams = currentThreadCodeLocationsHistory.filter { it >= 0 } + // Trying to find a cycle with them. + val cycleInfo = findMaxPrefixLengthWithNoCycleOnSuffix(currentThreadCodeLocationsHistory) + // If it's not possible - searching for the cycle in the filtered history list - without params and values. + ?: return findMaxPrefixLengthWithNoCycleOnSuffix(historyWithoutParams) to historyWithoutParams + + // If we found a spin cycle in the code locations history with parameters and values - + // than we need to calculate how many executions, + // that are potential switch points, before and during the cycle happened. + // We need it because in normal mode LoopDetector only uses potential switch points code locations + // to detect spin locks, so we have to count code locations, that don't represent parameters or values. + var operationsBefore = 0 + var cyclePeriod = 0 + + var operationsBeforeWithParams = 0 + var cyclePeriodWithParams = 0 + + var i = 0 + // Count how many potential switch point executions happened before the spin cycle. + while (operationsBeforeWithParams < cycleInfo.executionsBeforeCycle) { + // Potential switch point code locations are >= 0. + if (currentThreadCodeLocationsHistory[i] >= 0) { + operationsBefore++ + } + operationsBeforeWithParams++ + i++ + } + while (cyclePeriodWithParams < cycleInfo.cyclePeriod) { + // Potential switch point code locations are >= 0. + if (currentThreadCodeLocationsHistory[i] >= 0) { + cyclePeriod++ + } + cyclePeriodWithParams++ + i++ + } + + return CycleInfo(operationsBefore, cyclePeriod) to historyWithoutParams + } + /** * Is called before each interleaving part processing */ fun beforePart(nextThread: Int) { - clearRanThreads() if (!firstThreadSet) { setFirstThread(nextThread) } else if (lastExecutedThread != nextThread) { @@ -352,13 +503,6 @@ internal class LoopDetector( */ fun initialize() { lastExecutedThread = -1 - clearRanThreads() - } - - private fun clearRanThreads() { - for (i in 0 until nThreads) { - threadsRan[i] = false - } } private fun setFirstThread(iThread: Int) { @@ -373,12 +517,34 @@ internal class LoopDetector( replayModeLoopDetectorHelper?.initialize() } + /** + * Prints the interleaving sequences tree set. + * Utility function to debug spin-locks related internals. + */ + @Suppress("unused") + internal fun treeToString() = interleavingsLeadToSpinLockSet.treeToString() + + /** + * Maps any parameter, receiver or read value to a **negative** integer value. + * We map in the **negative** number as we want to be able to filter a code locations history list + * and drop all elements that don't represent potential switch points - all parameter, receiver + * and value representations produced by this method. + */ + private fun paramToIntRepresentation(value: Any?): Int { + var hashCode = primitiveHashCodeOrSystemHashCode(value) + if (hashCode < 0) return hashCode + hashCode++ + + return if (hashCode > 0) -hashCode else hashCode + } + } internal fun LoopDetector.Decision.isLivelockDetected() = when (this) { is LoopDetector.Decision.LivelockThreadSwitch, is LoopDetector.Decision.LivelockReplayRequired, is LoopDetector.Decision.LivelockFailureDetected -> true + else -> false } @@ -420,6 +586,14 @@ private class ReplayModeLoopDetectorHelper( */ private val threadsRan = hashSetOf() + private val currentHistoryNode: InterleavingHistoryNode get() = interleavingHistory[currentInterleavingNodeIndex] + + /** + * Returns if we ran in the spin cycle and now are performing executions inside it. + */ + val currentlyInSpinCycle: Boolean + get() = currentHistoryNode.cycleOccurred && currentHistoryNode.executions <= executionsPerformedInCurrentThread + fun initialize() { currentInterleavingNodeIndex = 0 executionsPerformedInCurrentThread = 0 @@ -436,10 +610,10 @@ private class ReplayModeLoopDetectorHelper( /** * Called before next thread switch */ - fun onNextSwitch(threadRunningFirstTime: Boolean) { + fun onNextSwitch() { currentInterleavingNodeIndex++ // See threadsRan field description to understand the following initialization logic - executionsPerformedInCurrentThread = if (threadRunningFirstTime) 0 else 1 + executionsPerformedInCurrentThread = 0 } /** diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt index 2def36af1e..2d44b85b4d 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt @@ -77,7 +77,7 @@ abstract class ManagedStrategy( protected val currentActorId = IntArray(nThreads) // Detector of loops or hangs (i.e. active locks). - internal val loopDetector: LoopDetector = LoopDetector(nThreads, testCfg.hangingDetectionThreshold) + internal val loopDetector: LoopDetector = LoopDetector(testCfg.hangingDetectionThreshold) // Tracker of acquisitions and releases of monitors. private lateinit var monitorTracker: MonitorTracker @@ -379,12 +379,9 @@ abstract class ManagedStrategy( if (decision.isLivelockDetected()) { failIfObstructionFreedomIsRequired { if (decision is LoopDetector.Decision.LivelockFailureDetected) { - if (decision.cyclePeriod != 0) { - traceCollector?.newActiveLockDetected(iThread, decision.cyclePeriod) - } // if failure is detected, add a special obstruction-freedom violation // trace point to account for that - traceCollector?.passObstructionFreedomViolationTracePoint(currentThread) + traceCollector?.passObstructionFreedomViolationTracePoint(currentThread, tracePoint is MethodCallTracePoint) } else { // otherwise log the last event that caused obstruction-freedom violation traceCollector?.passCodeLocation(tracePoint) @@ -394,21 +391,19 @@ abstract class ManagedStrategy( } // if live-lock failure was detected, then fail immediately if (decision is LoopDetector.Decision.LivelockFailureDetected) { - traceCollector?.newActiveLockDetected(iThread, decision.cyclePeriod) - traceCollector?.newSwitch(currentThread, SwitchReason.ACTIVE_LOCK) + traceCollector?.newSwitch(currentThread, SwitchReason.ACTIVE_LOCK, tracePoint is MethodCallTracePoint) failDueToDeadlock() } // if live-lock was detected, and replay was requested, // then abort current execution and start the replay - if (decision is LoopDetector.Decision.LivelockReplayRequired) { + if (decision is LoopDetector.Decision.LivelockReplayRequired || decision is LoopDetector.Decision.LivelockReplayToDetectCycleRequired) { suddenInvocationResult = SpinCycleFoundAndReplayRequired throw ForcibleExecutionFinishError } // if the current thread in a live-lock, then try to switch to another thread if (decision is LoopDetector.Decision.LivelockThreadSwitch) { - traceCollector?.newActiveLockDetected(iThread, decision.cyclePeriod) - switchCurrentThread(iThread, SwitchReason.ACTIVE_LOCK) - if (!loopDetector.replayModeEnabled) { + val switchHappened = switchCurrentThread(iThread, SwitchReason.ACTIVE_LOCK, tracePoint = tracePoint) + if (switchHappened) { loopDetector.initializeFirstCodeLocationAfterSwitch(codeLocation) } traceCollector?.passCodeLocation(tracePoint) @@ -416,8 +411,8 @@ abstract class ManagedStrategy( } // if strategy requested thread switch, then do it if (shouldSwitch) { - switchCurrentThread(iThread, SwitchReason.STRATEGY_SWITCH) - if (!loopDetector.replayModeEnabled) { + val switchHappened = switchCurrentThread(iThread, SwitchReason.STRATEGY_SWITCH, tracePoint = tracePoint) + if (switchHappened) { loopDetector.initializeFirstCodeLocationAfterSwitch(codeLocation) } traceCollector?.passCodeLocation(tracePoint) @@ -445,6 +440,7 @@ abstract class ManagedStrategy( awaitTurn(iThread) finished[iThread] = true loopDetector.onThreadFinish(iThread) + traceCollector?.onThreadFinish() doSwitchCurrentThread(iThread, true) } @@ -504,10 +500,17 @@ abstract class ManagedStrategy( /** * A regular context thread switch to another thread. */ - private fun switchCurrentThread(iThread: Int, reason: SwitchReason = SwitchReason.STRATEGY_SWITCH, mustSwitch: Boolean = false) { - traceCollector?.newSwitch(iThread, reason) + private fun switchCurrentThread( + iThread: Int, + reason: SwitchReason = SwitchReason.STRATEGY_SWITCH, + mustSwitch: Boolean = false, + tracePoint: TracePoint? = null + ): Boolean { + traceCollector?.newSwitch(iThread, reason, tracePoint != null && tracePoint is MethodCallTracePoint) doSwitchCurrentThread(iThread, mustSwitch) + val switchHappened = iThread != currentThread awaitTurn(iThread) + return switchHappened } private fun doSwitchCurrentThread(iThread: Int, mustSwitch: Boolean = false) { @@ -718,6 +721,7 @@ abstract class ManagedStrategy( lastReadTracePoint[iThread] = tracePoint } newSwitchPoint(iThread, codeLocation, tracePoint) + loopDetector.passValue(obj) true } @@ -771,6 +775,8 @@ abstract class ManagedStrategy( lastReadTracePoint[iThread] = tracePoint } newSwitchPoint(iThread, codeLocation, tracePoint) + loopDetector.passValue(array) + loopDetector.passValue(index) true } @@ -782,6 +788,9 @@ abstract class ManagedStrategy( lastReadTracePoint[iThread] = null } } + runInIgnoredSection { + loopDetector.passValue(value) + } } override fun beforeWriteField(obj: Any, className: String, fieldName: String, value: Any?, codeLocation: Int): Boolean = runInIgnoredSection { @@ -805,6 +814,8 @@ abstract class ManagedStrategy( null } newSwitchPoint(iThread, codeLocation, tracePoint) + loopDetector.passValue(obj) + loopDetector.passValue(value) true } @@ -827,6 +838,7 @@ abstract class ManagedStrategy( null } newSwitchPoint(iThread, codeLocation, tracePoint) + loopDetector.passValue(value) } override fun beforeWriteArrayElement(array: Any, index: Int, value: Any?, codeLocation: Int): Boolean = runInIgnoredSection { @@ -850,6 +862,9 @@ abstract class ManagedStrategy( null } newSwitchPoint(iThread, codeLocation, tracePoint) + loopDetector.passValue(array) + loopDetector.passValue(index) + loopDetector.passValue(value) true } @@ -911,11 +926,15 @@ abstract class ManagedStrategy( return null } + /** + * @param methodId identifier of the method, generated by [MethodIds]. + */ override fun beforeMethodCall( owner: Any?, className: String, methodName: String, codeLocation: Int, + methodId: Int, params: Array ) { val guarantee = methodGuaranteeType(owner, className, methodName) @@ -928,7 +947,7 @@ abstract class ManagedStrategy( } else { params } - beforeMethodCall(owner, currentThread, codeLocation, className, methodName, params) + beforeMethodCall(owner, currentThread, codeLocation, 0, className, methodName, params) } } // It's important that this method can't be called inside runInIgnoredSection, as the ignored section @@ -940,9 +959,9 @@ abstract class ManagedStrategy( ManagedGuaranteeType.TREAT_AS_ATOMIC -> { runInIgnoredSection { if (collectTrace) { - beforeMethodCall(owner, currentThread, codeLocation, className, methodName, params) + beforeMethodCall(owner, currentThread, codeLocation, 0, className, methodName, params) } - newSwitchPointOnAtomicMethodCall(codeLocation) + newSwitchPointOnAtomicMethodCall(codeLocation, params) } // It's important that this method can't be called inside runInIgnoredSection, as the ignored section // flag would be set to false when leaving runInIgnoredSection, @@ -956,14 +975,18 @@ abstract class ManagedStrategy( LincheckJavaAgent.ensureClassHierarchyIsTransformed(className.canonicalClassName) } } + runInIgnoredSection { + loopDetector.beforeRegularMethodCall(codeLocation, params) + } if (collectTrace) { runInIgnoredSection { + traceCollector!!.checkActiveLockDetected() val params = if (isSuspendFunction(className, methodName, params)) { params.dropLast(1).toTypedArray() } else { params } - beforeMethodCall(owner, currentThread, codeLocation, className, methodName, params) + beforeMethodCall(owner, currentThread, codeLocation, methodId, className, methodName, params) } } } @@ -978,12 +1001,15 @@ abstract class ManagedStrategy( params: Array ) = runInIgnoredSection { if (collectTrace) { - beforeMethodCall(owner, currentThread, codeLocation, className, methodName, params) + beforeMethodCall(owner, currentThread, codeLocation, 0, className, methodName, params) } - newSwitchPointOnAtomicMethodCall(codeLocation) + newSwitchPointOnAtomicMethodCall(codeLocation, params) } override fun onMethodCallFinishedSuccessfully(result: Any?) { + runInIgnoredSection { + loopDetector.afterRegularMethodCall() + } if (collectTrace) { runInIgnoredSection { val iThread = currentThread @@ -1004,6 +1030,9 @@ abstract class ManagedStrategy( } override fun onMethodCallThrewException(t: Throwable) { + runInIgnoredSection { + loopDetector.afterRegularMethodCall() + } if (collectTrace) { runInIgnoredSection { // We cannot simply read `thread` as Forcible???Exception can be thrown. @@ -1020,9 +1049,10 @@ abstract class ManagedStrategy( leaveIgnoredSectionIfEntered() } - private fun newSwitchPointOnAtomicMethodCall(codeLocation: Int) { + private fun newSwitchPointOnAtomicMethodCall(codeLocation: Int, params: Array) { // re-use last call trace point newSwitchPoint(currentThread, codeLocation, callStackTrace[currentThread].lastOrNull()?.call) + loopDetector.passParameters(params) } private fun isSuspendFunction(className: String, methodName: String, params: Array) = @@ -1106,13 +1136,14 @@ abstract class ManagedStrategy( owner: Any?, iThread: Int, codeLocation: Int, + identifier: Int, className: String, methodName: String, params: Array, ) { val callStackTrace = callStackTrace[iThread] val suspendedMethodStack = suspendedFunctionsStack[iThread] - val methodId = if (suspendedMethodStack.isNotEmpty()) { + val suspensionIdentifier = if (suspendedMethodStack.isNotEmpty()) { // If there was a suspension before, then instead of creating a new identifier, // use the one that the suspended call had val lastId = suspendedMethodStack.last() @@ -1124,7 +1155,12 @@ abstract class ManagedStrategy( // Code location of the new method call is currently the last one val tracePoint = createBeforeMethodCallTracePoint(owner, iThread, className, methodName, params, codeLocation) methodCallTracePointStack[iThread] += tracePoint - callStackTrace.add(CallStackTraceElement(tracePoint, methodId)) + // Method id used to calculate spin cycle start label call depth. + // Two calls are considered equals if two same methods were called with the same parameters. + val methodId = Objects.hash(identifier, + params.map { primitiveHashCodeOrSystemHashCode(it) }.toTypedArray().contentHashCode() + ) + callStackTrace.add(CallStackTraceElement(tracePoint, suspensionIdentifier, methodId)) if (owner == null) { beforeStaticMethodCall() } else { @@ -1210,7 +1246,7 @@ abstract class ManagedStrategy( ): MethodCallTracePoint { when (val atomicReferenceInfo = AtomicReferenceNames.getMethodCallType(runner.testInstance, receiver, params)) { is AtomicArrayMethod -> { - tracePoint.initializeOwnerName("${adornedStringRepresentation(atomicReferenceInfo.atomicArray)}[${atomicReferenceInfo.atomicArray}]") + tracePoint.initializeOwnerName("${adornedStringRepresentation(atomicReferenceInfo.atomicArray)}[${atomicReferenceInfo.index}]") tracePoint.initializeParameters(params.drop(1).map { adornedStringRepresentation(it) }) } is InstanceFieldAtomicArrayMethod -> { @@ -1326,15 +1362,17 @@ abstract class ManagedStrategy( /* Methods to control the current call context. */ private fun beforeStaticMethodCall() { - callStackContextPerThread[currentThread].add(CallContext.StaticCallContext) + val element = CallContext.StaticCallContext + callStackContextPerThread[currentThread].add(element) } private fun beforeInstanceMethodCall(receiver: Any) { - callStackContextPerThread[currentThread].add(CallContext.InstanceCallContext(receiver)) + val element = CallContext.InstanceCallContext(receiver) + callStackContextPerThread[currentThread].add(element) } - private fun afterExitMethod() { - val currentContext = callStackContextPerThread[currentThread] + private fun afterExitMethod(iThread: Int) { + val currentContext = callStackContextPerThread[iThread] currentContext.removeLast() check(currentContext.isNotEmpty()) { "Context cannot be empty" } } @@ -1346,11 +1384,11 @@ abstract class ManagedStrategy( * @param tracePoint the corresponding trace point for the invocation */ private fun afterMethodCall(iThread: Int, tracePoint: MethodCallTracePoint) { - afterExitMethod() + afterExitMethod(iThread) val callStackTrace = callStackTrace[iThread] if (tracePoint.wasSuspended) { // if a method call is suspended, save its identifier to reuse for continuation resuming - suspendedFunctionsStack[iThread].add(callStackTrace.last().identifier) + suspendedFunctionsStack[iThread].add(callStackTrace.last().suspensionIdentifier) } callStackTrace.removeAt(callStackTrace.lastIndex) } @@ -1425,32 +1463,300 @@ abstract class ManagedStrategy( private inner class TraceCollector { private val _trace = mutableListOf() val trace: List = _trace + private var spinCycleStartAdded = false - fun newSwitch(iThread: Int, reason: SwitchReason) { + private val spinCycleMethodCallsStackTraces: MutableList> = mutableListOf() + + fun newSwitch(iThread: Int, reason: SwitchReason, beforeMethodCallSwitch: Boolean = false) { + if (reason == SwitchReason.ACTIVE_LOCK) { + afterSpinCycleTraceCollected(iThread, beforeMethodCallSwitch) + } _trace += SwitchEventTracePoint( iThread = iThread, actorId = currentActorId[iThread], reason = reason, callStackTrace = callStackTrace[iThread] ) + spinCycleStartAdded = false } - fun newActiveLockDetected(iThread: Int, cyclePeriod: Int) { - val spinCycleStartPosition = _trace.size - cyclePeriod - val spinCycleStartStackTrace = if (spinCycleStartPosition <= _trace.lastIndex) { - _trace[spinCycleStartPosition].callStackTrace + /** + * Calculates the [SpinCycleStartTracePoint] trace point callStackTrace correctly after all trace points + * related to the spin cycle are collected. + * + * # The problem overview + * + * [TraceCollector] collects two types of the [TracePoint]: + * 1. TracePoints that represents places where potential context switch points could happened. + * 2. TracePoints, representing regular, non-atomic method calls. + * + * This division is important for us, as [LoopDetector] stores execution points sequences of the + * first type, causing the challenges, described below. + * + * When [SpinCycleStartTracePoint] is added initially to the [trace], its callStackTrace, constructed via + * `callStackTrace[currentThread]` may be inaccurate. Consider the following example: + * We have the following code: + * ```kotlin + * @Operation + * fun actor() { + * atomicInteger.set(false) // code location = 1 + * spinLock() // method call code location = 2 + * } + * + * fun spinLock() { + * while (true) { + * val value = getValue() // method call code location = 3 + * atomicInteger.compareAndSet(value, !value) // code location = 5 + * } + * } + * + * fun getValue() = atomicInteger.get() // code location = 4 + * ``` + * When we ran into a cycle, we'll have the following code locations history: + * ``` + * [1, 2, 3, 4, 5, 3, 4, 5, 3, 4 ,5 ..., 3, 4, 5] + * ``` + * Cycle period here is 3: `[3, 4, 5]` and only two `[1, 2]` executions happened before the cycle. + * Then, suppose we have a scenario with two actors. + * The interleaving we have: + * ``` + * [ + * 1. Execute 2 instructions (switch positions) in thread 1, + * 2. Execute 1 instruction (switch positions) in the thread 1, + * 3. Execute 3 instructions (switch positions) in the thread 1 -> execution hung. + * ] + * ``` + * It's worth noting that the LoopDetector operates with a switch positions when replaying the + * execution to collect trace. + * + * Due to the internals of the [LoopDetector], [LoopDetector.replayModeCurrentlyInSpinCycle] will only + * return `true` at the beginning of step 3 described above, as execution only contains a sequence of + * executed potential switch points, while method call isn't a potential switch point. + * But at the beginning of step 3 we'll be inside the `getValue` method, so when [LoopDetector.replayModeCurrentlyInSpinCycle] + * is triggered, it's not correct using `callStackTrace[currentThread]`, as it would contain `getValue`. + * + * Summarizing, we may receive the signal about spin cycle start some non-atomic method calls after the actual spin cycle start + * if we switch right before the first spin cycle potential switch point execution, + * and this happens as [LoopDetector] stores sequences of the only potential switch points executions and + * all enters to the non-atomic methods are considered as a part of a step 1, which doesn't correspond to a spin cycle. + * + * Hence, **we can only use trace points of the first type defined above (which are potential switch points) + * to calculate the correct spin cycle start label call depth**. + * + * The problem has two solutions, depending on the spin cycle type: + * * recursive + * * iterative. + * + * # Iterative spin lock + * Let's consider the solution for the iterative spin-cycle. + * + * When we report an iterative spin cycle, we **must** visit the first spin cycle execution point + * and the first execution point of the second iteration (with an exit before). + * Let's track the current stacktrace before each switch point, before and after each method calls. + * Consider the example of a spin cycle, representing call stack traces of the execution points: + * ``` + * A -> B -> C + * A -> B -> C -> K + * A -> B -> E -> D + * A -> B -> E + * A -> B -> X + * ``` + * As we can see, even if all the execution points are wrapped in the non-atomic method calls (which we track + * during the trace collection too), the longest common prefix of the method calls `A, B` in the stack traces + * is the right position to place spin cycle start label. + * Indeed, due to the iterative nature of the spin lock, the first spin cycle execution of the first iteration + * and of the second iteration are placed in the same depth of calls. So it's guaranteed that we'll visit + * the most nested call and the least nested call. The wanted recursive method (and its call depth) can be found + * using the longest prefix of all the points is the spin cycle, as it is going to be the same along all the + * trace points and method calls. + * + * So that's what we do in case of an iterative spin lock. + * Track all the trace points of the spin cycle, + * calculate the max common method call prefix of these trace points, and place the spin cycle start label + * onto the found call depth. + * + * # Recursive spin lock + * + * Unlike iterative spin lock, in case of a recursive spin lock, longest common prefix may include + * method calls, that are a part of a spin cycle. Consider the following example: + * ```kotlin + * fun actor() { + * a() + * } + * + * fun a() = b() + * + * fun b() { + * c() + * c() + * a() // recursive call + * } + * + * fun c() = value.get() + * ``` + * According to the problem described above, if we switch to another thread and back right before point X, + * the trace points of the spin cycle will have the following stack traces: + * ``` + * [ + * actor -> a -> b -> c + * actor -> a -> b -> c + * ] + * ``` + * And the first execution point of the second iteration will be + * ``` + * actor -> a -> b -> a -> b -> c + * ``` + * + * As mentioned, it's incorrect to say that spin cycle starts at the depth `actor -> a -> c`, as the first + * recursive call is method `b` call, so we can't use the approach for the iterative spin cycle type. + * + * Let's consider the last potential switch trace point before the first spin cycle trace point, + * the first iteration first potential switch trace point + * and the second iteration first potential switch trace point: + * + * 1. `actor -> a -> b -> c` + * 2. `actor -> a -> b -> c -> a -> b -> c` + * + * Due to the recursive nature of a spin cycle, the second and the third trace points must have common + * method call suffix. + * It's important how to compare method calls in the suffixes. We consider method calls identical if + * they have the same ids produced by [MethodIds] and the same parameters. That means we don't care + * here where this method is called - it's more convenient to see as short version of the spin cycle as possible. + * + * But this approach may produce a wrong result in case when we switch from the spin lock and then occur + * in the same spin lock again. + * Let's consider this situation using the example above. + * Trace points of the second spin lock would have the following call stacks: + * + * 1. `actor -> [a -> b -> c] -> [a -> b -> c]` + * 2. `actor -> [a -> b -> c] -> [a -> b -> c]` + * + * And the first execution point of the second iteration will be + * ``` + * actor -> [a -> b -> c] -> [a -> b -> c] -> [a -> b -> c] + * ``` + * + * If we apply this algorithm to the first point from the first iteration and the first point from the second iteration + * we get `[a -> b -> c] -> [a -> b -> c]` longest common suffix, which is not correct, because the first recursive call + * of this cycle starts at the depth `actor -> a -> b`. + * In this case, the last trace point before the spin cycle becomes handy. + * In the example above it would have the following call stack `actor -> a -> b -> c`. + * + * Here are the points we're interested in: + * 1. `actor -> [a -> b -> c]` - The last trace point before the cycle. + * 2. `actor -> [a -> b -> c] -> [a -> b -> c]` - The first trace point of the cycle. + * 3. ` actor -> [a -> b -> c] -> [a -> b -> c] -> [a -> b -> c]` - The first trace point of the second iteration. + * Let's note the following rule: if point 1 has the same method call on the same depth as point 2, + * then this call can't be a part of the current spin cycle. + * The reason is quite easy: if it was, we would mark point 1 as a first spin cycle trace point. + * + * So, to detect the spin cycle, start trace point depth in case of a recursive spin lock we: + * 1. Take the first trace point of the spin cycle on the first iteration (point A) and on the second iteration (point B). + * 2. Take the last trace point before the spin cycle (point C). + * 3. We walk synchronously on their call stacks from the end to the beginning, while call from the A stack + * is the same as a call from the stack B. But it also equals the call from the call C, then we need to stop. + * The count of the call passed the condition above equals to the call we need to lift from the first spin cycle + * node to get the correct spin cycle start trace point depth. + * + * @param beforeMethodCallSwitch flag if this method invoked right after [MethodCallTracePoint] is added to a trace, + * before the corresponding method is called. + */ + private fun afterSpinCycleTraceCollected( + iThread: Int, + beforeMethodCallSwitch: Boolean + ) { + // Obtaining spin cycle trace points. + val spinLockTracePoints = trace.takeLastWhile { it.iThread == iThread && it !is SpinCycleStartTracePoint } + // Nothing to do in this case (seems unreal). + if (spinLockTracePoints.isEmpty()) return + // Get the call stack of first trace point of the second spin cycle iteration + var currentCallStackTrace: List = callStackTrace[iThread] + // If this method is invoked after beforeMethodCall or beforeAtomicMethodCall + // than MethodCallTracePoint is already added, correct it by altering current stack trace + if (beforeMethodCallSwitch) { + currentCallStackTrace = currentCallStackTrace.dropLast(1) + } + + val cycleStartTracePointIndex = _trace.size - spinLockTracePoints.size - 1 + if (cycleStartTracePointIndex < 0 || _trace[cycleStartTracePointIndex] !is SpinCycleStartTracePoint) return + + val spinCycleFirstTracePointCallStackTrace = spinLockTracePoints.first().callStackTrace + val isRecursive = currentCallStackTrace.size != spinCycleFirstTracePointCallStackTrace.size + val spinCycleStartStackTrace = if (isRecursive) { + // See above the description of the algorithm for recursive spin lock. + val prevIndex = _trace.lastIndex - spinLockTracePoints.size - 1 + val tracePointBeforeCycle = if (prevIndex >= 0) (prevIndex downTo 0) + .firstOrNull { _trace[it] !is SwitchEventTracePoint && _trace[it].iThread == iThread }?.let { _trace[it] } + else null + + var currentI = currentCallStackTrace.lastIndex + var firstI = spinCycleFirstTracePointCallStackTrace.lastIndex + var count = 0 + while (firstI >= 0) { + val identifier = spinCycleFirstTracePointCallStackTrace[firstI].methodId + // Comparing corresponding calls. + if (identifier != currentCallStackTrace[currentI].methodId) break + // Check for the last trace point before the cycle. + if ((tracePointBeforeCycle != null) && + (tracePointBeforeCycle.callStackTrace.lastIndex >= firstI) && + (tracePointBeforeCycle.callStackTrace[firstI].methodId == identifier) + ) break + + currentI-- + firstI-- + count++ + } + spinCycleFirstTracePointCallStackTrace.dropLast(count) } else { - emptyList() + // See above the description of the algorithm for iterative spin lock. + getCommonMinStackTrace(spinLockTracePoints) + .dropLast(currentCallStackTrace.size - spinCycleFirstTracePointCallStackTrace.size) + } + _trace[cycleStartTracePointIndex] = + SpinCycleStartTracePoint(iThread, currentActorId[iThread], spinCycleStartStackTrace) + } + + /** + * @return Max common prefix of the [StackTraceElement] of the provided [spinCycleTracePoints] + */ + private fun getCommonMinStackTrace(spinCycleTracePoints: List): List { + val callStackTraces = spinCycleTracePoints.map { it.callStackTrace } + spinCycleMethodCallsStackTraces + var count = 0 + outer@while (true) { + if (count == callStackTraces[0].size) break + val stackTraceElement = callStackTraces[0][count].call.stackTraceElement + for (i in 1 until callStackTraces.size) { + val traceElements = callStackTraces[i] + if (count == traceElements.size) break@outer + if (stackTraceElement != traceElements[count].call.stackTraceElement) break@outer + } + count++ + } + return spinCycleTracePoints.first().callStackTrace.take(count) + } + + fun onThreadFinish() { + spinCycleStartAdded = false + } + + fun checkActiveLockDetected() { + if (!loopDetector.replayModeCurrentlyInSpinCycle) return + if (spinCycleStartAdded) { + spinCycleMethodCallsStackTraces += callStackTrace[currentThread].toList() + return } val spinCycleStartTracePoint = SpinCycleStartTracePoint( - iThread = iThread, - actorId = currentActorId[iThread], - callStackTrace = spinCycleStartStackTrace + iThread = currentThread, + actorId = currentActorId[currentThread], + callStackTrace = callStackTrace[currentThread] ) - _trace.add(spinCycleStartPosition, spinCycleStartTracePoint) + _trace.add(spinCycleStartTracePoint) + spinCycleStartAdded = true + spinCycleMethodCallsStackTraces.clear() } fun passCodeLocation(tracePoint: TracePoint?) { + if (tracePoint !is SectionDelimiterTracePoint) checkActiveLockDetected() // tracePoint can be null here if trace is not available, e.g. in case of suspension if (tracePoint != null) { _trace += tracePoint @@ -1471,7 +1777,8 @@ abstract class ManagedStrategy( } - fun passObstructionFreedomViolationTracePoint(iThread: Int) { + fun passObstructionFreedomViolationTracePoint(iThread: Int, beforeMethodCall: Boolean) { + afterSpinCycleTraceCollected(iThread, beforeMethodCall) _trace += ObstructionFreedomViolationExecutionAbortTracePoint( iThread = iThread, actorId = currentActorId[iThread], diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/TracePoint.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/TracePoint.kt index 7b45fbe6b9..6e5bb9a57c 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/TracePoint.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/TracePoint.kt @@ -12,9 +12,6 @@ package org.jetbrains.kotlinx.lincheck.strategy.managed import org.jetbrains.kotlinx.lincheck.* import org.jetbrains.kotlinx.lincheck.CancellationResult.* import org.jetbrains.kotlinx.lincheck.runner.ExecutionPart -import java.math.* -import kotlin.coroutines.* -import kotlin.coroutines.intrinsics.* data class Trace(val trace: List) @@ -60,7 +57,7 @@ internal class SwitchEventTracePoint( internal abstract class CodeLocationTracePoint( iThread: Int, actorId: Int, callStackTrace: CallStackTrace, - protected val stackTraceElement: StackTraceElement + val stackTraceElement: StackTraceElement ) : TracePoint(iThread, actorId, callStackTrace) { protected abstract fun toStringCompact(): String @@ -315,6 +312,7 @@ internal enum class SwitchReason(private val reason: String) { * Method call info. * * All methods calls are enumerated to make it possible to distinguish different calls of the same method. - * Suspended method calls have the same [identifier] before and after suspension, but different [call] points. + * Suspended method calls have the same [suspensionIdentifier] before and after suspension, but different [call] points. + * @param methodId Method identifier. See [org.jetbrains.kotlinx.lincheck.transformation.MethodIds]. */ -internal class CallStackTraceElement(val call: MethodCallTracePoint, val identifier: Int) +internal class CallStackTraceElement(val call: MethodCallTracePoint, val suspensionIdentifier: Int, val methodId: Int) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/TraceReporter.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/TraceReporter.kt index 3bd3578b04..e8d814953d 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/TraceReporter.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/TraceReporter.kt @@ -17,7 +17,6 @@ import org.jetbrains.kotlinx.lincheck.strategy.ManagedDeadlockFailure import org.jetbrains.kotlinx.lincheck.strategy.ObstructionFreedomViolationFailure import org.jetbrains.kotlinx.lincheck.strategy.TimeoutFailure import org.jetbrains.kotlinx.lincheck.strategy.ValidationFailure -import java.util.* import kotlin.math.* @Synchronized // we should avoid concurrent executions to keep `objectNumeration` consistent @@ -128,8 +127,10 @@ internal fun constructTraceGraph( exceptionStackTraces: Map ): List { val resultProvider = ExecutionResultsProvider(results, failure) - val scenario = failure.scenario val tracePoints = trace.trace + val prefixFactory = TraceNodePrefixFactory(failure.scenario.nThreads) + val scenario = failure.scenario + // last events that were executed for each thread. It is either thread finish events or events before crash val lastExecutedEvents = IntArray(scenario.nThreads) { iThread -> tracePoints.mapIndexed { i, e -> Pair(i, e) }.lastOrNull { it.second.iThread == iThread }?.first ?: -1 @@ -148,6 +149,7 @@ internal fun constructTraceGraph( val traceGraphNodesSections = arrayListOf>() var traceGraphNodes = arrayListOf() + for (eventId in tracePoints.indices) { val event = tracePoints[eventId] if (event is SectionDelimiterTracePoint) { @@ -169,6 +171,7 @@ internal fun constructTraceGraph( // create new actor node actor val actorNode = traceGraphNodes.createAndAppend { lastNode -> ActorNode( + prefixProvider = prefixFactory.actorNodePrefix(iThread), iThread = iThread, last = lastNode, callDepth = 0, @@ -181,13 +184,14 @@ internal fun constructTraceGraph( // add the event var innerNode: TraceInnerNode = actorNodes[iThread][actorId]!! for (call in event.callStackTrace) { - val callId = call.identifier + val callId = call.suspensionIdentifier // Switch events that happen as a first event of the method are lifted out of the method in the trace if (!callNodes.containsKey(callId) && event is SwitchEventTracePoint) break val callNode = callNodes.computeIfAbsent(callId) { // create a new call node if needed val result = traceGraphNodes.createAndAppend { lastNode -> - CallNode(iThread, lastNode, innerNode.callDepth + 1, call.call) + val callDepth = innerNode.callDepth + 1 + CallNode(prefixFactory.prefixForCallNode(iThread, callDepth), iThread, lastNode, callDepth, call.call) } // make it a child of the previous node innerNode.addInternalEvent(result) @@ -197,7 +201,8 @@ internal fun constructTraceGraph( } val isLastExecutedEvent = eventId == lastExecutedEvents[iThread] val node = traceGraphNodes.createAndAppend { lastNode -> - TraceLeafEvent(iThread, lastNode, innerNode.callDepth + 1, event, isLastExecutedEvent) + val callDepth = innerNode.callDepth + 1 + TraceLeafEvent(prefixFactory.prefix(event, callDepth), iThread, lastNode, callDepth, event, isLastExecutedEvent) } innerNode.addInternalEvent(node) } @@ -211,6 +216,7 @@ internal fun constructTraceGraph( if (actorNode == null && actorResult != null) { val lastNode = actorNodes[iThread].getOrNull(actorId - 1)?.lastInternalEvent actorNode = ActorNode( + prefixProvider = prefixFactory.actorNodePrefix(iThread), iThread = iThread, last = lastNode, callDepth = 0, @@ -227,10 +233,12 @@ internal fun constructTraceGraph( val lastEventNext = lastEvent.next val result = resultProvider[iThread, actorId] val resultRepresentation = result?.let { resultRepresentation(result, exceptionStackTraces) } + val callDepth = actorNode.callDepth + 1 val resultNode = ActorResultNode( + prefixProvider = prefixFactory.actorResultPrefix(iThread, callDepth), iThread = iThread, last = lastEvent, - callDepth = actorNode.callDepth + 1, + callDepth = callDepth, resultRepresentation = resultRepresentation, exceptionNumberIfExceptionResult = if (result is ExceptionResult) exceptionStackTraces[result.throwable]?.number else null ) @@ -338,10 +346,12 @@ private fun traceGraphToRepresentationList( } internal sealed class TraceNode( + private val prefixProvider: PrefixProvider, val iThread: Int, last: TraceNode?, val callDepth: Int // for tree indentation ) { + protected val prefix get() = prefixProvider.get() // `next` edges form an ordered single-directed event list var next: TraceNode? = null @@ -367,15 +377,19 @@ internal sealed class TraceNode( traceRepresentation: MutableList, verboseTrace: Boolean ): TraceNode? + + protected fun stateEventRepresentation(iThread: Int, stateRepresentation: String) = + TraceEventRepresentation(iThread, prefix + "STATE: $stateRepresentation") } internal class TraceLeafEvent( + prefix: PrefixProvider, iThread: Int, last: TraceNode?, callDepth: Int, internal val event: TracePoint, private val lastExecutedEvent: Boolean = false -) : TraceNode(iThread, last, callDepth) { +) : TraceNode(prefix, iThread, last, callDepth) { override val lastState: String? = if (event is StateRepresentationTracePoint) event.stateRepresentation else null @@ -398,14 +412,14 @@ internal class TraceLeafEvent( traceRepresentation: MutableList, verboseTrace: Boolean ): TraceNode? { - val representation = traceIndentation() + event.toString() + val representation = prefix + event.toString() traceRepresentation.add(TraceEventRepresentation(iThread, representation)) return next } } -internal abstract class TraceInnerNode(iThread: Int, last: TraceNode?, callDepth: Int) : - TraceNode(iThread, last, callDepth) { +internal abstract class TraceInnerNode(prefixProvider: PrefixProvider, iThread: Int, last: TraceNode?, callDepth: Int) : + TraceNode(prefixProvider, iThread, last, callDepth) { override val lastState: String? get() = internalEvents.map { it.lastState }.lastOrNull { it != null } override val lastInternalEvent: TraceNode @@ -424,11 +438,12 @@ internal abstract class TraceInnerNode(iThread: Int, last: TraceNode?, callDepth } internal class CallNode( + prefixProvider: PrefixProvider, iThread: Int, last: TraceNode?, callDepth: Int, internal val call: MethodCallTracePoint -) : TraceInnerNode(iThread, last, callDepth) { +) : TraceInnerNode(prefixProvider, iThread, last, callDepth) { // suspended method contents should be reported override fun shouldBeExpanded(verboseTrace: Boolean): Boolean { return call.wasSuspended || super.shouldBeExpanded(verboseTrace) @@ -439,27 +454,28 @@ internal class CallNode( verboseTrace: Boolean ): TraceNode? = if (!shouldBeExpanded(verboseTrace)) { - traceRepresentation.add(TraceEventRepresentation(iThread, traceIndentation() + "$call")) + traceRepresentation.add(TraceEventRepresentation(iThread, prefix + "$call")) lastState?.let { traceRepresentation.add(stateEventRepresentation(iThread, it)) } lastInternalEvent.next } else { - traceRepresentation.add(TraceEventRepresentation(iThread, traceIndentation() + "$call")) + traceRepresentation.add(TraceEventRepresentation(iThread, prefix + "$call")) next } } internal class ActorNode( + prefixProvider: PrefixProvider, iThread: Int, last: TraceNode?, callDepth: Int, internal val actorRepresentation: String, private val resultRepresentation: String? -) : TraceInnerNode(iThread, last, callDepth) { +) : TraceInnerNode(prefixProvider, iThread, last, callDepth) { override fun addRepresentationTo( traceRepresentation: MutableList, verboseTrace: Boolean ): TraceNode? { - val actorRepresentation = actorRepresentation + if (resultRepresentation != null) ": $resultRepresentation" else "" + val actorRepresentation = prefix + actorRepresentation + if (resultRepresentation != null) ": $resultRepresentation" else "" traceRepresentation.add(TraceEventRepresentation(iThread, actorRepresentation)) return if (!shouldBeExpanded(verboseTrace)) { lastState?.let { traceRepresentation.add(stateEventRepresentation(iThread, it)) } @@ -471,6 +487,7 @@ internal class ActorNode( } internal class ActorResultNode( + prefixProvider: PrefixProvider, iThread: Int, last: TraceNode?, callDepth: Int, @@ -479,7 +496,7 @@ internal class ActorResultNode( * This value presents only if an exception was the actor result. */ internal val exceptionNumberIfExceptionResult: Int? -) : TraceNode(iThread, last, callDepth) { +) : TraceNode(prefixProvider, iThread, last, callDepth) { override val lastState: String? = null override val lastInternalEvent: TraceNode = this override fun shouldBeExpanded(verboseTrace: Boolean): Boolean = false @@ -489,17 +506,137 @@ internal class ActorResultNode( verboseTrace: Boolean ): TraceNode? { if (resultRepresentation != null) - traceRepresentation.add(TraceEventRepresentation(iThread, traceIndentation() + "result: $resultRepresentation")) + traceRepresentation.add(TraceEventRepresentation(iThread, prefix + "result: $resultRepresentation")) return next } } -private const val TRACE_INDENTATION = " " +/** + * Provides the prefix output for the [TraceNode]. + * @see TraceNodePrefixFactory + */ +internal fun interface PrefixProvider { + fun get(): String +} -private fun TraceNode.traceIndentation() = TRACE_INDENTATION.repeat(callDepth) +/** + * When we create the trace representation, it may need to add two additional spaces before each line is we have a + * spin cycle starting in call depth 1. + * + * This factory encapsulates the logic of creating [PrefixProvider] for different call depths with + * spin cycle arrows or without. + * + * At the beginning of the trace processing, we can't know definitely should we add + * extra spaces at the beginning of the each line or not. + * That's why we return [PrefixProvider] closure that have a reference on this factory field, + * so when trace nodes are composing output, we definitely know should we add extra spaces or not. + * + * Example when extra spaces needed: + * | one(): | | + * | meaninglessActions2() at RecursiveTwoThreadsSpinLockTest.one(RecursiveSpinLockTest.kt:221) | | + * | /* The following events repeat infinitely: */ | | + * | ┌╶> meaninglessActions1() at RecursiveTwoThreadsSpinLockTest.one(RecursiveSpinLockTest.kt:222) | | + * | | sharedState2.compareAndSet(false,true): false at RecursiveTwoThreadsSpinLockTest.meaninglessActions1(RecursiveSpinLockTest.kt:242) | | + * | └╶╶╶╶ switch (reason: active lock detected) | | + * | + * + * Example when no extra spaces needed: + * | cas2_0(0, 0, 2, 1, 0, 3): | | + * | array.cas2(0,0,2,1,0,3,0) at BrokenCas2RecursiveLiveLockTest.cas2_0(RecursiveSpinLockTest.kt:271) | | + * | AtomicArrayWithCAS2$Descriptor.apply$default(Descriptor#3,false,0,false,5,null) at AtomicArrayWithCAS2.cas2(RecursiveSpinLockTest.kt:323) | | + * | Descriptor#3.apply(true,0,false) at AtomicArrayWithCAS2$Descriptor.apply$default(RecursiveSpinLockTest.kt:349) | | + * | AtomicArrayWithCAS2$Descriptor.installOrHelp$default(Descriptor#3,true,0,false,4,null) at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:356) | | + * | Descriptor#3.installOrHelp(true,0,false) at AtomicArrayWithCAS2$Descriptor.installOrHelp$default(RecursiveSpinLockTest.kt:368) | | + * | BrokenCas2RecursiveLiveLockTest#1.array.gate0.READ: 1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:390) | | + * | /* The following events repeat infinitely: */ | | + * | ┌╶> Descriptor#2.apply(false,0,true) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:391) | | + * | | status.READ: SUCCESS at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:351) | | + * | | status.compareAndSet(SUCCESS,SUCCESS): true at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:352) | | + * | | installOrHelp(true,0,true) at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:353) | | + * | | BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | + * | | AtomicReferenceArray#1[0].get(): Descriptor#2 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | + * | └╶╶╶╶╶╶ switch (reason: active lock detected) + * + */ +private class TraceNodePrefixFactory(nThreads: Int) { + + /** + * Indicates should we add extra spaces to all the thread lines or not. + */ + private val extraIndentPerThread = BooleanArray(nThreads) { false } + + /** + * Tells if the next node is the first node of the spin cycle. + */ + private var nextNodeIsSpinCycleStart = false + + /** + * Tells if we're processing spin cycle nodes now. + */ + private var inSpinCycle = false + + /** + * Call depth of the first node in the current spin cycle. + */ + private var arrowDepth: Int = -1 + + fun actorNodePrefix(iThread: Int) = PrefixProvider { extraPrefixIfNeeded(iThread) } + + fun actorResultPrefix(iThread: Int, callDepth: Int) = + PrefixProvider { extraPrefixIfNeeded(iThread) + TRACE_INDENTATION.repeat(callDepth) } + + fun prefix(event: TracePoint, callDepth: Int): PrefixProvider { + val isCycleEnd = inSpinCycle && (event is ObstructionFreedomViolationExecutionAbortTracePoint || event is SwitchEventTracePoint) + return prefixForNode(event.iThread, callDepth, isCycleEnd).also { + nextNodeIsSpinCycleStart = event is SpinCycleStartTracePoint + if (isCycleEnd) { + inSpinCycle = false + } + } + } + + fun prefixForCallNode(iThread: Int, callDepth: Int): PrefixProvider { + return prefixForNode(iThread, callDepth, false) + } + + private fun prefixForNode(iThread: Int, callDepth: Int, isCycleEnd: Boolean): PrefixProvider { + if (nextNodeIsSpinCycleStart) { + inSpinCycle = true + nextNodeIsSpinCycleStart = false + val extraPrefixRequired = callDepth == 1 + if (extraPrefixRequired) { + extraIndentPerThread[iThread] = true + } + arrowDepth = callDepth + val arrowDepth = arrowDepth + return PrefixProvider { + val extraPrefix = if (arrowDepth == 1) 0 else extraPrefixLength(iThread) + TRACE_INDENTATION.repeat(max(0, arrowDepth - 2 + extraPrefix)) + "┌╶> " + } + } + if (isCycleEnd) { + val arrowDepth = arrowDepth + return PrefixProvider { + val extraPrefix = if (arrowDepth == 1) 0 else extraPrefixLength(iThread) + TRACE_INDENTATION.repeat(max(0, arrowDepth - 2 + extraPrefix)) + "└╶" + "╶╶".repeat(max(0, callDepth - arrowDepth)) + "╶ " + } + } + if (inSpinCycle) { + val arrowDepth = arrowDepth + return PrefixProvider { + val extraPrefix = if (arrowDepth == 1) 0 else extraPrefixLength(iThread) + TRACE_INDENTATION.repeat(max(0, arrowDepth - 2 + extraPrefix)) + "| " + TRACE_INDENTATION.repeat(callDepth - arrowDepth + 1) + } + } + return PrefixProvider { extraPrefixIfNeeded(iThread) + TRACE_INDENTATION.repeat(callDepth) } + } + + private fun extraPrefixIfNeeded(iThread: Int): String = if (extraIndentPerThread[iThread]) " " else "" + private fun extraPrefixLength(iThread: Int): Int = if (extraIndentPerThread[iThread]) 1 else 0 +} + +private const val TRACE_INDENTATION = " " -private fun TraceNode.stateEventRepresentation(iThread: Int, stateRepresentation: String) = - TraceEventRepresentation(iThread, traceIndentation() + "STATE: $stateRepresentation") internal class TraceEventRepresentation(val iThread: Int, val representation: String) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt index 72cd23d17e..5f7ab4e64f 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt @@ -434,16 +434,22 @@ internal class ModelCheckingStrategy( } } - private inner class Choice(val node: InterleavingTreeNode, val value: Int) + private inner class Choice(val node: InterleavingTreeNode, val value: Int) { + // For easier debug. + override fun toString(): String { + return "Choice(node=$node, value=$value)" + } + } /** * This class specifies an interleaving that is re-producible. */ private inner class Interleaving( - private val switchPositions: List, - private val threadSwitchChoices: List, - private var lastNotInitializedNode: SwitchChoosingNode? + private val switchPositions: List, + private val threadSwitchChoices: List, + private val initialLastNotInitializedNode: SwitchChoosingNode? ) { + private var lastNotInitializedNode: SwitchChoosingNode? = initialLastNotInitializedNode private lateinit var interleavingFinishingRandom: Random private lateinit var nextThreadToSwitch: Iterator private var lastNotInitializedNodeChoices: MutableList? = null @@ -465,6 +471,7 @@ internal class ModelCheckingStrategy( } fun rollbackAfterSpinCycleFound() { + lastNotInitializedNode = initialLastNotInitializedNode lastNotInitializedNodeChoices?.clear() } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/transformation/CodeLocations.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/transformation/CodeLocations.kt index fab94c9e01..bdcef4f0b1 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/transformation/CodeLocations.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/transformation/CodeLocations.kt @@ -30,6 +30,7 @@ internal object CodeLocations { /** * Registers a new code location and returns its unique ID. + * Code locations are positive values. * * @param stackTraceElement Stack trace element representing the new code location. * @return Unique ID of the new code location. @@ -39,7 +40,7 @@ internal object CodeLocations { fun newCodeLocation(stackTraceElement: StackTraceElement): Int { val id = codeLocations.size codeLocations.add(stackTraceElement) - return id + return id + 1 } /** @@ -51,7 +52,20 @@ internal object CodeLocations { @JvmStatic @Synchronized fun stackTrace(codeLocationId: Int): StackTraceElement { - return codeLocations[codeLocationId] + return codeLocations[codeLocationId - 1] + } +} + +/** + * Provides unique IDs for all the methods that are called from the instrumented code. + * These IDs are used to detect the first recursive call in case of a recursive spin-cycle. + */ +internal object MethodIds { + + private val map: MutableMap = hashMapOf() + + fun getMethodId(owner: String, name: String, desc: String): Int { + return map.computeIfAbsent("$owner:$name:$desc") { map.size + 1 } } } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/transformation/LincheckClassVisitor.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/transformation/LincheckClassVisitor.kt index a3e8cfdc49..9d6b4e3821 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/transformation/LincheckClassVisitor.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/transformation/LincheckClassVisitor.kt @@ -1264,6 +1264,9 @@ internal class LincheckClassVisitor( loadNewCodeLocationId() // STACK [INVOKEVIRTUAL]: owner, owner, className, methodName, codeLocation // STACK [INVOKESTATIC]: null, className, methodName, codeLocation + adapter.push(MethodIds.getMethodId(owner, name, desc)) + // STACK [INVOKEVIRTUAL]: owner, owner, className, methodName, codeLocation, methodId + // STACK [INVOKESTATIC]: null, className, methodName, codeLocation, methodId val argumentTypes = getArgumentTypes(desc) push(argumentLocals.size) // size of the array visitTypeInsn(ANEWARRAY, OBJECT_TYPE.internalName) diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/representation/RecursiveSpinLockTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/representation/RecursiveSpinLockTest.kt new file mode 100644 index 0000000000..9380cd98a4 --- /dev/null +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/representation/RecursiveSpinLockTest.kt @@ -0,0 +1,502 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2024 JetBrains s.r.o. + * + * This Source Code Form is subject to the terms of the + * Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed + * with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.jetbrains.kotlinx.lincheck_test.representation + +import kotlinx.atomicfu.AtomicArray +import kotlinx.atomicfu.atomic +import kotlinx.atomicfu.atomicArrayOfNulls +import org.jetbrains.kotlinx.lincheck.annotations.Operation +import org.jetbrains.kotlinx.lincheck.checkImpl +import org.jetbrains.kotlinx.lincheck.strategy.managed.modelchecking.ModelCheckingOptions +import org.jetbrains.kotlinx.lincheck_test.util.checkLincheckOutput +import org.junit.Test +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicInteger + +/** + * Checks proper output in case of recursive spin-lock in one thread. + */ +class RecursiveSpinLockTest { + + private val counter = AtomicInteger(0) + private val someUselessSharedState = AtomicBoolean(false) + + @Operation + fun trigger() { + counter.incrementAndGet() + counter.decrementAndGet() + } + + @Operation + fun causesSpinLock() { + if (counter.get() != 0) { + deadSpinCycleRecursive() + } + } + + private fun deadSpinCycleRecursive() { + repeat(4) { + val value = getSharedVariable() + someUselessSharedState.compareAndSet(value, !value) + } + deadSpinCycleRecursive() + } + + private fun getSharedVariable(): Boolean = someUselessSharedState.get() + + @Test + fun test() = ModelCheckingOptions() + .addCustomScenario { + parallel { + thread { actor(RecursiveSpinLockTest::trigger) } + thread { actor(RecursiveSpinLockTest::causesSpinLock) } + } + } + .minimizeFailedScenario(false) + .checkImpl(this::class.java) + .checkLincheckOutput("spin_lock/recursive_spin_lock.txt") + +} + +/** + * Checks proper output in case of recursive spin-lock in one thread. + * Spin lock should be twice bigger because of flipping parameters of the method. + */ +class RecursiveSpinWithParamsLockTest { + + private val counter = AtomicInteger(0) + private val someUselessSharedState = AtomicBoolean(false) + private val flag = AtomicBoolean(false) + + @Operation + fun trigger() { + counter.incrementAndGet() + counter.decrementAndGet() + } + + @Operation + fun causesSpinLock() { + if (counter.get() != 0) { + deadSpinCycleRecursive(true) + } + } + + private fun deadSpinCycleRecursive(value: Boolean) { + flag.set(value) + repeat(4) { + val value = getSharedVariable() + someUselessSharedState.compareAndSet(value, !value) + } + deadSpinCycleRecursive(!value) + } + + private fun getSharedVariable(): Boolean = someUselessSharedState.get() + + @Test + fun test() = ModelCheckingOptions() + .addCustomScenario { + parallel { + thread { actor(RecursiveSpinLockTest::trigger) } + thread { actor(RecursiveSpinLockTest::causesSpinLock) } + } + } + .minimizeFailedScenario(false) + .checkImpl(this::class.java) + .checkLincheckOutput("spin_lock/recursive_spin_lock_params.txt") + +} + +/** + * Checks proper output in case of recursive spin-lock in one thread. + * Should correctly detect spin cycle and place spin cycle label in case + * when all potential switch points are nested in non-atomic methods. + */ +class RecursiveSpinLockWithInnerEventsTest { + + private val counter = AtomicInteger(0) + private val someUselessSharedState = AtomicBoolean(false) + + @Operation + fun trigger() { + counter.incrementAndGet() + counter.decrementAndGet() + } + + @Operation + fun causesSpinLock() { + if (counter.get() != 0) { + deadSpinCycleRecursive() + } + } + + private fun deadSpinCycleRecursive() { + repeat(4) { + val value = getSharedVariable() + action(value) + } + deadSpinCycleRecursive() + } + + private fun action(value: Boolean) = someUselessSharedState.compareAndSet(value, !value) + + private fun getSharedVariable(): Boolean = someUselessSharedState.get() + + @Test + fun test() = ModelCheckingOptions() + .addCustomScenario { + parallel { + thread { actor(RecursiveSpinLockWithInnerEventsTest::trigger) } + thread { actor(RecursiveSpinLockWithInnerEventsTest::causesSpinLock) } + } + } + .minimizeFailedScenario(false) + .checkImpl(this::class.java) + .checkLincheckOutput("spin_lock/recursive_spin_cycle_inner_events.txt") + +} + +/** + * Checks proper output in case of recursive spin-lock in one thread. + * Should correctly detect spin cycle and place the spin cycle labels when + * the recursion includes two different method calls. + */ +class RecursiveSpinLockTwoStepRecursionEventsTest { + + private val counter = AtomicInteger(0) + private val someUselessSharedState = AtomicBoolean(false) + + @Operation + fun trigger() { + counter.incrementAndGet() + counter.decrementAndGet() + } + + @Operation + fun causesSpinLock() { + if (counter.get() != 0) { + outerRecursiveSpinCycle() + } + } + + private fun outerRecursiveSpinCycle() { + deadSpinCycleRecursive() + } + + private fun deadSpinCycleRecursive() { + repeat(4) { + val value = getSharedVariable() + action(value) + } + outerRecursiveSpinCycle() + } + + private fun action(value: Boolean) = someUselessSharedState.compareAndSet(value, !value) + + private fun getSharedVariable(): Boolean = someUselessSharedState.get() + + @Test + fun test() = ModelCheckingOptions() + .addCustomScenario { + parallel { + thread { actor(RecursiveSpinLockTwoStepRecursionEventsTest::trigger) } + thread { actor(RecursiveSpinLockTwoStepRecursionEventsTest::causesSpinLock) } + } + } + .minimizeFailedScenario(false) + .checkImpl(this::class.java) + .checkLincheckOutput("spin_lock/recursive_spin_cycle_two_step.txt") + +} + +/** + * Checks proper output in case of recursive spin-lock in two threads. + */ +class RecursiveTwoThreadsSpinLockTest { + private val sharedState1 = AtomicBoolean(false) + private val sharedState2 = AtomicBoolean(false) + + @Operation + fun one(): Int { + meaninglessActions2() + meaninglessActions1() + + sharedState1.set(false) + sharedState2.set(false) + + return 1 + } + + @Operation + fun two(): Int { + meaninglessActions1() + meaninglessActions2() + + sharedState2.set(false) + sharedState1.set(false) + + return 2 + } + + private fun meaninglessActions1() { + if (!sharedState2.compareAndSet(false, true)) { + meaninglessActions1() + } + } + private fun meaninglessActions2() { + if (!sharedState1.compareAndSet(false, true)) { + meaninglessActions2() + } + } + + @Test + fun testWithModelCheckingStrategy() = ModelCheckingOptions() + .minimizeFailedScenario(true) + .checkImpl(this::class.java) + .checkLincheckOutput("spin_lock/recursive_two_threads_spin_lock.txt") +} + + +/** + * Checks recursive spin lock representation when execution hung due to alternation of two threads + * in recursive live-lock. + */ +class BrokenCas2RecursiveLiveLockTest { + private val array = AtomicArrayWithCAS2(ARRAY_SIZE, 0) + + @Operation + fun cas2_0( + index1: Int, expected1: Int, update1: Int, + index2: Int, expected2: Int, update2: Int, + ) = array.cas2(index1, expected1, update1, index2, expected2, update2, 0) + + @Operation + fun cas2_1( + index1: Int, expected1: Int, update1: Int, + index2: Int, expected2: Int, update2: Int, + ) = array.cas2(index1, expected1, update1, index2, expected2, update2, 1) + + + @Test + fun modelCheckingTest() = + ModelCheckingOptions() + .addCustomScenario { + parallel { + thread { actor(BrokenCas2RecursiveLiveLockTest::cas2_0, 0, 0, 2, 1, 0, 3) } + thread { actor(BrokenCas2RecursiveLiveLockTest::cas2_1, 0, 0, 4, 1, 0, 5) } + } + } + .iterations(500) + .invocationsPerIteration(1000) + .sequentialSpecification(IntAtomicArraySequential::class.java) + .checkImpl(this::class.java) + .checkLincheckOutput("spin_lock/broken-cas-2-recursive-live-lock.txt") +} + +/** + * Broken CAS-2-based array implementation with only 2 operations. Calling them may lead to alternation recursive live-lock. + */ +class AtomicArrayWithCAS2(size: Int, initialValue: E) { + private val array: AtomicArray = atomicArrayOfNulls(size) + private val gate0 = atomic(false) + private val gate1 = atomic(false) + + init { + // Fill array with the initial value. + for (i in 0 until size) { + array[i].value = Descriptor(i, initialValue, initialValue, i, initialValue, initialValue).apply { + status.value = Status.SUCCESS + } + } + } + + fun cas2( + index1: Int, expected1: E, update1: E, + index2: Int, expected2: E, update2: E, + turn: Int + ): Boolean { + require(index1 != index2) { "The indices should be different" } + + val descriptor = if (index1 < index2) Descriptor(index1, expected1, update1, index2, expected2, update2) + else Descriptor(index2, expected2, update2, index1, expected1, update1) + + descriptor.apply(turn = turn) + return descriptor.status.value == Status.SUCCESS + } + + private inner class Descriptor( + private val index1: Int, + private val expected1: Any?, + private val update1: Any?, + private val index2: Int, + private val expected2: Any, + private val update2: Any + ) { + val status = atomic(Status.UNDECIDED) + + fun read(index: Int): Any { + check(index == index1 || index == index2) + return if (status.value == Status.SUCCESS) { + if (index == index1) update1 else update2 + } else { + if (index == index1) expected1 else expected2 + }!! + } + + /** + * @param broken if true then we never will go out of recursive spin-cycle + */ + fun apply(initial: Boolean = true, turn: Int = -1, broken: Boolean = false) { + if (broken) { + status.value + status.compareAndSet(Status.SUCCESS, Status.SUCCESS) + installOrHelp(true, turn, true) + } + if (status.value == Status.UNDECIDED) { + val install1 = if (!initial || index1 == -1) true else installOrHelp(true, turn) + val install2 = installOrHelp(false, turn) + + if (install1 && install2) { + status.compareAndSet(Status.UNDECIDED, Status.SUCCESS) + } + } + } + + /** + * @param broken if true then we never will go out of recursive spin-cycle + */ + private fun installOrHelp(first: Boolean, turn: Int = -1, broken: Boolean = false): Boolean { + val index = if (first) index1 else index2 + val expected = if (first) expected1 else expected2 + check(index != -1) + while (true) { + val current = array[index].value!! + if (broken) { + current.apply(false, turn, true) + } + if (current === this) return true + + when (turn) { + 1 -> { + gate0.value = true + if (gate1.value) { + current.apply(false, turn = turn, true) + } + gate0.value = false + } + + 0 -> { + gate1.value = true + if (gate0.value) { + current.apply(false, turn = turn, true) + } + gate1.value = false + } + + else -> error("Not excepted: $turn") + } + + current.apply(false, turn = turn) + if (current.read(index) !== expected) { + status.compareAndSet(Status.UNDECIDED, Status.FAILED) + return false + } + if (status.value != Status.UNDECIDED) return false + if (array[index].compareAndSet(current, this)) { + return true + } + } + } + } + + enum class Status { + UNDECIDED, SUCCESS, FAILED + } + +} + +private const val ARRAY_SIZE = 3 + +class IntAtomicArraySequential { + private val array = IntArray(ARRAY_SIZE) + + fun get(index: Int): Int = array[index] + + fun set(index: Int, value: Int) { + array[index] = value + } + + fun cas(index: Int, expected: Int, update: Int): Boolean { + if (array[index] != expected) return false + array[index] = update + return true + } + + fun dcss( + index1: Int, expected1: Int, update1: Int, + index2: Int, expected2: Int + ): Boolean { + require(index1 != index2) { "The indices should be different" } + if (array[index1] != expected1 || array[index2] != expected2) return false + array[index1] = update1 + return true + } + + fun cas2( + index1: Int, expected1: Int, update1: Int, index2: Int, expected2: Int, update2: Int + ): Boolean { + require(index1 != index2) { "The indices should be different" } + if (array[index1] != expected1 || array[index2] != expected2) return false + array[index1] = update1 + array[index2] = update2 + return true + } + + fun cas2_0( + index1: Int, expected1: Int, update1: Int, index2: Int, expected2: Int, update2: Int + ) = cas2(index1, expected1, update1, index2, expected2, update2) + + fun cas2_1( + index1: Int, expected1: Int, update1: Int, index2: Int, expected2: Int, update2: Int + ) = cas2(index1, expected1, update1, index2, expected2, update2) +} + +/** + * Checks proper output in case of recursive spin-lock in one thread. + * Should correctly detect spin cycle and place spin cycle label in case + * when the last event before the cycle is located in the same method call but with + * different input parameters. + */ +class RecursiveParametersDependentSpinLockTest { + private val value = AtomicBoolean(false) + + @Operation + fun actorMethod() { + b(recursive = false) + b(recursive = true) + } + + + fun b(recursive: Boolean) { + value.compareAndSet(false, true) // point X + c() + if (recursive) { + b(recursive = true) + } + } + + fun c() = value.get() + + @Test + fun test() = ModelCheckingOptions() + .addCustomScenario { parallel { thread { actor(::actorMethod) } } } + .checkImpl(this::class.java) + .checkLincheckOutput("spin_lock/recursive_spin_lock_param_dependent.txt") +} diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/representation/SpinlockEventsCutTests.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/representation/SpinlockEventsCutTests.kt index 3c70546e93..1391e6cbce 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/representation/SpinlockEventsCutTests.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/representation/SpinlockEventsCutTests.kt @@ -50,7 +50,7 @@ class SpinlockEventsCutShortLengthTest : AbstractSpinLivelockTest() { private val sharedStateAny = AtomicBoolean(false) - override val outputFileName: String get() = "spin_lock_events_cut_single_action_cycle.txt" + override val outputFileName: String get() = "spin_lock/spin_lock_events_cut_single_action_cycle.txt" override fun meaninglessActions() { sharedStateAny.get() @@ -65,7 +65,7 @@ class SpinlockEventsCutMiddleLengthTest : AbstractSpinLivelockTest() { private val sharedStateAny = AtomicBoolean(false) - override val outputFileName: String get() = "spin_lock_events_cut_two_actions_cycle.txt" + override val outputFileName: String get() = "spin_lock/spin_lock_events_cut_two_actions_cycle.txt" override fun meaninglessActions() { val x = sharedStateAny.get() @@ -81,7 +81,7 @@ class SpinlockEventsCutInfiniteLoopTest : AbstractSpinLivelockTest() { private val sharedStateAny = AtomicBoolean(false) - override val outputFileName: String get() = "infinite_spin_loop_events_cut.txt" + override val outputFileName: String get() = "spin_lock/infinite_spin_loop_events_cut.txt" override fun meaninglessActions() { while (true) { @@ -91,13 +91,128 @@ class SpinlockEventsCutInfiniteLoopTest : AbstractSpinLivelockTest() { } } +/** + * Checks that spin-cycle repeated events are cut in case + * when one thread runs in the infinite loop while others terminate + */ +class SpinlockEventsCutInfiniteLoopWithParametersTest : AbstractSpinLivelockTest() { + + @Volatile + private var sharedState: Boolean = false + + override val outputFileName: String get() = "spin_lock/infinite_spin_loop_events_read_write.txt" + + override fun meaninglessActions() { + while (true) { + val x = sharedState + sharedState = !x + } + } +} + +/** + * Checks that spin cycle properly detected, and the spin cycle label is placed correctly + * when the spin cycle is twice bigger due to a flipping method receivers. + */ +class SpinlockEventsCutInfiniteLoopWithReceiversTest : AbstractSpinLivelockTest() { + + private val first = Receiver(false) + private val second = Receiver(false) + + override val outputFileName: String get() = "spin_lock/infinite_spin_loop_events_receivers.txt" + + override fun meaninglessActions() { + var pickFirst = false + val firstReceiver = first + val secondReceiver = second + while (true) { + val receiver = if (pickFirst) firstReceiver else secondReceiver + receiver.value = false + pickFirst = !pickFirst + } + } + + data class Receiver(@Volatile var value: Boolean) +} + +/** + * Checks that spin cycle properly detected, and the spin cycle label is placed correctly + * when the spin cycle is bigger due to a different arrays usage and cells access. + */ +class SpinlockEventsCutInfiniteLoopWithArrayOperationsTest : AbstractSpinLivelockTest() { + + @Volatile + private var array: Array = Array(3) { 0 } + + override val outputFileName: String get() = "spin_lock/infinite_spin_loop_events_arrays.txt" + + override fun meaninglessActions() { + var index = 0 + var valueToWrite = 0 + while (true) { + array[index] = valueToWrite + + index = (index + 1) % array.size + if (index == 0) { + valueToWrite = (valueToWrite + 1) % 3 + } + } + } +} + +/** + * Checks that spin cycle properly detected, and the spin cycle label is placed correctly + * when the spin cycle is twice bigger due to a flipping arrays receivers usage. + */ +class SpinlockEventsCutInfiniteLoopWithArrayReceiversTest : AbstractSpinLivelockTest() { + + private val first = Array(3) { 0 } + private val second = Array(3) { 0 } + + override val outputFileName: String get() = "spin_lock/infinite_spin_loop_events_arrays_receivers.txt" + + override fun meaninglessActions() { + var pickFirst = false + val firstReceiver = first + val secondReceiver = second + while (true) { + val receiver = if (pickFirst) firstReceiver else secondReceiver + receiver[0] = 1 + pickFirst = !pickFirst + } + } +} + +/** + * Checks that spin cycle properly detected, and the spin cycle label is placed correctly + * when spin cycle period can't be found using parameters and receivers, so + * LinCheck should calculate spin cycle period without params. + */ +class SpinlockEventsCutInfiniteNoCycleWithParamsTest : AbstractSpinLivelockTest() { + + private val array = Array(3) { 0 } + private val random = java.util.Random(0) + + override val outputFileName: String get() = "spin_lock/infinite_spin_loop_events_no_cycle_params.txt" + + override fun meaninglessActions() { + while (true) { + val value = random.nextInt() + array[0] = value + array[0] = value + 1 + array[0] = value + 2 + } + } +} + + /** * Checks that spin-cycle repeated events are cut in case when spin cycle contains many actions */ class SpinlockEventsCutLongCycleActionsTest : AbstractSpinLivelockTest() { private val data = AtomicReferenceArray(7) - override val outputFileName: String get() = "spin_lock_events_cut_long_cycle.txt" + override val outputFileName: String get() = "spin_lock/spin_lock_events_cut_long_cycle.txt" override fun meaninglessActions() { data[0] = 0 data[1] = 0 @@ -116,7 +231,7 @@ class SpinlockEventsCutLongCycleActionsTest : AbstractSpinLivelockTest() { class SpinlockEventsCutWithInnerLoopActionsTest : AbstractSpinLivelockTest() { private val data = AtomicReferenceArray(10) - override val outputFileName: String get() = "spin_lock_events_cut_inner_loop.txt" + override val outputFileName: String get() = "spin_lock/spin_lock_events_cut_inner_loop.txt" override fun meaninglessActions() { for (i in 0 until data.length()) { data[i] = 0 @@ -202,7 +317,7 @@ class SpinlockInIncorrectResultsWithClocksTest { .sequentialSpecification(ClocksTestSequential::class.java) .minimizeFailedScenario(false) .checkImpl(this::class.java) - .checkLincheckOutput("spin_lock_in_incorrect_results_failure.txt") + .checkLincheckOutput("spin_lock/spin_lock_in_incorrect_results_failure.txt") /** @@ -285,4 +400,75 @@ class SpinCycleWithSideEffectsTest { .iterations(100) .check(this::class) -} \ No newline at end of file +} + +/** + * Checks proper output in case of spin-lock in one thread. + * Should correctly detect spin cycle and place spin cycle label in case + * when all potential switch points are nested in non-atomic methods. + */ +class SpinLockWithAllEventsWrappedInMethodsTest { + + private val counter = AtomicInteger(0) + private val someUselessSharedState = AtomicBoolean(false) + + @Operation + fun trigger() { + counter.incrementAndGet() + counter.decrementAndGet() + } + + @Operation + fun causesSpinLock() { + if (counter.get() != 0) { + deadSpinCycle() + } + } + + private fun deadSpinCycle() { + while (true) { + val value = getSharedVariable() + action(value) + } + } + + private fun getSharedVariable(): Boolean = someUselessSharedState.get() + private fun action(value: Boolean) = someUselessSharedState.compareAndSet(value, !value) + + @Test + fun test() = ModelCheckingOptions() + .addCustomScenario { + parallel { + thread { actor(RecursiveSpinLockTest::trigger) } + thread { actor(RecursiveSpinLockTest::causesSpinLock) } + } + } + .minimizeFailedScenario(false) + .checkImpl(this::class.java) + .checkLincheckOutput("spin_lock/spin_lock_nested_events.txt") + +} + +/** + * Checks that spin cycle properly detected, and the spin cycle label is placed correctly + * when all the trace points are in the top-level, i.e., right in the actor. + */ +class SingleThreadTopLevelSpinLockTest { + + @Volatile + private var state: Boolean = false + + @Operation + fun spinLock() { + while (true) { + state = false + state = true + } + } + + @Test + fun test() = ModelCheckingOptions() + .checkImpl(this::class.java) + .checkLincheckOutput("spin_lock/spin_lock_top_level.txt") + +} diff --git a/src/jvm/test/resources/expected_logs/obstruction_freedom_violation_events_cut.txt b/src/jvm/test/resources/expected_logs/obstruction_freedom_violation_events_cut.txt index 5b1ef2f9eb..25b60bbe46 100644 --- a/src/jvm/test/resources/expected_logs/obstruction_freedom_violation_events_cut.txt +++ b/src/jvm/test/resources/expected_logs/obstruction_freedom_violation_events_cut.txt @@ -10,16 +10,16 @@ The following interleaving leads to the error: | Thread 1 | Thread 2 | | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | | enqueue(-1) | -| | q.enqueue(-1) at ObstructionFreedomViolationEventsCutTest.enqueue(SpinlockEventsCutTests.kt:33) | +| | q.enqueue(-1) at ObstructionFreedomViolationEventsCutTest.enqueue(SpinlockEventsCutTests.kt:34) | | | tail.get(): Node#1 at MSQueueBlocking.enqueue(ObstructionFreedomViolationTest.kt:27) | | | Node#1.next.compareAndSet(null,Node#2): true at MSQueueBlocking.enqueue(ObstructionFreedomViolationTest.kt:28) | | | switch | | enqueue(1) | | -| q.enqueue(1) at ObstructionFreedomViolationEventsCutTest.enqueue(SpinlockEventsCutTests.kt:33) | | +| q.enqueue(1) at ObstructionFreedomViolationEventsCutTest.enqueue(SpinlockEventsCutTests.kt:34) | | | /* The following events repeat infinitely: */ | | -| tail.get(): Node#1 at MSQueueBlocking.enqueue(ObstructionFreedomViolationTest.kt:27) | | -| Node#1.next.compareAndSet(null,Node#3): false at MSQueueBlocking.enqueue(ObstructionFreedomViolationTest.kt:28) | | -| /* An active lock was detected */ | | +| ┌╶> tail.get(): Node#1 at MSQueueBlocking.enqueue(ObstructionFreedomViolationTest.kt:27) | | +| | Node#1.next.compareAndSet(null,Node#3): false at MSQueueBlocking.enqueue(ObstructionFreedomViolationTest.kt:28) | | +| └╶╶ /* An active lock was detected */ | | | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | Detailed trace: @@ -27,14 +27,14 @@ Detailed trace: | Thread 1 | Thread 2 | | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | | enqueue(-1) | -| | q.enqueue(-1) at ObstructionFreedomViolationEventsCutTest.enqueue(SpinlockEventsCutTests.kt:33) | +| | q.enqueue(-1) at ObstructionFreedomViolationEventsCutTest.enqueue(SpinlockEventsCutTests.kt:34) | | | tail.get(): Node#1 at MSQueueBlocking.enqueue(ObstructionFreedomViolationTest.kt:27) | | | Node#1.next.compareAndSet(null,Node#2): true at MSQueueBlocking.enqueue(ObstructionFreedomViolationTest.kt:28) | | | switch | | enqueue(1) | | -| q.enqueue(1) at ObstructionFreedomViolationEventsCutTest.enqueue(SpinlockEventsCutTests.kt:33) | | +| q.enqueue(1) at ObstructionFreedomViolationEventsCutTest.enqueue(SpinlockEventsCutTests.kt:34) | | | /* The following events repeat infinitely: */ | | -| tail.get(): Node#1 at MSQueueBlocking.enqueue(ObstructionFreedomViolationTest.kt:27) | | -| Node#1.next.compareAndSet(null,Node#3): false at MSQueueBlocking.enqueue(ObstructionFreedomViolationTest.kt:28) | | -| /* An active lock was detected */ | | +| ┌╶> tail.get(): Node#1 at MSQueueBlocking.enqueue(ObstructionFreedomViolationTest.kt:27) | | +| | Node#1.next.compareAndSet(null,Node#3): false at MSQueueBlocking.enqueue(ObstructionFreedomViolationTest.kt:28) | | +| └╶╶ /* An active lock was detected */ | | | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | diff --git a/src/jvm/test/resources/expected_logs/obstruction_freedom_violation_with_no_detected_cycle.txt b/src/jvm/test/resources/expected_logs/obstruction_freedom_violation_with_no_detected_cycle.txt index 611ae7274a..db138c1ab2 100644 --- a/src/jvm/test/resources/expected_logs/obstruction_freedom_violation_with_no_detected_cycle.txt +++ b/src/jvm/test/resources/expected_logs/obstruction_freedom_violation_with_no_detected_cycle.txt @@ -6,15 +6,16 @@ | ----------- | The following interleaving leads to the error: -| --------------------------------------------------------------------------------------------------------------------------------------- | -| Thread 1 | -| --------------------------------------------------------------------------------------------------------------------------------------- | -| operation() | -| incrementManyTimes() at ObstructionFreedomActiveLockRepresentationTest.operation(ObstructionFreedomRepresentationTest.kt:34) | -| incrementManyTimes() at ObstructionFreedomActiveLockRepresentationTest.operation(ObstructionFreedomRepresentationTest.kt:37) | -| counter.get(): 101 at ObstructionFreedomActiveLockRepresentationTest.incrementManyTimes(ObstructionFreedomRepresentationTest.kt:41) | -| /* An active lock was detected */ | -| --------------------------------------------------------------------------------------------------------------------------------------- | +| --------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | +| --------------------------------------------------------------------------------------------------------------------------------------------------- | +| operation() | +| incrementManyTimes() at ObstructionFreedomActiveLockRepresentationTest.operation(ObstructionFreedomRepresentationTest.kt:34) | +| incrementManyTimes() at ObstructionFreedomActiveLockRepresentationTest.operation(ObstructionFreedomRepresentationTest.kt:37) | +| counter.get(): 101 at ObstructionFreedomActiveLockRepresentationTest.incrementManyTimes(ObstructionFreedomRepresentationTest.kt:41) | +| counter.incrementAndGet(): 102 at ObstructionFreedomActiveLockRepresentationTest.incrementManyTimes(ObstructionFreedomRepresentationTest.kt:44) | +| /* An active lock was detected */ | +| --------------------------------------------------------------------------------------------------------------------------------------------------- | Detailed trace: | --------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -126,5 +127,6 @@ Detailed trace: | counter.incrementAndGet(): 101 at ObstructionFreedomActiveLockRepresentationTest.incrementManyTimes(ObstructionFreedomRepresentationTest.kt:44) | | incrementManyTimes() at ObstructionFreedomActiveLockRepresentationTest.operation(ObstructionFreedomRepresentationTest.kt:37) | | counter.get(): 101 at ObstructionFreedomActiveLockRepresentationTest.incrementManyTimes(ObstructionFreedomRepresentationTest.kt:41) | +| counter.incrementAndGet(): 102 at ObstructionFreedomActiveLockRepresentationTest.incrementManyTimes(ObstructionFreedomRepresentationTest.kt:44) | | /* An active lock was detected */ | | --------------------------------------------------------------------------------------------------------------------------------------------------- | diff --git a/src/jvm/test/resources/expected_logs/spin_lock/broken-cas-2-recursive-live-lock.txt b/src/jvm/test/resources/expected_logs/spin_lock/broken-cas-2-recursive-live-lock.txt new file mode 100644 index 0000000000..b04dee8cab --- /dev/null +++ b/src/jvm/test/resources/expected_logs/spin_lock/broken-cas-2-recursive-live-lock.txt @@ -0,0 +1,140 @@ += The execution has hung = +| ------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| ------------------------------------------------------------------- | +| cas2_0(0, 0, 2, 1, 0, 3): | cas2_1(0, 0, 4, 1, 0, 5): | +| ------------------------------------------------------------------- | + + +The following interleaving leads to the error: +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | cas2_1(0, 0, 4, 1, 0, 5): | +| | array.cas2(0,0,4,1,0,5,1) at BrokenCas2RecursiveLiveLockTest.cas2_1(RecursiveSpinLockTest.kt:277) | +| | AtomicArrayWithCAS2$Descriptor.apply$default(Descriptor#1,false,1,false,5,null) at AtomicArrayWithCAS2.cas2(RecursiveSpinLockTest.kt:323) | +| | Descriptor#1.apply(true,1,false) at AtomicArrayWithCAS2$Descriptor.apply$default(RecursiveSpinLockTest.kt:349) | +| | AtomicArrayWithCAS2$Descriptor.installOrHelp$default(Descriptor#1,true,1,false,4,null): true at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:356) | +| | Descriptor#1.installOrHelp(true,1,false): true at AtomicArrayWithCAS2$Descriptor.installOrHelp$default(RecursiveSpinLockTest.kt:368) | +| | BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | +| | AtomicReferenceArray#1[0].get(): Descriptor#2 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | +| | BrokenCas2RecursiveLiveLockTest#1.array.gate0.WRITE(1) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:381) | +| | BrokenCas2RecursiveLiveLockTest#1.array.gate1.READ: 0 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:382) | +| | switch | +| cas2_0(0, 0, 2, 1, 0, 3): | | +| array.cas2(0,0,2,1,0,3,0) at BrokenCas2RecursiveLiveLockTest.cas2_0(RecursiveSpinLockTest.kt:271) | | +| AtomicArrayWithCAS2$Descriptor.apply$default(Descriptor#3,false,0,false,5,null) at AtomicArrayWithCAS2.cas2(RecursiveSpinLockTest.kt:323) | | +| Descriptor#3.apply(true,0,false) at AtomicArrayWithCAS2$Descriptor.apply$default(RecursiveSpinLockTest.kt:349) | | +| AtomicArrayWithCAS2$Descriptor.installOrHelp$default(Descriptor#3,true,0,false,4,null) at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:356) | | +| Descriptor#3.installOrHelp(true,0,false) at AtomicArrayWithCAS2$Descriptor.installOrHelp$default(RecursiveSpinLockTest.kt:368) | | +| BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | +| AtomicReferenceArray#1[0].get(): Descriptor#2 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | +| BrokenCas2RecursiveLiveLockTest#1.array.gate1.WRITE(1) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:389) | | +| BrokenCas2RecursiveLiveLockTest#1.array.gate0.READ: 1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:390) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> Descriptor#2.apply(false,0,true) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:391) | | +| | status.READ: SUCCESS at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:351) | | +| | status.compareAndSet(SUCCESS,SUCCESS): true at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:352) | | +| | installOrHelp(true,0,true) at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:353) | | +| | BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | +| | AtomicReferenceArray#1[0].get(): Descriptor#2 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | +| └╶╶╶╶╶╶ switch (reason: active lock detected) | | +| | BrokenCas2RecursiveLiveLockTest#1.array.gate0.WRITE(0) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:385) | +| | AtomicArrayWithCAS2$Descriptor.apply$default(Descriptor#2,false,1,false,4,null) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:399) | +| | Descriptor#2.read(0): 0 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:400) | +| | BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:405) | +| | AtomicReferenceArray#1[0].compareAndSet(Descriptor#2,Descriptor#1): true at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:405) | +| | AtomicArrayWithCAS2$Descriptor.installOrHelp$default(Descriptor#1,false,1,false,4,null) at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:357) | +| | Descriptor#1.installOrHelp(false,1,false) at AtomicArrayWithCAS2$Descriptor.installOrHelp$default(RecursiveSpinLockTest.kt:368) | +| | BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | +| | AtomicReferenceArray#1[1].get(): Descriptor#4 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | +| | BrokenCas2RecursiveLiveLockTest#1.array.gate0.WRITE(1) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:381) | +| | BrokenCas2RecursiveLiveLockTest#1.array.gate1.READ: 1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:382) | +| | /* The following events repeat infinitely: */ | +| | ┌╶> Descriptor#4.apply(false,1,true) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:383) | +| | | status.READ: SUCCESS at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:351) | +| | | status.compareAndSet(SUCCESS,SUCCESS): true at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:352) | +| | | installOrHelp(true,1,true) at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:353) | +| | | BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | +| | | AtomicReferenceArray#1[1].get(): Descriptor#4 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | +| | └╶╶╶╶╶╶ switch (reason: active lock detected) | +| apply(false,0,true) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:375) | | +| status.READ: SUCCESS at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:351) | | +| status.compareAndSet(SUCCESS,SUCCESS): true at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:352) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> installOrHelp(true,0,true) at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:353) | | +| | BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | +| | AtomicReferenceArray#1[0].get(): Descriptor#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | +| | Descriptor#1.apply(false,0,true) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:375) | | +| | status.compareAndSet(SUCCESS,SUCCESS): false at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:352) | | +| └╶╶╶╶╶╶ switch (reason: active lock detected) | | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock + +Detailed trace: +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | cas2_1(0, 0, 4, 1, 0, 5): | +| | array.cas2(0,0,4,1,0,5,1) at BrokenCas2RecursiveLiveLockTest.cas2_1(RecursiveSpinLockTest.kt:277) | +| | AtomicArrayWithCAS2$Descriptor.apply$default(Descriptor#1,false,1,false,5,null) at AtomicArrayWithCAS2.cas2(RecursiveSpinLockTest.kt:323) | +| | Descriptor#1.apply(true,1,false) at AtomicArrayWithCAS2$Descriptor.apply$default(RecursiveSpinLockTest.kt:349) | +| | AtomicArrayWithCAS2$Descriptor.installOrHelp$default(Descriptor#1,true,1,false,4,null): true at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:356) | +| | Descriptor#1.installOrHelp(true,1,false): true at AtomicArrayWithCAS2$Descriptor.installOrHelp$default(RecursiveSpinLockTest.kt:368) | +| | BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | +| | AtomicReferenceArray#1[0].get(): Descriptor#2 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | +| | BrokenCas2RecursiveLiveLockTest#1.array.gate0.WRITE(1) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:381) | +| | BrokenCas2RecursiveLiveLockTest#1.array.gate1.READ: 0 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:382) | +| | switch | +| cas2_0(0, 0, 2, 1, 0, 3): | | +| array.cas2(0,0,2,1,0,3,0) at BrokenCas2RecursiveLiveLockTest.cas2_0(RecursiveSpinLockTest.kt:271) | | +| AtomicArrayWithCAS2$Descriptor.apply$default(Descriptor#3,false,0,false,5,null) at AtomicArrayWithCAS2.cas2(RecursiveSpinLockTest.kt:323) | | +| Descriptor#3.apply(true,0,false) at AtomicArrayWithCAS2$Descriptor.apply$default(RecursiveSpinLockTest.kt:349) | | +| AtomicArrayWithCAS2$Descriptor.installOrHelp$default(Descriptor#3,true,0,false,4,null) at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:356) | | +| Descriptor#3.installOrHelp(true,0,false) at AtomicArrayWithCAS2$Descriptor.installOrHelp$default(RecursiveSpinLockTest.kt:368) | | +| BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | +| AtomicReferenceArray#1[0].get(): Descriptor#2 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | +| BrokenCas2RecursiveLiveLockTest#1.array.gate1.WRITE(1) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:389) | | +| BrokenCas2RecursiveLiveLockTest#1.array.gate0.READ: 1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:390) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> Descriptor#2.apply(false,0,true) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:391) | | +| | status.READ: SUCCESS at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:351) | | +| | status.compareAndSet(SUCCESS,SUCCESS): true at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:352) | | +| | installOrHelp(true,0,true) at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:353) | | +| | BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | +| | AtomicReferenceArray#1[0].get(): Descriptor#2 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | +| └╶╶╶╶╶╶ switch (reason: active lock detected) | | +| | BrokenCas2RecursiveLiveLockTest#1.array.gate0.WRITE(0) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:385) | +| | AtomicArrayWithCAS2$Descriptor.apply$default(Descriptor#2,false,1,false,4,null) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:399) | +| | Descriptor#2.apply(false,1,false) at AtomicArrayWithCAS2$Descriptor.apply$default(RecursiveSpinLockTest.kt:349) | +| | status.READ: SUCCESS at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:355) | +| | Descriptor#2.read(0): 0 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:400) | +| | status.READ: SUCCESS at AtomicArrayWithCAS2$Descriptor.read(RecursiveSpinLockTest.kt:339) | +| | BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:405) | +| | AtomicReferenceArray#1[0].compareAndSet(Descriptor#2,Descriptor#1): true at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:405) | +| | AtomicArrayWithCAS2$Descriptor.installOrHelp$default(Descriptor#1,false,1,false,4,null) at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:357) | +| | Descriptor#1.installOrHelp(false,1,false) at AtomicArrayWithCAS2$Descriptor.installOrHelp$default(RecursiveSpinLockTest.kt:368) | +| | BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | +| | AtomicReferenceArray#1[1].get(): Descriptor#4 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | +| | BrokenCas2RecursiveLiveLockTest#1.array.gate0.WRITE(1) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:381) | +| | BrokenCas2RecursiveLiveLockTest#1.array.gate1.READ: 1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:382) | +| | /* The following events repeat infinitely: */ | +| | ┌╶> Descriptor#4.apply(false,1,true) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:383) | +| | | status.READ: SUCCESS at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:351) | +| | | status.compareAndSet(SUCCESS,SUCCESS): true at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:352) | +| | | installOrHelp(true,1,true) at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:353) | +| | | BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | +| | | AtomicReferenceArray#1[1].get(): Descriptor#4 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | +| | └╶╶╶╶╶╶ switch (reason: active lock detected) | +| apply(false,0,true) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:375) | | +| status.READ: SUCCESS at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:351) | | +| status.compareAndSet(SUCCESS,SUCCESS): true at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:352) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> installOrHelp(true,0,true) at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:353) | | +| | BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | +| | AtomicReferenceArray#1[0].get(): Descriptor#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | +| | Descriptor#1.apply(false,0,true) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:375) | | +| | status.compareAndSet(SUCCESS,SUCCESS): false at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:352) | | +| └╶╶╶╶╶╶ switch (reason: active lock detected) | | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock diff --git a/src/jvm/test/resources/expected_logs/spin_lock/infinite_spin_loop_events_arrays.txt b/src/jvm/test/resources/expected_logs/spin_lock/infinite_spin_loop_events_arrays.txt new file mode 100644 index 0000000000..7c5253516c --- /dev/null +++ b/src/jvm/test/resources/expected_logs/spin_lock/infinite_spin_loop_events_arrays.txt @@ -0,0 +1,236 @@ += The execution has hung = +| ------------------------ | +| Thread 1 | Thread 2 | +| ------------------------ | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| two(): 2 | | +| ------------------------ | +| two(): | two(): 2 | +| | two(): 2 | +| | two(): 2 | +| | one(): 1 | +| | one(): 1 | +| ------------------------ | + +--- +All operations above the horizontal line | ----- | happen before those below the line +--- + + +The following interleaving leads to the error: +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| two(): 2 | | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | two(): 2 | +| | two(): 2 | +| | two(): 2 | +| | one(): 1 | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | switch | +| two(): | | +| sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:266) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[0].WRITE(0) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[1].WRITE(0) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[2].WRITE(0) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[0].WRITE(1) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[1].WRITE(1) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[2].WRITE(1) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[0].WRITE(2) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[1].WRITE(2) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[2].WRITE(2) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| └╶╶ switch (reason: active lock detected) | | +| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | +| | result: 1 | +| | one(): 1 | +| /* The following events repeat infinitely: */ | | +| ┌╶> array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[0].WRITE(0) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[1].WRITE(0) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[2].WRITE(0) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[0].WRITE(1) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[1].WRITE(1) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[2].WRITE(1) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[0].WRITE(2) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[1].WRITE(2) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[2].WRITE(2) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| └╶╶ switch (reason: active lock detected) | | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock + +Detailed trace: +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| two(): 2 | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | | +| result: 2 | | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | one(): 1 | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | switch | +| two(): | | +| sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:266) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[0].WRITE(0) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[1].WRITE(0) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[2].WRITE(0) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[0].WRITE(1) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[1].WRITE(1) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[2].WRITE(1) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[0].WRITE(2) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[1].WRITE(2) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[2].WRITE(2) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| └╶╶ switch (reason: active lock detected) | | +| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | +| | result: 1 | +| | one(): 1 | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | +| | result: 1 | +| /* The following events repeat infinitely: */ | | +| ┌╶> array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[0].WRITE(0) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[1].WRITE(0) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[2].WRITE(0) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[0].WRITE(1) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[1].WRITE(1) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[2].WRITE(1) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[0].WRITE(2) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[1].WRITE(2) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | Array#1[2].WRITE(2) at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:154) | | +| | array.READ: Array#1 at SpinlockEventsCutInfiniteLoopWithArrayOperationsTest.meaninglessActions(SpinlockEventsCutTests.kt:156) | | +| └╶╶ switch (reason: active lock detected) | | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock diff --git a/src/jvm/test/resources/expected_logs/spin_lock/infinite_spin_loop_events_arrays_receivers.txt b/src/jvm/test/resources/expected_logs/spin_lock/infinite_spin_loop_events_arrays_receivers.txt new file mode 100644 index 0000000000..050586d0d6 --- /dev/null +++ b/src/jvm/test/resources/expected_logs/spin_lock/infinite_spin_loop_events_arrays_receivers.txt @@ -0,0 +1,136 @@ += The execution has hung = +| ------------------------ | +| Thread 1 | Thread 2 | +| ------------------------ | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| two(): 2 | | +| ------------------------ | +| two(): | two(): 2 | +| | two(): 2 | +| | two(): 2 | +| | one(): 1 | +| | one(): 1 | +| ------------------------ | + +--- +All operations above the horizontal line | ----- | happen before those below the line +--- + + +The following interleaving leads to the error: +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| two(): 2 | | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | two(): 2 | +| | two(): 2 | +| | two(): 2 | +| | one(): 1 | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | switch | +| two(): | | +| sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:266) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> Array#1[0].WRITE(1) at SpinlockEventsCutInfiniteLoopWithArrayReceiversTest.meaninglessActions(SpinlockEventsCutTests.kt:181) | | +| | Array#2[0].WRITE(1) at SpinlockEventsCutInfiniteLoopWithArrayReceiversTest.meaninglessActions(SpinlockEventsCutTests.kt:181) | | +| └╶╶ switch (reason: active lock detected) | | +| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | +| | result: 1 | +| | one(): 1 | +| /* The following events repeat infinitely: */ | | +| ┌╶> Array#1[0].WRITE(1) at SpinlockEventsCutInfiniteLoopWithArrayReceiversTest.meaninglessActions(SpinlockEventsCutTests.kt:181) | | +| | Array#2[0].WRITE(1) at SpinlockEventsCutInfiniteLoopWithArrayReceiversTest.meaninglessActions(SpinlockEventsCutTests.kt:181) | | +| └╶╶ switch (reason: active lock detected) | | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock + +Detailed trace: +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| two(): 2 | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | | +| result: 2 | | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | one(): 1 | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | switch | +| two(): | | +| sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:266) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> Array#1[0].WRITE(1) at SpinlockEventsCutInfiniteLoopWithArrayReceiversTest.meaninglessActions(SpinlockEventsCutTests.kt:181) | | +| | Array#2[0].WRITE(1) at SpinlockEventsCutInfiniteLoopWithArrayReceiversTest.meaninglessActions(SpinlockEventsCutTests.kt:181) | | +| └╶╶ switch (reason: active lock detected) | | +| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | +| | result: 1 | +| | one(): 1 | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | +| | result: 1 | +| /* The following events repeat infinitely: */ | | +| ┌╶> Array#1[0].WRITE(1) at SpinlockEventsCutInfiniteLoopWithArrayReceiversTest.meaninglessActions(SpinlockEventsCutTests.kt:181) | | +| | Array#2[0].WRITE(1) at SpinlockEventsCutInfiniteLoopWithArrayReceiversTest.meaninglessActions(SpinlockEventsCutTests.kt:181) | | +| └╶╶ switch (reason: active lock detected) | | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock diff --git a/src/jvm/test/resources/expected_logs/infinite_spin_loop_events_cut.txt b/src/jvm/test/resources/expected_logs/spin_lock/infinite_spin_loop_events_cut.txt similarity index 78% rename from src/jvm/test/resources/expected_logs/infinite_spin_loop_events_cut.txt rename to src/jvm/test/resources/expected_logs/spin_lock/infinite_spin_loop_events_cut.txt index 0df0013f56..880b62e25c 100644 --- a/src/jvm/test/resources/expected_logs/infinite_spin_loop_events_cut.txt +++ b/src/jvm/test/resources/expected_logs/spin_lock/infinite_spin_loop_events_cut.txt @@ -34,24 +34,28 @@ The following interleaving leads to the error: | | two(): 2 | | | two(): 2 | | | one(): 1 | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | | switch | | two(): | | -| sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | | -| meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:151) | | +| sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:266) | | | /* The following events repeat infinitely: */ | | -| sharedStateAny.get(): false at SpinlockEventsCutInfiniteLoopTest.meaninglessActions(SpinlockEventsCutTests.kt:88) | | -| sharedStateAny.set(true) at SpinlockEventsCutInfiniteLoopTest.meaninglessActions(SpinlockEventsCutTests.kt:89) | | -| switch (reason: active lock detected) | | -| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | +| ┌╶> sharedStateAny.get(): false at SpinlockEventsCutInfiniteLoopTest.meaninglessActions(SpinlockEventsCutTests.kt:89) | | +| | sharedStateAny.set(true) at SpinlockEventsCutInfiniteLoopTest.meaninglessActions(SpinlockEventsCutTests.kt:90) | | +| | sharedStateAny.get(): true at SpinlockEventsCutInfiniteLoopTest.meaninglessActions(SpinlockEventsCutTests.kt:89) | | +| | sharedStateAny.set(false) at SpinlockEventsCutInfiniteLoopTest.meaninglessActions(SpinlockEventsCutTests.kt:90) | | +| └╶╶ switch (reason: active lock detected) | | +| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | | result: 1 | | | one(): 1 | | /* The following events repeat infinitely: */ | | -| sharedStateAny.get(): true at SpinlockEventsCutInfiniteLoopTest.meaninglessActions(SpinlockEventsCutTests.kt:88) | | -| sharedStateAny.set(false) at SpinlockEventsCutInfiniteLoopTest.meaninglessActions(SpinlockEventsCutTests.kt:89) | | -| switch (reason: active lock detected) | | +| ┌╶> sharedStateAny.get(): false at SpinlockEventsCutInfiniteLoopTest.meaninglessActions(SpinlockEventsCutTests.kt:89) | | +| | sharedStateAny.set(true) at SpinlockEventsCutInfiniteLoopTest.meaninglessActions(SpinlockEventsCutTests.kt:90) | | +| | sharedStateAny.get(): true at SpinlockEventsCutInfiniteLoopTest.meaninglessActions(SpinlockEventsCutTests.kt:89) | | +| | sharedStateAny.set(false) at SpinlockEventsCutInfiniteLoopTest.meaninglessActions(SpinlockEventsCutTests.kt:90) | | +| └╶╶ switch (reason: active lock detected) | | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | All unfinished threads are in deadlock @@ -60,77 +64,81 @@ Detailed trace: | Thread 1 | Thread 2 | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | one(): 1 | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | | result: 1 | | | one(): 1 | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | | result: 1 | | | one(): 1 | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | | result: 1 | | | one(): 1 | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | | result: 1 | | | two(): 2 | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:156) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:157) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | | | result: 2 | | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | | two(): 2 | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:156) | -| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:157) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | | | result: 2 | | | two(): 2 | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:156) | -| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:157) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | | | result: 2 | | | two(): 2 | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:156) | -| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:157) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | | | result: 2 | | | one(): 1 | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | | switch | | two(): | | -| sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | | -| meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:151) | | +| sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:266) | | | /* The following events repeat infinitely: */ | | -| sharedStateAny.get(): false at SpinlockEventsCutInfiniteLoopTest.meaninglessActions(SpinlockEventsCutTests.kt:88) | | -| sharedStateAny.set(true) at SpinlockEventsCutInfiniteLoopTest.meaninglessActions(SpinlockEventsCutTests.kt:89) | | -| switch (reason: active lock detected) | | -| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | +| ┌╶> sharedStateAny.get(): false at SpinlockEventsCutInfiniteLoopTest.meaninglessActions(SpinlockEventsCutTests.kt:89) | | +| | sharedStateAny.set(true) at SpinlockEventsCutInfiniteLoopTest.meaninglessActions(SpinlockEventsCutTests.kt:90) | | +| | sharedStateAny.get(): true at SpinlockEventsCutInfiniteLoopTest.meaninglessActions(SpinlockEventsCutTests.kt:89) | | +| | sharedStateAny.set(false) at SpinlockEventsCutInfiniteLoopTest.meaninglessActions(SpinlockEventsCutTests.kt:90) | | +| └╶╶ switch (reason: active lock detected) | | +| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | | result: 1 | | | one(): 1 | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | -| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | | result: 1 | | /* The following events repeat infinitely: */ | | -| sharedStateAny.get(): true at SpinlockEventsCutInfiniteLoopTest.meaninglessActions(SpinlockEventsCutTests.kt:88) | | -| sharedStateAny.set(false) at SpinlockEventsCutInfiniteLoopTest.meaninglessActions(SpinlockEventsCutTests.kt:89) | | -| switch (reason: active lock detected) | | +| ┌╶> sharedStateAny.get(): false at SpinlockEventsCutInfiniteLoopTest.meaninglessActions(SpinlockEventsCutTests.kt:89) | | +| | sharedStateAny.set(true) at SpinlockEventsCutInfiniteLoopTest.meaninglessActions(SpinlockEventsCutTests.kt:90) | | +| | sharedStateAny.get(): true at SpinlockEventsCutInfiniteLoopTest.meaninglessActions(SpinlockEventsCutTests.kt:89) | | +| | sharedStateAny.set(false) at SpinlockEventsCutInfiniteLoopTest.meaninglessActions(SpinlockEventsCutTests.kt:90) | | +| └╶╶ switch (reason: active lock detected) | | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | All unfinished threads are in deadlock diff --git a/src/jvm/test/resources/expected_logs/spin_lock/infinite_spin_loop_events_no_cycle_params.txt b/src/jvm/test/resources/expected_logs/spin_lock/infinite_spin_loop_events_no_cycle_params.txt new file mode 100644 index 0000000000..a6da7b0b14 --- /dev/null +++ b/src/jvm/test/resources/expected_logs/spin_lock/infinite_spin_loop_events_no_cycle_params.txt @@ -0,0 +1,140 @@ += The execution has hung = +| ------------------------ | +| Thread 1 | Thread 2 | +| ------------------------ | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| two(): 2 | | +| ------------------------ | +| two(): | two(): 2 | +| | two(): 2 | +| | two(): 2 | +| | one(): 1 | +| | one(): 1 | +| ------------------------ | + +--- +All operations above the horizontal line | ----- | happen before those below the line +--- + + +The following interleaving leads to the error: +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| two(): 2 | | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | two(): 2 | +| | two(): 2 | +| | two(): 2 | +| | one(): 1 | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | switch | +| two(): | | +| sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:266) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> Array#1[0].WRITE(-1147404850) at SpinlockEventsCutInfiniteNoCycleWithParamsTest.meaninglessActions(SpinlockEventsCutTests.kt:201) | | +| | Array#1[0].WRITE(-1147404849) at SpinlockEventsCutInfiniteNoCycleWithParamsTest.meaninglessActions(SpinlockEventsCutTests.kt:202) | | +| | Array#1[0].WRITE(-1147404848) at SpinlockEventsCutInfiniteNoCycleWithParamsTest.meaninglessActions(SpinlockEventsCutTests.kt:203) | | +| └╶╶ switch (reason: active lock detected) | | +| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | +| | result: 1 | +| | one(): 1 | +| /* The following events repeat infinitely: */ | | +| ┌╶> Array#1[0].WRITE(780672281) at SpinlockEventsCutInfiniteNoCycleWithParamsTest.meaninglessActions(SpinlockEventsCutTests.kt:201) | | +| | Array#1[0].WRITE(780672282) at SpinlockEventsCutInfiniteNoCycleWithParamsTest.meaninglessActions(SpinlockEventsCutTests.kt:202) | | +| | Array#1[0].WRITE(780672283) at SpinlockEventsCutInfiniteNoCycleWithParamsTest.meaninglessActions(SpinlockEventsCutTests.kt:203) | | +| └╶╶ switch (reason: active lock detected) | | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock + +Detailed trace: +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| two(): 2 | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | | +| result: 2 | | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | one(): 1 | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | switch | +| two(): | | +| sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:266) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> Array#1[0].WRITE(-1147404850) at SpinlockEventsCutInfiniteNoCycleWithParamsTest.meaninglessActions(SpinlockEventsCutTests.kt:201) | | +| | Array#1[0].WRITE(-1147404849) at SpinlockEventsCutInfiniteNoCycleWithParamsTest.meaninglessActions(SpinlockEventsCutTests.kt:202) | | +| | Array#1[0].WRITE(-1147404848) at SpinlockEventsCutInfiniteNoCycleWithParamsTest.meaninglessActions(SpinlockEventsCutTests.kt:203) | | +| └╶╶ switch (reason: active lock detected) | | +| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | +| | result: 1 | +| | one(): 1 | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | +| | result: 1 | +| /* The following events repeat infinitely: */ | | +| ┌╶> Array#1[0].WRITE(780672281) at SpinlockEventsCutInfiniteNoCycleWithParamsTest.meaninglessActions(SpinlockEventsCutTests.kt:201) | | +| | Array#1[0].WRITE(780672282) at SpinlockEventsCutInfiniteNoCycleWithParamsTest.meaninglessActions(SpinlockEventsCutTests.kt:202) | | +| | Array#1[0].WRITE(780672283) at SpinlockEventsCutInfiniteNoCycleWithParamsTest.meaninglessActions(SpinlockEventsCutTests.kt:203) | | +| └╶╶ switch (reason: active lock detected) | | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock diff --git a/src/jvm/test/resources/expected_logs/spin_lock/infinite_spin_loop_events_read_write.txt b/src/jvm/test/resources/expected_logs/spin_lock/infinite_spin_loop_events_read_write.txt new file mode 100644 index 0000000000..dc3f1a20f7 --- /dev/null +++ b/src/jvm/test/resources/expected_logs/spin_lock/infinite_spin_loop_events_read_write.txt @@ -0,0 +1,144 @@ += The execution has hung = +| ------------------------ | +| Thread 1 | Thread 2 | +| ------------------------ | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| two(): 2 | | +| ------------------------ | +| two(): | two(): 2 | +| | two(): 2 | +| | two(): 2 | +| | one(): 1 | +| | one(): 1 | +| ------------------------ | + +--- +All operations above the horizontal line | ----- | happen before those below the line +--- + + +The following interleaving leads to the error: +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| two(): 2 | | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | two(): 2 | +| | two(): 2 | +| | two(): 2 | +| | one(): 1 | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | switch | +| two(): | | +| sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:266) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> sharedState.READ: false at SpinlockEventsCutInfiniteLoopWithParametersTest.meaninglessActions(SpinlockEventsCutTests.kt:108) | | +| | sharedState.WRITE(true) at SpinlockEventsCutInfiniteLoopWithParametersTest.meaninglessActions(SpinlockEventsCutTests.kt:109) | | +| | sharedState.READ: true at SpinlockEventsCutInfiniteLoopWithParametersTest.meaninglessActions(SpinlockEventsCutTests.kt:108) | | +| | sharedState.WRITE(false) at SpinlockEventsCutInfiniteLoopWithParametersTest.meaninglessActions(SpinlockEventsCutTests.kt:109) | | +| └╶╶ switch (reason: active lock detected) | | +| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | +| | result: 1 | +| | one(): 1 | +| /* The following events repeat infinitely: */ | | +| ┌╶> sharedState.READ: false at SpinlockEventsCutInfiniteLoopWithParametersTest.meaninglessActions(SpinlockEventsCutTests.kt:108) | | +| | sharedState.WRITE(true) at SpinlockEventsCutInfiniteLoopWithParametersTest.meaninglessActions(SpinlockEventsCutTests.kt:109) | | +| | sharedState.READ: true at SpinlockEventsCutInfiniteLoopWithParametersTest.meaninglessActions(SpinlockEventsCutTests.kt:108) | | +| | sharedState.WRITE(false) at SpinlockEventsCutInfiniteLoopWithParametersTest.meaninglessActions(SpinlockEventsCutTests.kt:109) | | +| └╶╶ switch (reason: active lock detected) | | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock + +Detailed trace: +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| two(): 2 | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | | +| result: 2 | | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | one(): 1 | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | switch | +| two(): | | +| sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:266) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> sharedState.READ: false at SpinlockEventsCutInfiniteLoopWithParametersTest.meaninglessActions(SpinlockEventsCutTests.kt:108) | | +| | sharedState.WRITE(true) at SpinlockEventsCutInfiniteLoopWithParametersTest.meaninglessActions(SpinlockEventsCutTests.kt:109) | | +| | sharedState.READ: true at SpinlockEventsCutInfiniteLoopWithParametersTest.meaninglessActions(SpinlockEventsCutTests.kt:108) | | +| | sharedState.WRITE(false) at SpinlockEventsCutInfiniteLoopWithParametersTest.meaninglessActions(SpinlockEventsCutTests.kt:109) | | +| └╶╶ switch (reason: active lock detected) | | +| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | +| | result: 1 | +| | one(): 1 | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | +| | result: 1 | +| /* The following events repeat infinitely: */ | | +| ┌╶> sharedState.READ: false at SpinlockEventsCutInfiniteLoopWithParametersTest.meaninglessActions(SpinlockEventsCutTests.kt:108) | | +| | sharedState.WRITE(true) at SpinlockEventsCutInfiniteLoopWithParametersTest.meaninglessActions(SpinlockEventsCutTests.kt:109) | | +| | sharedState.READ: true at SpinlockEventsCutInfiniteLoopWithParametersTest.meaninglessActions(SpinlockEventsCutTests.kt:108) | | +| | sharedState.WRITE(false) at SpinlockEventsCutInfiniteLoopWithParametersTest.meaninglessActions(SpinlockEventsCutTests.kt:109) | | +| └╶╶ switch (reason: active lock detected) | | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock diff --git a/src/jvm/test/resources/expected_logs/spin_lock/infinite_spin_loop_events_receivers.txt b/src/jvm/test/resources/expected_logs/spin_lock/infinite_spin_loop_events_receivers.txt new file mode 100644 index 0000000000..05e77b330e --- /dev/null +++ b/src/jvm/test/resources/expected_logs/spin_lock/infinite_spin_loop_events_receivers.txt @@ -0,0 +1,140 @@ += The execution has hung = +| ------------------------ | +| Thread 1 | Thread 2 | +| ------------------------ | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| two(): 2 | | +| ------------------------ | +| two(): | two(): 2 | +| | two(): 2 | +| | two(): 2 | +| | one(): 1 | +| | one(): 1 | +| ------------------------ | + +--- +All operations above the horizontal line | ----- | happen before those below the line +--- + + +The following interleaving leads to the error: +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| two(): 2 | | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | two(): 2 | +| | two(): 2 | +| | two(): 2 | +| | one(): 1 | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | switch | +| two(): | | +| sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:266) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> second.setValue(false) at SpinlockEventsCutInfiniteLoopWithReceiversTest.meaninglessActions(SpinlockEventsCutTests.kt:131) | | +| | first.setValue(false) at SpinlockEventsCutInfiniteLoopWithReceiversTest.meaninglessActions(SpinlockEventsCutTests.kt:131) | | +| └╶╶ switch (reason: active lock detected) | | +| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | +| | result: 1 | +| | one(): 1 | +| /* The following events repeat infinitely: */ | | +| ┌╶> second.setValue(false) at SpinlockEventsCutInfiniteLoopWithReceiversTest.meaninglessActions(SpinlockEventsCutTests.kt:131) | | +| | first.setValue(false) at SpinlockEventsCutInfiniteLoopWithReceiversTest.meaninglessActions(SpinlockEventsCutTests.kt:131) | | +| └╶╶ switch (reason: active lock detected) | | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock + +Detailed trace: +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| two(): 2 | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | | +| result: 2 | | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | one(): 1 | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | switch | +| two(): | | +| sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:266) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> second.setValue(false) at SpinlockEventsCutInfiniteLoopWithReceiversTest.meaninglessActions(SpinlockEventsCutTests.kt:131) | | +| | value.WRITE(false) at SpinlockEventsCutInfiniteLoopWithReceiversTest$Receiver.setValue(SpinlockEventsCutTests.kt:136) | | +| | first.setValue(false) at SpinlockEventsCutInfiniteLoopWithReceiversTest.meaninglessActions(SpinlockEventsCutTests.kt:131) | | +| | value.WRITE(false) at SpinlockEventsCutInfiniteLoopWithReceiversTest$Receiver.setValue(SpinlockEventsCutTests.kt:136) | | +| └╶╶ switch (reason: active lock detected) | | +| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | +| | result: 1 | +| | one(): 1 | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | +| | result: 1 | +| /* The following events repeat infinitely: */ | | +| ┌╶> second.setValue(false) at SpinlockEventsCutInfiniteLoopWithReceiversTest.meaninglessActions(SpinlockEventsCutTests.kt:131) | | +| | value.WRITE(false) at SpinlockEventsCutInfiniteLoopWithReceiversTest$Receiver.setValue(SpinlockEventsCutTests.kt:136) | | +| | first.setValue(false) at SpinlockEventsCutInfiniteLoopWithReceiversTest.meaninglessActions(SpinlockEventsCutTests.kt:131) | | +| | value.WRITE(false) at SpinlockEventsCutInfiniteLoopWithReceiversTest$Receiver.setValue(SpinlockEventsCutTests.kt:136) | | +| └╶╶ switch (reason: active lock detected) | | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock diff --git a/src/jvm/test/resources/expected_logs/spin_lock/recursive_spin_cycle_inner_events.txt b/src/jvm/test/resources/expected_logs/spin_lock/recursive_spin_cycle_inner_events.txt new file mode 100644 index 0000000000..6ed60546d7 --- /dev/null +++ b/src/jvm/test/resources/expected_logs/spin_lock/recursive_spin_cycle_inner_events.txt @@ -0,0 +1,95 @@ += The execution has hung = +| ------------------------------------------ | +| Thread 1 | Thread 2 | +| ------------------------------------------ | +| trigger(): void | causesSpinLock(): | +| ------------------------------------------ | + + +The following interleaving leads to the error: +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| trigger() | | +| counter.incrementAndGet(): 1 at RecursiveSpinLockWithInnerEventsTest.trigger(RecursiveSpinLockTest.kt:126) | | +| switch | | +| | causesSpinLock(): | +| | counter.get(): 1 at RecursiveSpinLockWithInnerEventsTest.causesSpinLock(RecursiveSpinLockTest.kt:132) | +| | /* The following events repeat infinitely: */ | +| | ┌╶> deadSpinCycleRecursive() at RecursiveSpinLockWithInnerEventsTest.causesSpinLock(RecursiveSpinLockTest.kt:133) | +| | | getSharedVariable(): false at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:139) | +| | | action(false): true at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:140) | +| | | getSharedVariable(): true at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:139) | +| | | action(true): true at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:140) | +| | | getSharedVariable(): false at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:139) | +| | | action(false): true at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:140) | +| | | getSharedVariable(): true at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:139) | +| | | action(true): true at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:140) | +| | └╶╶╶╶ switch (reason: active lock detected) | +| counter.decrementAndGet(): 0 at RecursiveSpinLockWithInnerEventsTest.trigger(RecursiveSpinLockTest.kt:127) | | +| result: void | | +| | /* The following events repeat infinitely: */ | +| | ┌╶> deadSpinCycleRecursive() at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:142) | +| | | getSharedVariable(): false at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:139) | +| | | action(false): true at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:140) | +| | | getSharedVariable(): true at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:139) | +| | | action(true): true at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:140) | +| | | getSharedVariable(): false at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:139) | +| | | action(false): true at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:140) | +| | | getSharedVariable(): true at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:139) | +| | | action(true): true at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:140) | +| | └╶╶╶╶ switch (reason: active lock detected) | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock + +Detailed trace: +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Thread 1 | Thread 2 | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| trigger() | | +| counter.incrementAndGet(): 1 at RecursiveSpinLockWithInnerEventsTest.trigger(RecursiveSpinLockTest.kt:126) | | +| switch | | +| | causesSpinLock(): | +| | counter.get(): 1 at RecursiveSpinLockWithInnerEventsTest.causesSpinLock(RecursiveSpinLockTest.kt:132) | +| | /* The following events repeat infinitely: */ | +| | ┌╶> deadSpinCycleRecursive() at RecursiveSpinLockWithInnerEventsTest.causesSpinLock(RecursiveSpinLockTest.kt:133) | +| | | getSharedVariable(): false at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:139) | +| | | someUselessSharedState.get(): false at RecursiveSpinLockWithInnerEventsTest.getSharedVariable(RecursiveSpinLockTest.kt:147) | +| | | action(false): true at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:140) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinLockWithInnerEventsTest.action(RecursiveSpinLockTest.kt:145) | +| | | getSharedVariable(): true at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:139) | +| | | someUselessSharedState.get(): true at RecursiveSpinLockWithInnerEventsTest.getSharedVariable(RecursiveSpinLockTest.kt:147) | +| | | action(true): true at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:140) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinLockWithInnerEventsTest.action(RecursiveSpinLockTest.kt:145) | +| | | getSharedVariable(): false at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:139) | +| | | someUselessSharedState.get(): false at RecursiveSpinLockWithInnerEventsTest.getSharedVariable(RecursiveSpinLockTest.kt:147) | +| | | action(false): true at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:140) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinLockWithInnerEventsTest.action(RecursiveSpinLockTest.kt:145) | +| | | getSharedVariable(): true at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:139) | +| | | someUselessSharedState.get(): true at RecursiveSpinLockWithInnerEventsTest.getSharedVariable(RecursiveSpinLockTest.kt:147) | +| | | action(true): true at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:140) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinLockWithInnerEventsTest.action(RecursiveSpinLockTest.kt:145) | +| | └╶╶╶╶ switch (reason: active lock detected) | +| counter.decrementAndGet(): 0 at RecursiveSpinLockWithInnerEventsTest.trigger(RecursiveSpinLockTest.kt:127) | | +| result: void | | +| | /* The following events repeat infinitely: */ | +| | ┌╶> deadSpinCycleRecursive() at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:142) | +| | | getSharedVariable(): false at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:139) | +| | | someUselessSharedState.get(): false at RecursiveSpinLockWithInnerEventsTest.getSharedVariable(RecursiveSpinLockTest.kt:147) | +| | | action(false): true at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:140) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinLockWithInnerEventsTest.action(RecursiveSpinLockTest.kt:145) | +| | | getSharedVariable(): true at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:139) | +| | | someUselessSharedState.get(): true at RecursiveSpinLockWithInnerEventsTest.getSharedVariable(RecursiveSpinLockTest.kt:147) | +| | | action(true): true at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:140) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinLockWithInnerEventsTest.action(RecursiveSpinLockTest.kt:145) | +| | | getSharedVariable(): false at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:139) | +| | | someUselessSharedState.get(): false at RecursiveSpinLockWithInnerEventsTest.getSharedVariable(RecursiveSpinLockTest.kt:147) | +| | | action(false): true at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:140) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinLockWithInnerEventsTest.action(RecursiveSpinLockTest.kt:145) | +| | | getSharedVariable(): true at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:139) | +| | | someUselessSharedState.get(): true at RecursiveSpinLockWithInnerEventsTest.getSharedVariable(RecursiveSpinLockTest.kt:147) | +| | | action(true): true at RecursiveSpinLockWithInnerEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:140) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinLockWithInnerEventsTest.action(RecursiveSpinLockTest.kt:145) | +| | └╶╶╶╶ switch (reason: active lock detected) | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +All unfinished threads are in deadlock diff --git a/src/jvm/test/resources/expected_logs/spin_lock/recursive_spin_cycle_two_step.txt b/src/jvm/test/resources/expected_logs/spin_lock/recursive_spin_cycle_two_step.txt new file mode 100644 index 0000000000..cdf76c5fa2 --- /dev/null +++ b/src/jvm/test/resources/expected_logs/spin_lock/recursive_spin_cycle_two_step.txt @@ -0,0 +1,99 @@ += The execution has hung = +| ------------------------------------------ | +| Thread 1 | Thread 2 | +| ------------------------------------------ | +| trigger(): void | causesSpinLock(): | +| ------------------------------------------ | + + +The following interleaving leads to the error: +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| trigger() | | +| counter.incrementAndGet(): 1 at RecursiveSpinLockTwoStepRecursionEventsTest.trigger(RecursiveSpinLockTest.kt:173) | | +| switch | | +| | causesSpinLock(): | +| | counter.get(): 1 at RecursiveSpinLockTwoStepRecursionEventsTest.causesSpinLock(RecursiveSpinLockTest.kt:179) | +| | /* The following events repeat infinitely: */ | +| | ┌╶> outerRecursiveSpinCycle() at RecursiveSpinLockTwoStepRecursionEventsTest.causesSpinLock(RecursiveSpinLockTest.kt:180) | +| | | deadSpinCycleRecursive() at RecursiveSpinLockTwoStepRecursionEventsTest.outerRecursiveSpinCycle(RecursiveSpinLockTest.kt:185) | +| | | getSharedVariable(): false at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:190) | +| | | action(false): true at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:191) | +| | | getSharedVariable(): true at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:190) | +| | | action(true): true at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:191) | +| | | getSharedVariable(): false at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:190) | +| | | action(false): true at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:191) | +| | | getSharedVariable(): true at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:190) | +| | | action(true): true at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:191) | +| | └╶╶╶╶╶╶ switch (reason: active lock detected) | +| counter.decrementAndGet(): 0 at RecursiveSpinLockTwoStepRecursionEventsTest.trigger(RecursiveSpinLockTest.kt:174) | | +| result: void | | +| | /* The following events repeat infinitely: */ | +| | ┌╶> outerRecursiveSpinCycle() at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:193) | +| | | deadSpinCycleRecursive() at RecursiveSpinLockTwoStepRecursionEventsTest.outerRecursiveSpinCycle(RecursiveSpinLockTest.kt:185) | +| | | getSharedVariable(): false at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:190) | +| | | action(false): true at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:191) | +| | | getSharedVariable(): true at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:190) | +| | | action(true): true at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:191) | +| | | getSharedVariable(): false at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:190) | +| | | action(false): true at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:191) | +| | | getSharedVariable(): true at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:190) | +| | | action(true): true at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:191) | +| | └╶╶╶╶╶╶ switch (reason: active lock detected) | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock + +Detailed trace: +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Thread 1 | Thread 2 | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| trigger() | | +| counter.incrementAndGet(): 1 at RecursiveSpinLockTwoStepRecursionEventsTest.trigger(RecursiveSpinLockTest.kt:173) | | +| switch | | +| | causesSpinLock(): | +| | counter.get(): 1 at RecursiveSpinLockTwoStepRecursionEventsTest.causesSpinLock(RecursiveSpinLockTest.kt:179) | +| | /* The following events repeat infinitely: */ | +| | ┌╶> outerRecursiveSpinCycle() at RecursiveSpinLockTwoStepRecursionEventsTest.causesSpinLock(RecursiveSpinLockTest.kt:180) | +| | | deadSpinCycleRecursive() at RecursiveSpinLockTwoStepRecursionEventsTest.outerRecursiveSpinCycle(RecursiveSpinLockTest.kt:185) | +| | | getSharedVariable(): false at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:190) | +| | | someUselessSharedState.get(): false at RecursiveSpinLockTwoStepRecursionEventsTest.getSharedVariable(RecursiveSpinLockTest.kt:198) | +| | | action(false): true at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:191) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinLockTwoStepRecursionEventsTest.action(RecursiveSpinLockTest.kt:196) | +| | | getSharedVariable(): true at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:190) | +| | | someUselessSharedState.get(): true at RecursiveSpinLockTwoStepRecursionEventsTest.getSharedVariable(RecursiveSpinLockTest.kt:198) | +| | | action(true): true at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:191) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinLockTwoStepRecursionEventsTest.action(RecursiveSpinLockTest.kt:196) | +| | | getSharedVariable(): false at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:190) | +| | | someUselessSharedState.get(): false at RecursiveSpinLockTwoStepRecursionEventsTest.getSharedVariable(RecursiveSpinLockTest.kt:198) | +| | | action(false): true at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:191) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinLockTwoStepRecursionEventsTest.action(RecursiveSpinLockTest.kt:196) | +| | | getSharedVariable(): true at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:190) | +| | | someUselessSharedState.get(): true at RecursiveSpinLockTwoStepRecursionEventsTest.getSharedVariable(RecursiveSpinLockTest.kt:198) | +| | | action(true): true at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:191) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinLockTwoStepRecursionEventsTest.action(RecursiveSpinLockTest.kt:196) | +| | └╶╶╶╶╶╶ switch (reason: active lock detected) | +| counter.decrementAndGet(): 0 at RecursiveSpinLockTwoStepRecursionEventsTest.trigger(RecursiveSpinLockTest.kt:174) | | +| result: void | | +| | /* The following events repeat infinitely: */ | +| | ┌╶> outerRecursiveSpinCycle() at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:193) | +| | | deadSpinCycleRecursive() at RecursiveSpinLockTwoStepRecursionEventsTest.outerRecursiveSpinCycle(RecursiveSpinLockTest.kt:185) | +| | | getSharedVariable(): false at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:190) | +| | | someUselessSharedState.get(): false at RecursiveSpinLockTwoStepRecursionEventsTest.getSharedVariable(RecursiveSpinLockTest.kt:198) | +| | | action(false): true at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:191) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinLockTwoStepRecursionEventsTest.action(RecursiveSpinLockTest.kt:196) | +| | | getSharedVariable(): true at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:190) | +| | | someUselessSharedState.get(): true at RecursiveSpinLockTwoStepRecursionEventsTest.getSharedVariable(RecursiveSpinLockTest.kt:198) | +| | | action(true): true at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:191) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinLockTwoStepRecursionEventsTest.action(RecursiveSpinLockTest.kt:196) | +| | | getSharedVariable(): false at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:190) | +| | | someUselessSharedState.get(): false at RecursiveSpinLockTwoStepRecursionEventsTest.getSharedVariable(RecursiveSpinLockTest.kt:198) | +| | | action(false): true at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:191) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinLockTwoStepRecursionEventsTest.action(RecursiveSpinLockTest.kt:196) | +| | | getSharedVariable(): true at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:190) | +| | | someUselessSharedState.get(): true at RecursiveSpinLockTwoStepRecursionEventsTest.getSharedVariable(RecursiveSpinLockTest.kt:198) | +| | | action(true): true at RecursiveSpinLockTwoStepRecursionEventsTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:191) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinLockTwoStepRecursionEventsTest.action(RecursiveSpinLockTest.kt:196) | +| | └╶╶╶╶╶╶ switch (reason: active lock detected) | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +All unfinished threads are in deadlock diff --git a/src/jvm/test/resources/expected_logs/spin_lock/recursive_spin_lock.txt b/src/jvm/test/resources/expected_logs/spin_lock/recursive_spin_lock.txt new file mode 100644 index 0000000000..b73f0c29a5 --- /dev/null +++ b/src/jvm/test/resources/expected_logs/spin_lock/recursive_spin_lock.txt @@ -0,0 +1,87 @@ += The execution has hung = +| ------------------------------------------ | +| Thread 1 | Thread 2 | +| ------------------------------------------ | +| trigger(): void | causesSpinLock(): | +| ------------------------------------------ | + + +The following interleaving leads to the error: +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Thread 1 | Thread 2 | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| trigger() | | +| counter.incrementAndGet(): 1 at RecursiveSpinLockTest.trigger(RecursiveSpinLockTest.kt:34) | | +| switch | | +| | causesSpinLock(): | +| | counter.get(): 1 at RecursiveSpinLockTest.causesSpinLock(RecursiveSpinLockTest.kt:40) | +| | /* The following events repeat infinitely: */ | +| | ┌╶> deadSpinCycleRecursive() at RecursiveSpinLockTest.causesSpinLock(RecursiveSpinLockTest.kt:41) | +| | | getSharedVariable(): false at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:47) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:48) | +| | | getSharedVariable(): true at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:47) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:48) | +| | | getSharedVariable(): false at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:47) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:48) | +| | | getSharedVariable(): true at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:47) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:48) | +| | └╶╶╶╶ switch (reason: active lock detected) | +| counter.decrementAndGet(): 0 at RecursiveSpinLockTest.trigger(RecursiveSpinLockTest.kt:35) | | +| result: void | | +| | /* The following events repeat infinitely: */ | +| | ┌╶> deadSpinCycleRecursive() at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:50) | +| | | getSharedVariable(): false at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:47) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:48) | +| | | getSharedVariable(): true at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:47) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:48) | +| | | getSharedVariable(): false at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:47) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:48) | +| | | getSharedVariable(): true at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:47) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:48) | +| | └╶╶╶╶ switch (reason: active lock detected) | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +All unfinished threads are in deadlock + +Detailed trace: +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Thread 1 | Thread 2 | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| trigger() | | +| counter.incrementAndGet(): 1 at RecursiveSpinLockTest.trigger(RecursiveSpinLockTest.kt:34) | | +| switch | | +| | causesSpinLock(): | +| | counter.get(): 1 at RecursiveSpinLockTest.causesSpinLock(RecursiveSpinLockTest.kt:40) | +| | /* The following events repeat infinitely: */ | +| | ┌╶> deadSpinCycleRecursive() at RecursiveSpinLockTest.causesSpinLock(RecursiveSpinLockTest.kt:41) | +| | | getSharedVariable(): false at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:47) | +| | | someUselessSharedState.get(): false at RecursiveSpinLockTest.getSharedVariable(RecursiveSpinLockTest.kt:53) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:48) | +| | | getSharedVariable(): true at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:47) | +| | | someUselessSharedState.get(): true at RecursiveSpinLockTest.getSharedVariable(RecursiveSpinLockTest.kt:53) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:48) | +| | | getSharedVariable(): false at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:47) | +| | | someUselessSharedState.get(): false at RecursiveSpinLockTest.getSharedVariable(RecursiveSpinLockTest.kt:53) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:48) | +| | | getSharedVariable(): true at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:47) | +| | | someUselessSharedState.get(): true at RecursiveSpinLockTest.getSharedVariable(RecursiveSpinLockTest.kt:53) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:48) | +| | └╶╶╶╶ switch (reason: active lock detected) | +| counter.decrementAndGet(): 0 at RecursiveSpinLockTest.trigger(RecursiveSpinLockTest.kt:35) | | +| result: void | | +| | /* The following events repeat infinitely: */ | +| | ┌╶> deadSpinCycleRecursive() at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:50) | +| | | getSharedVariable(): false at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:47) | +| | | someUselessSharedState.get(): false at RecursiveSpinLockTest.getSharedVariable(RecursiveSpinLockTest.kt:53) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:48) | +| | | getSharedVariable(): true at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:47) | +| | | someUselessSharedState.get(): true at RecursiveSpinLockTest.getSharedVariable(RecursiveSpinLockTest.kt:53) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:48) | +| | | getSharedVariable(): false at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:47) | +| | | someUselessSharedState.get(): false at RecursiveSpinLockTest.getSharedVariable(RecursiveSpinLockTest.kt:53) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:48) | +| | | getSharedVariable(): true at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:47) | +| | | someUselessSharedState.get(): true at RecursiveSpinLockTest.getSharedVariable(RecursiveSpinLockTest.kt:53) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:48) | +| | └╶╶╶╶ switch (reason: active lock detected) | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +All unfinished threads are in deadlock diff --git a/src/jvm/test/resources/expected_logs/spin_lock/recursive_spin_lock_param_dependent.txt b/src/jvm/test/resources/expected_logs/spin_lock/recursive_spin_lock_param_dependent.txt new file mode 100644 index 0000000000..9aae9f288c --- /dev/null +++ b/src/jvm/test/resources/expected_logs/spin_lock/recursive_spin_lock_param_dependent.txt @@ -0,0 +1,39 @@ += The execution has hung = +| --------------------- | +| Thread 1 | +| --------------------- | +| actorMethod(): | +| --------------------- | + + +The following interleaving leads to the error: +| ------------------------------------------------------------------------------------------------------------------------ | +| Thread 1 | +| ------------------------------------------------------------------------------------------------------------------------ | +| actorMethod(): | +| b(false) at RecursiveParametersDependentSpinLockTest.actorMethod(RecursiveSpinLockTest.kt:470) | +| /* The following events repeat infinitely: */ | +| ┌╶> b(true) at RecursiveParametersDependentSpinLockTest.actorMethod(RecursiveSpinLockTest.kt:471) | +| | value.compareAndSet(false,true): false at RecursiveParametersDependentSpinLockTest.b(RecursiveSpinLockTest.kt:476) | +| | c(): true at RecursiveParametersDependentSpinLockTest.b(RecursiveSpinLockTest.kt:477) | +| └╶╶╶╶ switch (reason: active lock detected) | +| ------------------------------------------------------------------------------------------------------------------------ | +All unfinished threads are in deadlock + +Detailed trace: +| ------------------------------------------------------------------------------------------------------------------------ | +| Thread 1 | +| ------------------------------------------------------------------------------------------------------------------------ | +| actorMethod(): | +| b(false) at RecursiveParametersDependentSpinLockTest.actorMethod(RecursiveSpinLockTest.kt:470) | +| value.compareAndSet(false,true): true at RecursiveParametersDependentSpinLockTest.b(RecursiveSpinLockTest.kt:476) | +| c(): true at RecursiveParametersDependentSpinLockTest.b(RecursiveSpinLockTest.kt:477) | +| value.get(): true at RecursiveParametersDependentSpinLockTest.c(RecursiveSpinLockTest.kt:483) | +| /* The following events repeat infinitely: */ | +| ┌╶> b(true) at RecursiveParametersDependentSpinLockTest.actorMethod(RecursiveSpinLockTest.kt:471) | +| | value.compareAndSet(false,true): false at RecursiveParametersDependentSpinLockTest.b(RecursiveSpinLockTest.kt:476) | +| | c(): true at RecursiveParametersDependentSpinLockTest.b(RecursiveSpinLockTest.kt:477) | +| | value.get(): true at RecursiveParametersDependentSpinLockTest.c(RecursiveSpinLockTest.kt:483) | +| └╶╶╶╶ switch (reason: active lock detected) | +| ------------------------------------------------------------------------------------------------------------------------ | +All unfinished threads are in deadlock diff --git a/src/jvm/test/resources/expected_logs/spin_lock/recursive_spin_lock_params.txt b/src/jvm/test/resources/expected_logs/spin_lock/recursive_spin_lock_params.txt new file mode 100644 index 0000000000..daeb88d598 --- /dev/null +++ b/src/jvm/test/resources/expected_logs/spin_lock/recursive_spin_lock_params.txt @@ -0,0 +1,139 @@ += The execution has hung = +| ------------------------------------------ | +| Thread 1 | Thread 2 | +| ------------------------------------------ | +| trigger(): void | causesSpinLock(): | +| ------------------------------------------ | + + +The following interleaving leads to the error: +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Thread 1 | Thread 2 | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| trigger() | | +| counter.incrementAndGet(): 1 at RecursiveSpinWithParamsLockTest.trigger(RecursiveSpinLockTest.kt:80) | | +| switch | | +| | causesSpinLock(): | +| | counter.get(): 1 at RecursiveSpinWithParamsLockTest.causesSpinLock(RecursiveSpinLockTest.kt:86) | +| | /* The following events repeat infinitely: */ | +| | ┌╶> deadSpinCycleRecursive(true) at RecursiveSpinWithParamsLockTest.causesSpinLock(RecursiveSpinLockTest.kt:87) | +| | | flag.set(true) at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:92) | +| | | getSharedVariable(): false at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | getSharedVariable(): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | getSharedVariable(): false at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | getSharedVariable(): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | deadSpinCycleRecursive(false) at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:97) | +| | | flag.set(false) at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:92) | +| | | getSharedVariable(): false at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | getSharedVariable(): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | getSharedVariable(): false at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | getSharedVariable(): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | └╶╶╶╶╶╶ switch (reason: active lock detected) | +| counter.decrementAndGet(): 0 at RecursiveSpinWithParamsLockTest.trigger(RecursiveSpinLockTest.kt:81) | | +| result: void | | +| | /* The following events repeat infinitely: */ | +| | ┌╶> deadSpinCycleRecursive(true) at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:97) | +| | | flag.set(true) at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:92) | +| | | getSharedVariable(): false at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | getSharedVariable(): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | getSharedVariable(): false at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | getSharedVariable(): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | deadSpinCycleRecursive(false) at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:97) | +| | | flag.set(false) at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:92) | +| | | getSharedVariable(): false at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | getSharedVariable(): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | getSharedVariable(): false at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | getSharedVariable(): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | └╶╶╶╶╶╶ switch (reason: active lock detected) | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +All unfinished threads are in deadlock + +Detailed trace: +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Thread 1 | Thread 2 | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| trigger() | | +| counter.incrementAndGet(): 1 at RecursiveSpinWithParamsLockTest.trigger(RecursiveSpinLockTest.kt:80) | | +| switch | | +| | causesSpinLock(): | +| | counter.get(): 1 at RecursiveSpinWithParamsLockTest.causesSpinLock(RecursiveSpinLockTest.kt:86) | +| | /* The following events repeat infinitely: */ | +| | ┌╶> deadSpinCycleRecursive(true) at RecursiveSpinWithParamsLockTest.causesSpinLock(RecursiveSpinLockTest.kt:87) | +| | | flag.set(true) at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:92) | +| | | getSharedVariable(): false at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.get(): false at RecursiveSpinWithParamsLockTest.getSharedVariable(RecursiveSpinLockTest.kt:100) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | getSharedVariable(): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.get(): true at RecursiveSpinWithParamsLockTest.getSharedVariable(RecursiveSpinLockTest.kt:100) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | getSharedVariable(): false at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.get(): false at RecursiveSpinWithParamsLockTest.getSharedVariable(RecursiveSpinLockTest.kt:100) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | getSharedVariable(): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.get(): true at RecursiveSpinWithParamsLockTest.getSharedVariable(RecursiveSpinLockTest.kt:100) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | deadSpinCycleRecursive(false) at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:97) | +| | | flag.set(false) at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:92) | +| | | getSharedVariable(): false at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.get(): false at RecursiveSpinWithParamsLockTest.getSharedVariable(RecursiveSpinLockTest.kt:100) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | getSharedVariable(): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.get(): true at RecursiveSpinWithParamsLockTest.getSharedVariable(RecursiveSpinLockTest.kt:100) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | getSharedVariable(): false at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.get(): false at RecursiveSpinWithParamsLockTest.getSharedVariable(RecursiveSpinLockTest.kt:100) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | getSharedVariable(): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.get(): true at RecursiveSpinWithParamsLockTest.getSharedVariable(RecursiveSpinLockTest.kt:100) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | └╶╶╶╶╶╶ switch (reason: active lock detected) | +| counter.decrementAndGet(): 0 at RecursiveSpinWithParamsLockTest.trigger(RecursiveSpinLockTest.kt:81) | | +| result: void | | +| | /* The following events repeat infinitely: */ | +| | ┌╶> deadSpinCycleRecursive(true) at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:97) | +| | | flag.set(true) at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:92) | +| | | getSharedVariable(): false at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.get(): false at RecursiveSpinWithParamsLockTest.getSharedVariable(RecursiveSpinLockTest.kt:100) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | getSharedVariable(): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.get(): true at RecursiveSpinWithParamsLockTest.getSharedVariable(RecursiveSpinLockTest.kt:100) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | getSharedVariable(): false at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.get(): false at RecursiveSpinWithParamsLockTest.getSharedVariable(RecursiveSpinLockTest.kt:100) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | getSharedVariable(): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.get(): true at RecursiveSpinWithParamsLockTest.getSharedVariable(RecursiveSpinLockTest.kt:100) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | deadSpinCycleRecursive(false) at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:97) | +| | | flag.set(false) at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:92) | +| | | getSharedVariable(): false at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.get(): false at RecursiveSpinWithParamsLockTest.getSharedVariable(RecursiveSpinLockTest.kt:100) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | getSharedVariable(): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.get(): true at RecursiveSpinWithParamsLockTest.getSharedVariable(RecursiveSpinLockTest.kt:100) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | getSharedVariable(): false at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.get(): false at RecursiveSpinWithParamsLockTest.getSharedVariable(RecursiveSpinLockTest.kt:100) | +| | | someUselessSharedState.compareAndSet(false,true): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | | getSharedVariable(): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:94) | +| | | someUselessSharedState.get(): true at RecursiveSpinWithParamsLockTest.getSharedVariable(RecursiveSpinLockTest.kt:100) | +| | | someUselessSharedState.compareAndSet(true,false): true at RecursiveSpinWithParamsLockTest.deadSpinCycleRecursive(RecursiveSpinLockTest.kt:95) | +| | └╶╶╶╶╶╶ switch (reason: active lock detected) | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +All unfinished threads are in deadlock diff --git a/src/jvm/test/resources/expected_logs/spin_lock/recursive_two_threads_spin_lock.txt b/src/jvm/test/resources/expected_logs/spin_lock/recursive_two_threads_spin_lock.txt new file mode 100644 index 0000000000..e0f62af22e --- /dev/null +++ b/src/jvm/test/resources/expected_logs/spin_lock/recursive_two_threads_spin_lock.txt @@ -0,0 +1,49 @@ += The execution has hung = +| ----------------------------- | +| Thread 1 | Thread 2 | +| ----------------------------- | +| one(): | two(): | +| ----------------------------- | + + +The following interleaving leads to the error: +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | two(): | +| | meaninglessActions1() at RecursiveTwoThreadsSpinLockTest.two(RecursiveSpinLockTest.kt:232) | +| | switch | +| one(): | | +| meaninglessActions2() at RecursiveTwoThreadsSpinLockTest.one(RecursiveSpinLockTest.kt:221) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> meaninglessActions1() at RecursiveTwoThreadsSpinLockTest.one(RecursiveSpinLockTest.kt:222) | | +| | sharedState2.compareAndSet(false,true): false at RecursiveTwoThreadsSpinLockTest.meaninglessActions1(RecursiveSpinLockTest.kt:242) | | +| └╶╶╶╶ switch (reason: active lock detected) | | +| | /* The following events repeat infinitely: */ | +| | ┌╶> meaninglessActions2() at RecursiveTwoThreadsSpinLockTest.two(RecursiveSpinLockTest.kt:233) | +| | | sharedState1.compareAndSet(false,true): false at RecursiveTwoThreadsSpinLockTest.meaninglessActions2(RecursiveSpinLockTest.kt:247) | +| | └╶╶╶╶ switch (reason: active lock detected) | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock + +Detailed trace: +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | two(): | +| | meaninglessActions1() at RecursiveTwoThreadsSpinLockTest.two(RecursiveSpinLockTest.kt:232) | +| | sharedState2.compareAndSet(false,true): true at RecursiveTwoThreadsSpinLockTest.meaninglessActions1(RecursiveSpinLockTest.kt:242) | +| | switch | +| one(): | | +| meaninglessActions2() at RecursiveTwoThreadsSpinLockTest.one(RecursiveSpinLockTest.kt:221) | | +| sharedState1.compareAndSet(false,true): true at RecursiveTwoThreadsSpinLockTest.meaninglessActions2(RecursiveSpinLockTest.kt:247) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> meaninglessActions1() at RecursiveTwoThreadsSpinLockTest.one(RecursiveSpinLockTest.kt:222) | | +| | sharedState2.compareAndSet(false,true): false at RecursiveTwoThreadsSpinLockTest.meaninglessActions1(RecursiveSpinLockTest.kt:242) | | +| └╶╶╶╶ switch (reason: active lock detected) | | +| | /* The following events repeat infinitely: */ | +| | ┌╶> meaninglessActions2() at RecursiveTwoThreadsSpinLockTest.two(RecursiveSpinLockTest.kt:233) | +| | | sharedState1.compareAndSet(false,true): false at RecursiveTwoThreadsSpinLockTest.meaninglessActions2(RecursiveSpinLockTest.kt:247) | +| | └╶╶╶╶ switch (reason: active lock detected) | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock diff --git a/src/jvm/test/resources/expected_logs/spin_lock/spin_lock_events_cut_inner_loop.txt b/src/jvm/test/resources/expected_logs/spin_lock/spin_lock_events_cut_inner_loop.txt new file mode 100644 index 0000000000..22ff174621 --- /dev/null +++ b/src/jvm/test/resources/expected_logs/spin_lock/spin_lock_events_cut_inner_loop.txt @@ -0,0 +1,148 @@ += The execution has hung = +| ----------------------------- | +| Thread 1 | Thread 2 | +| ----------------------------- | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| two(): 2 | | +| ----------------------------- | +| two(): | two(): 2 | +| | two(): 2 | +| | two(): 2 | +| | one(): 1 | +| | one(): | +| ----------------------------- | + +--- +All operations above the horizontal line | ----- | happen before those below the line +--- + + +The following interleaving leads to the error: +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| two(): 2 | | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | two(): 2 | +| | two(): 2 | +| | two(): 2 | +| | one(): 1 | +| | one(): | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | switch | +| two(): | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> sharedState1.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | | +| | meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:269) | | +| └╶╶ switch (reason: active lock detected) | | +| | /* The following events repeat infinitely: */ | +| | ┌╶> sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | | meaninglessActions() at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:255) | +| | └╶╶ switch (reason: active lock detected) | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock + +Detailed trace: +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| two(): 2 | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | | +| result: 2 | | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | one(): 1 | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | +| | result: 1 | +| | one(): | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | switch | +| two(): | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> sharedState1.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | | +| | meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:269) | | +| | data.length(): 10 at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:236) | | +| | data[0].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | +| | data[1].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | +| | data[2].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | +| | data[3].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | +| | data[4].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | +| | data[5].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | +| | data[6].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | +| | data[7].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | +| | data[8].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | +| | data[9].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | +| └╶╶ switch (reason: active lock detected) | | +| | /* The following events repeat infinitely: */ | +| | ┌╶> sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | | meaninglessActions() at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:255) | +| | | data.length(): 10 at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:236) | +| | | data[0].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | +| | | data[1].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | +| | | data[2].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | +| | | data[3].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | +| | | data[4].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | +| | | data[5].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | +| | | data[6].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | +| | | data[7].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | +| | | data[8].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | +| | | data[9].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | +| | └╶╶ switch (reason: active lock detected) | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock diff --git a/src/jvm/test/resources/expected_logs/spin_lock/spin_lock_events_cut_long_cycle.txt b/src/jvm/test/resources/expected_logs/spin_lock/spin_lock_events_cut_long_cycle.txt new file mode 100644 index 0000000000..5e0439a2ca --- /dev/null +++ b/src/jvm/test/resources/expected_logs/spin_lock/spin_lock_events_cut_long_cycle.txt @@ -0,0 +1,140 @@ += The execution has hung = +| ----------------------------- | +| Thread 1 | Thread 2 | +| ----------------------------- | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| two(): 2 | | +| ----------------------------- | +| two(): | two(): 2 | +| | two(): 2 | +| | two(): 2 | +| | one(): 1 | +| | one(): | +| ----------------------------- | + +--- +All operations above the horizontal line | ----- | happen before those below the line +--- + + +The following interleaving leads to the error: +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| two(): 2 | | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | two(): 2 | +| | two(): 2 | +| | two(): 2 | +| | one(): 1 | +| | one(): | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | switch | +| two(): | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> sharedState1.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | | +| | meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:269) | | +| └╶╶ switch (reason: active lock detected) | | +| | /* The following events repeat infinitely: */ | +| | ┌╶> sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | | meaninglessActions() at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:255) | +| | └╶╶ switch (reason: active lock detected) | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock + +Detailed trace: +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| two(): 2 | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | | +| result: 2 | | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | one(): 1 | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | +| | result: 1 | +| | one(): | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | switch | +| two(): | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> sharedState1.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | | +| | meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:269) | | +| | data[0].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:217) | | +| | data[1].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:218) | | +| | data[2].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:219) | | +| | data[3].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:220) | | +| | data[4].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:221) | | +| | data[5].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:222) | | +| | data[6].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:223) | | +| └╶╶ switch (reason: active lock detected) | | +| | /* The following events repeat infinitely: */ | +| | ┌╶> sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | | meaninglessActions() at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:255) | +| | | data[0].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:217) | +| | | data[1].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:218) | +| | | data[2].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:219) | +| | | data[3].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:220) | +| | | data[4].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:221) | +| | | data[5].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:222) | +| | | data[6].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:223) | +| | └╶╶ switch (reason: active lock detected) | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock diff --git a/src/jvm/test/resources/expected_logs/spin_lock/spin_lock_events_cut_single_action_cycle.txt b/src/jvm/test/resources/expected_logs/spin_lock/spin_lock_events_cut_single_action_cycle.txt new file mode 100644 index 0000000000..92b04fa094 --- /dev/null +++ b/src/jvm/test/resources/expected_logs/spin_lock/spin_lock_events_cut_single_action_cycle.txt @@ -0,0 +1,128 @@ += The execution has hung = +| ----------------------------- | +| Thread 1 | Thread 2 | +| ----------------------------- | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| two(): 2 | | +| ----------------------------- | +| two(): | two(): 2 | +| | two(): 2 | +| | two(): 2 | +| | one(): 1 | +| | one(): | +| ----------------------------- | + +--- +All operations above the horizontal line | ----- | happen before those below the line +--- + + +The following interleaving leads to the error: +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| two(): 2 | | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | two(): 2 | +| | two(): 2 | +| | two(): 2 | +| | one(): 1 | +| | one(): | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | switch | +| two(): | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> sharedState1.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | | +| | meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:269) | | +| └╶╶ switch (reason: active lock detected) | | +| | /* The following events repeat infinitely: */ | +| | ┌╶> sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | | meaninglessActions() at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:255) | +| | └╶╶ switch (reason: active lock detected) | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock + +Detailed trace: +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| two(): 2 | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | | +| result: 2 | | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | one(): 1 | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | +| | result: 1 | +| | one(): | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | switch | +| two(): | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> sharedState1.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | | +| | meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:269) | | +| | sharedStateAny.get(): false at SpinlockEventsCutShortLengthTest.meaninglessActions(SpinlockEventsCutTests.kt:57) | | +| └╶╶ switch (reason: active lock detected) | | +| | /* The following events repeat infinitely: */ | +| | ┌╶> sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | | meaninglessActions() at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:255) | +| | | sharedStateAny.get(): false at SpinlockEventsCutShortLengthTest.meaninglessActions(SpinlockEventsCutTests.kt:57) | +| | └╶╶ switch (reason: active lock detected) | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock diff --git a/src/jvm/test/resources/expected_logs/spin_lock/spin_lock_events_cut_two_actions_cycle.txt b/src/jvm/test/resources/expected_logs/spin_lock/spin_lock_events_cut_two_actions_cycle.txt new file mode 100644 index 0000000000..e0dd427c90 --- /dev/null +++ b/src/jvm/test/resources/expected_logs/spin_lock/spin_lock_events_cut_two_actions_cycle.txt @@ -0,0 +1,142 @@ += The execution has hung = +| ----------------------------- | +| Thread 1 | Thread 2 | +| ----------------------------- | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| two(): 2 | | +| ----------------------------- | +| two(): | two(): 2 | +| | two(): 2 | +| | two(): 2 | +| | one(): 1 | +| | one(): | +| ----------------------------- | + +--- +All operations above the horizontal line | ----- | happen before those below the line +--- + + +The following interleaving leads to the error: +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| one(): 1 | | +| two(): 2 | | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | two(): 2 | +| | two(): 2 | +| | two(): 2 | +| | one(): 1 | +| | one(): | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | switch | +| two(): | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> sharedState1.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | | +| | meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:269) | | +| | sharedState1.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | | +| | meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:269) | | +| └╶╶ switch (reason: active lock detected) | | +| | /* The following events repeat infinitely: */ | +| | ┌╶> sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | | meaninglessActions() at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:255) | +| | | sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | | meaninglessActions() at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:255) | +| | └╶╶ switch (reason: active lock detected) | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock + +Detailed trace: +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| two(): 2 | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | | +| result: 2 | | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | one(): 1 | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | +| | result: 1 | +| | one(): | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | switch | +| two(): | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> sharedState1.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | | +| | meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:269) | | +| | sharedStateAny.get(): false at SpinlockEventsCutMiddleLengthTest.meaninglessActions(SpinlockEventsCutTests.kt:72) | | +| | sharedStateAny.set(true) at SpinlockEventsCutMiddleLengthTest.meaninglessActions(SpinlockEventsCutTests.kt:73) | | +| | sharedState1.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | | +| | meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:269) | | +| | sharedStateAny.get(): true at SpinlockEventsCutMiddleLengthTest.meaninglessActions(SpinlockEventsCutTests.kt:72) | | +| | sharedStateAny.set(false) at SpinlockEventsCutMiddleLengthTest.meaninglessActions(SpinlockEventsCutTests.kt:73) | | +| └╶╶ switch (reason: active lock detected) | | +| | /* The following events repeat infinitely: */ | +| | ┌╶> sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | | meaninglessActions() at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:255) | +| | | sharedStateAny.get(): false at SpinlockEventsCutMiddleLengthTest.meaninglessActions(SpinlockEventsCutTests.kt:72) | +| | | sharedStateAny.set(true) at SpinlockEventsCutMiddleLengthTest.meaninglessActions(SpinlockEventsCutTests.kt:73) | +| | | sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | | meaninglessActions() at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:255) | +| | | sharedStateAny.get(): true at SpinlockEventsCutMiddleLengthTest.meaninglessActions(SpinlockEventsCutTests.kt:72) | +| | | sharedStateAny.set(false) at SpinlockEventsCutMiddleLengthTest.meaninglessActions(SpinlockEventsCutTests.kt:73) | +| | └╶╶ switch (reason: active lock detected) | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock diff --git a/src/jvm/test/resources/expected_logs/spin_lock_in_incorrect_results_failure.txt b/src/jvm/test/resources/expected_logs/spin_lock/spin_lock_in_incorrect_results_failure.txt similarity index 55% rename from src/jvm/test/resources/expected_logs/spin_lock_in_incorrect_results_failure.txt rename to src/jvm/test/resources/expected_logs/spin_lock/spin_lock_in_incorrect_results_failure.txt index 35df1f18c8..0f713136e5 100644 --- a/src/jvm/test/resources/expected_logs/spin_lock_in_incorrect_results_failure.txt +++ b/src/jvm/test/resources/expected_logs/spin_lock/spin_lock_in_incorrect_results_failure.txt @@ -12,33 +12,33 @@ in each of the parallel threads seen at the beginning of the current operation --- The following interleaving leads to the error: -| -------------------------------------------------------------------------------------------------------------- | -| Thread 1 | Thread 2 | -| -------------------------------------------------------------------------------------------------------------- | -| | c() | -| | /* The following events repeat infinitely: */ | -| | bStarted.READ: false at SpinlockInIncorrectResultsWithClocksTest.c(SpinlockEventsCutTests.kt:190) | -| | switch (reason: active lock detected) | -| a() | | -| b() | | -| | bStarted.READ: true at SpinlockInIncorrectResultsWithClocksTest.c(SpinlockEventsCutTests.kt:190) | -| | result: void | -| | d(): 0 | -| -------------------------------------------------------------------------------------------------------------- | +| ---------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| ---------------------------------------------------------------------------------------------------------------- | +| | c() | +| | /* The following events repeat infinitely: */ | +| | ┌╶> bStarted.READ: false at SpinlockInIncorrectResultsWithClocksTest.c(SpinlockEventsCutTests.kt:306) | +| | └╶╶ switch (reason: active lock detected) | +| a() | | +| b() | | +| | bStarted.READ: true at SpinlockInIncorrectResultsWithClocksTest.c(SpinlockEventsCutTests.kt:306) | +| | result: void | +| | d(): 0 | +| ---------------------------------------------------------------------------------------------------------------- | Detailed trace: -| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Thread 1 | Thread 2 | -| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| | c() | -| | /* The following events repeat infinitely: */ | -| | bStarted.READ: false at SpinlockInIncorrectResultsWithClocksTest.c(SpinlockEventsCutTests.kt:190) | -| | switch (reason: active lock detected) | -| a() | | -| b() | | -| bStarted.WRITE(true) at SpinlockInIncorrectResultsWithClocksTest.b(SpinlockEventsCutTests.kt:185) | | -| result: void | | -| | bStarted.READ: true at SpinlockInIncorrectResultsWithClocksTest.c(SpinlockEventsCutTests.kt:190) | -| | result: void | -| | d(): 0 | -| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | c() | +| | /* The following events repeat infinitely: */ | +| | ┌╶> bStarted.READ: false at SpinlockInIncorrectResultsWithClocksTest.c(SpinlockEventsCutTests.kt:306) | +| | └╶╶ switch (reason: active lock detected) | +| a() | | +| b() | | +| bStarted.WRITE(true) at SpinlockInIncorrectResultsWithClocksTest.b(SpinlockEventsCutTests.kt:301) | | +| result: void | | +| | bStarted.READ: true at SpinlockInIncorrectResultsWithClocksTest.c(SpinlockEventsCutTests.kt:306) | +| | result: void | +| | d(): 0 | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | diff --git a/src/jvm/test/resources/expected_logs/spin_lock/spin_lock_nested_events.txt b/src/jvm/test/resources/expected_logs/spin_lock/spin_lock_nested_events.txt new file mode 100644 index 0000000000..9ca9ed31b2 --- /dev/null +++ b/src/jvm/test/resources/expected_logs/spin_lock/spin_lock_nested_events.txt @@ -0,0 +1,69 @@ += The execution has hung = +| ------------------------------------------ | +| Thread 1 | Thread 2 | +| ------------------------------------------ | +| trigger(): void | causesSpinLock(): | +| ------------------------------------------ | + + +The following interleaving leads to the error: +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| trigger() | | +| counter.incrementAndGet(): 1 at SpinLockWithAllEventsWrappedInMethodsTest.trigger(SpinlockEventsCutTests.kt:415) | | +| switch | | +| | causesSpinLock(): | +| | counter.get(): 1 at SpinLockWithAllEventsWrappedInMethodsTest.causesSpinLock(SpinlockEventsCutTests.kt:421) | +| | deadSpinCycle() at SpinLockWithAllEventsWrappedInMethodsTest.causesSpinLock(SpinlockEventsCutTests.kt:422) | +| | /* The following events repeat infinitely: */ | +| | ┌╶> getSharedVariable(): false at SpinLockWithAllEventsWrappedInMethodsTest.deadSpinCycle(SpinlockEventsCutTests.kt:428) | +| | | action(false): true at SpinLockWithAllEventsWrappedInMethodsTest.deadSpinCycle(SpinlockEventsCutTests.kt:429) | +| | | getSharedVariable(): true at SpinLockWithAllEventsWrappedInMethodsTest.deadSpinCycle(SpinlockEventsCutTests.kt:428) | +| | | action(true): true at SpinLockWithAllEventsWrappedInMethodsTest.deadSpinCycle(SpinlockEventsCutTests.kt:429) | +| | └╶╶ switch (reason: active lock detected) | +| counter.decrementAndGet(): 0 at SpinLockWithAllEventsWrappedInMethodsTest.trigger(SpinlockEventsCutTests.kt:416) | | +| result: void | | +| | /* The following events repeat infinitely: */ | +| | ┌╶> getSharedVariable(): false at SpinLockWithAllEventsWrappedInMethodsTest.deadSpinCycle(SpinlockEventsCutTests.kt:428) | +| | | action(false): true at SpinLockWithAllEventsWrappedInMethodsTest.deadSpinCycle(SpinlockEventsCutTests.kt:429) | +| | | getSharedVariable(): true at SpinLockWithAllEventsWrappedInMethodsTest.deadSpinCycle(SpinlockEventsCutTests.kt:428) | +| | | action(true): true at SpinLockWithAllEventsWrappedInMethodsTest.deadSpinCycle(SpinlockEventsCutTests.kt:429) | +| | └╶╶ switch (reason: active lock detected) | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock + +Detailed trace: +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| trigger() | | +| counter.incrementAndGet(): 1 at SpinLockWithAllEventsWrappedInMethodsTest.trigger(SpinlockEventsCutTests.kt:415) | | +| switch | | +| | causesSpinLock(): | +| | counter.get(): 1 at SpinLockWithAllEventsWrappedInMethodsTest.causesSpinLock(SpinlockEventsCutTests.kt:421) | +| | deadSpinCycle() at SpinLockWithAllEventsWrappedInMethodsTest.causesSpinLock(SpinlockEventsCutTests.kt:422) | +| | /* The following events repeat infinitely: */ | +| | ┌╶> getSharedVariable(): false at SpinLockWithAllEventsWrappedInMethodsTest.deadSpinCycle(SpinlockEventsCutTests.kt:428) | +| | | someUselessSharedState.get(): false at SpinLockWithAllEventsWrappedInMethodsTest.getSharedVariable(SpinlockEventsCutTests.kt:433) | +| | | action(false): true at SpinLockWithAllEventsWrappedInMethodsTest.deadSpinCycle(SpinlockEventsCutTests.kt:429) | +| | | someUselessSharedState.compareAndSet(false,true): true at SpinLockWithAllEventsWrappedInMethodsTest.action(SpinlockEventsCutTests.kt:434) | +| | | getSharedVariable(): true at SpinLockWithAllEventsWrappedInMethodsTest.deadSpinCycle(SpinlockEventsCutTests.kt:428) | +| | | someUselessSharedState.get(): true at SpinLockWithAllEventsWrappedInMethodsTest.getSharedVariable(SpinlockEventsCutTests.kt:433) | +| | | action(true): true at SpinLockWithAllEventsWrappedInMethodsTest.deadSpinCycle(SpinlockEventsCutTests.kt:429) | +| | | someUselessSharedState.compareAndSet(true,false): true at SpinLockWithAllEventsWrappedInMethodsTest.action(SpinlockEventsCutTests.kt:434) | +| | └╶╶ switch (reason: active lock detected) | +| counter.decrementAndGet(): 0 at SpinLockWithAllEventsWrappedInMethodsTest.trigger(SpinlockEventsCutTests.kt:416) | | +| result: void | | +| | /* The following events repeat infinitely: */ | +| | ┌╶> getSharedVariable(): false at SpinLockWithAllEventsWrappedInMethodsTest.deadSpinCycle(SpinlockEventsCutTests.kt:428) | +| | | someUselessSharedState.get(): false at SpinLockWithAllEventsWrappedInMethodsTest.getSharedVariable(SpinlockEventsCutTests.kt:433) | +| | | action(false): true at SpinLockWithAllEventsWrappedInMethodsTest.deadSpinCycle(SpinlockEventsCutTests.kt:429) | +| | | someUselessSharedState.compareAndSet(false,true): true at SpinLockWithAllEventsWrappedInMethodsTest.action(SpinlockEventsCutTests.kt:434) | +| | | getSharedVariable(): true at SpinLockWithAllEventsWrappedInMethodsTest.deadSpinCycle(SpinlockEventsCutTests.kt:428) | +| | | someUselessSharedState.get(): true at SpinLockWithAllEventsWrappedInMethodsTest.getSharedVariable(SpinlockEventsCutTests.kt:433) | +| | | action(true): true at SpinLockWithAllEventsWrappedInMethodsTest.deadSpinCycle(SpinlockEventsCutTests.kt:429) | +| | | someUselessSharedState.compareAndSet(true,false): true at SpinLockWithAllEventsWrappedInMethodsTest.action(SpinlockEventsCutTests.kt:434) | +| | └╶╶ switch (reason: active lock detected) | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock diff --git a/src/jvm/test/resources/expected_logs/spin_lock/spin_lock_top_level.txt b/src/jvm/test/resources/expected_logs/spin_lock/spin_lock_top_level.txt new file mode 100644 index 0000000000..e63efb5202 --- /dev/null +++ b/src/jvm/test/resources/expected_logs/spin_lock/spin_lock_top_level.txt @@ -0,0 +1,31 @@ += The execution has hung = +| ------------------ | +| Thread 1 | +| ------------------ | +| spinLock(): | +| ------------------ | + + +The following interleaving leads to the error: +| -------------------------------------------------------------------------------------------------- | +| Thread 1 | +| -------------------------------------------------------------------------------------------------- | +| spinLock(): | +| /* The following events repeat infinitely: */ | +| ┌╶> state.WRITE(false) at SingleThreadTopLevelSpinLockTest.spinLock(SpinlockEventsCutTests.kt:459) | +| | state.WRITE(true) at SingleThreadTopLevelSpinLockTest.spinLock(SpinlockEventsCutTests.kt:460) | +| └╶╶ switch (reason: active lock detected) | +| -------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock + +Detailed trace: +| -------------------------------------------------------------------------------------------------- | +| Thread 1 | +| -------------------------------------------------------------------------------------------------- | +| spinLock(): | +| /* The following events repeat infinitely: */ | +| ┌╶> state.WRITE(false) at SingleThreadTopLevelSpinLockTest.spinLock(SpinlockEventsCutTests.kt:459) | +| | state.WRITE(true) at SingleThreadTopLevelSpinLockTest.spinLock(SpinlockEventsCutTests.kt:460) | +| └╶╶ switch (reason: active lock detected) | +| -------------------------------------------------------------------------------------------------- | +All unfinished threads are in deadlock diff --git a/src/jvm/test/resources/expected_logs/spin_lock_events_cut_inner_loop.txt b/src/jvm/test/resources/expected_logs/spin_lock_events_cut_inner_loop.txt deleted file mode 100644 index c259f12748..0000000000 --- a/src/jvm/test/resources/expected_logs/spin_lock_events_cut_inner_loop.txt +++ /dev/null @@ -1,148 +0,0 @@ -= The execution has hung = -| ----------------------------- | -| Thread 1 | Thread 2 | -| ----------------------------- | -| one(): 1 | | -| one(): 1 | | -| one(): 1 | | -| one(): 1 | | -| two(): 2 | | -| ----------------------------- | -| two(): | two(): 2 | -| | two(): 2 | -| | two(): 2 | -| | one(): 1 | -| | one(): | -| ----------------------------- | - ---- -All operations above the horizontal line | ----- | happen before those below the line ---- - - -The following interleaving leads to the error: -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Thread 1 | Thread 2 | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| one(): 1 | | -| one(): 1 | | -| one(): 1 | | -| one(): 1 | | -| two(): 2 | | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| | two(): 2 | -| | two(): 2 | -| | two(): 2 | -| | one(): 1 | -| | one(): | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | -| | switch | -| two(): | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | | -| /* The following events repeat infinitely: */ | | -| sharedState1.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | | -| meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:154) | | -| switch (reason: active lock detected) | | -| | /* The following events repeat infinitely: */ | -| | sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | -| | meaninglessActions() at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:140) | -| | switch (reason: active lock detected) | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -All unfinished threads are in deadlock - -Detailed trace: -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Thread 1 | Thread 2 | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| one(): 1 | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | | -| result: 1 | | -| one(): 1 | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | | -| result: 1 | | -| one(): 1 | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | | -| result: 1 | | -| one(): 1 | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | | -| result: 1 | | -| two(): 2 | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:156) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:157) | | -| result: 2 | | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| | two(): 2 | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:156) | -| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:157) | -| | result: 2 | -| | two(): 2 | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:156) | -| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:157) | -| | result: 2 | -| | two(): 2 | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:156) | -| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:157) | -| | result: 2 | -| | one(): 1 | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | -| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | -| | result: 1 | -| | one(): | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | -| | switch | -| two(): | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | | -| /* The following events repeat infinitely: */ | | -| sharedState1.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | | -| meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:154) | | -| data.length(): 10 at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:121) | | -| data[0].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:122) | | -| data[1].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:122) | | -| data[2].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:122) | | -| data[3].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:122) | | -| data[4].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:122) | | -| data[5].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:122) | | -| data[6].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:122) | | -| data[7].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:122) | | -| data[8].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:122) | | -| data[9].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:122) | | -| switch (reason: active lock detected) | | -| | /* The following events repeat infinitely: */ | -| | sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | -| | meaninglessActions() at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:140) | -| | data.length(): 10 at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:121) | -| | data[0].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:122) | -| | data[1].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:122) | -| | data[2].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:122) | -| | data[3].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:122) | -| | data[4].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:122) | -| | data[5].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:122) | -| | data[6].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:122) | -| | data[7].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:122) | -| | data[8].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:122) | -| | data[9].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:122) | -| | switch (reason: active lock detected) | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -All unfinished threads are in deadlock diff --git a/src/jvm/test/resources/expected_logs/spin_lock_events_cut_long_cycle.txt b/src/jvm/test/resources/expected_logs/spin_lock_events_cut_long_cycle.txt deleted file mode 100644 index b09e414340..0000000000 --- a/src/jvm/test/resources/expected_logs/spin_lock_events_cut_long_cycle.txt +++ /dev/null @@ -1,140 +0,0 @@ -= The execution has hung = -| ----------------------------- | -| Thread 1 | Thread 2 | -| ----------------------------- | -| one(): 1 | | -| one(): 1 | | -| one(): 1 | | -| one(): 1 | | -| two(): 2 | | -| ----------------------------- | -| two(): | two(): 2 | -| | two(): 2 | -| | two(): 2 | -| | one(): 1 | -| | one(): | -| ----------------------------- | - ---- -All operations above the horizontal line | ----- | happen before those below the line ---- - - -The following interleaving leads to the error: -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Thread 1 | Thread 2 | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| one(): 1 | | -| one(): 1 | | -| one(): 1 | | -| one(): 1 | | -| two(): 2 | | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| | two(): 2 | -| | two(): 2 | -| | two(): 2 | -| | one(): 1 | -| | one(): | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | -| | switch | -| two(): | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | | -| /* The following events repeat infinitely: */ | | -| sharedState1.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | | -| meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:154) | | -| switch (reason: active lock detected) | | -| | /* The following events repeat infinitely: */ | -| | sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | -| | meaninglessActions() at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:140) | -| | switch (reason: active lock detected) | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -All unfinished threads are in deadlock - -Detailed trace: -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Thread 1 | Thread 2 | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| one(): 1 | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | | -| result: 1 | | -| one(): 1 | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | | -| result: 1 | | -| one(): 1 | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | | -| result: 1 | | -| one(): 1 | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | | -| result: 1 | | -| two(): 2 | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:156) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:157) | | -| result: 2 | | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| | two(): 2 | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:156) | -| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:157) | -| | result: 2 | -| | two(): 2 | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:156) | -| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:157) | -| | result: 2 | -| | two(): 2 | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:156) | -| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:157) | -| | result: 2 | -| | one(): 1 | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | -| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | -| | result: 1 | -| | one(): | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | -| | switch | -| two(): | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | | -| /* The following events repeat infinitely: */ | | -| sharedState1.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | | -| meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:154) | | -| data[0].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:102) | | -| data[1].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:103) | | -| data[2].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:104) | | -| data[3].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:105) | | -| data[4].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:106) | | -| data[5].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:107) | | -| data[6].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:108) | | -| switch (reason: active lock detected) | | -| | /* The following events repeat infinitely: */ | -| | sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | -| | meaninglessActions() at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:140) | -| | data[0].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:102) | -| | data[1].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:103) | -| | data[2].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:104) | -| | data[3].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:105) | -| | data[4].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:106) | -| | data[5].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:107) | -| | data[6].set(0) at SpinlockEventsCutLongCycleActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:108) | -| | switch (reason: active lock detected) | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -All unfinished threads are in deadlock diff --git a/src/jvm/test/resources/expected_logs/spin_lock_events_cut_single_action_cycle.txt b/src/jvm/test/resources/expected_logs/spin_lock_events_cut_single_action_cycle.txt deleted file mode 100644 index c91a33d552..0000000000 --- a/src/jvm/test/resources/expected_logs/spin_lock_events_cut_single_action_cycle.txt +++ /dev/null @@ -1,128 +0,0 @@ -= The execution has hung = -| ----------------------------- | -| Thread 1 | Thread 2 | -| ----------------------------- | -| one(): 1 | | -| one(): 1 | | -| one(): 1 | | -| one(): 1 | | -| two(): 2 | | -| ----------------------------- | -| two(): | two(): 2 | -| | two(): 2 | -| | two(): 2 | -| | one(): 1 | -| | one(): | -| ----------------------------- | - ---- -All operations above the horizontal line | ----- | happen before those below the line ---- - - -The following interleaving leads to the error: -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Thread 1 | Thread 2 | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| one(): 1 | | -| one(): 1 | | -| one(): 1 | | -| one(): 1 | | -| two(): 2 | | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| | two(): 2 | -| | two(): 2 | -| | two(): 2 | -| | one(): 1 | -| | one(): | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | -| | switch | -| two(): | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | | -| /* The following events repeat infinitely: */ | | -| sharedState1.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | | -| meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:154) | | -| switch (reason: active lock detected) | | -| | /* The following events repeat infinitely: */ | -| | sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | -| | meaninglessActions() at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:140) | -| | switch (reason: active lock detected) | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -All unfinished threads are in deadlock - -Detailed trace: -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Thread 1 | Thread 2 | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| one(): 1 | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | | -| result: 1 | | -| one(): 1 | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | | -| result: 1 | | -| one(): 1 | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | | -| result: 1 | | -| one(): 1 | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | | -| result: 1 | | -| two(): 2 | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:156) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:157) | | -| result: 2 | | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| | two(): 2 | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:156) | -| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:157) | -| | result: 2 | -| | two(): 2 | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:156) | -| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:157) | -| | result: 2 | -| | two(): 2 | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:156) | -| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:157) | -| | result: 2 | -| | one(): 1 | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | -| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | -| | result: 1 | -| | one(): | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | -| | switch | -| two(): | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | | -| /* The following events repeat infinitely: */ | | -| sharedState1.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | | -| meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:154) | | -| sharedStateAny.get(): false at SpinlockEventsCutShortLengthTest.meaninglessActions(SpinlockEventsCutTests.kt:56) | | -| switch (reason: active lock detected) | | -| | /* The following events repeat infinitely: */ | -| | sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | -| | meaninglessActions() at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:140) | -| | sharedStateAny.get(): false at SpinlockEventsCutShortLengthTest.meaninglessActions(SpinlockEventsCutTests.kt:56) | -| | switch (reason: active lock detected) | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -All unfinished threads are in deadlock diff --git a/src/jvm/test/resources/expected_logs/spin_lock_events_cut_two_actions_cycle.txt b/src/jvm/test/resources/expected_logs/spin_lock_events_cut_two_actions_cycle.txt deleted file mode 100644 index e7f77cc611..0000000000 --- a/src/jvm/test/resources/expected_logs/spin_lock_events_cut_two_actions_cycle.txt +++ /dev/null @@ -1,130 +0,0 @@ -= The execution has hung = -| ----------------------------- | -| Thread 1 | Thread 2 | -| ----------------------------- | -| one(): 1 | | -| one(): 1 | | -| one(): 1 | | -| one(): 1 | | -| two(): 2 | | -| ----------------------------- | -| two(): | two(): 2 | -| | two(): 2 | -| | two(): 2 | -| | one(): 1 | -| | one(): | -| ----------------------------- | - ---- -All operations above the horizontal line | ----- | happen before those below the line ---- - - -The following interleaving leads to the error: -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Thread 1 | Thread 2 | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| one(): 1 | | -| one(): 1 | | -| one(): 1 | | -| one(): 1 | | -| two(): 2 | | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| | two(): 2 | -| | two(): 2 | -| | two(): 2 | -| | one(): 1 | -| | one(): | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | -| | switch | -| two(): | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | | -| /* The following events repeat infinitely: */ | | -| sharedState1.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | | -| meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:154) | | -| switch (reason: active lock detected) | | -| | /* The following events repeat infinitely: */ | -| | sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | -| | meaninglessActions() at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:140) | -| | switch (reason: active lock detected) | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -All unfinished threads are in deadlock - -Detailed trace: -| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Thread 1 | Thread 2 | -| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| one(): 1 | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | | -| result: 1 | | -| one(): 1 | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | | -| result: 1 | | -| one(): 1 | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | | -| result: 1 | | -| one(): 1 | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | | -| result: 1 | | -| two(): 2 | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:156) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:157) | | -| result: 2 | | -| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| | two(): 2 | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:156) | -| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:157) | -| | result: 2 | -| | two(): 2 | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:156) | -| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:157) | -| | result: 2 | -| | two(): 2 | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:156) | -| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:157) | -| | result: 2 | -| | one(): 1 | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | -| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:142) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:143) | -| | result: 1 | -| | one(): | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:136) | -| | switch | -| two(): | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:150) | | -| /* The following events repeat infinitely: */ | | -| sharedState1.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:153) | | -| meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:154) | | -| sharedStateAny.get(): false at SpinlockEventsCutMiddleLengthTest.meaninglessActions(SpinlockEventsCutTests.kt:71) | | -| sharedStateAny.set(true) at SpinlockEventsCutMiddleLengthTest.meaninglessActions(SpinlockEventsCutTests.kt:72) | | -| switch (reason: active lock detected) | | -| | /* The following events repeat infinitely: */ | -| | sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:139) | -| | meaninglessActions() at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:140) | -| | sharedStateAny.get(): true at SpinlockEventsCutMiddleLengthTest.meaninglessActions(SpinlockEventsCutTests.kt:71) | -| | sharedStateAny.set(false) at SpinlockEventsCutMiddleLengthTest.meaninglessActions(SpinlockEventsCutTests.kt:72) | -| | switch (reason: active lock detected) | -| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -All unfinished threads are in deadlock diff --git a/src/jvm/test/resources/expected_logs/switch_in_the_middle_of_spin_cycle_causes_error.txt b/src/jvm/test/resources/expected_logs/switch_in_the_middle_of_spin_cycle_causes_error.txt index df8ccc4845..e3c57ae1f8 100644 --- a/src/jvm/test/resources/expected_logs/switch_in_the_middle_of_spin_cycle_causes_error.txt +++ b/src/jvm/test/resources/expected_logs/switch_in_the_middle_of_spin_cycle_causes_error.txt @@ -13,23 +13,17 @@ The following interleaving leads to the error: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Thread 1 | Thread 2 | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| | spinLock(): IllegalStateException #2 | -| | switch | | causeSpinLock(): IllegalStateException #1 | | | counter.incrementAndGet(): 1 at InterleavingAnalysisPresentInSpinCycleFirstIterationTest.causeSpinLock(InterleavingAnalysisPresentInSpinCycleFirstIterationTest.kt:39) | | | switch | | -| | /* The following events repeat infinitely: */ | +| | spinLock(): IllegalStateException #2 | | | counter.get(): 1 at InterleavingAnalysisPresentInSpinCycleFirstIterationTest.spinLock(InterleavingAnalysisPresentInSpinCycleFirstIterationTest.kt:50) | | | counter.get(): 1 at InterleavingAnalysisPresentInSpinCycleFirstIterationTest.spinLock(InterleavingAnalysisPresentInSpinCycleFirstIterationTest.kt:51) | | | shouldAlwaysBeZero.incrementAndGet(): 1 at InterleavingAnalysisPresentInSpinCycleFirstIterationTest.spinLock(InterleavingAnalysisPresentInSpinCycleFirstIterationTest.kt:52) | -| | illegalInterleavingFound.get(): false at InterleavingAnalysisPresentInSpinCycleFirstIterationTest.spinLock(InterleavingAnalysisPresentInSpinCycleFirstIterationTest.kt:53) | -| | switch (reason: active lock detected) | +| | switch | | shouldAlwaysBeZero.get(): 1 at InterleavingAnalysisPresentInSpinCycleFirstIterationTest.causeSpinLock(InterleavingAnalysisPresentInSpinCycleFirstIterationTest.kt:41) | | | illegalInterleavingFound.set(true) at InterleavingAnalysisPresentInSpinCycleFirstIterationTest.causeSpinLock(InterleavingAnalysisPresentInSpinCycleFirstIterationTest.kt:42) | | | result: IllegalStateException #1 | | -| | shouldAlwaysBeZero.decrementAndGet(): 0 at InterleavingAnalysisPresentInSpinCycleFirstIterationTest.spinLock(InterleavingAnalysisPresentInSpinCycleFirstIterationTest.kt:54) | -| | counter.get(): 1 at InterleavingAnalysisPresentInSpinCycleFirstIterationTest.spinLock(InterleavingAnalysisPresentInSpinCycleFirstIterationTest.kt:51) | -| | shouldAlwaysBeZero.incrementAndGet(): 1 at InterleavingAnalysisPresentInSpinCycleFirstIterationTest.spinLock(InterleavingAnalysisPresentInSpinCycleFirstIterationTest.kt:52) | | | illegalInterleavingFound.get(): true at InterleavingAnalysisPresentInSpinCycleFirstIterationTest.spinLock(InterleavingAnalysisPresentInSpinCycleFirstIterationTest.kt:53) | | | result: IllegalStateException #2 | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -45,23 +39,17 @@ Detailed trace: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Thread 1 | Thread 2 | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| | spinLock(): IllegalStateException #2 | -| | switch | | causeSpinLock(): IllegalStateException #1 | | | counter.incrementAndGet(): 1 at InterleavingAnalysisPresentInSpinCycleFirstIterationTest.causeSpinLock(InterleavingAnalysisPresentInSpinCycleFirstIterationTest.kt:39) | | | switch | | -| | /* The following events repeat infinitely: */ | +| | spinLock(): IllegalStateException #2 | | | counter.get(): 1 at InterleavingAnalysisPresentInSpinCycleFirstIterationTest.spinLock(InterleavingAnalysisPresentInSpinCycleFirstIterationTest.kt:50) | | | counter.get(): 1 at InterleavingAnalysisPresentInSpinCycleFirstIterationTest.spinLock(InterleavingAnalysisPresentInSpinCycleFirstIterationTest.kt:51) | | | shouldAlwaysBeZero.incrementAndGet(): 1 at InterleavingAnalysisPresentInSpinCycleFirstIterationTest.spinLock(InterleavingAnalysisPresentInSpinCycleFirstIterationTest.kt:52) | -| | illegalInterleavingFound.get(): false at InterleavingAnalysisPresentInSpinCycleFirstIterationTest.spinLock(InterleavingAnalysisPresentInSpinCycleFirstIterationTest.kt:53) | -| | switch (reason: active lock detected) | +| | switch | | shouldAlwaysBeZero.get(): 1 at InterleavingAnalysisPresentInSpinCycleFirstIterationTest.causeSpinLock(InterleavingAnalysisPresentInSpinCycleFirstIterationTest.kt:41) | | | illegalInterleavingFound.set(true) at InterleavingAnalysisPresentInSpinCycleFirstIterationTest.causeSpinLock(InterleavingAnalysisPresentInSpinCycleFirstIterationTest.kt:42) | | | result: IllegalStateException #1 | | -| | shouldAlwaysBeZero.decrementAndGet(): 0 at InterleavingAnalysisPresentInSpinCycleFirstIterationTest.spinLock(InterleavingAnalysisPresentInSpinCycleFirstIterationTest.kt:54) | -| | counter.get(): 1 at InterleavingAnalysisPresentInSpinCycleFirstIterationTest.spinLock(InterleavingAnalysisPresentInSpinCycleFirstIterationTest.kt:51) | -| | shouldAlwaysBeZero.incrementAndGet(): 1 at InterleavingAnalysisPresentInSpinCycleFirstIterationTest.spinLock(InterleavingAnalysisPresentInSpinCycleFirstIterationTest.kt:52) | | | illegalInterleavingFound.get(): true at InterleavingAnalysisPresentInSpinCycleFirstIterationTest.spinLock(InterleavingAnalysisPresentInSpinCycleFirstIterationTest.kt:53) | | | result: IllegalStateException #2 | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | From 5c33ca3b7e7798f53b68e27c026c94ad892bd05e Mon Sep 17 00:00:00 2001 From: "Aleksandr.Potapov" Date: Mon, 10 Jun 2024 10:19:48 +0200 Subject: [PATCH 02/13] Timout increased --- .../org/jetbrains/kotlinx/lincheck_test/AbstractLincheckTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/AbstractLincheckTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/AbstractLincheckTest.kt index 0e703927ac..4f14f2f958 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/AbstractLincheckTest.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/AbstractLincheckTest.kt @@ -68,4 +68,4 @@ abstract class AbstractLincheckTest( } } -private const val TIMEOUT = 2 * 60_000L // 2 min \ No newline at end of file +private const val TIMEOUT = 3 * 60_000L // 2 min \ No newline at end of file From aea245ee09104dbc74a97cffe5dfd3a5cf675702 Mon Sep 17 00:00:00 2001 From: "Aleksandr.Potapov" Date: Mon, 10 Jun 2024 11:52:58 +0200 Subject: [PATCH 03/13] Merge fixes --- .../kotlinx/lincheck/strategy/managed/ManagedStrategy.kt | 3 +++ .../transformation/transformers/MethodCallTransformer.kt | 5 ++++- .../jetbrains/kotlinx/lincheck_test/AbstractLincheckTest.kt | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt index 487c3da36b..7175c7da54 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt @@ -734,6 +734,7 @@ abstract class ManagedStrategy( lastReadTracePoint[iThread] = tracePoint } newSwitchPoint(iThread, codeLocation, tracePoint) + loopDetector.passValue(obj) return@runInIgnoredSection true } @@ -805,6 +806,8 @@ abstract class ManagedStrategy( null } newSwitchPoint(iThread, codeLocation, tracePoint) + loopDetector.passValue(obj) + loopDetector.passValue(value) return@runInIgnoredSection true } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/transformation/transformers/MethodCallTransformer.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/transformation/transformers/MethodCallTransformer.kt index 389973c699..b3c5ce0022 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/transformation/transformers/MethodCallTransformer.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/transformation/transformers/MethodCallTransformer.kt @@ -77,7 +77,10 @@ internal class MethodCallTransformer( push(name) loadNewCodeLocationId() // STACK [INVOKEVIRTUAL]: owner, owner, className, methodName, codeLocation - // STACK [INVOKESTATIC]: null , className, methodName, codeLocation + // STACK [INVOKESTATIC]: null, className, methodName, codeLocation + adapter.push(MethodIds.getMethodId(owner, name, desc)) + // STACK [INVOKEVIRTUAL]: owner, owner, className, methodName, codeLocation, methodId + // STACK [INVOKESTATIC]: null, className, methodName, codeLocation, methodId pushArray(argumentLocals) // STACK: ..., argumentsArray invokeStatic(Injections::beforeMethodCall) diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/AbstractLincheckTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/AbstractLincheckTest.kt index 4f14f2f958..f260d5f97a 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/AbstractLincheckTest.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/AbstractLincheckTest.kt @@ -68,4 +68,4 @@ abstract class AbstractLincheckTest( } } -private const val TIMEOUT = 3 * 60_000L // 2 min \ No newline at end of file +private const val TIMEOUT = 4 * 60_000L // 4 min \ No newline at end of file From 4879c906c5370d848a5b3c527f23fce58a74d501 Mon Sep 17 00:00:00 2001 From: "Aleksandr.Potapov" Date: Mon, 10 Jun 2024 13:21:59 +0200 Subject: [PATCH 04/13] Timeout increased --- .../org/jetbrains/kotlinx/lincheck_test/AbstractLincheckTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/AbstractLincheckTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/AbstractLincheckTest.kt index f260d5f97a..92dc2df5e3 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/AbstractLincheckTest.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/AbstractLincheckTest.kt @@ -68,4 +68,4 @@ abstract class AbstractLincheckTest( } } -private const val TIMEOUT = 4 * 60_000L // 4 min \ No newline at end of file +private const val TIMEOUT = 10 * 60_000L // 10 min \ No newline at end of file From a3ffd752a9766eedd8c737105b43181415dccbdc Mon Sep 17 00:00:00 2001 From: "Aleksandr.Potapov" Date: Wed, 19 Jun 2024 22:14:01 +0200 Subject: [PATCH 05/13] Review fixes --- build.gradle.kts | 3 + .../org/jetbrains/kotlinx/lincheck/Utils.kt | 10 +- .../lincheck/strategy/managed/LoopDetector.kt | 367 ++++++++++++++++-- .../strategy/managed/ManagedStrategy.kt | 315 ++------------- .../lincheck/strategy/managed/TracePoint.kt | 2 +- .../modelchecking/ModelCheckingStrategy.kt | 1 - .../representation/ScalaBugTest.kt | 19 + 7 files changed, 409 insertions(+), 308 deletions(-) create mode 100644 src/jvm/test/org/jetbrains/kotlinx/lincheck_test/representation/ScalaBugTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index 0fd14d3693..360b176179 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -20,6 +20,9 @@ plugins { repositories { mavenCentral() + flatDir { + dirs = mutableSetOf(File("/Users/Aleksandr.Potapov/Documents/Work/lincheck/libs")) + } } kotlin { diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt index 1484e8af48..0dba767051 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt @@ -91,9 +91,13 @@ private fun Class.getMethod(name: String, parameterTypes: Array value.hashCode() - else -> System.identityHashCode(value) +internal fun primitiveOrIdentityHashCode(value: Any?): Int { + return if (value.isPrimitiveWrapper) return value.hashCode() else System.identityHashCode(value) +} + +private val Any?.isPrimitiveWrapper get() = when (this) { + is Boolean, is Int, is Short, is Long, is Double, is Float, is Char, is Byte -> true + else -> false } /** diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt index 6eaeb8554d..6c7b11602c 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt @@ -10,7 +10,8 @@ package org.jetbrains.kotlinx.lincheck.strategy.managed -import org.jetbrains.kotlinx.lincheck.primitiveHashCodeOrSystemHashCode +import org.jetbrains.kotlinx.lincheck.primitiveOrIdentityHashCode +import org.jetbrains.kotlinx.lincheck.transformation.MethodIds import java.util.ArrayList /** @@ -24,7 +25,7 @@ import java.util.ArrayList * * In the default mode, LoopDetector has two states. * When a spin cycle is not detected yet, LoopDetector ignores method parameters and read/write values and receivers, - * operating only with switch points code locations. + * operating only with switch points and code locations. * When a spin cycle is detected, LoopDetector requests the replay of the execution and starts tracking * the parameters, etc., to detect a spin cycle period properly, taking into account all changes during the spin cycle. * When we run into a spin cycle again, LoopDetector tries to find the period using [currentThreadCodeLocationsHistory]. @@ -106,7 +107,7 @@ internal class LoopDetector( /** * Indicates if we analyze method parameters to calculate the spin-cycle period. */ - private var cycleCalculationPhase: Boolean = false + private var mode: WorkMode = WorkMode.DEFAULT /** * Indicates that we are in a spin cycle iteration now. @@ -210,14 +211,14 @@ internal class LoopDetector( // when we can't find a cycle period and can't switch to another thread. // Check whether the count exceeds the maximum number of repetitions for loop/hang detection. if (detectedFirstTime && !detectedEarly) { - if (!cycleCalculationPhase) { + if (mode == WorkMode.DEFAULT) { // Turn on parameters and read/write values and receivers tracking and request one more replay. - cycleCalculationPhase = true + mode = WorkMode.CYCLE_PERIOD_CALCULATION return Decision.LivelockReplayToDetectCycleRequired } registerCycle() // Turn off parameters tracking and request one more replay to avoid side effects. - cycleCalculationPhase = false + mode = WorkMode.DEFAULT // Enormous operations count considered as total spin lock if (totalExecutionsCount > ManagedCTestConfiguration.LIVELOCK_EVENTS_THRESHOLD) { return Decision.EventsThresholdReached @@ -252,7 +253,7 @@ internal class LoopDetector( /** * Called before regular method calls. */ - fun beforeRegularMethodCall(codeLocation: Int, params: Array) { + fun beforeMethodCall(codeLocation: Int, params: Array) { replayModeLoopDetectorHelper?.let { it.onNextExecution() return @@ -271,6 +272,9 @@ internal class LoopDetector( * Called after any method calls. */ fun afterRegularMethodCall() { + // Because we want to track the fact of the method exit, + // but the sequence of the beforeMethodCall code locations values determines what exactly method we exit. + // That's why we can use just 0 as a label of a method exit. val afterMethodCallLocation = 0 replayModeLoopDetectorHelper?.let { it.onNextExecution() @@ -291,24 +295,45 @@ internal class LoopDetector( * Otherwise, does nothing. */ fun passParameters(params: Array) { - if (!cycleCalculationPhase) return + if (mode == WorkMode.DEFAULT) return params.forEach { param -> currentThreadCodeLocationsHistory += paramToIntRepresentation(param) } } /** - * Called when we: - * - Read/write some value. - * - Use some object as a receiver (receiver is passed as a parameter [obj]). - * - Use an index to access an array cell (index is passed as a parameter [obj]). - * - * Used only if LoopDetector is in the cycle calculation mode. - * Otherwise, does nothing. + * Should be called before each read non-local mutable field read. */ - fun passValue(obj: Any?) { - if (!cycleCalculationPhase) return - currentThreadCodeLocationsHistory += paramToIntRepresentation(obj) + fun beforeReadField(receiver: Any?) = passValue(receiver) + + /** + * Should be called after each read non-local mutable field read. + */ + fun afterRead(value: Any?) = passValue(value) + + /** + * Should be called before each non-local array element read. + */ + fun beforeReadArrayElement(array: Any, index: Int) { + passValue(array) + passValue(index) + } + + /** + * Should be called before each read non-local field write. + */ + fun beforeWriteField(receiver: Any?, value: Any?) { + passValue(receiver) + passValue(value) + } + + /** + * Should be called before each read non-local array element write. + */ + fun beforeWriteArrayElement(array: Any, index: Int, value: Any?) { + passValue(array) + passValue(index) + passValue(value) } fun onActorStart(iThread: Int) { @@ -384,6 +409,20 @@ internal class LoopDetector( loopTrackingCursor.onNextExecutionPoint() } + /** + * Called when we: + * - Read/write some value. + * - Use some object as a receiver (receiver is passed as a parameter [obj]). + * - Use an index to access an array cell (index is passed as a parameter [obj]). + * + * Used only if LoopDetector is in the cycle calculation mode. + * Otherwise, does nothing. + */ + private fun passValue(obj: Any?) { + if (mode == WorkMode.DEFAULT) return + currentThreadCodeLocationsHistory += paramToIntRepresentation(obj) + } + private fun registerCycle() { val (cycleInfo, switchPointsCodeLocationsHistory) = tryFindCycleWithParamsOrWithout() @@ -531,16 +570,38 @@ internal class LoopDetector( * and value representations produced by this method. */ private fun paramToIntRepresentation(value: Any?): Int { - var hashCode = primitiveHashCodeOrSystemHashCode(value) + val hashCode = primitiveOrIdentityHashCode(value) if (hashCode < 0) return hashCode - hashCode++ + // When we're trying to find a spin cycle and can't do it with parameters, receivers, etc., + // we try to calculate the spin cycle without them. We need to filter executions story list + // of integers fast, so we keep the contract: + // 1. All potential switch points code locations, method call code locations are strictly greater than 0. + // 2. All method exits have zero (0) code locations. + // 3. All parameters, receivers, and value integer representations (the things we have to ignore in case + // After this line [hashCode] is positive value or Integer.MIN_VALUE in case of the overflow. + // So by `if` below we make sure that this value is strictly negative. + return if (hashCode == 0) -1 else -hashCode + } + + private enum class WorkMode { + + /** + * The default mode of [LoopDetector]: analyzing only potential switch points code locations + * and method enters/exits. + */ + DEFAULT, - return if (hashCode > 0) -hashCode else hashCode + /** + * In this mode [LoopDetector] also takes into account parameters, + * receivers and written/wrote values to calculate the spin cycle period properly. + * After spin cycle is found, [DEFAULT] mode is enabled. + */ + CYCLE_PERIOD_CALCULATION } } -internal fun LoopDetector.Decision.isLivelockDetected() = when (this) { +internal val LoopDetector.Decision.isLivelockDetected get() = when (this) { is LoopDetector.Decision.LivelockThreadSwitch, is LoopDetector.Decision.LivelockReplayRequired, is LoopDetector.Decision.LivelockFailureDetected -> true @@ -548,6 +609,13 @@ internal fun LoopDetector.Decision.isLivelockDetected() = when (this) { else -> false } +internal val LoopDetector.Decision.isReplayRequired get() = when (this) { + is LoopDetector.Decision.LivelockReplayRequired, + is LoopDetector.Decision.LivelockReplayToDetectCycleRequired -> true + + else -> false +} + /** * Helper class to halt execution on replay (trace collection phase) and to switch thread early on spin-cycles */ @@ -644,3 +712,258 @@ private class ReplayModeLoopDetectorHelper( LoopDetector.Decision.Idle } } + +/** + * Calculates the [SpinCycleStartTracePoint] trace point callStackTrace correctly after all trace points + * related to the spin cycle are collected. + * + * # The problem overview + * + * [TraceCollector] collects two types of the [TracePoint]: + * 1. TracePoints that represents places where potential context switch points could happened. + * 2. TracePoints, representing regular, non-atomic method calls. + * + * This division is important for us, as [LoopDetector] stores execution points sequences of the + * first type, causing the challenges, described below. + * + * When [SpinCycleStartTracePoint] is added initially to the [trace], its callStackTrace, constructed via + * `callStackTrace[currentThread]` may be inaccurate. Consider the following example: + * We have the following code: + * ```kotlin + * @Operation + * fun actor() { + * atomicInteger.set(false) // code location = 1 + * spinLock() // method call code location = 2 + * } + * + * fun spinLock() { + * while (true) { + * val value = getValue() // method call code location = 3 + * atomicInteger.compareAndSet(value, !value) // code location = 5 + * } + * } + * + * fun getValue() = atomicInteger.get() // code location = 4 + * ``` + * When we ran into a cycle, we'll have the following code locations history: + * ``` + * [1, 2, 3, 4, 5, 3, 4, 5, 3, 4 ,5 ..., 3, 4, 5] + * ``` + * Cycle period here is 3: `[3, 4, 5]` and only two `[1, 2]` executions happened before the cycle. + * Then, suppose we have a scenario with two actors. + * The interleaving we have: + * ``` + * [ + * 1. Execute 2 instructions (switch positions) in thread 1, + * 2. Execute 1 instruction (switch positions) in the thread 1, + * 3. Execute 3 instructions (switch positions) in the thread 1 -> execution hung. + * ] + * ``` + * It's worth noting that the LoopDetector operates with a switch positions when replaying the + * execution to collect trace. + * + * Due to the internals of the [LoopDetector], [LoopDetector.replayModeCurrentlyInSpinCycle] will only + * return `true` at the beginning of step 3 described above, as execution only contains a sequence of + * executed potential switch points, while method call isn't a potential switch point. + * But at the beginning of step 3 we'll be inside the `getValue` method, so when [LoopDetector.replayModeCurrentlyInSpinCycle] + * is triggered, it's not correct using `callStackTrace[currentThread]`, as it would contain `getValue`. + * + * Summarizing, we may receive the signal about spin cycle start some non-atomic method calls after the actual spin cycle start + * if we switch right before the first spin cycle potential switch point execution, + * and this happens as [LoopDetector] stores sequences of the only potential switch points executions and + * all enters to the non-atomic methods are considered as a part of a step 1, which doesn't correspond to a spin cycle. + * + * Hence, **we can only use trace points of the first type defined above (which are potential switch points) + * to calculate the correct spin cycle start label call depth**. + * + * The problem has two solutions, depending on the spin cycle type: + * * recursive + * * iterative. + * + * # Iterative spin lock + * Let's consider the solution for the iterative spin-cycle. + * + * When we report an iterative spin cycle, we **must** visit the first spin cycle execution point + * and the first execution point of the second iteration (with an exit before). + * Let's track the current stacktrace before each switch point, before and after each method calls. + * Consider the example of a spin cycle, representing call stack traces of the execution points: + * ``` + * A -> B -> C + * A -> B -> C -> K + * A -> B -> E -> D + * A -> B -> E + * A -> B -> X + * ``` + * As we can see, even if all the execution points are wrapped in the non-atomic method calls (which we track + * during the trace collection too), the longest common prefix of the method calls `A, B` in the stack traces + * is the right position to place spin cycle start label. + * Indeed, due to the iterative nature of the spin lock, the first spin cycle execution of the first iteration + * and of the second iteration are placed in the same depth of calls. So it's guaranteed that we'll visit + * the most nested call and the least nested call. The wanted recursive method (and its call depth) can be found + * using the longest prefix of all the points is the spin cycle, as it is going to be the same along all the + * trace points and method calls. + * + * So that's what we do in case of an iterative spin lock. + * Track all the trace points of the spin cycle, + * calculate the max common method call prefix of these trace points, and place the spin cycle start label + * onto the found call depth. + * + * # Recursive spin lock + * + * Unlike iterative spin lock, in case of a recursive spin lock, longest common prefix may include + * method calls, that are a part of a spin cycle. Consider the following example: + * ```kotlin + * fun actor() { + * a() + * } + * + * fun a() = b() + * + * fun b() { + * c() + * c() + * a() // recursive call + * } + * + * fun c() = value.get() + * ``` + * According to the problem described above, if we switch to another thread and back right before point X, + * the trace points of the spin cycle will have the following stack traces: + * ``` + * [ + * actor -> a -> b -> c + * actor -> a -> b -> c + * ] + * ``` + * And the first execution point of the second iteration will be + * ``` + * actor -> a -> b -> a -> b -> c + * ``` + * + * As mentioned, it's incorrect to say that spin cycle starts at the depth `actor -> a -> c`, as the first + * recursive call is method `b` call, so we can't use the approach for the iterative spin cycle type. + * + * Let's consider the last potential switch trace point before the first spin cycle trace point, + * the first iteration first potential switch trace point + * and the second iteration first potential switch trace point: + * + * 1. `actor -> a -> b -> c` + * 2. `actor -> a -> b -> c -> a -> b -> c` + * + * Due to the recursive nature of a spin cycle, the second and the third trace points must have common + * method call suffix. + * It's important how to compare method calls in the suffixes. We consider method calls identical if + * they have the same ids produced by [MethodIds] and the same parameters. That means we don't care + * here where this method is called - it's more convenient to see as short version of the spin cycle as possible. + * + * But this approach may produce a wrong result in case when we switch from the spin lock and then occur + * in the same spin lock again. + * Let's consider this situation using the example above. + * Trace points of the second spin lock would have the following call stacks: + * + * 1. `actor -> [a -> b -> c] -> [a -> b -> c]` + * 2. `actor -> [a -> b -> c] -> [a -> b -> c]` + * + * And the first execution point of the second iteration will be + * ``` + * actor -> [a -> b -> c] -> [a -> b -> c] -> [a -> b -> c] + * ``` + * + * If we apply this algorithm to the first point from the first iteration and the first point from the second iteration + * we get `[a -> b -> c] -> [a -> b -> c]` longest common suffix, which is not correct, because the first recursive call + * of this cycle starts at the depth `actor -> a -> b`. + * In this case, the last trace point before the spin cycle becomes handy. + * In the example above it would have the following call stack `actor -> a -> b -> c`. + * + * Here are the points we're interested in: + * 1. `actor -> [a -> b -> c]` - The last trace point before the cycle. + * 2. `actor -> [a -> b -> c] -> [a -> b -> c]` - The first trace point of the cycle. + * 3. ` actor -> [a -> b -> c] -> [a -> b -> c] -> [a -> b -> c]` - The first trace point of the second iteration. + * Let's note the following rule: if point 1 has the same method call on the same depth as point 2, + * then this call can't be a part of the current spin cycle. + * The reason is quite easy: if it was, we would mark point 1 as a first spin cycle trace point. + * + * So, to detect the spin cycle, start trace point depth in case of a recursive spin lock we: + * 1. Take the first trace point of the spin cycle on the first iteration (point A) and on the second iteration (point B). + * 2. Take the last trace point before the spin cycle (point C). + * 3. We walk synchronously on their call stacks from the end to the beginning, while call from the A stack + * is the same as a call from the stack B. But it also equals the call from the call C, then we need to stop. + * The count of the call passed the condition above equals to the call we need to lift from the first spin cycle + * node to get the correct spin cycle start trace point depth. + * + * @param beforeMethodCallSwitch flag if this method invoked right after [MethodCallTracePoint] is added to a trace, + * before the corresponding method is called. + */ +internal fun afterSpinCycleTraceCollected( + trace: MutableList, + callStackTrace: List, + spinCycleMethodCallsStackTraces: List>, + iThread: Int, + currentActorId: Int, + beforeMethodCallSwitch: Boolean +) { + // Obtaining spin cycle trace points. + val spinLockTracePoints = trace.takeLastWhile { it.iThread == iThread && it !is SpinCycleStartTracePoint } + // Nothing to do in this case (seems unreal). + if (spinLockTracePoints.isEmpty()) return + // If this method is invoked after beforeMethodCall or beforeAtomicMethodCall + // than MethodCallTracePoint is already added, correct it by altering current stack trace + val currentCallStackTrace = if (beforeMethodCallSwitch) callStackTrace.dropLast(1) else callStackTrace + + val cycleStartTracePointIndex = trace.size - spinLockTracePoints.size - 1 + if (cycleStartTracePointIndex < 0 || trace[cycleStartTracePointIndex] !is SpinCycleStartTracePoint) return + + val spinCycleFirstTracePointCallStackTrace = spinLockTracePoints.first().callStackTrace + val isRecursive = currentCallStackTrace.size != spinCycleFirstTracePointCallStackTrace.size + val spinCycleStartStackTrace = if (isRecursive) { + // See above the description of the algorithm for recursive spin lock. + val prevIndex = trace.lastIndex - spinLockTracePoints.size - 1 + val tracePointBeforeCycle = if (prevIndex >= 0) (prevIndex downTo 0) + .firstOrNull { trace[it] !is SwitchEventTracePoint && trace[it].iThread == iThread }?.let { trace[it] } + else null + + var currentI = currentCallStackTrace.lastIndex + var firstI = spinCycleFirstTracePointCallStackTrace.lastIndex + var count = 0 + while (firstI >= 0) { + val identifier = spinCycleFirstTracePointCallStackTrace[firstI].methodId + // Comparing corresponding calls. + if (identifier != currentCallStackTrace[currentI].methodId) break + // Check for the last trace point before the cycle. + if ((tracePointBeforeCycle != null) && + (tracePointBeforeCycle.callStackTrace.lastIndex >= firstI) && + (tracePointBeforeCycle.callStackTrace[firstI].methodId == identifier) + ) break + + currentI-- + firstI-- + count++ + } + spinCycleFirstTracePointCallStackTrace.dropLast(count) + } else { + // See above the description of the algorithm for iterative spin lock. + getCommonMinStackTrace(spinLockTracePoints, spinCycleMethodCallsStackTraces) + .dropLast(currentCallStackTrace.size - spinCycleFirstTracePointCallStackTrace.size) + } + trace[cycleStartTracePointIndex] = + SpinCycleStartTracePoint(iThread, currentActorId, spinCycleStartStackTrace) +} + +/** + * @return Max common prefix of the [StackTraceElement] of the provided [spinCycleTracePoints] + */ +private fun getCommonMinStackTrace(spinCycleTracePoints: List, spinCycleMethodCallsStackTraces: List>): List { + val callStackTraces = spinCycleTracePoints.map { it.callStackTrace } + spinCycleMethodCallsStackTraces + var count = 0 + outer@while (true) { + if (count == callStackTraces[0].size) break + val stackTraceElement = callStackTraces[0][count].call.stackTraceElement + for (i in 1 until callStackTraces.size) { + val traceElements = callStackTraces[i] + if (count == traceElements.size) break@outer + if (stackTraceElement != traceElements[count].call.stackTraceElement) break@outer + } + count++ + } + return spinCycleTracePoints.first().callStackTrace.take(count) +} \ No newline at end of file diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt index 7175c7da54..cb1f06fbc7 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt @@ -376,12 +376,12 @@ abstract class ManagedStrategy( failDueToDeadlock() } // if any kind of live-lock was detected, check for obstruction-freedom violation - if (decision.isLivelockDetected()) { + if (decision.isLivelockDetected) { failIfObstructionFreedomIsRequired { if (decision is LoopDetector.Decision.LivelockFailureDetected) { // if failure is detected, add a special obstruction-freedom violation // trace point to account for that - traceCollector?.passObstructionFreedomViolationTracePoint(currentThread, tracePoint is MethodCallTracePoint) + traceCollector?.passObstructionFreedomViolationTracePoint(currentThread, beforeMethodCall = tracePoint is MethodCallTracePoint) } else { // otherwise log the last event that caused obstruction-freedom violation traceCollector?.passCodeLocation(tracePoint) @@ -391,12 +391,12 @@ abstract class ManagedStrategy( } // if live-lock failure was detected, then fail immediately if (decision is LoopDetector.Decision.LivelockFailureDetected) { - traceCollector?.newSwitch(currentThread, SwitchReason.ACTIVE_LOCK, tracePoint is MethodCallTracePoint) + traceCollector?.newSwitch(currentThread, SwitchReason.ACTIVE_LOCK, beforeMethodCallSwitch = tracePoint is MethodCallTracePoint) failDueToDeadlock() } // if live-lock was detected, and replay was requested, // then abort current execution and start the replay - if (decision is LoopDetector.Decision.LivelockReplayRequired || decision is LoopDetector.Decision.LivelockReplayToDetectCycleRequired) { + if (decision.isReplayRequired) { suddenInvocationResult = SpinCycleFoundAndReplayRequired throw ForcibleExecutionFinishError } @@ -499,6 +499,8 @@ abstract class ManagedStrategy( /** * A regular context thread switch to another thread. + * + * @return was this thread actually switched to another or not */ private fun switchCurrentThread( iThread: Int, @@ -506,7 +508,7 @@ abstract class ManagedStrategy( mustSwitch: Boolean = false, tracePoint: TracePoint? = null ): Boolean { - traceCollector?.newSwitch(iThread, reason, tracePoint != null && tracePoint is MethodCallTracePoint) + traceCollector?.newSwitch(iThread, reason, beforeMethodCallSwitch = tracePoint != null && tracePoint is MethodCallTracePoint) doSwitchCurrentThread(iThread, mustSwitch) val switchHappened = iThread != currentThread awaitTurn(iThread) @@ -734,7 +736,7 @@ abstract class ManagedStrategy( lastReadTracePoint[iThread] = tracePoint } newSwitchPoint(iThread, codeLocation, tracePoint) - loopDetector.passValue(obj) + loopDetector.beforeReadField(obj) return@runInIgnoredSection true } @@ -758,22 +760,17 @@ abstract class ManagedStrategy( lastReadTracePoint[iThread] = tracePoint } newSwitchPoint(iThread, codeLocation, tracePoint) - loopDetector.passValue(array) - loopDetector.passValue(index) + loopDetector.beforeReadArrayElement(array, index) true } - override fun afterRead(value: Any?) { + override fun afterRead(value: Any?) = runInIgnoredSection { if (collectTrace) { - runInIgnoredSection { val iThread = currentThread lastReadTracePoint[iThread]?.initializeReadValue(adornedStringRepresentation(value)) lastReadTracePoint[iThread] = null - } - } - runInIgnoredSection { - loopDetector.passValue(value) } + loopDetector.afterRead(value) } override fun beforeWriteField(obj: Any?, className: String, fieldName: String, value: Any?, codeLocation: Int, @@ -806,8 +803,7 @@ abstract class ManagedStrategy( null } newSwitchPoint(iThread, codeLocation, tracePoint) - loopDetector.passValue(obj) - loopDetector.passValue(value) + loopDetector.beforeWriteField(obj, value) return@runInIgnoredSection true } @@ -832,9 +828,7 @@ abstract class ManagedStrategy( null } newSwitchPoint(iThread, codeLocation, tracePoint) - loopDetector.passValue(array) - loopDetector.passValue(index) - loopDetector.passValue(value) + loopDetector.beforeWriteArrayElement(array, index, value) true } @@ -943,7 +937,7 @@ abstract class ManagedStrategy( } } runInIgnoredSection { - loopDetector.beforeRegularMethodCall(codeLocation, params) + loopDetector.beforeMethodCall(codeLocation, params) } if (collectTrace) { runInIgnoredSection { @@ -1103,7 +1097,7 @@ abstract class ManagedStrategy( owner: Any?, iThread: Int, codeLocation: Int, - identifier: Int, + methodId: Int, className: String, methodName: String, params: Array, @@ -1124,10 +1118,10 @@ abstract class ManagedStrategy( methodCallTracePointStack[iThread] += tracePoint // Method id used to calculate spin cycle start label call depth. // Two calls are considered equals if two same methods were called with the same parameters. - val methodId = Objects.hash(identifier, - params.map { primitiveHashCodeOrSystemHashCode(it) }.toTypedArray().contentHashCode() + val methodIdentifierWithSignatureAndParams = Objects.hash(methodId, + params.map { primitiveOrIdentityHashCode(it) }.toTypedArray().contentHashCode() ) - callStackTrace.add(CallStackTraceElement(tracePoint, suspensionIdentifier, methodId)) + callStackTrace.add(CallStackTraceElement(tracePoint, suspensionIdentifier, methodIdentifierWithSignatureAndParams)) if (owner == null) { beforeStaticMethodCall() } else { @@ -1436,7 +1430,14 @@ abstract class ManagedStrategy( fun newSwitch(iThread: Int, reason: SwitchReason, beforeMethodCallSwitch: Boolean = false) { if (reason == SwitchReason.ACTIVE_LOCK) { - afterSpinCycleTraceCollected(iThread, beforeMethodCallSwitch) + afterSpinCycleTraceCollected( + trace = _trace, + callStackTrace = callStackTrace[currentThread], + spinCycleMethodCallsStackTraces = spinCycleMethodCallsStackTraces, + iThread = iThread, + currentActorId = currentActorId[iThread], + beforeMethodCallSwitch = beforeMethodCallSwitch + ) } _trace += SwitchEventTracePoint( iThread = iThread, @@ -1447,261 +1448,6 @@ abstract class ManagedStrategy( spinCycleStartAdded = false } - /** - * Calculates the [SpinCycleStartTracePoint] trace point callStackTrace correctly after all trace points - * related to the spin cycle are collected. - * - * # The problem overview - * - * [TraceCollector] collects two types of the [TracePoint]: - * 1. TracePoints that represents places where potential context switch points could happened. - * 2. TracePoints, representing regular, non-atomic method calls. - * - * This division is important for us, as [LoopDetector] stores execution points sequences of the - * first type, causing the challenges, described below. - * - * When [SpinCycleStartTracePoint] is added initially to the [trace], its callStackTrace, constructed via - * `callStackTrace[currentThread]` may be inaccurate. Consider the following example: - * We have the following code: - * ```kotlin - * @Operation - * fun actor() { - * atomicInteger.set(false) // code location = 1 - * spinLock() // method call code location = 2 - * } - * - * fun spinLock() { - * while (true) { - * val value = getValue() // method call code location = 3 - * atomicInteger.compareAndSet(value, !value) // code location = 5 - * } - * } - * - * fun getValue() = atomicInteger.get() // code location = 4 - * ``` - * When we ran into a cycle, we'll have the following code locations history: - * ``` - * [1, 2, 3, 4, 5, 3, 4, 5, 3, 4 ,5 ..., 3, 4, 5] - * ``` - * Cycle period here is 3: `[3, 4, 5]` and only two `[1, 2]` executions happened before the cycle. - * Then, suppose we have a scenario with two actors. - * The interleaving we have: - * ``` - * [ - * 1. Execute 2 instructions (switch positions) in thread 1, - * 2. Execute 1 instruction (switch positions) in the thread 1, - * 3. Execute 3 instructions (switch positions) in the thread 1 -> execution hung. - * ] - * ``` - * It's worth noting that the LoopDetector operates with a switch positions when replaying the - * execution to collect trace. - * - * Due to the internals of the [LoopDetector], [LoopDetector.replayModeCurrentlyInSpinCycle] will only - * return `true` at the beginning of step 3 described above, as execution only contains a sequence of - * executed potential switch points, while method call isn't a potential switch point. - * But at the beginning of step 3 we'll be inside the `getValue` method, so when [LoopDetector.replayModeCurrentlyInSpinCycle] - * is triggered, it's not correct using `callStackTrace[currentThread]`, as it would contain `getValue`. - * - * Summarizing, we may receive the signal about spin cycle start some non-atomic method calls after the actual spin cycle start - * if we switch right before the first spin cycle potential switch point execution, - * and this happens as [LoopDetector] stores sequences of the only potential switch points executions and - * all enters to the non-atomic methods are considered as a part of a step 1, which doesn't correspond to a spin cycle. - * - * Hence, **we can only use trace points of the first type defined above (which are potential switch points) - * to calculate the correct spin cycle start label call depth**. - * - * The problem has two solutions, depending on the spin cycle type: - * * recursive - * * iterative. - * - * # Iterative spin lock - * Let's consider the solution for the iterative spin-cycle. - * - * When we report an iterative spin cycle, we **must** visit the first spin cycle execution point - * and the first execution point of the second iteration (with an exit before). - * Let's track the current stacktrace before each switch point, before and after each method calls. - * Consider the example of a spin cycle, representing call stack traces of the execution points: - * ``` - * A -> B -> C - * A -> B -> C -> K - * A -> B -> E -> D - * A -> B -> E - * A -> B -> X - * ``` - * As we can see, even if all the execution points are wrapped in the non-atomic method calls (which we track - * during the trace collection too), the longest common prefix of the method calls `A, B` in the stack traces - * is the right position to place spin cycle start label. - * Indeed, due to the iterative nature of the spin lock, the first spin cycle execution of the first iteration - * and of the second iteration are placed in the same depth of calls. So it's guaranteed that we'll visit - * the most nested call and the least nested call. The wanted recursive method (and its call depth) can be found - * using the longest prefix of all the points is the spin cycle, as it is going to be the same along all the - * trace points and method calls. - * - * So that's what we do in case of an iterative spin lock. - * Track all the trace points of the spin cycle, - * calculate the max common method call prefix of these trace points, and place the spin cycle start label - * onto the found call depth. - * - * # Recursive spin lock - * - * Unlike iterative spin lock, in case of a recursive spin lock, longest common prefix may include - * method calls, that are a part of a spin cycle. Consider the following example: - * ```kotlin - * fun actor() { - * a() - * } - * - * fun a() = b() - * - * fun b() { - * c() - * c() - * a() // recursive call - * } - * - * fun c() = value.get() - * ``` - * According to the problem described above, if we switch to another thread and back right before point X, - * the trace points of the spin cycle will have the following stack traces: - * ``` - * [ - * actor -> a -> b -> c - * actor -> a -> b -> c - * ] - * ``` - * And the first execution point of the second iteration will be - * ``` - * actor -> a -> b -> a -> b -> c - * ``` - * - * As mentioned, it's incorrect to say that spin cycle starts at the depth `actor -> a -> c`, as the first - * recursive call is method `b` call, so we can't use the approach for the iterative spin cycle type. - * - * Let's consider the last potential switch trace point before the first spin cycle trace point, - * the first iteration first potential switch trace point - * and the second iteration first potential switch trace point: - * - * 1. `actor -> a -> b -> c` - * 2. `actor -> a -> b -> c -> a -> b -> c` - * - * Due to the recursive nature of a spin cycle, the second and the third trace points must have common - * method call suffix. - * It's important how to compare method calls in the suffixes. We consider method calls identical if - * they have the same ids produced by [MethodIds] and the same parameters. That means we don't care - * here where this method is called - it's more convenient to see as short version of the spin cycle as possible. - * - * But this approach may produce a wrong result in case when we switch from the spin lock and then occur - * in the same spin lock again. - * Let's consider this situation using the example above. - * Trace points of the second spin lock would have the following call stacks: - * - * 1. `actor -> [a -> b -> c] -> [a -> b -> c]` - * 2. `actor -> [a -> b -> c] -> [a -> b -> c]` - * - * And the first execution point of the second iteration will be - * ``` - * actor -> [a -> b -> c] -> [a -> b -> c] -> [a -> b -> c] - * ``` - * - * If we apply this algorithm to the first point from the first iteration and the first point from the second iteration - * we get `[a -> b -> c] -> [a -> b -> c]` longest common suffix, which is not correct, because the first recursive call - * of this cycle starts at the depth `actor -> a -> b`. - * In this case, the last trace point before the spin cycle becomes handy. - * In the example above it would have the following call stack `actor -> a -> b -> c`. - * - * Here are the points we're interested in: - * 1. `actor -> [a -> b -> c]` - The last trace point before the cycle. - * 2. `actor -> [a -> b -> c] -> [a -> b -> c]` - The first trace point of the cycle. - * 3. ` actor -> [a -> b -> c] -> [a -> b -> c] -> [a -> b -> c]` - The first trace point of the second iteration. - * Let's note the following rule: if point 1 has the same method call on the same depth as point 2, - * then this call can't be a part of the current spin cycle. - * The reason is quite easy: if it was, we would mark point 1 as a first spin cycle trace point. - * - * So, to detect the spin cycle, start trace point depth in case of a recursive spin lock we: - * 1. Take the first trace point of the spin cycle on the first iteration (point A) and on the second iteration (point B). - * 2. Take the last trace point before the spin cycle (point C). - * 3. We walk synchronously on their call stacks from the end to the beginning, while call from the A stack - * is the same as a call from the stack B. But it also equals the call from the call C, then we need to stop. - * The count of the call passed the condition above equals to the call we need to lift from the first spin cycle - * node to get the correct spin cycle start trace point depth. - * - * @param beforeMethodCallSwitch flag if this method invoked right after [MethodCallTracePoint] is added to a trace, - * before the corresponding method is called. - */ - private fun afterSpinCycleTraceCollected( - iThread: Int, - beforeMethodCallSwitch: Boolean - ) { - // Obtaining spin cycle trace points. - val spinLockTracePoints = trace.takeLastWhile { it.iThread == iThread && it !is SpinCycleStartTracePoint } - // Nothing to do in this case (seems unreal). - if (spinLockTracePoints.isEmpty()) return - // Get the call stack of first trace point of the second spin cycle iteration - var currentCallStackTrace: List = callStackTrace[iThread] - // If this method is invoked after beforeMethodCall or beforeAtomicMethodCall - // than MethodCallTracePoint is already added, correct it by altering current stack trace - if (beforeMethodCallSwitch) { - currentCallStackTrace = currentCallStackTrace.dropLast(1) - } - - val cycleStartTracePointIndex = _trace.size - spinLockTracePoints.size - 1 - if (cycleStartTracePointIndex < 0 || _trace[cycleStartTracePointIndex] !is SpinCycleStartTracePoint) return - - val spinCycleFirstTracePointCallStackTrace = spinLockTracePoints.first().callStackTrace - val isRecursive = currentCallStackTrace.size != spinCycleFirstTracePointCallStackTrace.size - val spinCycleStartStackTrace = if (isRecursive) { - // See above the description of the algorithm for recursive spin lock. - val prevIndex = _trace.lastIndex - spinLockTracePoints.size - 1 - val tracePointBeforeCycle = if (prevIndex >= 0) (prevIndex downTo 0) - .firstOrNull { _trace[it] !is SwitchEventTracePoint && _trace[it].iThread == iThread }?.let { _trace[it] } - else null - - var currentI = currentCallStackTrace.lastIndex - var firstI = spinCycleFirstTracePointCallStackTrace.lastIndex - var count = 0 - while (firstI >= 0) { - val identifier = spinCycleFirstTracePointCallStackTrace[firstI].methodId - // Comparing corresponding calls. - if (identifier != currentCallStackTrace[currentI].methodId) break - // Check for the last trace point before the cycle. - if ((tracePointBeforeCycle != null) && - (tracePointBeforeCycle.callStackTrace.lastIndex >= firstI) && - (tracePointBeforeCycle.callStackTrace[firstI].methodId == identifier) - ) break - - currentI-- - firstI-- - count++ - } - spinCycleFirstTracePointCallStackTrace.dropLast(count) - } else { - // See above the description of the algorithm for iterative spin lock. - getCommonMinStackTrace(spinLockTracePoints) - .dropLast(currentCallStackTrace.size - spinCycleFirstTracePointCallStackTrace.size) - } - _trace[cycleStartTracePointIndex] = - SpinCycleStartTracePoint(iThread, currentActorId[iThread], spinCycleStartStackTrace) - } - - /** - * @return Max common prefix of the [StackTraceElement] of the provided [spinCycleTracePoints] - */ - private fun getCommonMinStackTrace(spinCycleTracePoints: List): List { - val callStackTraces = spinCycleTracePoints.map { it.callStackTrace } + spinCycleMethodCallsStackTraces - var count = 0 - outer@while (true) { - if (count == callStackTraces[0].size) break - val stackTraceElement = callStackTraces[0][count].call.stackTraceElement - for (i in 1 until callStackTraces.size) { - val traceElements = callStackTraces[i] - if (count == traceElements.size) break@outer - if (stackTraceElement != traceElements[count].call.stackTraceElement) break@outer - } - count++ - } - return spinCycleTracePoints.first().callStackTrace.take(count) - } - fun onThreadFinish() { spinCycleStartAdded = false } @@ -1745,7 +1491,14 @@ abstract class ManagedStrategy( } fun passObstructionFreedomViolationTracePoint(iThread: Int, beforeMethodCall: Boolean) { - afterSpinCycleTraceCollected(iThread, beforeMethodCall) + afterSpinCycleTraceCollected( + trace = _trace, + callStackTrace = callStackTrace[currentThread], + spinCycleMethodCallsStackTraces = spinCycleMethodCallsStackTraces, + iThread = iThread, + currentActorId = currentActorId[iThread], + beforeMethodCallSwitch = beforeMethodCall + ) _trace += ObstructionFreedomViolationExecutionAbortTracePoint( iThread = iThread, actorId = currentActorId[iThread], diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/TracePoint.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/TracePoint.kt index 6e5bb9a57c..d97874cf25 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/TracePoint.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/TracePoint.kt @@ -313,6 +313,6 @@ internal enum class SwitchReason(private val reason: String) { * * All methods calls are enumerated to make it possible to distinguish different calls of the same method. * Suspended method calls have the same [suspensionIdentifier] before and after suspension, but different [call] points. - * @param methodId Method identifier. See [org.jetbrains.kotlinx.lincheck.transformation.MethodIds]. + * @see [org.jetbrains.kotlinx.lincheck.transformation.MethodIds]. */ internal class CallStackTraceElement(val call: MethodCallTracePoint, val suspensionIdentifier: Int, val methodId: Int) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt index 5f7ab4e64f..d7c450c338 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt @@ -435,7 +435,6 @@ internal class ModelCheckingStrategy( } private inner class Choice(val node: InterleavingTreeNode, val value: Int) { - // For easier debug. override fun toString(): String { return "Choice(node=$node, value=$value)" } diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/representation/ScalaBugTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/representation/ScalaBugTest.kt new file mode 100644 index 0000000000..96aa4dbd1a --- /dev/null +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/representation/ScalaBugTest.kt @@ -0,0 +1,19 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2024 JetBrains s.r.o. + * + * This Source Code Form is subject to the terms of the + * Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed + * with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.jetbrains.kotlinx.lincheck_test.representation + +class ScalaBugTest { + + + fun x() { + } + +} \ No newline at end of file From 067953cc2ecc8087c059b2ca5e0afac5bbbde446 Mon Sep 17 00:00:00 2001 From: "Aleksandr.Potapov" Date: Sat, 22 Jun 2024 14:17:58 +0200 Subject: [PATCH 06/13] Review fixes --- build.gradle.kts | 3 --- .../org/jetbrains/kotlinx/lincheck/Utils.kt | 2 +- .../lincheck/strategy/managed/LoopDetector.kt | 16 ++++++++-------- .../strategy/managed/ManagedStrategy.kt | 15 ++++++++++----- .../representation/ScalaBugTest.kt | 19 ------------------- 5 files changed, 19 insertions(+), 36 deletions(-) delete mode 100644 src/jvm/test/org/jetbrains/kotlinx/lincheck_test/representation/ScalaBugTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index 360b176179..0fd14d3693 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -20,9 +20,6 @@ plugins { repositories { mavenCentral() - flatDir { - dirs = mutableSetOf(File("/Users/Aleksandr.Potapov/Documents/Work/lincheck/libs")) - } } kotlin { diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt index 0dba767051..f4d99203a5 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt @@ -92,7 +92,7 @@ private fun Class.getMethod(name: String, parameterTypes: Array ManagedCTestConfiguration.LIVELOCK_EVENTS_THRESHOLD) { return Decision.EventsThresholdReached @@ -271,7 +271,7 @@ internal class LoopDetector( /** * Called after any method calls. */ - fun afterRegularMethodCall() { + fun afterMethodCall() { // Because we want to track the fact of the method exit, // but the sequence of the beforeMethodCall code locations values determines what exactly method we exit. // That's why we can use just 0 as a label of a method exit. @@ -295,7 +295,7 @@ internal class LoopDetector( * Otherwise, does nothing. */ fun passParameters(params: Array) { - if (mode == WorkMode.DEFAULT) return + if (mode == Mode.DEFAULT) return params.forEach { param -> currentThreadCodeLocationsHistory += paramToIntRepresentation(param) } @@ -419,7 +419,7 @@ internal class LoopDetector( * Otherwise, does nothing. */ private fun passValue(obj: Any?) { - if (mode == WorkMode.DEFAULT) return + if (mode == Mode.DEFAULT) return currentThreadCodeLocationsHistory += paramToIntRepresentation(obj) } @@ -583,7 +583,7 @@ internal class LoopDetector( return if (hashCode == 0) -1 else -hashCode } - private enum class WorkMode { + private enum class Mode { /** * The default mode of [LoopDetector]: analyzing only potential switch points code locations diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt index cb1f06fbc7..8008172670 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt @@ -908,7 +908,7 @@ abstract class ManagedStrategy( } else { params } - beforeMethodCall(owner, currentThread, codeLocation, 0, className, methodName, params) + beforeMethodCall(owner, currentThread, codeLocation, ATOMIC_METHOD_ID, className, methodName, params) } } // It's important that this method can't be called inside runInIgnoredSection, as the ignored section @@ -920,7 +920,7 @@ abstract class ManagedStrategy( ManagedGuaranteeType.TREAT_AS_ATOMIC -> { runInIgnoredSection { if (collectTrace) { - beforeMethodCall(owner, currentThread, codeLocation, 0, className, methodName, params) + beforeMethodCall(owner, currentThread, codeLocation, ATOMIC_METHOD_ID, className, methodName, params) } newSwitchPointOnAtomicMethodCall(codeLocation, params) } @@ -962,14 +962,14 @@ abstract class ManagedStrategy( params: Array ) = runInIgnoredSection { if (collectTrace) { - beforeMethodCall(owner, currentThread, codeLocation, 0, className, methodName, params) + beforeMethodCall(owner, currentThread, codeLocation, ATOMIC_METHOD_ID, className, methodName, params) } newSwitchPointOnAtomicMethodCall(codeLocation, params) } override fun onMethodCallReturn(result: Any?) { runInIgnoredSection { - loopDetector.afterRegularMethodCall() + loopDetector.afterMethodCall() } if (collectTrace) { runInIgnoredSection { @@ -992,7 +992,7 @@ abstract class ManagedStrategy( override fun onMethodCallException(t: Throwable) { runInIgnoredSection { - loopDetector.afterRegularMethodCall() + loopDetector.afterMethodCall() } if (collectTrace) { runInIgnoredSection { @@ -1798,3 +1798,8 @@ private const val INFINITE_TIMEOUT = 1000L * 60 * 60 * 24 * 365 private fun getTimeOutMs(strategy: ManagedStrategy, defaultTimeOutMs: Long): Long = if (strategy is ModelCheckingStrategy && strategy.replay) INFINITE_TIMEOUT else defaultTimeOutMs + +// This is a "Stub" as we don't need methodId in case of atomic method call as there can't be +// any other trace points -> no possible recursive spin cycles. +// So it's just a workaround caused by beforeMethodCall is taking methodId parameter. +private const val ATOMIC_METHOD_ID = 0 diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/representation/ScalaBugTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/representation/ScalaBugTest.kt deleted file mode 100644 index 96aa4dbd1a..0000000000 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/representation/ScalaBugTest.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Lincheck - * - * Copyright (C) 2019 - 2024 JetBrains s.r.o. - * - * This Source Code Form is subject to the terms of the - * Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed - * with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package org.jetbrains.kotlinx.lincheck_test.representation - -class ScalaBugTest { - - - fun x() { - } - -} \ No newline at end of file From d8fb339cde4f90eed00985575158213a65e925ff Mon Sep 17 00:00:00 2001 From: "Aleksandr.Potapov" Date: Tue, 2 Jul 2024 20:35:57 +0200 Subject: [PATCH 07/13] Review fixes --- .../strategy/managed/ManagedStrategy.kt | 20 +- .../lincheck/transformation/CodeLocations.kt | 1 + .../AtomicArrayWithCAS2LiveLockTest.kt | 224 +++++++++++++++ .../representation/RecursiveSpinLockTest.kt | 204 -------------- .../broken-cas-2-recursive-live-lock.txt | 254 +++++++++--------- .../spin_lock_events_cut_inner_loop.txt | 186 +++++++------ 6 files changed, 453 insertions(+), 436 deletions(-) create mode 100644 src/jvm/test/org/jetbrains/kotlinx/lincheck_test/representation/AtomicArrayWithCAS2LiveLockTest.kt diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt index a402c3b89d..48ef33670c 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt @@ -31,7 +31,6 @@ import org.jetbrains.kotlinx.lincheck.strategy.managed.VarHandleMethodType.* import java.lang.invoke.VarHandle import java.lang.reflect.* import java.util.* -import java.util.concurrent.atomic.* import kotlin.collections.set import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED @@ -897,10 +896,14 @@ abstract class ManagedStrategy( LincheckJavaAgent.ensureClassHierarchyIsTransformed(className.canonicalClassName) } if (collectTrace) { - addBeforeMethodCallTracePoint(owner, codeLocation, className, methodName, params, atomicMethodDescriptor) + traceCollector!!.checkActiveLockDetected() + addBeforeMethodCallTracePoint(owner, codeLocation, methodId, className, methodName, params, atomicMethodDescriptor) } if (guarantee == ManagedGuaranteeType.TREAT_AS_ATOMIC) { - newSwitchPointOnAtomicMethodCall(codeLocation) + newSwitchPointOnAtomicMethodCall(codeLocation, params) + } + if (guarantee == null) { + loopDetector.beforeMethodCall(codeLocation, params) } guarantee } @@ -1265,8 +1268,8 @@ abstract class ManagedStrategy( callStackContextPerThread[currentThread].add(CallContext.InstanceCallContext(receiver)) } - private fun afterExitMethod() { - val currentContext = callStackContextPerThread[currentThread] + private fun afterExitMethod(iThread: Int) { + val currentContext = callStackContextPerThread[iThread] currentContext.removeLast() check(currentContext.isNotEmpty()) { "Context cannot be empty" } } @@ -1278,7 +1281,7 @@ abstract class ManagedStrategy( * @param tracePoint the corresponding trace point for the invocation */ private fun afterMethodCall(iThread: Int, tracePoint: MethodCallTracePoint) { - afterExitMethod() + afterExitMethod(iThread) val callStackTrace = callStackTrace[iThread] if (tracePoint.wasSuspended) { // if a method call is suspended, save its identifier to reuse for continuation resuming @@ -1735,8 +1738,3 @@ private const val INFINITE_TIMEOUT = 1000L * 60 * 60 * 24 * 365 private fun getTimeOutMs(strategy: ManagedStrategy, defaultTimeOutMs: Long): Long = if (strategy is ModelCheckingStrategy && strategy.replay) INFINITE_TIMEOUT else defaultTimeOutMs - -// This is a "Stub" as we don't need methodId in case of atomic method call as there can't be -// any other trace points -> no possible recursive spin cycles. -// So it's just a workaround caused by beforeMethodCall is taking methodId parameter. -private const val ATOMIC_METHOD_ID = 0 diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/transformation/CodeLocations.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/transformation/CodeLocations.kt index bdcef4f0b1..8bc41b212e 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/transformation/CodeLocations.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/transformation/CodeLocations.kt @@ -40,6 +40,7 @@ internal object CodeLocations { fun newCodeLocation(stackTraceElement: StackTraceElement): Int { val id = codeLocations.size codeLocations.add(stackTraceElement) + // Code location 0 stands for the method exit, so we start this sequence from 1. return id + 1 } diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/representation/AtomicArrayWithCAS2LiveLockTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/representation/AtomicArrayWithCAS2LiveLockTest.kt new file mode 100644 index 0000000000..0869b06863 --- /dev/null +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/representation/AtomicArrayWithCAS2LiveLockTest.kt @@ -0,0 +1,224 @@ +/* + * Lincheck + * + * Copyright (C) 2019 - 2024 JetBrains s.r.o. + * + * This Source Code Form is subject to the terms of the + * Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed + * with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.jetbrains.kotlinx.lincheck_test.representation + +import kotlinx.atomicfu.AtomicArray +import kotlinx.atomicfu.atomic +import kotlinx.atomicfu.atomicArrayOfNulls +import org.jetbrains.kotlinx.lincheck.annotations.Operation +import org.jetbrains.kotlinx.lincheck.checkImpl +import org.jetbrains.kotlinx.lincheck.strategy.managed.modelchecking.ModelCheckingOptions +import org.jetbrains.kotlinx.lincheck_test.util.checkLincheckOutput +import org.junit.Test + +/** + * Checks recursive spin lock representation when execution hung due to alternation of two threads + * in recursive live-lock. + */ +class AtomicArrayWithCAS2LiveLockTest { + private val array = AtomicArrayWithCAS2(ARRAY_SIZE, 0) + + @Operation + fun cas2_0( + index1: Int, expected1: Int, update1: Int, + index2: Int, expected2: Int, update2: Int, + ) = array.cas2(index1, expected1, update1, index2, expected2, update2, 0) + + @Operation + fun cas2_1( + index1: Int, expected1: Int, update1: Int, + index2: Int, expected2: Int, update2: Int, + ) = array.cas2(index1, expected1, update1, index2, expected2, update2, 1) + + + @Test + fun modelCheckingTest() = + ModelCheckingOptions() + .addCustomScenario { + parallel { + thread { actor(AtomicArrayWithCAS2LiveLockTest::cas2_0, 0, 0, 2, 1, 0, 3) } + thread { actor(AtomicArrayWithCAS2LiveLockTest::cas2_1, 0, 0, 4, 1, 0, 5) } + } + } + .iterations(500) + .invocationsPerIteration(1000) + .sequentialSpecification(IntAtomicArraySequential::class.java) + .checkImpl(this::class.java) + .checkLincheckOutput("spin_lock/broken-cas-2-recursive-live-lock.txt") +} + +/** + * Broken CAS-2-based array implementation with only 2 operations. Calling them may lead to alternation recursive live-lock. + */ +class AtomicArrayWithCAS2(size: Int, initialValue: E) { + private val array: AtomicArray = atomicArrayOfNulls(size) + private val gate0 = atomic(false) + private val gate1 = atomic(false) + + init { + // Fill array with the initial value. + for (i in 0 until size) { + array[i].value = Descriptor(i, initialValue, initialValue, i, initialValue, initialValue).apply { + status.value = Status.SUCCESS + } + } + } + + fun cas2( + index1: Int, expected1: E, update1: E, + index2: Int, expected2: E, update2: E, + turn: Int + ): Boolean { + require(index1 != index2) { "The indices should be different" } + + val descriptor = if (index1 < index2) Descriptor(index1, expected1, update1, index2, expected2, update2) + else Descriptor(index2, expected2, update2, index1, expected1, update1) + + descriptor.apply(turn = turn) + return descriptor.status.value == Status.SUCCESS + } + + private inner class Descriptor( + private val index1: Int, + private val expected1: Any?, + private val update1: Any?, + private val index2: Int, + private val expected2: Any, + private val update2: Any + ) { + val status = atomic(Status.UNDECIDED) + + fun read(index: Int): Any { + check(index == index1 || index == index2) + return if (status.value == Status.SUCCESS) { + if (index == index1) update1 else update2 + } else { + if (index == index1) expected1 else expected2 + }!! + } + + /** + * @param broken if true then we never will go out of recursive spin-cycle + */ + fun apply(initial: Boolean = true, turn: Int = -1, broken: Boolean = false) { + if (broken) { + status.value + status.compareAndSet(Status.SUCCESS, Status.SUCCESS) + installOrHelp(true, turn, true) + } + if (status.value == Status.UNDECIDED) { + val install1 = if (!initial || index1 == -1) true else installOrHelp(true, turn) + val install2 = installOrHelp(false, turn) + + if (install1 && install2) { + status.compareAndSet(Status.UNDECIDED, Status.SUCCESS) + } + } + } + + /** + * @param broken if true then we never will go out of recursive spin-cycle + */ + private fun installOrHelp(first: Boolean, turn: Int = -1, broken: Boolean = false): Boolean { + val index = if (first) index1 else index2 + val expected = if (first) expected1 else expected2 + check(index != -1) + while (true) { + val current = array[index].value!! + if (broken) { + current.apply(false, turn, true) + } + if (current === this) return true + + when (turn) { + 1 -> { + gate0.value = true + if (gate1.value) { + current.apply(false, turn = turn, true) + } + gate0.value = false + } + + 0 -> { + gate1.value = true + if (gate0.value) { + current.apply(false, turn = turn, true) + } + gate1.value = false + } + + else -> error("Not excepted: $turn") + } + + current.apply(false, turn = turn) + if (current.read(index) !== expected) { + status.compareAndSet(Status.UNDECIDED, Status.FAILED) + return false + } + if (status.value != Status.UNDECIDED) return false + if (array[index].compareAndSet(current, this)) { + return true + } + } + } + } + + enum class Status { + UNDECIDED, SUCCESS, FAILED + } + +} + +private const val ARRAY_SIZE = 3 + +class IntAtomicArraySequential { + private val array = IntArray(ARRAY_SIZE) + + fun get(index: Int): Int = array[index] + + fun set(index: Int, value: Int) { + array[index] = value + } + + fun cas(index: Int, expected: Int, update: Int): Boolean { + if (array[index] != expected) return false + array[index] = update + return true + } + + fun dcss( + index1: Int, expected1: Int, update1: Int, + index2: Int, expected2: Int + ): Boolean { + require(index1 != index2) { "The indices should be different" } + if (array[index1] != expected1 || array[index2] != expected2) return false + array[index1] = update1 + return true + } + + fun cas2( + index1: Int, expected1: Int, update1: Int, index2: Int, expected2: Int, update2: Int + ): Boolean { + require(index1 != index2) { "The indices should be different" } + if (array[index1] != expected1 || array[index2] != expected2) return false + array[index1] = update1 + array[index2] = update2 + return true + } + + fun cas2_0( + index1: Int, expected1: Int, update1: Int, index2: Int, expected2: Int, update2: Int + ) = cas2(index1, expected1, update1, index2, expected2, update2) + + fun cas2_1( + index1: Int, expected1: Int, update1: Int, index2: Int, expected2: Int, update2: Int + ) = cas2(index1, expected1, update1, index2, expected2, update2) +} diff --git a/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/representation/RecursiveSpinLockTest.kt b/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/representation/RecursiveSpinLockTest.kt index 9380cd98a4..fe9f1dbd02 100644 --- a/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/representation/RecursiveSpinLockTest.kt +++ b/src/jvm/test/org/jetbrains/kotlinx/lincheck_test/representation/RecursiveSpinLockTest.kt @@ -264,210 +264,6 @@ class RecursiveTwoThreadsSpinLockTest { } -/** - * Checks recursive spin lock representation when execution hung due to alternation of two threads - * in recursive live-lock. - */ -class BrokenCas2RecursiveLiveLockTest { - private val array = AtomicArrayWithCAS2(ARRAY_SIZE, 0) - - @Operation - fun cas2_0( - index1: Int, expected1: Int, update1: Int, - index2: Int, expected2: Int, update2: Int, - ) = array.cas2(index1, expected1, update1, index2, expected2, update2, 0) - - @Operation - fun cas2_1( - index1: Int, expected1: Int, update1: Int, - index2: Int, expected2: Int, update2: Int, - ) = array.cas2(index1, expected1, update1, index2, expected2, update2, 1) - - - @Test - fun modelCheckingTest() = - ModelCheckingOptions() - .addCustomScenario { - parallel { - thread { actor(BrokenCas2RecursiveLiveLockTest::cas2_0, 0, 0, 2, 1, 0, 3) } - thread { actor(BrokenCas2RecursiveLiveLockTest::cas2_1, 0, 0, 4, 1, 0, 5) } - } - } - .iterations(500) - .invocationsPerIteration(1000) - .sequentialSpecification(IntAtomicArraySequential::class.java) - .checkImpl(this::class.java) - .checkLincheckOutput("spin_lock/broken-cas-2-recursive-live-lock.txt") -} - -/** - * Broken CAS-2-based array implementation with only 2 operations. Calling them may lead to alternation recursive live-lock. - */ -class AtomicArrayWithCAS2(size: Int, initialValue: E) { - private val array: AtomicArray = atomicArrayOfNulls(size) - private val gate0 = atomic(false) - private val gate1 = atomic(false) - - init { - // Fill array with the initial value. - for (i in 0 until size) { - array[i].value = Descriptor(i, initialValue, initialValue, i, initialValue, initialValue).apply { - status.value = Status.SUCCESS - } - } - } - - fun cas2( - index1: Int, expected1: E, update1: E, - index2: Int, expected2: E, update2: E, - turn: Int - ): Boolean { - require(index1 != index2) { "The indices should be different" } - - val descriptor = if (index1 < index2) Descriptor(index1, expected1, update1, index2, expected2, update2) - else Descriptor(index2, expected2, update2, index1, expected1, update1) - - descriptor.apply(turn = turn) - return descriptor.status.value == Status.SUCCESS - } - - private inner class Descriptor( - private val index1: Int, - private val expected1: Any?, - private val update1: Any?, - private val index2: Int, - private val expected2: Any, - private val update2: Any - ) { - val status = atomic(Status.UNDECIDED) - - fun read(index: Int): Any { - check(index == index1 || index == index2) - return if (status.value == Status.SUCCESS) { - if (index == index1) update1 else update2 - } else { - if (index == index1) expected1 else expected2 - }!! - } - - /** - * @param broken if true then we never will go out of recursive spin-cycle - */ - fun apply(initial: Boolean = true, turn: Int = -1, broken: Boolean = false) { - if (broken) { - status.value - status.compareAndSet(Status.SUCCESS, Status.SUCCESS) - installOrHelp(true, turn, true) - } - if (status.value == Status.UNDECIDED) { - val install1 = if (!initial || index1 == -1) true else installOrHelp(true, turn) - val install2 = installOrHelp(false, turn) - - if (install1 && install2) { - status.compareAndSet(Status.UNDECIDED, Status.SUCCESS) - } - } - } - - /** - * @param broken if true then we never will go out of recursive spin-cycle - */ - private fun installOrHelp(first: Boolean, turn: Int = -1, broken: Boolean = false): Boolean { - val index = if (first) index1 else index2 - val expected = if (first) expected1 else expected2 - check(index != -1) - while (true) { - val current = array[index].value!! - if (broken) { - current.apply(false, turn, true) - } - if (current === this) return true - - when (turn) { - 1 -> { - gate0.value = true - if (gate1.value) { - current.apply(false, turn = turn, true) - } - gate0.value = false - } - - 0 -> { - gate1.value = true - if (gate0.value) { - current.apply(false, turn = turn, true) - } - gate1.value = false - } - - else -> error("Not excepted: $turn") - } - - current.apply(false, turn = turn) - if (current.read(index) !== expected) { - status.compareAndSet(Status.UNDECIDED, Status.FAILED) - return false - } - if (status.value != Status.UNDECIDED) return false - if (array[index].compareAndSet(current, this)) { - return true - } - } - } - } - - enum class Status { - UNDECIDED, SUCCESS, FAILED - } - -} - -private const val ARRAY_SIZE = 3 - -class IntAtomicArraySequential { - private val array = IntArray(ARRAY_SIZE) - - fun get(index: Int): Int = array[index] - - fun set(index: Int, value: Int) { - array[index] = value - } - - fun cas(index: Int, expected: Int, update: Int): Boolean { - if (array[index] != expected) return false - array[index] = update - return true - } - - fun dcss( - index1: Int, expected1: Int, update1: Int, - index2: Int, expected2: Int - ): Boolean { - require(index1 != index2) { "The indices should be different" } - if (array[index1] != expected1 || array[index2] != expected2) return false - array[index1] = update1 - return true - } - - fun cas2( - index1: Int, expected1: Int, update1: Int, index2: Int, expected2: Int, update2: Int - ): Boolean { - require(index1 != index2) { "The indices should be different" } - if (array[index1] != expected1 || array[index2] != expected2) return false - array[index1] = update1 - array[index2] = update2 - return true - } - - fun cas2_0( - index1: Int, expected1: Int, update1: Int, index2: Int, expected2: Int, update2: Int - ) = cas2(index1, expected1, update1, index2, expected2, update2) - - fun cas2_1( - index1: Int, expected1: Int, update1: Int, index2: Int, expected2: Int, update2: Int - ) = cas2(index1, expected1, update1, index2, expected2, update2) -} - /** * Checks proper output in case of recursive spin-lock in one thread. * Should correctly detect spin cycle and place spin cycle label in case diff --git a/src/jvm/test/resources/expected_logs/spin_lock/broken-cas-2-recursive-live-lock.txt b/src/jvm/test/resources/expected_logs/spin_lock/broken-cas-2-recursive-live-lock.txt index b04dee8cab..72710c2e1b 100644 --- a/src/jvm/test/resources/expected_logs/spin_lock/broken-cas-2-recursive-live-lock.txt +++ b/src/jvm/test/resources/expected_logs/spin_lock/broken-cas-2-recursive-live-lock.txt @@ -7,134 +7,134 @@ The following interleaving leads to the error: -| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Thread 1 | Thread 2 | -| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| | cas2_1(0, 0, 4, 1, 0, 5): | -| | array.cas2(0,0,4,1,0,5,1) at BrokenCas2RecursiveLiveLockTest.cas2_1(RecursiveSpinLockTest.kt:277) | -| | AtomicArrayWithCAS2$Descriptor.apply$default(Descriptor#1,false,1,false,5,null) at AtomicArrayWithCAS2.cas2(RecursiveSpinLockTest.kt:323) | -| | Descriptor#1.apply(true,1,false) at AtomicArrayWithCAS2$Descriptor.apply$default(RecursiveSpinLockTest.kt:349) | -| | AtomicArrayWithCAS2$Descriptor.installOrHelp$default(Descriptor#1,true,1,false,4,null): true at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:356) | -| | Descriptor#1.installOrHelp(true,1,false): true at AtomicArrayWithCAS2$Descriptor.installOrHelp$default(RecursiveSpinLockTest.kt:368) | -| | BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | -| | AtomicReferenceArray#1[0].get(): Descriptor#2 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | -| | BrokenCas2RecursiveLiveLockTest#1.array.gate0.WRITE(1) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:381) | -| | BrokenCas2RecursiveLiveLockTest#1.array.gate1.READ: 0 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:382) | -| | switch | -| cas2_0(0, 0, 2, 1, 0, 3): | | -| array.cas2(0,0,2,1,0,3,0) at BrokenCas2RecursiveLiveLockTest.cas2_0(RecursiveSpinLockTest.kt:271) | | -| AtomicArrayWithCAS2$Descriptor.apply$default(Descriptor#3,false,0,false,5,null) at AtomicArrayWithCAS2.cas2(RecursiveSpinLockTest.kt:323) | | -| Descriptor#3.apply(true,0,false) at AtomicArrayWithCAS2$Descriptor.apply$default(RecursiveSpinLockTest.kt:349) | | -| AtomicArrayWithCAS2$Descriptor.installOrHelp$default(Descriptor#3,true,0,false,4,null) at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:356) | | -| Descriptor#3.installOrHelp(true,0,false) at AtomicArrayWithCAS2$Descriptor.installOrHelp$default(RecursiveSpinLockTest.kt:368) | | -| BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | -| AtomicReferenceArray#1[0].get(): Descriptor#2 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | -| BrokenCas2RecursiveLiveLockTest#1.array.gate1.WRITE(1) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:389) | | -| BrokenCas2RecursiveLiveLockTest#1.array.gate0.READ: 1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:390) | | -| /* The following events repeat infinitely: */ | | -| ┌╶> Descriptor#2.apply(false,0,true) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:391) | | -| | status.READ: SUCCESS at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:351) | | -| | status.compareAndSet(SUCCESS,SUCCESS): true at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:352) | | -| | installOrHelp(true,0,true) at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:353) | | -| | BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | -| | AtomicReferenceArray#1[0].get(): Descriptor#2 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | -| └╶╶╶╶╶╶ switch (reason: active lock detected) | | -| | BrokenCas2RecursiveLiveLockTest#1.array.gate0.WRITE(0) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:385) | -| | AtomicArrayWithCAS2$Descriptor.apply$default(Descriptor#2,false,1,false,4,null) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:399) | -| | Descriptor#2.read(0): 0 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:400) | -| | BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:405) | -| | AtomicReferenceArray#1[0].compareAndSet(Descriptor#2,Descriptor#1): true at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:405) | -| | AtomicArrayWithCAS2$Descriptor.installOrHelp$default(Descriptor#1,false,1,false,4,null) at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:357) | -| | Descriptor#1.installOrHelp(false,1,false) at AtomicArrayWithCAS2$Descriptor.installOrHelp$default(RecursiveSpinLockTest.kt:368) | -| | BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | -| | AtomicReferenceArray#1[1].get(): Descriptor#4 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | -| | BrokenCas2RecursiveLiveLockTest#1.array.gate0.WRITE(1) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:381) | -| | BrokenCas2RecursiveLiveLockTest#1.array.gate1.READ: 1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:382) | -| | /* The following events repeat infinitely: */ | -| | ┌╶> Descriptor#4.apply(false,1,true) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:383) | -| | | status.READ: SUCCESS at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:351) | -| | | status.compareAndSet(SUCCESS,SUCCESS): true at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:352) | -| | | installOrHelp(true,1,true) at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:353) | -| | | BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | -| | | AtomicReferenceArray#1[1].get(): Descriptor#4 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | -| | └╶╶╶╶╶╶ switch (reason: active lock detected) | -| apply(false,0,true) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:375) | | -| status.READ: SUCCESS at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:351) | | -| status.compareAndSet(SUCCESS,SUCCESS): true at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:352) | | -| /* The following events repeat infinitely: */ | | -| ┌╶> installOrHelp(true,0,true) at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:353) | | -| | BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | -| | AtomicReferenceArray#1[0].get(): Descriptor#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | -| | Descriptor#1.apply(false,0,true) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:375) | | -| | status.compareAndSet(SUCCESS,SUCCESS): false at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:352) | | -| └╶╶╶╶╶╶ switch (reason: active lock detected) | | -| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | cas2_1(0, 0, 4, 1, 0, 5): | +| | array.cas2(0,0,4,1,0,5,1) at AtomicArrayWithCAS2LiveLockTest.cas2_1(AtomicArrayWithCAS2LiveLockTest.kt:39) | +| | AtomicArrayWithCAS2$Descriptor.apply$default(Descriptor#1,false,1,false,5,null) at AtomicArrayWithCAS2.cas2(AtomicArrayWithCAS2LiveLockTest.kt:85) | +| | Descriptor#1.apply(true,1,false) at AtomicArrayWithCAS2$Descriptor.apply$default(AtomicArrayWithCAS2LiveLockTest.kt:111) | +| | AtomicArrayWithCAS2$Descriptor.installOrHelp$default(Descriptor#1,true,1,false,4,null): true at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:118) | +| | Descriptor#1.installOrHelp(true,1,false): true at AtomicArrayWithCAS2$Descriptor.installOrHelp$default(AtomicArrayWithCAS2LiveLockTest.kt:130) | +| | AtomicArrayWithCAS2LiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:135) | +| | AtomicReferenceArray#1[0].get(): Descriptor#2 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:135) | +| | AtomicArrayWithCAS2LiveLockTest#1.array.gate0.WRITE(1) at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:143) | +| | AtomicArrayWithCAS2LiveLockTest#1.array.gate1.READ: 0 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:144) | +| | switch | +| cas2_0(0, 0, 2, 1, 0, 3): | | +| array.cas2(0,0,2,1,0,3,0) at AtomicArrayWithCAS2LiveLockTest.cas2_0(AtomicArrayWithCAS2LiveLockTest.kt:33) | | +| AtomicArrayWithCAS2$Descriptor.apply$default(Descriptor#3,false,0,false,5,null) at AtomicArrayWithCAS2.cas2(AtomicArrayWithCAS2LiveLockTest.kt:85) | | +| Descriptor#3.apply(true,0,false) at AtomicArrayWithCAS2$Descriptor.apply$default(AtomicArrayWithCAS2LiveLockTest.kt:111) | | +| AtomicArrayWithCAS2$Descriptor.installOrHelp$default(Descriptor#3,true,0,false,4,null) at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:118) | | +| Descriptor#3.installOrHelp(true,0,false) at AtomicArrayWithCAS2$Descriptor.installOrHelp$default(AtomicArrayWithCAS2LiveLockTest.kt:130) | | +| AtomicArrayWithCAS2LiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:135) | | +| AtomicReferenceArray#1[0].get(): Descriptor#2 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:135) | | +| AtomicArrayWithCAS2LiveLockTest#1.array.gate1.WRITE(1) at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:151) | | +| AtomicArrayWithCAS2LiveLockTest#1.array.gate0.READ: 1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:152) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> Descriptor#2.apply(false,0,true) at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:153) | | +| | status.READ: SUCCESS at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:113) | | +| | status.compareAndSet(SUCCESS,SUCCESS): true at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:114) | | +| | installOrHelp(true,0,true) at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:115) | | +| | AtomicArrayWithCAS2LiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:135) | | +| | AtomicReferenceArray#1[0].get(): Descriptor#2 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:135) | | +| └╶╶╶╶╶╶ switch (reason: active lock detected) | | +| | AtomicArrayWithCAS2LiveLockTest#1.array.gate0.WRITE(0) at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:147) | +| | AtomicArrayWithCAS2$Descriptor.apply$default(Descriptor#2,false,1,false,4,null) at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:161) | +| | Descriptor#2.read(0): 0 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:162) | +| | AtomicArrayWithCAS2LiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:167) | +| | AtomicReferenceArray#1[0].compareAndSet(Descriptor#2,Descriptor#1): true at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:167) | +| | AtomicArrayWithCAS2$Descriptor.installOrHelp$default(Descriptor#1,false,1,false,4,null) at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:119) | +| | Descriptor#1.installOrHelp(false,1,false) at AtomicArrayWithCAS2$Descriptor.installOrHelp$default(AtomicArrayWithCAS2LiveLockTest.kt:130) | +| | AtomicArrayWithCAS2LiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:135) | +| | AtomicReferenceArray#1[1].get(): Descriptor#4 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:135) | +| | AtomicArrayWithCAS2LiveLockTest#1.array.gate0.WRITE(1) at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:143) | +| | AtomicArrayWithCAS2LiveLockTest#1.array.gate1.READ: 1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:144) | +| | /* The following events repeat infinitely: */ | +| | ┌╶> Descriptor#4.apply(false,1,true) at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:145) | +| | | status.READ: SUCCESS at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:113) | +| | | status.compareAndSet(SUCCESS,SUCCESS): true at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:114) | +| | | installOrHelp(true,1,true) at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:115) | +| | | AtomicArrayWithCAS2LiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:135) | +| | | AtomicReferenceArray#1[1].get(): Descriptor#4 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:135) | +| | └╶╶╶╶╶╶ switch (reason: active lock detected) | +| apply(false,0,true) at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:137) | | +| status.READ: SUCCESS at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:113) | | +| status.compareAndSet(SUCCESS,SUCCESS): true at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:114) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> installOrHelp(true,0,true) at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:115) | | +| | AtomicArrayWithCAS2LiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:135) | | +| | AtomicReferenceArray#1[0].get(): Descriptor#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:135) | | +| | Descriptor#1.apply(false,0,true) at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:137) | | +| | status.compareAndSet(SUCCESS,SUCCESS): false at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:114) | | +| └╶╶╶╶╶╶ switch (reason: active lock detected) | | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | All unfinished threads are in deadlock Detailed trace: -| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Thread 1 | Thread 2 | -| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| | cas2_1(0, 0, 4, 1, 0, 5): | -| | array.cas2(0,0,4,1,0,5,1) at BrokenCas2RecursiveLiveLockTest.cas2_1(RecursiveSpinLockTest.kt:277) | -| | AtomicArrayWithCAS2$Descriptor.apply$default(Descriptor#1,false,1,false,5,null) at AtomicArrayWithCAS2.cas2(RecursiveSpinLockTest.kt:323) | -| | Descriptor#1.apply(true,1,false) at AtomicArrayWithCAS2$Descriptor.apply$default(RecursiveSpinLockTest.kt:349) | -| | AtomicArrayWithCAS2$Descriptor.installOrHelp$default(Descriptor#1,true,1,false,4,null): true at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:356) | -| | Descriptor#1.installOrHelp(true,1,false): true at AtomicArrayWithCAS2$Descriptor.installOrHelp$default(RecursiveSpinLockTest.kt:368) | -| | BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | -| | AtomicReferenceArray#1[0].get(): Descriptor#2 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | -| | BrokenCas2RecursiveLiveLockTest#1.array.gate0.WRITE(1) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:381) | -| | BrokenCas2RecursiveLiveLockTest#1.array.gate1.READ: 0 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:382) | -| | switch | -| cas2_0(0, 0, 2, 1, 0, 3): | | -| array.cas2(0,0,2,1,0,3,0) at BrokenCas2RecursiveLiveLockTest.cas2_0(RecursiveSpinLockTest.kt:271) | | -| AtomicArrayWithCAS2$Descriptor.apply$default(Descriptor#3,false,0,false,5,null) at AtomicArrayWithCAS2.cas2(RecursiveSpinLockTest.kt:323) | | -| Descriptor#3.apply(true,0,false) at AtomicArrayWithCAS2$Descriptor.apply$default(RecursiveSpinLockTest.kt:349) | | -| AtomicArrayWithCAS2$Descriptor.installOrHelp$default(Descriptor#3,true,0,false,4,null) at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:356) | | -| Descriptor#3.installOrHelp(true,0,false) at AtomicArrayWithCAS2$Descriptor.installOrHelp$default(RecursiveSpinLockTest.kt:368) | | -| BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | -| AtomicReferenceArray#1[0].get(): Descriptor#2 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | -| BrokenCas2RecursiveLiveLockTest#1.array.gate1.WRITE(1) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:389) | | -| BrokenCas2RecursiveLiveLockTest#1.array.gate0.READ: 1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:390) | | -| /* The following events repeat infinitely: */ | | -| ┌╶> Descriptor#2.apply(false,0,true) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:391) | | -| | status.READ: SUCCESS at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:351) | | -| | status.compareAndSet(SUCCESS,SUCCESS): true at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:352) | | -| | installOrHelp(true,0,true) at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:353) | | -| | BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | -| | AtomicReferenceArray#1[0].get(): Descriptor#2 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | -| └╶╶╶╶╶╶ switch (reason: active lock detected) | | -| | BrokenCas2RecursiveLiveLockTest#1.array.gate0.WRITE(0) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:385) | -| | AtomicArrayWithCAS2$Descriptor.apply$default(Descriptor#2,false,1,false,4,null) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:399) | -| | Descriptor#2.apply(false,1,false) at AtomicArrayWithCAS2$Descriptor.apply$default(RecursiveSpinLockTest.kt:349) | -| | status.READ: SUCCESS at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:355) | -| | Descriptor#2.read(0): 0 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:400) | -| | status.READ: SUCCESS at AtomicArrayWithCAS2$Descriptor.read(RecursiveSpinLockTest.kt:339) | -| | BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:405) | -| | AtomicReferenceArray#1[0].compareAndSet(Descriptor#2,Descriptor#1): true at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:405) | -| | AtomicArrayWithCAS2$Descriptor.installOrHelp$default(Descriptor#1,false,1,false,4,null) at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:357) | -| | Descriptor#1.installOrHelp(false,1,false) at AtomicArrayWithCAS2$Descriptor.installOrHelp$default(RecursiveSpinLockTest.kt:368) | -| | BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | -| | AtomicReferenceArray#1[1].get(): Descriptor#4 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | -| | BrokenCas2RecursiveLiveLockTest#1.array.gate0.WRITE(1) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:381) | -| | BrokenCas2RecursiveLiveLockTest#1.array.gate1.READ: 1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:382) | -| | /* The following events repeat infinitely: */ | -| | ┌╶> Descriptor#4.apply(false,1,true) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:383) | -| | | status.READ: SUCCESS at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:351) | -| | | status.compareAndSet(SUCCESS,SUCCESS): true at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:352) | -| | | installOrHelp(true,1,true) at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:353) | -| | | BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | -| | | AtomicReferenceArray#1[1].get(): Descriptor#4 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | -| | └╶╶╶╶╶╶ switch (reason: active lock detected) | -| apply(false,0,true) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:375) | | -| status.READ: SUCCESS at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:351) | | -| status.compareAndSet(SUCCESS,SUCCESS): true at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:352) | | -| /* The following events repeat infinitely: */ | | -| ┌╶> installOrHelp(true,0,true) at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:353) | | -| | BrokenCas2RecursiveLiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | -| | AtomicReferenceArray#1[0].get(): Descriptor#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:373) | | -| | Descriptor#1.apply(false,0,true) at AtomicArrayWithCAS2$Descriptor.installOrHelp(RecursiveSpinLockTest.kt:375) | | -| | status.compareAndSet(SUCCESS,SUCCESS): false at AtomicArrayWithCAS2$Descriptor.apply(RecursiveSpinLockTest.kt:352) | | -| └╶╶╶╶╶╶ switch (reason: active lock detected) | | -| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | cas2_1(0, 0, 4, 1, 0, 5): | +| | array.cas2(0,0,4,1,0,5,1) at AtomicArrayWithCAS2LiveLockTest.cas2_1(AtomicArrayWithCAS2LiveLockTest.kt:39) | +| | AtomicArrayWithCAS2$Descriptor.apply$default(Descriptor#1,false,1,false,5,null) at AtomicArrayWithCAS2.cas2(AtomicArrayWithCAS2LiveLockTest.kt:85) | +| | Descriptor#1.apply(true,1,false) at AtomicArrayWithCAS2$Descriptor.apply$default(AtomicArrayWithCAS2LiveLockTest.kt:111) | +| | AtomicArrayWithCAS2$Descriptor.installOrHelp$default(Descriptor#1,true,1,false,4,null): true at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:118) | +| | Descriptor#1.installOrHelp(true,1,false): true at AtomicArrayWithCAS2$Descriptor.installOrHelp$default(AtomicArrayWithCAS2LiveLockTest.kt:130) | +| | AtomicArrayWithCAS2LiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:135) | +| | AtomicReferenceArray#1[0].get(): Descriptor#2 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:135) | +| | AtomicArrayWithCAS2LiveLockTest#1.array.gate0.WRITE(1) at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:143) | +| | AtomicArrayWithCAS2LiveLockTest#1.array.gate1.READ: 0 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:144) | +| | switch | +| cas2_0(0, 0, 2, 1, 0, 3): | | +| array.cas2(0,0,2,1,0,3,0) at AtomicArrayWithCAS2LiveLockTest.cas2_0(AtomicArrayWithCAS2LiveLockTest.kt:33) | | +| AtomicArrayWithCAS2$Descriptor.apply$default(Descriptor#3,false,0,false,5,null) at AtomicArrayWithCAS2.cas2(AtomicArrayWithCAS2LiveLockTest.kt:85) | | +| Descriptor#3.apply(true,0,false) at AtomicArrayWithCAS2$Descriptor.apply$default(AtomicArrayWithCAS2LiveLockTest.kt:111) | | +| AtomicArrayWithCAS2$Descriptor.installOrHelp$default(Descriptor#3,true,0,false,4,null) at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:118) | | +| Descriptor#3.installOrHelp(true,0,false) at AtomicArrayWithCAS2$Descriptor.installOrHelp$default(AtomicArrayWithCAS2LiveLockTest.kt:130) | | +| AtomicArrayWithCAS2LiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:135) | | +| AtomicReferenceArray#1[0].get(): Descriptor#2 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:135) | | +| AtomicArrayWithCAS2LiveLockTest#1.array.gate1.WRITE(1) at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:151) | | +| AtomicArrayWithCAS2LiveLockTest#1.array.gate0.READ: 1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:152) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> Descriptor#2.apply(false,0,true) at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:153) | | +| | status.READ: SUCCESS at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:113) | | +| | status.compareAndSet(SUCCESS,SUCCESS): true at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:114) | | +| | installOrHelp(true,0,true) at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:115) | | +| | AtomicArrayWithCAS2LiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:135) | | +| | AtomicReferenceArray#1[0].get(): Descriptor#2 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:135) | | +| └╶╶╶╶╶╶ switch (reason: active lock detected) | | +| | AtomicArrayWithCAS2LiveLockTest#1.array.gate0.WRITE(0) at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:147) | +| | AtomicArrayWithCAS2$Descriptor.apply$default(Descriptor#2,false,1,false,4,null) at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:161) | +| | Descriptor#2.apply(false,1,false) at AtomicArrayWithCAS2$Descriptor.apply$default(AtomicArrayWithCAS2LiveLockTest.kt:111) | +| | status.READ: SUCCESS at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:117) | +| | Descriptor#2.read(0): 0 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:162) | +| | status.READ: SUCCESS at AtomicArrayWithCAS2$Descriptor.read(AtomicArrayWithCAS2LiveLockTest.kt:101) | +| | AtomicArrayWithCAS2LiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:167) | +| | AtomicReferenceArray#1[0].compareAndSet(Descriptor#2,Descriptor#1): true at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:167) | +| | AtomicArrayWithCAS2$Descriptor.installOrHelp$default(Descriptor#1,false,1,false,4,null) at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:119) | +| | Descriptor#1.installOrHelp(false,1,false) at AtomicArrayWithCAS2$Descriptor.installOrHelp$default(AtomicArrayWithCAS2LiveLockTest.kt:130) | +| | AtomicArrayWithCAS2LiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:135) | +| | AtomicReferenceArray#1[1].get(): Descriptor#4 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:135) | +| | AtomicArrayWithCAS2LiveLockTest#1.array.gate0.WRITE(1) at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:143) | +| | AtomicArrayWithCAS2LiveLockTest#1.array.gate1.READ: 1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:144) | +| | /* The following events repeat infinitely: */ | +| | ┌╶> Descriptor#4.apply(false,1,true) at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:145) | +| | | status.READ: SUCCESS at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:113) | +| | | status.compareAndSet(SUCCESS,SUCCESS): true at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:114) | +| | | installOrHelp(true,1,true) at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:115) | +| | | AtomicArrayWithCAS2LiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:135) | +| | | AtomicReferenceArray#1[1].get(): Descriptor#4 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:135) | +| | └╶╶╶╶╶╶ switch (reason: active lock detected) | +| apply(false,0,true) at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:137) | | +| status.READ: SUCCESS at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:113) | | +| status.compareAndSet(SUCCESS,SUCCESS): true at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:114) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> installOrHelp(true,0,true) at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:115) | | +| | AtomicArrayWithCAS2LiveLockTest#1.array.array.READ: AtomicReferenceArray#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:135) | | +| | AtomicReferenceArray#1[0].get(): Descriptor#1 at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:135) | | +| | Descriptor#1.apply(false,0,true) at AtomicArrayWithCAS2$Descriptor.installOrHelp(AtomicArrayWithCAS2LiveLockTest.kt:137) | | +| | status.compareAndSet(SUCCESS,SUCCESS): false at AtomicArrayWithCAS2$Descriptor.apply(AtomicArrayWithCAS2LiveLockTest.kt:114) | | +| └╶╶╶╶╶╶ switch (reason: active lock detected) | | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | All unfinished threads are in deadlock diff --git a/src/jvm/test/resources/expected_logs/spin_lock/spin_lock_events_cut_inner_loop.txt b/src/jvm/test/resources/expected_logs/spin_lock/spin_lock_events_cut_inner_loop.txt index 22ff174621..7738ec8d39 100644 --- a/src/jvm/test/resources/expected_logs/spin_lock/spin_lock_events_cut_inner_loop.txt +++ b/src/jvm/test/resources/expected_logs/spin_lock/spin_lock_events_cut_inner_loop.txt @@ -51,98 +51,96 @@ The following interleaving leads to the error: All unfinished threads are in deadlock Detailed trace: -| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Thread 1 | Thread 2 | -| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| one(): 1 | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | -| result: 1 | | -| one(): 1 | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | -| result: 1 | | -| one(): 1 | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | -| result: 1 | | -| one(): 1 | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | -| result: 1 | | -| two(): 2 | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | -| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | | -| sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | | -| sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | | -| result: 2 | | -| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| | two(): 2 | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | -| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | -| | result: 2 | -| | two(): 2 | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | -| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | -| | result: 2 | -| | two(): 2 | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | -| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | -| | result: 2 | -| | one(): 1 | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | -| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | -| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | -| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | -| | result: 1 | -| | one(): | -| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | -| | switch | -| two(): | | -| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | -| /* The following events repeat infinitely: */ | | -| ┌╶> sharedState1.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | | -| | meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:269) | | -| | data.length(): 10 at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:236) | | -| | data[0].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | -| | data[1].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | -| | data[2].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | -| | data[3].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | -| | data[4].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | -| | data[5].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | -| | data[6].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | -| | data[7].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | -| | data[8].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | -| | data[9].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | -| └╶╶ switch (reason: active lock detected) | | -| | /* The following events repeat infinitely: */ | -| | ┌╶> sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | -| | | meaninglessActions() at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:255) | -| | | data.length(): 10 at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:236) | -| | | data[0].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | -| | | data[1].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | -| | | data[2].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | -| | | data[3].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | -| | | data[4].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | -| | | data[5].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | -| | | data[6].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | -| | | data[7].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | -| | | data[8].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | -| | | data[9].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | -| | └╶╶ switch (reason: active lock detected) | -| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Thread 1 | Thread 2 | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| one(): 1 | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | | +| result: 1 | | +| two(): 2 | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | | +| sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | | +| sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | | +| result: 2 | | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | two(): 2 | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:271) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:272) | +| | result: 2 | +| | one(): 1 | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | sharedState1.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:257) | +| | sharedState2.set(false) at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:258) | +| | result: 1 | +| | one(): | +| | sharedState1.compareAndSet(false,true): true at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:251) | +| | switch | +| two(): | | +| sharedState2.compareAndSet(false,true): true at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:265) | | +| /* The following events repeat infinitely: */ | | +| ┌╶> sharedState1.compareAndSet(false,true): false at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:268) | | +| | meaninglessActions() at AbstractSpinLivelockTest.two(SpinlockEventsCutTests.kt:269) | | +| | data[0].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | +| | data[1].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | +| | data[2].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | +| | data[3].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | +| | data[4].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | +| | data[5].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | +| | data[6].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | +| | data[7].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | +| | data[8].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | +| | data[9].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | | +| └╶╶ switch (reason: active lock detected) | | +| | /* The following events repeat infinitely: */ | +| | ┌╶> sharedState2.compareAndSet(false,true): false at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:254) | +| | | meaninglessActions() at AbstractSpinLivelockTest.one(SpinlockEventsCutTests.kt:255) | +| | | data[0].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | +| | | data[1].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | +| | | data[2].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | +| | | data[3].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | +| | | data[4].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | +| | | data[5].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | +| | | data[6].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | +| | | data[7].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | +| | | data[8].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | +| | | data[9].set(0) at SpinlockEventsCutWithInnerLoopActionsTest.meaninglessActions(SpinlockEventsCutTests.kt:237) | +| | └╶╶ switch (reason: active lock detected) | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | All unfinished threads are in deadlock From 417191349b8f40cfd00c8f3fdd6e8d261aafff60 Mon Sep 17 00:00:00 2001 From: "Aleksandr.Potapov" Date: Mon, 8 Jul 2024 18:01:24 +0200 Subject: [PATCH 08/13] Some KDoc added --- .../modelchecking/ModelCheckingStrategy.kt | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt index a4c7401d20..0a20577c00 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/modelchecking/ModelCheckingStrategy.kt @@ -427,9 +427,20 @@ internal class ModelCheckingStrategy( * This class specifies an interleaving that is re-producible. */ private inner class Interleaving( - private val switchPositions: List, - private val threadSwitchChoices: List, - private val initialLastNotInitializedNode: SwitchChoosingNode? + /** + * Numbers of execution positions [executionPosition] where thread switch must be performed. + */ + private val switchPositions: List, + /** + * Numbers of the threads where to switch if the [switchPositions]. + */ + private val threadSwitchChoices: List, + /** + * The next not initialized switch node. It's stored as a field because sometimes execution may be replayed + * due to spin cycles, and we have to drop information about odd executions, that was performed during + * unnecessary spin cycle iterations. + */ + private val initialLastNotInitializedNode: SwitchChoosingNode? ) { private var lastNotInitializedNode: SwitchChoosingNode? = initialLastNotInitializedNode private lateinit var interleavingFinishingRandom: Random From f3868668405996e4412ac9ec1a9b24394a09e14c Mon Sep 17 00:00:00 2001 From: "Aleksandr.Potapov" Date: Tue, 9 Jul 2024 03:36:44 +0200 Subject: [PATCH 09/13] Minor fixes --- .../lincheck/strategy/managed/LoopDetector.kt | 64 ++++++++----------- .../lincheck/transformation/CodeLocations.kt | 5 +- 2 files changed, 28 insertions(+), 41 deletions(-) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt index 3cd799a3d1..ba9acf5335 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt @@ -12,6 +12,7 @@ package org.jetbrains.kotlinx.lincheck.strategy.managed import org.jetbrains.kotlinx.lincheck.primitiveOrIdentityHashCode import org.jetbrains.kotlinx.lincheck.transformation.MethodIds +import org.jetbrains.kotlinx.lincheck.transformation.CodeLocations import java.util.ArrayList /** @@ -71,7 +72,7 @@ internal class LoopDetector( /** * Is used to find a cycle period inside exact thread execution if it has hung */ - private val currentThreadCodeLocationsHistory = mutableListOf() + private val currentThreadCodeLocationsHistory = mutableListOf() /** * Threads switches and executions history to store sequences lead to loops @@ -204,7 +205,7 @@ internal class LoopDetector( // Increment the number of times the specified code location is visited. val count = currentThreadCodeLocationVisitCountMap.getOrDefault(codeLocation, 0) + 1 currentThreadCodeLocationVisitCountMap[codeLocation] = count - currentThreadCodeLocationsHistory += codeLocation + currentThreadCodeLocationsHistory += CodeIdentity.RegularCodeLocationIdentity(codeLocation) val detectedFirstTime = count > hangingDetectionThreshold val detectedEarly = loopTrackingCursor.isInCycle // DetectedFirstTime and detectedEarly can both sometimes be true @@ -258,7 +259,7 @@ internal class LoopDetector( it.onNextExecution() return } - currentThreadCodeLocationsHistory += codeLocation + currentThreadCodeLocationsHistory += CodeIdentity.RegularCodeLocationIdentity(codeLocation) passParameters(params) val lastInterleavingHistoryNode = currentInterleavingHistory.last() if (lastInterleavingHistoryNode.cycleOccurred) { @@ -272,20 +273,17 @@ internal class LoopDetector( * Called after any method calls. */ fun afterMethodCall() { - // Because we want to track the fact of the method exit, - // but the sequence of the beforeMethodCall code locations values determines what exactly method we exit. - // That's why we can use just 0 as a label of a method exit. - val afterMethodCallLocation = 0 replayModeLoopDetectorHelper?.let { it.onNextExecution() return } - currentThreadCodeLocationsHistory += afterMethodCallLocation + val methodExitLocationIdentity = CodeIdentity.METHOD_EXIT_LOCATION_IDENTITY + currentThreadCodeLocationsHistory += methodExitLocationIdentity val lastInterleavingHistoryNode = currentInterleavingHistory.last() if (lastInterleavingHistoryNode.cycleOccurred) { return /* If we already ran into cycle and haven't switched than no need to track executions */ } - lastInterleavingHistoryNode.addExecution(afterMethodCallLocation) + lastInterleavingHistoryNode.addExecution(methodExitLocationIdentity.location) loopTrackingCursor.onNextExecutionPoint() } @@ -297,7 +295,7 @@ internal class LoopDetector( fun passParameters(params: Array) { if (mode == Mode.DEFAULT) return params.forEach { param -> - currentThreadCodeLocationsHistory += paramToIntRepresentation(param) + currentThreadCodeLocationsHistory += CodeIdentity.ValueRepresentationIdentity(primitiveOrIdentityHashCode(param)) } } @@ -394,7 +392,7 @@ internal class LoopDetector( // Increment the number of times the specified code location is visited. val count = currentThreadCodeLocationVisitCountMap.getOrDefault(codeLocation, 0) + 1 currentThreadCodeLocationVisitCountMap[codeLocation] = count - currentThreadCodeLocationsHistory += codeLocation + currentThreadCodeLocationsHistory += CodeIdentity.RegularCodeLocationIdentity(codeLocation) } /** @@ -420,7 +418,7 @@ internal class LoopDetector( */ private fun passValue(obj: Any?) { if (mode == Mode.DEFAULT) return - currentThreadCodeLocationsHistory += paramToIntRepresentation(obj) + currentThreadCodeLocationsHistory += CodeIdentity.ValueRepresentationIdentity(primitiveOrIdentityHashCode(obj)) } private fun registerCycle() { @@ -486,8 +484,7 @@ internal class LoopDetector( */ private fun tryFindCycleWithParamsOrWithout(): Pair> { // Get the code locations history of potential switch points, without parameters and values. - // Potential switch point code locations are >= 0. - val historyWithoutParams = currentThreadCodeLocationsHistory.filter { it >= 0 } + val historyWithoutParams = currentThreadCodeLocationsHistory.mapNotNull { (it as? CodeIdentity.RegularCodeLocationIdentity)?.location } // Trying to find a cycle with them. val cycleInfo = findMaxPrefixLengthWithNoCycleOnSuffix(currentThreadCodeLocationsHistory) // If it's not possible - searching for the cycle in the filtered history list - without params and values. @@ -507,16 +504,16 @@ internal class LoopDetector( var i = 0 // Count how many potential switch point executions happened before the spin cycle. while (operationsBeforeWithParams < cycleInfo.executionsBeforeCycle) { - // Potential switch point code locations are >= 0. - if (currentThreadCodeLocationsHistory[i] >= 0) { + // Potential switch point code locations are only RegularCodeLocationIdentity + if (currentThreadCodeLocationsHistory[i] is CodeIdentity.RegularCodeLocationIdentity) { operationsBefore++ } operationsBeforeWithParams++ i++ } while (cyclePeriodWithParams < cycleInfo.cyclePeriod) { - // Potential switch point code locations are >= 0. - if (currentThreadCodeLocationsHistory[i] >= 0) { + // Potential switch point code locations are only RegularCodeLocationIdentity + if (currentThreadCodeLocationsHistory[i] is CodeIdentity.RegularCodeLocationIdentity) { cyclePeriod++ } cyclePeriodWithParams++ @@ -563,26 +560,6 @@ internal class LoopDetector( @Suppress("unused") internal fun treeToString() = interleavingsLeadToSpinLockSet.treeToString() - /** - * Maps any parameter, receiver or read value to a **negative** integer value. - * We map in the **negative** number as we want to be able to filter a code locations history list - * and drop all elements that don't represent potential switch points - all parameter, receiver - * and value representations produced by this method. - */ - private fun paramToIntRepresentation(value: Any?): Int { - val hashCode = primitiveOrIdentityHashCode(value) - if (hashCode < 0) return hashCode - // When we're trying to find a spin cycle and can't do it with parameters, receivers, etc., - // we try to calculate the spin cycle without them. We need to filter executions story list - // of integers fast, so we keep the contract: - // 1. All potential switch points code locations, method call code locations are strictly greater than 0. - // 2. All method exits have zero (0) code locations. - // 3. All parameters, receivers, and value integer representations (the things we have to ignore in case - // After this line [hashCode] is positive value or Integer.MIN_VALUE in case of the overflow. - // So by `if` below we make sure that this value is strictly negative. - return if (hashCode == 0) -1 else -hashCode - } - private enum class Mode { /** @@ -599,6 +576,17 @@ internal class LoopDetector( CYCLE_PERIOD_CALCULATION } + /** + * Represents either a regular code location [RegularCodeLocationIdentity], determined by [CodeLocations], or + * a value [ValueRepresentationIdentity] (i.e. parameter, receiver written/read value) hashcode. + */ + private sealed interface CodeIdentity { + data class RegularCodeLocationIdentity(val location: Int): CodeIdentity + data class ValueRepresentationIdentity(val identity: Int) : CodeIdentity + companion object { + val METHOD_EXIT_LOCATION_IDENTITY = RegularCodeLocationIdentity(-1) + } + } } internal val LoopDetector.Decision.isLivelockDetected get() = when (this) { diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/transformation/CodeLocations.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/transformation/CodeLocations.kt index 8bc41b212e..cdf277ac42 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/transformation/CodeLocations.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/transformation/CodeLocations.kt @@ -40,8 +40,7 @@ internal object CodeLocations { fun newCodeLocation(stackTraceElement: StackTraceElement): Int { val id = codeLocations.size codeLocations.add(stackTraceElement) - // Code location 0 stands for the method exit, so we start this sequence from 1. - return id + 1 + return id } /** @@ -53,7 +52,7 @@ internal object CodeLocations { @JvmStatic @Synchronized fun stackTrace(codeLocationId: Int): StackTraceElement { - return codeLocations[codeLocationId - 1] + return codeLocations[codeLocationId] } } From 89818b0726ee556dad44f741fdafad5fcfe5a223 Mon Sep 17 00:00:00 2001 From: "Aleksandr.Potapov" Date: Tue, 9 Jul 2024 22:16:06 +0200 Subject: [PATCH 10/13] Minor fix --- .../kotlinx/lincheck/strategy/managed/LoopDetector.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt index ba9acf5335..07f2f4e873 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt @@ -11,6 +11,7 @@ package org.jetbrains.kotlinx.lincheck.strategy.managed import org.jetbrains.kotlinx.lincheck.primitiveOrIdentityHashCode +import org.jetbrains.kotlinx.lincheck.strategy.managed.LoopDetector.CodeIdentity.RegularCodeLocationIdentity import org.jetbrains.kotlinx.lincheck.transformation.MethodIds import org.jetbrains.kotlinx.lincheck.transformation.CodeLocations import java.util.ArrayList @@ -277,7 +278,7 @@ internal class LoopDetector( it.onNextExecution() return } - val methodExitLocationIdentity = CodeIdentity.METHOD_EXIT_LOCATION_IDENTITY + val methodExitLocationIdentity = RegularCodeLocationIdentity(0) currentThreadCodeLocationsHistory += methodExitLocationIdentity val lastInterleavingHistoryNode = currentInterleavingHistory.last() if (lastInterleavingHistoryNode.cycleOccurred) { @@ -584,7 +585,7 @@ internal class LoopDetector( data class RegularCodeLocationIdentity(val location: Int): CodeIdentity data class ValueRepresentationIdentity(val identity: Int) : CodeIdentity companion object { - val METHOD_EXIT_LOCATION_IDENTITY = RegularCodeLocationIdentity(-1) + val METHOD_EXIT_LOCATION_IDENTITY = RegularCodeLocationIdentity(0) } } } From bd38ad3a0096e9d55ec44cc6a6a00d2869b1c5aa Mon Sep 17 00:00:00 2001 From: "Aleksandr.Potapov" Date: Tue, 9 Jul 2024 23:29:09 +0200 Subject: [PATCH 11/13] Minor fix --- .../jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt index 07f2f4e873..024c05fe96 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt @@ -278,7 +278,7 @@ internal class LoopDetector( it.onNextExecution() return } - val methodExitLocationIdentity = RegularCodeLocationIdentity(0) + val methodExitLocationIdentity = RegularCodeLocationIdentity(-1) currentThreadCodeLocationsHistory += methodExitLocationIdentity val lastInterleavingHistoryNode = currentInterleavingHistory.last() if (lastInterleavingHistoryNode.cycleOccurred) { From 190684ded846e9642745d43ab968ecdf15626267 Mon Sep 17 00:00:00 2001 From: "Aleksandr.Potapov" Date: Wed, 10 Jul 2024 00:30:15 +0200 Subject: [PATCH 12/13] Minor fix --- .../jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt index 024c05fe96..07f2f4e873 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt @@ -278,7 +278,7 @@ internal class LoopDetector( it.onNextExecution() return } - val methodExitLocationIdentity = RegularCodeLocationIdentity(-1) + val methodExitLocationIdentity = RegularCodeLocationIdentity(0) currentThreadCodeLocationsHistory += methodExitLocationIdentity val lastInterleavingHistoryNode = currentInterleavingHistory.last() if (lastInterleavingHistoryNode.cycleOccurred) { From 3fc1d651911852dd04872d723320bacbdab9be96 Mon Sep 17 00:00:00 2001 From: "Aleksandr.Potapov" Date: Wed, 10 Jul 2024 14:05:15 +0200 Subject: [PATCH 13/13] Minor fix --- .../kotlinx/lincheck/strategy/managed/LoopDetector.kt | 7 +++---- .../kotlinx/lincheck/transformation/CodeLocations.kt | 1 - 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt index 07f2f4e873..e80c98578f 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/LoopDetector.kt @@ -31,7 +31,9 @@ import java.util.ArrayList * When a spin cycle is detected, LoopDetector requests the replay of the execution and starts tracking * the parameters, etc., to detect a spin cycle period properly, taking into account all changes during the spin cycle. * When we run into a spin cycle again, LoopDetector tries to find the period using [currentThreadCodeLocationsHistory]. - * Parameters etc. are converted to the negative integer representation using hashcode (see [paramToIntRepresentation]). + * Visited regular code locations are stored in [currentThreadCodeLocationsHistory] as [RegularCodeLocationIdentity]. + * Parameters etc. are converted to the integer representation using hashcode and stored in [currentThreadCodeLocationsHistory] + * as [ValueRepresentationIdentity]. * But sometimes it's not possible, for example, in case of using random. In that case, LoopDetector omits parameters, * read/written values, etc., and tries to find the period using only switch points code locations. * Once it's found (or not) LoopDetector requests one more replay to avoid side effects, made by this spin cycle. @@ -584,9 +586,6 @@ internal class LoopDetector( private sealed interface CodeIdentity { data class RegularCodeLocationIdentity(val location: Int): CodeIdentity data class ValueRepresentationIdentity(val identity: Int) : CodeIdentity - companion object { - val METHOD_EXIT_LOCATION_IDENTITY = RegularCodeLocationIdentity(0) - } } } diff --git a/src/jvm/main/org/jetbrains/kotlinx/lincheck/transformation/CodeLocations.kt b/src/jvm/main/org/jetbrains/kotlinx/lincheck/transformation/CodeLocations.kt index cdf277ac42..44c58d16c5 100644 --- a/src/jvm/main/org/jetbrains/kotlinx/lincheck/transformation/CodeLocations.kt +++ b/src/jvm/main/org/jetbrains/kotlinx/lincheck/transformation/CodeLocations.kt @@ -30,7 +30,6 @@ internal object CodeLocations { /** * Registers a new code location and returns its unique ID. - * Code locations are positive values. * * @param stackTraceElement Stack trace element representing the new code location. * @return Unique ID of the new code location.