11package kotlinx.coroutines.sync
22
3- import kotlinx.atomicfu.atomic
4- import kotlinx.atomicfu.atomicArrayOfNulls
5- import kotlinx.atomicfu.getAndUpdate
6- import kotlinx.atomicfu.loop
3+ import kotlinx.atomicfu.*
74import kotlinx.coroutines.*
85import kotlinx.coroutines.internal.*
9- import kotlin.coroutines.resume
10- import kotlin.math.max
6+ import kotlin.coroutines.*
7+ import kotlin.jvm.*
8+ import kotlin.math.*
119
1210/* *
13- * A counting semaphore for coroutines. It maintains a number of available permits.
14- * Each [acquire] suspends if necessary until a permit is available, and then takes it .
11+ * A counting semaphore for coroutines that logically maintains a number of available permits.
12+ * Each [acquire] takes a single permit or suspends until it is available .
1513 * Each [release] adds a permit, potentially releasing a suspended acquirer.
14+ * Semaphore is fair and maintains a FIFO order of acquirers.
1615 *
16+ * Semaphores are mostly used to limit the number of coroutines that have an access to particular resource.
1717 * Semaphore with `permits = 1` is essentially a [Mutex].
1818 **/
1919public interface Semaphore {
@@ -29,11 +29,12 @@ public interface Semaphore {
2929 * This suspending function is cancellable. If the [Job] of the current coroutine is cancelled or completed while this
3030 * function is suspended, this function immediately resumes with [CancellationException].
3131 *
32- * *Cancellation of suspended semaphore acquisition` is atomic* -- when this function
32+ * *Cancellation of suspended semaphore acquisition is atomic* -- when this function
3333 * throws [CancellationException] it means that the semaphore was not acquired.
3434 *
35- * Note, that this function does not check for cancellation when it is not suspended.
36- * Use [yield] or [CoroutineScope.isActive] to periodically check for cancellation in tight loops if needed.
35+ * Note, that this function does not check for cancellation when it does not suspend.
36+ * Use [CoroutineScope.isActive] or [CoroutineScope.ensureActive] to periodically
37+ * check for cancellation in tight loops if needed.
3738 *
3839 * Use [tryAcquire] to try acquire a permit of this semaphore without suspension.
3940 */
@@ -49,8 +50,7 @@ public interface Semaphore {
4950 /* *
5051 * Releases a permit, returning it into this semaphore. Resumes the first
5152 * suspending acquirer if there is one at the point of invocation.
52- * Throws [IllegalStateException] if there is no acquired permit
53- * at the point of invocation.
53+ * Throws [IllegalStateException] if the number of [release] invocations is greater than the number of preceding [acquire].
5454 */
5555 public fun release ()
5656}
@@ -83,8 +83,8 @@ private class SemaphoreImpl(
8383 private val permits : Int , acquiredPermits : Int
8484) : Semaphore, SegmentQueue<SemaphoreSegment>() {
8585 init {
86- require(permits > 0 ) { " Semaphore should have at least 1 permit" }
87- require(acquiredPermits in 0 .. permits) { " The number of acquired permits should be in 0..permits" }
86+ require(permits > 0 ) { " Semaphore should have at least 1 permit, but had $permits " }
87+ require(acquiredPermits in 0 .. permits) { " The number of acquired permits should be in 0..$ permits" }
8888 }
8989
9090 override fun newSegment (id : Long , prev : SemaphoreSegment ? ) = SemaphoreSegment (id, prev)
@@ -126,8 +126,8 @@ private class SemaphoreImpl(
126126 resumeNextFromQueue()
127127 }
128128
129- internal fun incPermits () = _availablePermits .getAndUpdate { cur ->
130- check(cur < permits) { " The number of acquired permits cannot be greater than ` permits` " }
129+ fun incPermits () = _availablePermits .getAndUpdate { cur ->
130+ check(cur < permits) { " The number of released permits cannot be greater than $ permits" }
131131 cur + 1
132132 }
133133
@@ -176,6 +176,8 @@ private class CancelSemaphoreAcquisitionHandler(
176176
177177private class SemaphoreSegment (id : Long , prev : SemaphoreSegment ? ): Segment<SemaphoreSegment>(id, prev) {
178178 val acquirers = atomicArrayOfNulls<Any ?>(SEGMENT_SIZE )
179+ private val cancelledSlots = atomic(0 )
180+ override val removed get() = cancelledSlots.value == SEGMENT_SIZE
179181
180182 @Suppress(" NOTHING_TO_INLINE" )
181183 inline fun get (index : Int ): Any? = acquirers[index].value
@@ -186,9 +188,6 @@ private class SemaphoreSegment(id: Long, prev: SemaphoreSegment?): Segment<Semap
186188 @Suppress(" NOTHING_TO_INLINE" )
187189 inline fun getAndSet (index : Int , value : Any? ) = acquirers[index].getAndSet(value)
188190
189- private val cancelledSlots = atomic(0 )
190- override val removed get() = cancelledSlots.value == SEGMENT_SIZE
191-
192191 // Cleans the acquirer slot located by the specified index
193192 // and removes this segment physically if all slots are cleaned.
194193 fun cancel (index : Int ): Boolean {
0 commit comments