@@ -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