Skip to content

Commit 1d1ce37

Browse files
Nikita Kovalqwwdfsad
Nikita Koval
authored andcommitted
Improve select documentation
1 parent 1b248c1 commit 1d1ce37

File tree

1 file changed

+29
-11
lines changed
  • kotlinx-coroutines-core/common/src/selects

1 file changed

+29
-11
lines changed

kotlinx-coroutines-core/common/src/selects/Select.kt

+29-11
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,8 @@ internal open class SelectImplementation<R> constructor(
456456
// carefully to ensure that the cancellation handler has not been
457457
// installed when clauses re-register, so the logic below cannot
458458
// be invoked concurrently with the clean-up procedure.
459+
// This also guarantees that the list of clauses cannot be cleared
460+
// in the registration phase, so it is safe to read it with "!!".
459461
if (!reregister) clauses!! += this
460462
disposableHandle = this@SelectImplementation.disposableHandle
461463
this@SelectImplementation.disposableHandle = null
@@ -470,7 +472,12 @@ internal open class SelectImplementation<R> constructor(
470472
* Checks that there does not exist another clause with the same object.
471473
*/
472474
private fun checkClauseObject(clauseObject: Any) {
473-
check(clauses!!.none { it.clauseObject === clauseObject }) {
475+
// Read the list of clauses, it is guaranteed that it is non-null.
476+
// In fact, it can become `null` only in the clean-up phase, while
477+
// this check can be called only in the registration one.
478+
val clauses = clauses!!
479+
// Check that there does not exist another clause with the same object.
480+
check(clauses.none { it.clauseObject === clauseObject }) {
474481
"Cannot use select clauses on the same object: $clauseObject"
475482
}
476483
}
@@ -538,7 +545,7 @@ internal open class SelectImplementation<R> constructor(
538545
* was still in REGISTRATION phase.
539546
*/
540547
private fun reregisterClause(clauseObject: Any) {
541-
val clause = findClause(clauseObject)!!
548+
val clause = findClause(clauseObject)!! // it is guaranteed that the corresponding clause is presented
542549
clause.disposableHandle = null
543550
clause.register(reregister = true)
544551
}
@@ -601,8 +608,13 @@ internal open class SelectImplementation<R> constructor(
601608
* If the reference to the list of clauses is already cleared due to completion/cancellation,
602609
* this function returns `null`
603610
*/
604-
private fun findClause(clauseObject: Any) = clauses?.run {
605-
find { it.clauseObject === clauseObject } ?: error("Clause with object $clauseObject is not found")
611+
private fun findClause(clauseObject: Any): ClauseData<R>? {
612+
// Read the list of clauses. If the `clauses` field is already `null`,
613+
// the clean-up phase has already completed, and this function returns `null`.
614+
val clauses = this.clauses ?: return null
615+
// Find the clause with the specified clause object.
616+
return clauses.find { it.clauseObject === clauseObject }
617+
?: error("Clause with object $clauseObject is not found")
606618
}
607619

608620
// ==============
@@ -663,15 +675,18 @@ internal open class SelectImplementation<R> constructor(
663675
*/
664676
private fun cleanup(selectedClause: ClauseData<R>) {
665677
assert { state.value == selectedClause }
678+
// Read the list of clauses. If the `clauses` field is already `null`,
679+
// a concurrent clean-up procedure has already completed, and it is safe to finish.
680+
val clauses = this.clauses ?: return
666681
// Invoke all cancellation handlers except for the
667682
// one related to the selected clause, if specified.
668-
clauses?.forEach { clause ->
683+
clauses.forEach { clause ->
669684
if (clause !== selectedClause) clause.disposableHandle?.dispose()
670685
}
671686
// We do need to clean all the data to avoid memory leaks.
672-
state.value = STATE_COMPLETED
673-
internalResult = NO_RESULT
674-
clauses = null
687+
this.state.value = STATE_COMPLETED
688+
this.internalResult = NO_RESULT
689+
this.clauses = null
675690
}
676691

677692
// [CompletionHandler] implementation, must be invoked on cancellation.
@@ -682,11 +697,14 @@ internal open class SelectImplementation<R> constructor(
682697
if (cur is ClauseData<*> || cur == STATE_COMPLETED) return
683698
STATE_CANCELLED
684699
}
700+
// Read the list of clauses. If the `clauses` field is already `null`,
701+
// a concurrent clean-up procedure has already completed, and it is safe to finish.
702+
val clauses = this.clauses ?: return
685703
// Remove this `select` instance from all the clause object (channels, mutexes, etc.).
686-
clauses?.forEach { it.disposableHandle?.dispose() }
704+
clauses.forEach { it.disposableHandle?.dispose() }
687705
// We do need to clean all the data to avoid memory leaks.
688-
internalResult = NO_RESULT
689-
clauses = null
706+
this.internalResult = NO_RESULT
707+
this.clauses = null
690708
}
691709

692710
/**

0 commit comments

Comments
 (0)