@@ -3,35 +3,62 @@ package kotlinx.coroutines.internal
3
3
import kotlinx.atomicfu.AtomicRef
4
4
import kotlinx.atomicfu.atomic
5
5
6
- internal abstract class SegmentQueue <S : Segment <S >>(createFirstSegment : Boolean = true ) {
6
+ /* *
7
+ * Essentially, this segment queue is an infinite array of segments, which is represented as
8
+ * a Michael-Scott queue of them. All segments are instances of [Segment] interface and
9
+ * follow in natural order (see [Segment.id]) in the queue.
10
+ *
11
+ * In some data structures, like `Semaphore`, this queue is used for storing suspended continuations
12
+ * and is always empty in uncontended scenarios. Therefore, there is no need in creating
13
+ * the first segment in advance in this case. A special `createFirstSegmentLazily` is introduced
14
+ * to create segments lazily, on the first [getSegment] invocation; it is set to `false` by default.
15
+ */
16
+ internal abstract class SegmentQueue <S : Segment <S >>(createFirstSegmentLazily : Boolean = false ) {
7
17
private val _head : AtomicRef <S ?>
8
- protected val head: S ? get() = _head .value
18
+ /* *
19
+ * Returns the first segment in the queue. All segments with lower [id]
20
+ */
21
+ protected val first: S ? get() = _head .value
9
22
10
23
private val _tail : AtomicRef <S ?>
11
- protected val tail : S ? get() = _tail .value
24
+ protected val last : S ? get() = _tail .value
12
25
13
26
init {
14
- val initialSegment = if (createFirstSegment) newSegment(0 ) else null
27
+ val initialSegment = if (createFirstSegmentLazily) null else newSegment(0 )
15
28
_head = atomic(initialSegment)
16
29
_tail = atomic(initialSegment)
17
30
}
18
31
32
+ /* *
33
+ * The implementation should create an instance of segment [S] with the specified id
34
+ * and initial reference to the previous one.
35
+ */
19
36
abstract fun newSegment (id : Long , prev : S ? = null): S
20
37
38
+ /* *
39
+ * Finds a segment with the specified [id] following by next references from the
40
+ * [startFrom] segment. The typical use-case is reading [last] or [first], doing some
41
+ * synchronization, and invoking [getSegment] or [getSegmentAndMoveFirst] correspondingly
42
+ * to find the required segment.
43
+ */
21
44
protected fun getSegment (startFrom : S ? , id : Long ): S ? {
45
+ // Try to create the first segment if [startFrom] is null.
46
+ // This occurs if `createFirstSegmentLazily` was set to `true`.
22
47
var startFrom = startFrom
23
48
if (startFrom == = null ) {
24
49
val firstSegment = newSegment(0 )
25
50
if (_head .compareAndSet(null , firstSegment))
26
51
startFrom = firstSegment
27
52
else {
28
- startFrom = head !!
53
+ startFrom = first !!
29
54
}
30
55
}
31
56
if (startFrom.id > id) return null
32
- // This method goes through `next` references and
33
- // adds new segments if needed, similarly to the `push` in
34
- // the Michael-Scott queue algorithm.
57
+ // Go through `next` references and add new segments if needed,
58
+ // similarly to the `push` in the Michael-Scott queue algorithm.
59
+ // The only difference is that `CAS failure` means that the
60
+ // required segment has already been added, so the algorithm just
61
+ // uses it. This way, only one segment with each id can be in the queue.
35
62
var cur: S = startFrom
36
63
while (cur.id < id) {
37
64
var curNext = cur.next.value
@@ -54,20 +81,23 @@ internal abstract class SegmentQueue<S: Segment<S>>(createFirstSegment: Boolean
54
81
return cur
55
82
}
56
83
57
- protected fun getSegmentAndMoveHeadForward (startFrom : S ? , id : Long ): S ? {
84
+ /* *
85
+ * Invokes [getSegment] and replaces [first] with the result if its [id] is greater.
86
+ */
87
+ protected fun getSegmentAndMoveFirst (startFrom : S ? , id : Long ): S ? {
58
88
if (startFrom != = null && startFrom.id == id) return startFrom
59
89
val s = getSegment(startFrom, id) ? : return null
60
90
moveHeadForward(s)
61
91
return s
62
92
}
63
93
64
94
/* *
65
- * Updates [head ] to the specified segment
95
+ * Updates [_head ] to the specified segment
66
96
* if its `id` is greater.
67
97
*/
68
98
private fun moveHeadForward (new : S ) {
69
99
while (true ) {
70
- val cur = head !!
100
+ val cur = first !!
71
101
if (cur.id > new.id) return
72
102
if (_head .compareAndSet(cur, new)) {
73
103
new.prev.value = null
@@ -77,71 +107,62 @@ internal abstract class SegmentQueue<S: Segment<S>>(createFirstSegment: Boolean
77
107
}
78
108
79
109
/* *
80
- * Updates [tail ] to the specified segment
110
+ * Updates [_tail ] to the specified segment
81
111
* if its `id` is greater.
82
112
*/
83
113
private fun moveTailForward (new : S ) {
84
114
while (true ) {
85
- val cur = tail
115
+ val cur = last
86
116
if (cur != = null && cur.id > new.id) return
87
117
if (_tail .compareAndSet(cur, new)) return
88
118
}
89
119
}
90
120
}
91
121
122
+ /* *
123
+ * Each segment in [SegmentQueue] has a unique id and is created by [SegmentQueue.newSegment].
124
+ * Essentially, this is a node in the Michael-Scott queue algorithm, but with
125
+ * maintaining [prev] pointer for efficient [remove] implementation.
126
+ */
92
127
internal abstract class Segment <S : Segment <S >>(val id : Long , prev : S ? ) {
93
- // Pointer to the next segments, updates
94
- // similarly to the Michael-Scott queue algorithm.
95
- val next = atomic<S ?>(null ) // null (not set) | Segment | CLOSED
96
- // Pointer to the previous non-empty segment (can be null!),
97
- // updates lazily (see `remove()` function).
128
+ // Pointer to the next segment, updates similarly to the Michael-Scott queue algorithm.
129
+ val next = atomic<S ?>(null )
130
+ // Pointer to the previous segment, updates in [remove] function.
98
131
val prev = atomic<S ?>(null )
99
132
133
+ /* *
134
+ * Returns `true` if this segment is logically removed from the queue.
135
+ * The [remove] function should be called right after it becomes logically removed.
136
+ */
100
137
abstract val removed: Boolean
101
138
102
139
init {
103
140
this .prev.value = prev
104
141
}
105
142
106
143
/* *
107
- * Removes this node from the waiting queue and cleans all references to it.
144
+ * Removes this segment physically from the segment queue. The segment should be
145
+ * logically removed (so [removed] returns `true`) at the point of invocation.
108
146
*/
109
147
fun remove () {
110
148
check(removed) { " The segment should be logically removed at first " }
111
- val next = this .next.value ? : return // tail can't be removed
112
- // Find the first non-removed node ( tail is always non- removed)
149
+ // Read `next` and `prev` pointers.
150
+ val next = this .next.value ? : return // tail cannot be removed
113
151
val prev = prev.value ? : return // head cannot be removed
152
+ // Link `next` and `prev`.
114
153
next.movePrevToLeft(prev)
115
154
prev.movePrevNextToRight(next)
155
+ // Check whether `prev` and `next` are still in the queue
156
+ // and help with removing them if needed.
116
157
if (prev.removed)
117
158
prev.remove()
118
159
if (next.removed)
119
160
next.remove()
120
-
121
- // while (next.removed) {
122
- // next = next.next.value ?: return
123
- // }
124
- // // Find the first non-removed `prev` and remove this node
125
- // var prev = prev.value
126
- // while (true) {
127
- // if (prev === null) {
128
- // next.prev.value = null
129
- // return
130
- // }
131
- // if (prev.removed) {
132
- // prev = prev.prev.value
133
- // continue
134
- // }
135
- // next.movePrevToLeft(prev)
136
- // prev.movePrevNextToRight(next)
137
- // if (next.removed || !prev.removed) return
138
- // prev = prev.prev.value
139
- // }
140
161
}
141
162
142
163
/* *
143
- * Update [Segment. next] pointer to the specified one if
144
- * the `id` of the specified segment is greater.
164
+ * Updates [ next] pointer to the specified segment if
165
+ * the [id] of the specified segment is greater.
145
166
*/
146
167
private fun movePrevNextToRight (next : S ) {
147
168
while (true ) {
@@ -152,8 +173,8 @@ internal abstract class Segment<S: Segment<S>>(val id: Long, prev: S?) {
152
173
}
153
174
154
175
/* *
155
- * Update [Segment. prev] pointer to the specified segment if
156
- * its `id` is lower.
176
+ * Updates [ prev] pointer to the specified segment if
177
+ * the [id] of the specified segment is lower.
157
178
*/
158
179
private fun movePrevToLeft (prev : S ) {
159
180
while (true ) {
0 commit comments