@@ -118,19 +118,7 @@ private class SemaphoreImpl(
118
118
if (p > 0 ) {
119
119
// try to get a permit by updating the counter
120
120
if (_availablePermits .compareAndSet(p, p - 1 )) return true
121
- } else {
122
- // check whether there is a permit in the queue
123
- val enqIdx = this .enqIdx.value
124
- val deqIdx = this .deqIdx.value
125
- if (enqIdx >= deqIdx) return false
126
- // try to get a permit from the queue
127
- if (this .enqIdx.compareAndSet(enqIdx, enqIdx + 1 )) {
128
- // update the counter of available permits and check
129
- // whether it was legal to get a permit from the queue
130
- if (_availablePermits .getAndDecrement() <= 0 ) return true
131
- else release()
132
- }
133
- }
121
+ } else return false
134
122
}
135
123
}
136
124
@@ -140,15 +128,19 @@ private class SemaphoreImpl(
140
128
addToQueueAndSuspend()
141
129
}
142
130
143
- override fun release () {
144
- val p = incPermits()
145
- if (p >= 0 ) return // no waiters
146
- resumeNextFromQueue()
131
+ override fun release () = check(releaseImpl()) {
132
+ " The number of released permits cannot be greater than $permits "
147
133
}
148
134
149
- fun incPermits () = _availablePermits .getAndUpdate { cur ->
150
- check(cur < permits) { " The number of released permits cannot be greater than $permits " }
151
- cur + 1
135
+ internal fun releaseImpl (): Boolean {
136
+ while (true ) {
137
+ val p = _availablePermits .getAndUpdate { cur ->
138
+ if (cur == permits) return false // the number of released permits cannot be greater than `permits`
139
+ cur + 1
140
+ }
141
+ if (p >= 0 ) return true
142
+ if (tryResumeNextFromQueue()) return true
143
+ }
152
144
}
153
145
154
146
private suspend fun addToQueueAndSuspend () = suspendAtomicCancellableCoroutineReusable<Unit > sc@ { cont ->
@@ -158,41 +150,45 @@ private class SemaphoreImpl(
158
150
val i = (enqIdx % SEGMENT_SIZE ).toInt()
159
151
if (segment == = null || segment.get(i) == = RESUMED || ! segment.cas(i, null , cont)) {
160
152
// already resumed
153
+ segment?.set(i, TAKEN )
161
154
cont.resume(Unit )
162
155
return @sc
163
156
}
164
- cont.invokeOnCancellation(CancelSemaphoreAcquisitionHandler (this , segment, i).asHandler)
157
+ cont.invokeOnCancellation(CancelSemaphoreAcquisitionHandler (segment, i).asHandler)
165
158
}
166
159
167
160
@Suppress(" UNCHECKED_CAST" )
168
- internal fun resumeNextFromQueue () {
169
- try_again@while (true ) {
161
+ private fun tryResumeNextFromQueue (): Boolean {
170
162
val first = this .head
171
163
val deqIdx = deqIdx.getAndIncrement()
172
- val segment = getSegmentAndMoveHead(first, deqIdx / SEGMENT_SIZE ) ? : continue @try_again
164
+ val segment = getSegmentAndMoveHead(first, deqIdx / SEGMENT_SIZE ) ? : return false
173
165
val i = (deqIdx % SEGMENT_SIZE ).toInt()
174
166
val cont = segment.getAndSet(i, RESUMED )
175
- if (cont == = null ) return // just resumed
176
- if (cont == = CANCELLED ) continue @try_again
177
- (cont as CancellableContinuation <Unit >).resume(Unit )
178
- return
179
- }
167
+ if (cont == = CANCELLED ) return false
168
+ if (cont == = null ) {
169
+ while (segment.get(i) != TAKEN ) { /* spin wait */ }
170
+ return true
171
+ } // just resumed
172
+ return (cont as CancellableContinuation <Unit >).tryResume()
180
173
}
181
174
}
182
175
176
+ private fun CancellableContinuation<Unit>.tryResume (): Boolean {
177
+ val token = tryResume(Unit ) ? : return false
178
+ completeResume(token)
179
+ return true
180
+ }
181
+
182
+
183
183
private class CancelSemaphoreAcquisitionHandler (
184
- private val semaphore : SemaphoreImpl ,
185
184
private val segment : SemaphoreSegment ,
186
185
private val index : Int
187
186
) : CancelHandler() {
188
187
override fun invoke (cause : Throwable ? ) {
189
- val p = semaphore.incPermits()
190
- if (p >= 0 ) return
191
- if (segment.cancel(index)) return
192
- semaphore.resumeNextFromQueue()
188
+ segment.cancel(index)
193
189
}
194
190
195
- override fun toString () = " CancelSemaphoreAcquisitionHandler[$semaphore , $ segment , $index ]"
191
+ override fun toString () = " CancelSemaphoreAcquisitionHandler[$segment , $index ]"
196
192
}
197
193
198
194
private class SemaphoreSegment (id : Long , prev : SemaphoreSegment ? ): Segment<SemaphoreSegment>(id, prev) {
@@ -203,6 +199,11 @@ private class SemaphoreSegment(id: Long, prev: SemaphoreSegment?): Segment<Semap
203
199
@Suppress(" NOTHING_TO_INLINE" )
204
200
inline fun get (index : Int ): Any? = acquirers[index].value
205
201
202
+ @Suppress(" NOTHING_TO_INLINE" )
203
+ inline fun set (index : Int , value : Any? ) {
204
+ acquirers[index].value = value
205
+ }
206
+
206
207
@Suppress(" NOTHING_TO_INLINE" )
207
208
inline fun cas (index : Int , expected : Any? , value : Any? ): Boolean = acquirers[index].compareAndSet(expected, value)
208
209
@@ -211,13 +212,12 @@ private class SemaphoreSegment(id: Long, prev: SemaphoreSegment?): Segment<Semap
211
212
212
213
// Cleans the acquirer slot located by the specified index
213
214
// and removes this segment physically if all slots are cleaned.
214
- fun cancel (index : Int ): Boolean {
215
- // Try to cancel the slot
216
- val cancelled = getAndSet (index, CANCELLED ) != = RESUMED
215
+ fun cancel (index : Int ) {
216
+ // Clean the slot
217
+ set (index, CANCELLED )
217
218
// Remove this segment if needed
218
219
if (cancelledSlots.incrementAndGet() == SEGMENT_SIZE )
219
220
remove()
220
- return cancelled
221
221
}
222
222
223
223
override fun toString () = " SemaphoreSegment[id=$id , hashCode=${hashCode()} ]"
@@ -226,6 +226,8 @@ private class SemaphoreSegment(id: Long, prev: SemaphoreSegment?): Segment<Semap
226
226
@SharedImmutable
227
227
private val RESUMED = Symbol (" RESUMED" )
228
228
@SharedImmutable
229
+ private val TAKEN = Symbol (" TAKEN" )
230
+ @SharedImmutable
229
231
private val CANCELLED = Symbol (" CANCELLED" )
230
232
@SharedImmutable
231
233
private val SEGMENT_SIZE = systemProp(" kotlinx.coroutines.semaphore.segmentSize" , 16 )
0 commit comments