@@ -121,14 +121,16 @@ private class SemaphoreImpl(
121
121
}
122
122
123
123
override fun release () {
124
- val p = _availablePermits .getAndUpdate { cur ->
125
- check(cur < permits) { " The number of acquired permits cannot be greater than `permits`" }
126
- cur + 1
127
- }
124
+ val p = incPermits()
128
125
if (p >= 0 ) return // no waiters
129
126
resumeNextFromQueue()
130
127
}
131
128
129
+ internal fun incPermits () = _availablePermits .getAndUpdate { cur ->
130
+ check(cur < permits) { " The number of acquired permits cannot be greater than `permits`" }
131
+ cur + 1
132
+ }
133
+
132
134
private suspend fun addToQueueAndSuspend () = suspendAtomicCancellableCoroutine<Unit > sc@ { cont ->
133
135
val last = this .tail
134
136
val enqIdx = enqIdx.getAndIncrement()
@@ -143,63 +145,59 @@ private class SemaphoreImpl(
143
145
}
144
146
145
147
@Suppress(" UNCHECKED_CAST" )
146
- private fun resumeNextFromQueue () {
147
- val first = this .head
148
- val deqIdx = deqIdx.getAndIncrement()
149
- val segment = getSegmentAndMoveHead(first, deqIdx / SEGMENT_SIZE ) ? : return
150
- val i = (deqIdx % SEGMENT_SIZE ).toInt()
151
- val cont = segment.getAndUpdate(i) {
152
- // Cancelled continuation invokes `release`
153
- // and resumes next suspended acquirer if needed.
154
- if (it == = CANCELLED ) return
155
- RESUMED
148
+ internal fun resumeNextFromQueue () {
149
+ try_again@while (true ) {
150
+ val first = this .head
151
+ val deqIdx = deqIdx.getAndIncrement()
152
+ val segment = getSegmentAndMoveHead(first, deqIdx / SEGMENT_SIZE ) ? : continue @try_again
153
+ val i = (deqIdx % SEGMENT_SIZE ).toInt()
154
+ val cont = segment.getAndSet(i, RESUMED )
155
+ if (cont == = null ) return // just resumed
156
+ if (cont == = CANCELLED ) continue @try_again
157
+ (cont as CancellableContinuation <Unit >).resume(Unit )
158
+ return
156
159
}
157
- if (cont == = null ) return // just resumed
158
- (cont as CancellableContinuation <Unit >).resume(Unit )
159
160
}
160
161
}
161
162
162
163
private class CancelSemaphoreAcquisitionHandler (
163
- private val semaphore : Semaphore ,
164
+ private val semaphore : SemaphoreImpl ,
164
165
private val segment : SemaphoreSegment ,
165
166
private val index : Int
166
167
) : CancelHandler() {
167
168
override fun invoke (cause : Throwable ? ) {
168
- segment.cancel(index)
169
- semaphore.release()
169
+ semaphore.incPermits()
170
+ if (segment.cancel(index)) return
171
+ semaphore.resumeNextFromQueue()
170
172
}
171
173
172
174
override fun toString () = " CancelSemaphoreAcquisitionHandler[$semaphore , $segment , $index ]"
173
175
}
174
176
175
177
private class SemaphoreSegment (id : Long , prev : SemaphoreSegment ? ): Segment<SemaphoreSegment>(id, prev) {
176
- private val acquirers = atomicArrayOfNulls<Any ?>(SEGMENT_SIZE )
178
+ val acquirers = atomicArrayOfNulls<Any ?>(SEGMENT_SIZE )
177
179
178
180
@Suppress(" NOTHING_TO_INLINE" )
179
181
inline fun get (index : Int ): Any? = acquirers[index].value
180
182
181
183
@Suppress(" NOTHING_TO_INLINE" )
182
184
inline fun cas (index : Int , expected : Any? , value : Any? ): Boolean = acquirers[index].compareAndSet(expected, value)
183
185
184
- inline fun getAndUpdate (index : Int , function : (Any? ) -> Any? ): Any? {
185
- while (true ) {
186
- val cur = acquirers[index].value
187
- val upd = function(cur)
188
- if (cas(index, cur, upd)) return cur
189
- }
190
- }
186
+ @Suppress(" NOTHING_TO_INLINE" )
187
+ inline fun getAndSet (index : Int , value : Any? ) = acquirers[index].getAndSet(value)
191
188
192
189
private val cancelledSlots = atomic(0 )
193
190
override val removed get() = cancelledSlots.value == SEGMENT_SIZE
194
191
195
192
// Cleans the acquirer slot located by the specified index
196
193
// and removes this segment physically if all slots are cleaned.
197
- fun cancel (index : Int ) {
198
- // Clean the specified waiter
199
- acquirers[index].value = CANCELLED
194
+ fun cancel (index : Int ): Boolean {
195
+ // Try to cancel the slot
196
+ val cancelled = getAndSet(index, CANCELLED ) != = RESUMED
200
197
// Remove this segment if needed
201
198
if (cancelledSlots.incrementAndGet() == SEGMENT_SIZE )
202
199
remove()
200
+ return cancelled
203
201
}
204
202
205
203
override fun toString () = " SemaphoreSegment[id=$id , hashCode=${hashCode()} ]"
0 commit comments