Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spin locks detection enhancements #228

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ kotlin {
api("org.ow2.asm:asm-commons:$asmVersion")
api("org.ow2.asm:asm-util:$asmVersion")
api("org.reflections:reflections:$reflectionsVersion")
api("net.sf.trove4j:trove4j:3.0.3")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please repackage it to org.jetbrains.kotlinx.lincheck

}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ private static boolean doNotTransform(String className) {
className.equals(kotlinx.coroutines.CoroutineContextKt.class.getName()) ||
className.equals(kotlinx.coroutines.CancellableContinuation.class.getName()) ||
className.equals(kotlinx.coroutines.CoroutineExceptionHandler.class.getName()) ||
className.equals(kotlinx.coroutines.CoroutineDispatcher.class.getName());
className.equals(kotlinx.coroutines.CoroutineDispatcher.class.getName()) ||
className.equals(TrackMethodsFlagHolder.class.getName());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly to other Lincheck classes, it should not be transformed by default

}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ class ObstructionFreedomViolationInvocationResult(
) : InvocationResult()

/**
* Indicates that spin-cycle has been found for the first time and replay of current interleaving is required.
* Indicates that spin-cycle has been found for the first time, and replay with additional events tracking
* (methods enter/exit, receiver and parameters) of current interleaving is required to measure its period.
*/
object SpinCycleFoundAndReplayRequired: InvocationResult()
object SpinCycleFoundForTheFirstTimeAndReplayRequired: InvocationResult()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can shorten the name to "SpinCycleDetectedAndReplayRequired"


/**
* Indicates that the period of the spin-cycle has been calculated, additional events tracking can be turned off,
* and we can resume standard flow from the current interleaving.
*/
object SpinCyclePeriodMeasuredAndExecutionCanBeContinued: InvocationResult()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can shorten the name to just "SpinCycleDetected"


Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep only one empty line at the end

Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ internal fun <T> findMaxPrefixLengthWithNoCycleOnSuffix(elements: List<T>): Cycl

return if (targetCycleLength == elements.size) {
null // cycle not found
} else CycleInfo(minLastPositionNotRelatedToCycle + 1, targetCycleLength) // number of prefix elements with first cycle
} else CycleInfo(
minLastPositionNotRelatedToCycle + 1,
targetCycleLength
) // number of prefix elements with first cycle
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the comment in the correct place?

}

data class CycleInfo(
Expand Down Expand Up @@ -83,6 +86,9 @@ private fun <T> findLastIndexNotRelatedToCycle(elements: List<T>, cycleLength: I
*/
internal class InterleavingHistoryNode(
val threadId: Int,
/**
* Sum of executions before cycle and the cycle period
*/
var executions: Int = 0,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by "execution"? Event?

/**
* Hash code calculated by execution ids of this switch,
Expand All @@ -100,7 +106,8 @@ internal class InterleavingHistoryNode(
/**
* This field is updated when execution is replayed and we meet a sequence that previously led to a cycle.
*/
var spinCyclePeriod: Int = 0
var spinCyclePeriod: Int = 0,
var executionsBeforeSpinCycleWithAdditionalEvents: Int,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this mean?

) {
val cycleOccurred: Boolean get() = spinCyclePeriod != 0

Expand All @@ -119,22 +126,26 @@ internal class InterleavingHistoryNode(
fun asNodeCorrespondingToCycle(
executionsBeforeCycle: Int,
cyclePeriod: Int,
cycleExecutionsHash: Int
executionsBeforeSpinCycleWithAdditionalEvents: Int,
cycleExecutionsHash: Int,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert this line, please

): InterleavingHistoryNode {
check(executions >= executionsBeforeCycle)
check(executions >= executionsBeforeCycle) { "?" }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please replace "?" with a meaningful message


return InterleavingHistoryNode(
threadId = threadId,
executions = executionsBeforeCycle,
spinCyclePeriod = cyclePeriod,
executionHash = cycleExecutionsHash
executionHash = cycleExecutionsHash,
executionsBeforeSpinCycleWithAdditionalEvents = executionsBeforeSpinCycleWithAdditionalEvents,
)
}

fun copy() = InterleavingHistoryNode(
threadId = threadId,
executions = executions,
executionHash = executionHash
executionHash = executionHash,
spinCyclePeriod = spinCyclePeriod,
executionsBeforeSpinCycleWithAdditionalEvents = executionsBeforeSpinCycleWithAdditionalEvents,
)


Expand Down Expand Up @@ -219,6 +230,10 @@ internal class InterleavingSequenceTrackableSet {
* Only present when this node corresponds to cycle, i.e. [cycleOccurred] != 0
*/
val cycleLocationsHash: Int,
/**
* Only present when this node corresponds to cycle, i.e. [cycleOccurred] != 0
*/
val executionsBeforeSpinCycleWithAdditionalEvents: Int,
private var transitions: MutableMap<Int, InterleavingSequenceSetNode>? = null
) {

Expand All @@ -241,7 +256,8 @@ internal class InterleavingSequenceTrackableSet {
fun mergeBranch(newChain: List<InterleavingHistoryNode>, startIndex: Int, executionsCountedEarlier: Int) {
if (startIndex > newChain.lastIndex) return
val firstNewNode = newChain[startIndex]
val firstNewNodeExecutions = (firstNewNode.executions + firstNewNode.spinCyclePeriod) - executionsCountedEarlier
val firstNewNodeExecutions =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert the formatting

(firstNewNode.executions + firstNewNode.spinCyclePeriod) - executionsCountedEarlier
check(firstNewNode.threadId == threadId)

when {
Expand All @@ -263,7 +279,8 @@ internal class InterleavingSequenceTrackableSet {
transitions = transitions,
cyclePeriod = cyclePeriod,
cycleOccurred = cycleOccurred,
cycleLocationsHash = cycleLocationsHash
cycleLocationsHash = cycleLocationsHash,
executionsBeforeSpinCycleWithAdditionalEvents = executionsBeforeSpinCycleWithAdditionalEvents
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shorten the parameter name

)
executions = firstNewNodeExecutions
cyclePeriod = firstNewNode.spinCyclePeriod
Expand Down Expand Up @@ -320,6 +337,7 @@ internal class InterleavingSequenceTrackableSet {
cyclePeriod = first.spinCyclePeriod,
cycleLocationsHash = first.executionHash,
cycleOccurred = startIndex == chain.lastIndex,
executionsBeforeSpinCycleWithAdditionalEvents = first.executionsBeforeSpinCycleWithAdditionalEvents
)
var current = root

Expand All @@ -329,8 +347,9 @@ internal class InterleavingSequenceTrackableSet {
threadId = next.threadId,
executions = next.executions + next.spinCyclePeriod,
cyclePeriod = next.spinCyclePeriod,
cycleLocationsHash = first.executionHash,
cycleOccurred = i == chain.lastIndex
cycleLocationsHash = next.executionHash,
cycleOccurred = i == chain.lastIndex,
executionsBeforeSpinCycleWithAdditionalEvents = next.executionsBeforeSpinCycleWithAdditionalEvents
)
current.addTransition(next.threadId, nextNode)
current = nextNode
Expand Down Expand Up @@ -396,6 +415,7 @@ internal class InterleavingSequenceTrackableSet {
Otherwise, if the next thread id is, for example, 5, then the cursor will become invalid and
`isInCycle` property will return false and won't react on any events until the next cursor `reset` or `setTo` methods calls.
*/

/**
* Excepted to call only if [isInCycle] returned `true`
*/
Expand Down Expand Up @@ -424,6 +444,8 @@ internal class InterleavingSequenceTrackableSet {
it.cycleOccurred && executionsCount == it.executions
} ?: false

val executionsBeforeSpinCycleWithAdditionalEvents: Int get() = currentNode!!.executionsBeforeSpinCycleWithAdditionalEvents
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please document


/**
* Resets cursor to the leaf of the new added cycle
*/
Expand Down
Loading