Skip to content

Commit ffaa790

Browse files
committed
Generalize the concept of list closing to any number of levels
1 parent 532ff3d commit ffaa790

File tree

6 files changed

+31
-41
lines changed

6 files changed

+31
-41
lines changed

kotlinx-coroutines-core/common/src/JobSupport.kt

+9-6
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
320320
private fun notifyCancelling(list: NodeList, cause: Throwable) {
321321
// first cancel our own children
322322
onCancelling(cause)
323-
list.closeForSome()
323+
list.close(LIST_CANCELLATION_PERMISSION)
324324
notifyHandlers<JobCancellingNode>(list, cause)
325325
// then cancel parent
326326
cancelParent(cause) // tentative cancellation -- does not matter if there is no parent
@@ -353,7 +353,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
353353
}
354354

355355
private fun NodeList.notifyCompletion(cause: Throwable?) {
356-
close()
356+
close(LIST_MAX_PERMISSION)
357357
notifyHandlers<JobNode>(this, cause)
358358
}
359359

@@ -466,13 +466,13 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
466466
if (onCancelling) {
467467
val rootCause = (state as? Finishing)?.let { synchronized(it) { it.rootCause } }
468468
if (rootCause == null) {
469-
list.addLast(node, allowedAfterPartialClosing = false)
469+
list.addLast(node, LIST_CANCELLATION_PERMISSION)
470470
} else {
471471
if (invokeImmediately) handler.invoke(rootCause)
472472
return NonDisposableHandle
473473
}
474474
} else {
475-
list.addLast(node, allowedAfterPartialClosing = true)
475+
list.addLast(node, LIST_MAX_PERMISSION)
476476
}
477477
}
478478
when {
@@ -987,7 +987,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
987987
// or we are adding a child to a coroutine that is not completing yet
988988
if (maybeRootCause == null || !state.isCompleting) {
989989
// Note: add node the list while holding lock on state (make sure it cannot change)
990-
if (!list.addLast(node, allowedAfterPartialClosing = true))
990+
if (!list.addLast(node, LIST_MAX_PERMISSION))
991991
return@tryPutNodeIntoList false // retry
992992
// just return the node if we don't have to invoke the handler (not cancelling yet)
993993
rootCause = maybeRootCause ?: return@tryPutNodeIntoList true
@@ -1000,7 +1000,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
10001000
}
10011001
node.invoke(rootCause)
10021002
return handle
1003-
} else list.addLast(node, allowedAfterPartialClosing = true).also { success ->
1003+
} else list.addLast(node, LIST_MAX_PERMISSION).also { success ->
10041004
if (success) {
10051005
/** Handling the following case:
10061006
* - A child requested to be added to the list;
@@ -1347,6 +1347,9 @@ private val SEALED = Symbol("SEALED")
13471347
private val EMPTY_NEW = Empty(false)
13481348
private val EMPTY_ACTIVE = Empty(true)
13491349

1350+
private const val LIST_MAX_PERMISSION = Int.MAX_VALUE
1351+
private const val LIST_CANCELLATION_PERMISSION = 0
1352+
13501353
private class Empty(override val isActive: Boolean) : Incomplete {
13511354
override val list: NodeList? get() = null
13521355
override fun toString(): String = "Empty{${if (isActive) "Active" else "New" }}"

kotlinx-coroutines-core/common/src/internal/LockFreeLinkedList.common.kt

+6-3
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ public expect open class LockFreeLinkedListNode() {
77
public val isRemoved: Boolean
88
public val nextNode: LockFreeLinkedListNode
99
public val prevNode: LockFreeLinkedListNode
10-
public fun addLast(node: LockFreeLinkedListNode, allowedAfterPartialClosing: Boolean): Boolean
10+
public fun addLast(node: LockFreeLinkedListNode, clearanceLevel: Int): Boolean
1111
public fun addOneIfEmpty(node: LockFreeLinkedListNode): Boolean
1212
public open fun remove(): Boolean
13-
public fun close()
14-
public fun closeForSome()
13+
14+
/**
15+
* Closes the list for [maxForbidden] and all numbers below.
16+
*/
17+
public fun close(maxForbidden: Int)
1518

1619
}
1720

kotlinx-coroutines-core/concurrent/src/internal/LockFreeLinkedList.kt

+5-13
Original file line numberDiff line numberDiff line change
@@ -80,28 +80,22 @@ public actual open class LockFreeLinkedListNode {
8080
/**
8181
* Adds last item to this list. Returns `false` if the list is closed.
8282
*/
83-
public actual fun addLast(node: Node, allowedAfterPartialClosing: Boolean): Boolean {
83+
public actual fun addLast(node: Node, clearanceLevel: Int): Boolean {
8484
while (true) { // lock-free loop on prev.next
8585
val currentPrev = prevNode
8686
return when {
87-
currentPrev is ListClosedForAll -> false
88-
currentPrev is ListClosedForSome ->
89-
allowedAfterPartialClosing && currentPrev.addLast(node, allowedAfterPartialClosing)
87+
currentPrev is ListClosed ->
88+
currentPrev.maxForbidden < clearanceLevel && currentPrev.addLast(node, clearanceLevel)
9089
currentPrev.addNext(node, this) -> true
9190
else -> continue
9291
}
9392
}
9493
}
9594

96-
/**
97-
* Forbids adding some of the new items to this list.
98-
*/
99-
public actual fun closeForSome() { addLast(ListClosedForSome(), allowedAfterPartialClosing = false) }
100-
10195
/**
10296
* Forbids adding new items to this list.
10397
*/
104-
public actual fun close() { addLast(ListClosedForAll(), allowedAfterPartialClosing = true) }
98+
public actual fun close(maxForbidden: Int) { addLast(ListClosed(maxForbidden), maxForbidden) }
10599

106100
/**
107101
* Given:
@@ -291,6 +285,4 @@ public actual open class LockFreeLinkedListHead : LockFreeLinkedListNode() {
291285
override val isRemoved: Boolean get() = false
292286
}
293287

294-
private class ListClosedForSome: LockFreeLinkedListNode()
295-
296-
private class ListClosedForAll: LockFreeLinkedListNode()
288+
private class ListClosed(val maxForbidden: Int): LockFreeLinkedListNode()

kotlinx-coroutines-core/jsAndWasmShared/src/internal/LinkedList.kt

+6-13
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@ public open class LinkedListNode : DisposableHandle {
2222
public inline val prevNode get() = _prev
2323
public inline val isRemoved get() = _removed
2424

25-
public fun addLast(node: Node, allowedAfterPartialClosing: Boolean): Boolean = when (val prev = this._prev) {
26-
is ListClosedForAll -> false
27-
is ListClosedForSome -> allowedAfterPartialClosing && prev.addLast(node, allowedAfterPartialClosing)
25+
public fun addLast(node: Node, clearanceLevel: Int): Boolean = when (val prev = this._prev) {
26+
is ListClosed -> prev.maxForbidden < clearanceLevel && prev.addLast(node, clearanceLevel)
2827
else -> {
2928
node._next = this
3029
node._prev = prev
@@ -34,12 +33,8 @@ public open class LinkedListNode : DisposableHandle {
3433
}
3534
}
3635

37-
public fun closeForSome() {
38-
addLast(ListClosedForSome(), allowedAfterPartialClosing = true)
39-
}
40-
41-
public fun close() {
42-
addLast(ListClosedForAll(), allowedAfterPartialClosing = false)
36+
public fun close(maxForbidden: Int) {
37+
addLast(ListClosed(maxForbidden), maxForbidden)
4338
}
4439

4540
/*
@@ -64,7 +59,7 @@ public open class LinkedListNode : DisposableHandle {
6459

6560
public fun addOneIfEmpty(node: Node): Boolean {
6661
if (_next !== this) return false
67-
addLast(node, allowedAfterPartialClosing = false)
62+
addLast(node, Int.MIN_VALUE)
6863
return true
6964
}
7065
}
@@ -88,6 +83,4 @@ public open class LinkedListHead : LinkedListNode() {
8883
public final override fun remove(): Nothing = throw UnsupportedOperationException()
8984
}
9085

91-
private class ListClosedForSome: LinkedListNode()
92-
93-
private class ListClosedForAll: LinkedListNode()
86+
private class ListClosed(val maxForbidden: Int): LinkedListNode()

kotlinx-coroutines-core/jsAndWasmShared/test/internal/LinkedListTest.kt

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ class LinkedListTest {
1212
fun testSimpleAddLastRemove() {
1313
val list = LinkedListHead()
1414
assertContents(list)
15-
val n1 = IntNode(1).apply { list.addLast(this, allowedAfterPartialClosing = false) }
15+
val n1 = IntNode(1).apply { list.addLast(this, Int.MAX_VALUE) }
1616
assertContents(list, 1)
17-
val n2 = IntNode(2).apply { list.addLast(this, allowedAfterPartialClosing = false) }
17+
val n2 = IntNode(2).apply { list.addLast(this, Int.MAX_VALUE) }
1818
assertContents(list, 1, 2)
19-
val n3 = IntNode(3).apply { list.addLast(this, allowedAfterPartialClosing = false) }
19+
val n3 = IntNode(3).apply { list.addLast(this, Int.MAX_VALUE) }
2020
assertContents(list, 1, 2, 3)
21-
val n4 = IntNode(4).apply { list.addLast(this, allowedAfterPartialClosing = false) }
21+
val n4 = IntNode(4).apply { list.addLast(this, Int.MAX_VALUE) }
2222
assertContents(list, 1, 2, 3, 4)
2323
assertTrue(n1.remove())
2424
assertContents(list, 2, 3, 4)

kotlinx-coroutines-core/jvm/test/internal/LockFreeLinkedListLongStressTest.kt

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package kotlinx.coroutines.internal
22

3-
import kotlinx.coroutines.testing.*
43
import kotlinx.coroutines.testing.TestBase
54
import org.junit.Test
65
import java.util.*
@@ -31,7 +30,7 @@ class LockFreeLinkedListLongStressTest : TestBase() {
3130
for (j in 0 until nAddThreads)
3231
threads += thread(start = false, name = "adder-$j") {
3332
for (i in j until nAdded step nAddThreads) {
34-
list.addLast(IntNode(i), allowedAfterPartialClosing = false)
33+
list.addLast(IntNode(i), Int.MAX_VALUE)
3534
}
3635
println("${Thread.currentThread().name} completed")
3736
workingAdders.decrementAndGet()

0 commit comments

Comments
 (0)