Skip to content

Commit ca38c77

Browse files
authored
Custom threads: remove run(), remove lambda and remove result (#557)
1 parent 0d10007 commit ca38c77

File tree

52 files changed

+2091
-1710
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+2091
-1710
lines changed

src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/TraceReporter.kt

Lines changed: 36 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,6 @@ internal fun constructTraceGraph(
225225
prefixProvider = prefixFactory.actorNodePrefix(iThread),
226226
iThread = iThread,
227227
last = lastNode,
228-
callDepth = 0,
229228
actorRepresentation = actorRepresentations[iThread][nextActor],
230229
resultRepresentation = actorNodeResultRepresentation(
231230
result = resultProvider[iThread, nextActor],
@@ -246,9 +245,9 @@ internal fun constructTraceGraph(
246245
prefixProvider = prefixFactory.actorNodePrefix(iCustomThread),
247246
iThread = iThread,
248247
last = lastNode,
249-
callDepth = 0,
250248
actorRepresentation = "run()",
251249
resultRepresentation = null,
250+
isCustomThreadActor = true,
252251
)
253252
}
254253
}
@@ -308,7 +307,6 @@ internal fun constructTraceGraph(
308307
prefixProvider = prefixFactory.actorNodePrefix(iThread),
309308
iThread = iThread,
310309
last = lastNode,
311-
callDepth = 0,
312310
actorRepresentation = actorRepresentations[iThread][actorId],
313311
resultRepresentation = actorNodeResultRepresentation(
314312
result = actorResult,
@@ -340,39 +338,6 @@ internal fun constructTraceGraph(
340338
}
341339
}
342340

343-
// custom threads are handled separately
344-
for (iCustomThread in customThreadActors.indices) {
345-
val iThread = scenario.nThreads + iCustomThread
346-
var actorNode = customThreadActors[iCustomThread]
347-
if (actorNode == null)
348-
continue
349-
val lastEvent = actorNode.lastInternalEvent
350-
val lastEventNext = lastEvent.next
351-
// TODO: a hacky-way to detect if the thread was aborted due to a detected live-lock;
352-
// in the future we need a better way to pass the results of the custom threads,
353-
// but currently it is not possible and would require large refactoring of the related code
354-
val isHung = (
355-
lastEvent is TraceLeafEvent &&
356-
lastEvent.event is SwitchEventTracePoint &&
357-
lastEvent.event.reason is SwitchReason.ActiveLock
358-
)
359-
val result = if (isHung) null else VoidResult
360-
if (result === null)
361-
continue
362-
val resultRepresentation = resultRepresentation(result, exceptionStackTraces)
363-
val callDepth = actorNode.callDepth + 1
364-
val resultNode = ActorResultNode(
365-
prefixProvider = prefixFactory.actorResultPrefix(iThread, callDepth),
366-
iThread = iThread,
367-
last = lastEvent,
368-
callDepth = callDepth,
369-
resultRepresentation = resultRepresentation,
370-
exceptionNumberIfExceptionResult = null
371-
)
372-
actorNode.addInternalEvent(resultNode)
373-
resultNode.next = lastEventNext
374-
}
375-
376341
// add last section
377342
if (traceGraphNodes.isNotEmpty()) {
378343
traceGraphNodesSections += traceGraphNodes
@@ -444,12 +409,12 @@ private fun compressCallStackTrace(
444409
val currentElement = oldStacktrace.removeFirst()
445410

446411
// if element was removed (or seen) by previous iteration continue
447-
if (removed.contains(currentElement.methodInvocationId)) continue
448-
if (seen.contains(currentElement.methodInvocationId)) {
412+
if (removed.contains(currentElement.id)) continue
413+
if (seen.contains(currentElement.id)) {
449414
compressedStackTrace.add(currentElement)
450415
continue
451416
}
452-
seen.add(currentElement.methodInvocationId)
417+
seen.add(currentElement.id)
453418

454419
// if next element is null, we reached end of list
455420
val nextElement = oldStacktrace.firstOrNull()
@@ -460,6 +425,14 @@ private fun compressCallStackTrace(
460425
break
461426
}
462427

428+
// Check if current and next are custom thread start
429+
if (isUserThreadStart(currentElement, nextElement)) {
430+
// we do not mark currentElement as removed, since that is a unique call from Thread.kt
431+
// marking it prevents starts of other threads from being detected.
432+
removed.add(nextElement.id)
433+
continue
434+
}
435+
463436
// Check if current and next are compressible
464437
if (isCompressiblePair(currentElement.tracePoint.methodName, nextElement.tracePoint.methodName)) {
465438
// Combine fields of next and current, and store in current
@@ -472,7 +445,7 @@ private fun compressCallStackTrace(
472445
check(currentElement.tracePoint.thrownException == nextElement.tracePoint.thrownException)
473446

474447
// Mark next as removed
475-
removed.add(nextElement.methodInvocationId)
448+
removed.add(nextElement.id)
476449
compressedStackTrace.add(currentElement)
477450
continue
478451
}
@@ -497,6 +470,16 @@ private fun actorNodeResultRepresentation(result: Result?, failure: LincheckFail
497470
}
498471
}
499472

473+
/**
474+
* Used by [compressCallStackTrace] to remove the two `invoke()` lines at the beginning of
475+
* a user-defined thread trace.
476+
*/
477+
private fun isUserThreadStart(currentElement: CallStackTraceElement, nextElement: CallStackTraceElement): Boolean =
478+
currentElement.tracePoint.stackTraceElement.methodName == "run"
479+
&& currentElement.tracePoint.stackTraceElement.fileName == "Thread.kt"
480+
&& currentElement.tracePoint.methodName == "invoke"
481+
&& nextElement.tracePoint.methodName == "invoke"
482+
500483
private fun isCompressiblePair(currentName: String, nextName: String): Boolean =
501484
isDefaultPair(currentName, nextName) || isAccessPair(currentName, nextName)
502485

@@ -714,6 +697,7 @@ internal abstract class TraceInnerNode(prefixProvider: PrefixProvider, iThread:
714697

715698
private val _internalEvents = mutableListOf<TraceNode>()
716699
internal val internalEvents: List<TraceNode> get() = _internalEvents
700+
internal val directChildren: List<TraceNode> get() = internalEvents.filter { it.callDepth == callDepth + 1 }
717701

718702
override fun shouldBeExpanded(verboseTrace: Boolean) =
719703
_internalEvents.any {
@@ -723,6 +707,7 @@ internal abstract class TraceInnerNode(prefixProvider: PrefixProvider, iThread:
723707
fun addInternalEvent(node: TraceNode) {
724708
_internalEvents.add(node)
725709
}
710+
726711
}
727712

728713
internal class CallNode(
@@ -755,22 +740,25 @@ internal class ActorNode(
755740
prefixProvider: PrefixProvider,
756741
iThread: Int,
757742
last: TraceNode?,
758-
callDepth: Int,
743+
callDepth: Int = 0,
759744
internal val actorRepresentation: String,
760-
private val resultRepresentation: String?
745+
private val resultRepresentation: String?,
746+
private val isCustomThreadActor: Boolean = false
761747
) : TraceInnerNode(prefixProvider, iThread, last, callDepth) {
762748
override fun addRepresentationTo(
763749
traceRepresentation: MutableList<TraceEventRepresentation>,
764750
verboseTrace: Boolean
765751
): TraceNode? {
766-
val actorRepresentation = prefix + actorRepresentation + if (resultRepresentation != null) ": $resultRepresentation" else ""
752+
val actorRepresentation =
753+
prefix + actorRepresentation + if (resultRepresentation != null) ": $resultRepresentation" else ""
767754
traceRepresentation.add(TraceEventRepresentation(iThread, actorRepresentation))
768-
return if (!shouldBeExpanded(verboseTrace)) {
755+
756+
if (!shouldBeExpanded(verboseTrace)) {
757+
if (isCustomThreadActor) directChildren.forEach { it.addRepresentationTo(traceRepresentation, true) }
769758
lastState?.let { traceRepresentation.add(stateEventRepresentation(iThread, it)) }
770-
lastInternalEvent.next
771-
} else {
772-
next
773-
}
759+
return lastInternalEvent.next
760+
}
761+
return next
774762
}
775763
}
776764

src/jvm/test/org/jetbrains/kotlinx/lincheck_test/representation/ThreadCreationRepresentationTest.kt

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,21 @@ class ThreadCreationRepresentationTest: BaseRunConcurrentRepresentationTest<Unit
3434
private fun callMe() {
3535
a += 1
3636
}
37-
}
37+
}
38+
39+
class SimpleThreadCreationRepresentationTest: BaseRunConcurrentRepresentationTest<Unit>(
40+
"simple_thread_creation_representation_test"
41+
) {
42+
43+
@Volatile
44+
private var a = 0
45+
46+
override fun block() {
47+
val t1 = thread {
48+
a +=1
49+
}
50+
t1.join()
51+
check(false)
52+
}
53+
54+
}

0 commit comments

Comments
 (0)