Skip to content

Commit b6da0f1

Browse files
committed
Introduce the concept of closing a list
1 parent b37fb22 commit b6da0f1

File tree

3 files changed

+30
-42
lines changed

3 files changed

+30
-42
lines changed

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ public expect open class LockFreeLinkedListNode() {
1313
public val isRemoved: Boolean
1414
public val nextNode: LockFreeLinkedListNode
1515
public val prevNode: LockFreeLinkedListNode
16-
public fun addLast(node: LockFreeLinkedListNode)
16+
public fun addLast(node: LockFreeLinkedListNode): Boolean
1717
public fun addOneIfEmpty(node: LockFreeLinkedListNode): Boolean
1818
public inline fun addLastIf(node: LockFreeLinkedListNode, crossinline condition: () -> Boolean): Boolean
1919
public open fun remove(): Boolean
20+
public fun close()
2021

2122
}
2223

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

+14-3
Original file line numberDiff line numberDiff line change
@@ -120,21 +120,29 @@ public actual open class LockFreeLinkedListNode {
120120
// ------ addLastXXX ------
121121

122122
/**
123-
* Adds last item to this list.
123+
* Adds last item to this list. Returns `false` if the list is closed.
124124
*/
125-
public actual fun addLast(node: Node) {
125+
public actual fun addLast(node: Node): Boolean {
126126
while (true) { // lock-free loop on prev.next
127-
if (prevNode.addNext(node, this)) return
127+
val currentPrev = prevNode
128+
if (currentPrev is CLOSED) return false
129+
if (currentPrev.addNext(node, this)) return true
128130
}
129131
}
130132

133+
/**
134+
* Forbids adding new items to this list.
135+
*/
136+
public actual fun close() { addLast(CLOSED()) }
137+
131138
/**
132139
* Adds last item to this list atomically if the [condition] is true.
133140
*/
134141
public actual inline fun addLastIf(node: Node, crossinline condition: () -> Boolean): Boolean {
135142
val condAdd = makeCondAddOp(node, condition)
136143
while (true) { // lock-free loop on prev.next
137144
val prev = prevNode // sentinel node is never removed, so prev is always defined
145+
if (prev is CLOSED) return false
138146
when (prev.tryCondAddNext(node, this, condAdd)) {
139147
SUCCESS -> return true
140148
FAILURE -> return false
@@ -362,3 +370,6 @@ public actual open class LockFreeLinkedListHead : LockFreeLinkedListNode() {
362370
validateNode(prev, next as Node)
363371
}
364372
}
373+
374+
// not private due to what seems to be a compiler bug
375+
internal class CLOSED: LockFreeLinkedListNode()

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

+14-38
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,18 @@ public open class LinkedListNode : DisposableHandle {
2626
public inline val prevNode get() = _prev
2727
public inline val isRemoved get() = _removed
2828

29-
public fun addLast(node: Node) {
29+
public fun addLast(node: Node): Boolean {
3030
val prev = this._prev
31+
if (prev is CLOSED) return false
3132
node._next = this
3233
node._prev = prev
3334
prev._next = node
3435
this._prev = node
36+
return true
37+
}
38+
39+
public fun close() {
40+
addLast(CLOSED())
3541
}
3642

3743
/*
@@ -41,15 +47,6 @@ public open class LinkedListNode : DisposableHandle {
4147
* invokes handler on remove
4248
*/
4349
public open fun remove(): Boolean {
44-
return removeImpl()
45-
}
46-
47-
override fun dispose() {
48-
remove()
49-
}
50-
51-
@PublishedApi
52-
internal fun removeImpl(): Boolean {
5350
if (_removed) return false
5451
val prev = this._prev
5552
val next = this._next
@@ -59,6 +56,10 @@ public open class LinkedListNode : DisposableHandle {
5956
return true
6057
}
6158

59+
override fun dispose() {
60+
remove()
61+
}
62+
6263
public fun addOneIfEmpty(node: Node): Boolean {
6364
if (_next !== this) return false
6465
addLast(node)
@@ -67,34 +68,7 @@ public open class LinkedListNode : DisposableHandle {
6768

6869
public inline fun addLastIf(node: Node, crossinline condition: () -> Boolean): Boolean {
6970
if (!condition()) return false
70-
addLast(node)
71-
return true
72-
}
73-
74-
public inline fun addLastIfPrev(node: Node, predicate: (Node) -> Boolean): Boolean {
75-
if (!predicate(_prev)) return false
76-
addLast(node)
77-
return true
78-
}
79-
80-
public inline fun addLastIfPrevAndIf(
81-
node: Node,
82-
predicate: (Node) -> Boolean, // prev node predicate
83-
crossinline condition: () -> Boolean // atomically checked condition
84-
): Boolean {
85-
if (!predicate(_prev)) return false
86-
if (!condition()) return false
87-
addLast(node)
88-
return true
89-
}
90-
91-
public fun helpRemove() {} // No concurrency on JS -> no removal
92-
93-
public fun removeFirstOrNull(): Node? {
94-
val next = _next
95-
if (next === this) return null
96-
check(next.removeImpl()) { "Should remove" }
97-
return next
71+
return addLast(node)
9872
}
9973
}
10074

@@ -116,3 +90,5 @@ public open class LinkedListHead : LinkedListNode() {
11690
// just a defensive programming -- makes sure that list head sentinel is never removed
11791
public final override fun remove(): Nothing = throw UnsupportedOperationException()
11892
}
93+
94+
private class CLOSED: LinkedListNode()

0 commit comments

Comments
 (0)