Skip to content

Commit 6f214c8

Browse files
qwwdfsadigoriakovlev
authored andcommitted
Introduce the notion of @BeningDataRace (#3873)
* Mark BufferedChannelIterator.continuation as @BeningDataRace to address potential UB and OoTA on K/N * Explain benign data-race on SelectImplementation.clauses and mark it with @BenignDataRace * Explain benign data-race on SelectImplementation.internalResult and mark it with @BenignDataRace Fixes #3834 Fixes #3843
1 parent d7a3e3f commit 6f214c8

File tree

4 files changed

+32
-3
lines changed

4 files changed

+32
-3
lines changed

kotlinx-coroutines-core/common/src/channels/BufferedChannel.kt

+5
Original file line numberDiff line numberDiff line change
@@ -1582,7 +1582,12 @@ internal open class BufferedChannel<E>(
15821582
* When [hasNext] suspends, this field stores the corresponding
15831583
* continuation. The [tryResumeHasNext] and [tryResumeHasNextOnClosedChannel]
15841584
* function resume this continuation when the [hasNext] invocation should complete.
1585+
*
1586+
* This property is the subject to bening data race:
1587+
* It is nulled-out on both completion and cancellation paths that
1588+
* could happen concurrently.
15851589
*/
1590+
@BenignDataRace
15861591
private var continuation: CancellableContinuationImpl<Boolean>? = null
15871592

15881593
// `hasNext()` is just a special receive operation.

kotlinx-coroutines-core/common/src/internal/Concurrent.common.kt

+12
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,15 @@ internal expect class ReentrantLock() {
1212
internal expect inline fun <T> ReentrantLock.withLock(action: () -> T): T
1313

1414
internal expect fun <E> identitySet(expectedSize: Int): MutableSet<E>
15+
16+
/**
17+
* Annotation indicating that the marked property is the subject of benign data race.
18+
* LLVM does not support this notion, so on K/N platforms we alias it into `@Volatile` to prevent potential OoTA.
19+
*
20+
* The purpose of this annotation is not to save an extra-volatile on JVM platform, but rather to explicitly emphasize
21+
* that data-race is benign.
22+
*/
23+
@OptionalExpectation
24+
@Target(AnnotationTarget.FIELD)
25+
@OptIn(ExperimentalMultiplatform::class)
26+
internal expect annotation class BenignDataRace()

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

+13-3
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,12 @@ internal open class SelectImplementation<R>(
372372

373373
/**
374374
* List of clauses waiting on this `select` instance.
375+
*
376+
* This property is the subject to bening data race: concurrent cancellation might null-out this property
377+
* while [trySelect] operation reads it and iterates over its content.
378+
* A logical race is resolved by the consensus on [state] property.
375379
*/
380+
@BenignDataRace
376381
private var clauses: MutableList<ClauseData>? = ArrayList(2)
377382

378383
/**
@@ -407,7 +412,13 @@ internal open class SelectImplementation<R>(
407412
* one that stores either result when the clause is successfully registered ([inRegistrationPhase] is `true`),
408413
* or [DisposableHandle] instance when the clause is completed during registration ([inRegistrationPhase] is `false`).
409414
* Yet, this optimization is omitted for code simplicity.
415+
*
416+
* This property is the subject to benign data race:
417+
* [Cleanup][cleanup] procedure can be invoked both as part of the completion sequence
418+
* and as a cancellation handler triggered by an external cancellation.
419+
* In both scenarios, [NO_RESULT] is written to this property via race.
410420
*/
421+
@BenignDataRace
411422
private var internalResult: Any? = NO_RESULT
412423

413424
/**
@@ -621,9 +632,8 @@ internal open class SelectImplementation<R>(
621632
// try to resume the continuation.
622633
this.internalResult = internalResult
623634
if (cont.tryResume(onCancellation)) return TRY_SELECT_SUCCESSFUL
624-
// If the resumption failed, we need to clean
625-
// the [result] field to avoid memory leaks.
626-
this.internalResult = null
635+
// If the resumption failed, we need to clean the [result] field to avoid memory leaks.
636+
this.internalResult = NO_RESULT
627637
return TRY_SELECT_CANCELLED
628638
}
629639
}

kotlinx-coroutines-core/native/src/internal/Concurrent.kt

+2
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,5 @@ internal open class SuppressSupportingThrowableImpl : Throwable() {
2929
}
3030
}
3131

32+
@Suppress("ACTUAL_WITHOUT_EXPECT") // This suppress can be removed in 2.0: KT-59355
33+
internal actual typealias BenignDataRace = kotlin.concurrent.Volatile

0 commit comments

Comments
 (0)