Skip to content

Commit 623be54

Browse files
committed
Make VcrSelfie concurrency safe.
1 parent 33d8a76 commit 623be54

File tree

1 file changed

+27
-18
lines changed
  • jvm/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie

1 file changed

+27
-18
lines changed

jvm/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/VcrSelfie.kt

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ package com.diffplug.selfie
1717

1818
import com.diffplug.selfie.guts.CallStack
1919
import com.diffplug.selfie.guts.DiskStorage
20+
import com.diffplug.selfie.guts.atomic
2021
import com.diffplug.selfie.guts.recordCall
22+
import com.diffplug.selfie.guts.reentrantLock
23+
import com.diffplug.selfie.guts.withLock
2124

2225
private const val OPEN = "«"
2326
private const val CLOSE = "»"
@@ -32,29 +35,34 @@ internal constructor(
3235
private val call = recordCall(false)
3336
fun createVcr() = VcrSelfie(sub, call, disk)
3437
}
38+
private val state: State
3539

3640
internal sealed class State {
3741
class Read(val frames: List<Pair<String, SnapshotValue>>) : State() {
38-
var currentFrame = 0
42+
fun currentFrameThenAdvance(): Int = cf.getAndUpdate { it + 1 }
43+
fun framesReadSoFar(): Int = cf.get()
44+
private val cf = atomic(0)
3945
}
4046

4147
class Write : State() {
42-
private val frames = mutableListOf<Pair<String, SnapshotValue>>()
48+
private val lock = reentrantLock()
49+
private var frames: MutableList<Map.Entry<String, SnapshotValue>>? =
50+
mutableListOf<Map.Entry<String, SnapshotValue>>()
4351
fun add(key: String, value: SnapshotValue) {
44-
frames.add(key to value)
45-
}
46-
fun toSnapshot(): Snapshot {
47-
var snapshot = Snapshot.of("")
48-
var idx = 1
49-
for ((key, value) in frames) {
50-
snapshot = snapshot.plusFacet("$OPEN$idx$CLOSE$key", value)
51-
++idx
52+
lock.withLock {
53+
frames?.add(entry(key, value))
54+
?: throw IllegalStateException("This VCR was already closed.")
5255
}
53-
return snapshot
5456
}
57+
fun closeAndGetSnapshot(): Snapshot =
58+
Snapshot.ofEntries(
59+
lock.withLock {
60+
val frozen = frames ?: throw IllegalStateException("This VCR was already closed.")
61+
frames = null
62+
frozen
63+
})
5564
}
5665
}
57-
private val state: State
5866

5967
init {
6068
val canWrite = Selfie.system.mode.canWrite(isTodo = false, call, Selfie.system)
@@ -81,24 +89,25 @@ internal constructor(
8189
}
8290
override fun close() {
8391
if (state is State.Read) {
84-
if (state.frames.size != state.currentFrame) {
92+
if (state.frames.size != state.framesReadSoFar()) {
8593
throw Selfie.system.fs.assertFailed(
86-
Selfie.system.mode.msgVcrUnread(state.frames.size, state.currentFrame))
94+
Selfie.system.mode.msgVcrUnread(state.frames.size, state.framesReadSoFar()))
8795
}
8896
} else {
89-
disk.writeDisk((state as State.Write).toSnapshot(), sub, call)
97+
disk.writeDisk((state as State.Write).closeAndGetSnapshot(), sub, call)
9098
}
9199
}
92100
private fun nextFrameValue(state: State.Read, key: String): SnapshotValue {
93101
val mode = Selfie.system.mode
94102
val fs = Selfie.system.fs
95-
if (state.frames.size <= state.currentFrame) {
103+
val currentFrame = state.currentFrameThenAdvance()
104+
if (state.frames.size <= currentFrame) {
96105
throw fs.assertFailed(mode.msgVcrUnderflow(state.frames.size))
97106
}
98-
val expected = state.frames[state.currentFrame++]
107+
val expected = state.frames[currentFrame]
99108
if (expected.first != key) {
100109
throw fs.assertFailed(
101-
mode.msgVcrMismatch("$sub[$OPEN${state.currentFrame}$CLOSE]", expected.first, key),
110+
mode.msgVcrMismatch("$sub[$OPEN${currentFrame}$CLOSE]", expected.first, key),
102111
expected.first,
103112
key)
104113
}

0 commit comments

Comments
 (0)