@@ -456,6 +456,8 @@ internal open class SelectImplementation<R> constructor(
456
456
// carefully to ensure that the cancellation handler has not been
457
457
// installed when clauses re-register, so the logic below cannot
458
458
// 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 "!!".
459
461
if (! reregister) clauses!! + = this
460
462
disposableHandle = this @SelectImplementation.disposableHandle
461
463
this @SelectImplementation.disposableHandle = null
@@ -470,7 +472,12 @@ internal open class SelectImplementation<R> constructor(
470
472
* Checks that there does not exist another clause with the same object.
471
473
*/
472
474
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 }) {
474
481
" Cannot use select clauses on the same object: $clauseObject "
475
482
}
476
483
}
@@ -538,7 +545,7 @@ internal open class SelectImplementation<R> constructor(
538
545
* was still in REGISTRATION phase.
539
546
*/
540
547
private fun reregisterClause (clauseObject : Any ) {
541
- val clause = findClause(clauseObject)!!
548
+ val clause = findClause(clauseObject)!! // it is guaranteed that the corresponding clause is presented
542
549
clause.disposableHandle = null
543
550
clause.register(reregister = true )
544
551
}
@@ -601,8 +608,13 @@ internal open class SelectImplementation<R> constructor(
601
608
* If the reference to the list of clauses is already cleared due to completion/cancellation,
602
609
* this function returns `null`
603
610
*/
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" )
606
618
}
607
619
608
620
// ==============
@@ -663,15 +675,18 @@ internal open class SelectImplementation<R> constructor(
663
675
*/
664
676
private fun cleanup (selectedClause : ClauseData <R >) {
665
677
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
666
681
// Invoke all cancellation handlers except for the
667
682
// one related to the selected clause, if specified.
668
- clauses? .forEach { clause ->
683
+ clauses.forEach { clause ->
669
684
if (clause != = selectedClause) clause.disposableHandle?.dispose()
670
685
}
671
686
// 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
675
690
}
676
691
677
692
// [CompletionHandler] implementation, must be invoked on cancellation.
@@ -682,11 +697,14 @@ internal open class SelectImplementation<R> constructor(
682
697
if (cur is ClauseData <* > || cur == STATE_COMPLETED ) return
683
698
STATE_CANCELLED
684
699
}
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
685
703
// Remove this `select` instance from all the clause object (channels, mutexes, etc.).
686
- clauses? .forEach { it.disposableHandle?.dispose() }
704
+ clauses.forEach { it.disposableHandle?.dispose() }
687
705
// 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
690
708
}
691
709
692
710
/* *
0 commit comments