Skip to content

Commit e458bdb

Browse files
committed
NonConcurrentlyModifiable concurrency failures should identify both parties
Fixes Kotlin#3395 by capturing the stack frames for both the reader+writer or the writer+writer, depending upon the scenario.
1 parent eac0b07 commit e458bdb

File tree

1 file changed

+11
-8
lines changed

1 file changed

+11
-8
lines changed

kotlinx-coroutines-test/common/src/internal/TestMainDispatcher.kt

+11-8
Original file line numberDiff line numberDiff line change
@@ -61,29 +61,32 @@ internal class TestMainDispatcher(delegate: CoroutineDispatcher):
6161
* next modification.
6262
*/
6363
private class NonConcurrentlyModifiable<T>(initialValue: T, private val name: String) {
64+
private val reader: AtomicRef<Throwable?> = atomic(null) // last reader to attempt access
6465
private val readers = atomic(0) // number of concurrent readers
65-
private val isWriting = atomic(false) // a modification is happening currently
66+
private val writer: AtomicRef<Throwable?> = atomic(null) // writer currently performing value modification
6667
private val exceptionWhenReading: AtomicRef<Throwable?> = atomic(null) // exception from reading
6768
private val _value = atomic(initialValue) // the backing field for the value
6869

69-
private fun concurrentWW() = IllegalStateException("$name is modified concurrently")
70-
private fun concurrentRW() = IllegalStateException("$name is used concurrently with setting it")
70+
private fun concurrentWW(location: Throwable) = IllegalStateException("$name is modified concurrently", location)
71+
private fun concurrentRW(location: Throwable) = IllegalStateException("$name is used concurrently with setting it", location)
7172

7273
var value: T
7374
get() {
75+
reader.getAndSet(Throwable("reader location"))
7476
readers.incrementAndGet()
75-
if (isWriting.value) exceptionWhenReading.value = concurrentRW()
77+
writer.value?.let { exceptionWhenReading.value = concurrentRW(it) }
7678
val result = _value.value
7779
readers.decrementAndGet()
7880
return result
7981
}
8082
set(value) {
8183
exceptionWhenReading.getAndSet(null)?.let { throw it }
82-
if (readers.value != 0) throw concurrentRW()
83-
if (!isWriting.compareAndSet(expect = false, update = true)) throw concurrentWW()
84+
if (readers.value != 0) reader.value?.let { throw concurrentRW(it) }
85+
val writerLocation = Throwable()
86+
writer.getAndSet(writerLocation)?.let { throw concurrentWW(it) }
8487
_value.value = value
85-
isWriting.value = false
86-
if (readers.value != 0) throw concurrentRW()
88+
writer.compareAndSet(writerLocation, null)
89+
if (readers.value != 0) reader.value?.let { throw concurrentRW(it) }
8790
}
8891
}
8992
}

0 commit comments

Comments
 (0)