Concurrency and Source Panels
Thread Local Context
Give each thread its own local label while the main thread combines stable results.
thread local
`ThreadLocal` stores one value per thread, which keeps per-thread context separate even when the same function runs twice.
Thread Local Context
ThreadLocalContext.kt
Replay: real traced execution (multi-file project)
val currentLabel = ThreadLocal.withInitial { "unset" }
fun collectLabel(label: String, slot: Int, output: Array<String>) {
currentLabel.set(label)
val local = currentLabel.get()
output[slot] = "$local:${label.length}"
currentLabel.remove()
}
fun main() {
val suffix = "api"
val output = Array(2) { "" }
val first = Thread { collectLabel("read-$suffix", 0, output) }
val second = Thread { collectLabel("write-$suffix", 1, output) }
first.start()
second.start()
first.join()
second.join()
println("suffix=$suffix")
println(output.joinToString("|"))
}
val currentLabel = ThreadLocal.withInitial { "unset" }
fun collectLabel(label: String, slot: Int, output: Array<String>) {
currentLabel.set(label)
val local = currentLabel.get()
output[slot] = "$local:${label.length}"
currentLabel.remove()
}
fun main() {
val suffix = "ui"
val output = Array(2) { "" }
val first = Thread { collectLabel("read-$suffix", 0, output) }
val second = Thread { collectLabel("write-$suffix", 1, output) }
first.start()
second.start()
first.join()
second.join()
println("suffix=$suffix")
println(output.joinToString("|"))
}
val currentLabel = ThreadLocal.withInitial { "unset" }
fun collectLabel(label: String, slot: Int, output: Array<String>) {
currentLabel.set(label)
val local = currentLabel.get()
output[slot] = "$local:${label.length}"
currentLabel.remove()
}
fun main() {
val suffix = "jobs"
val output = Array(2) { "" }
val first = Thread { collectLabel("read-$suffix", 0, output) }
val second = Thread { collectLabel("write-$suffix", 1, output) }
first.start()
second.start()
first.join()
second.join()
println("suffix=$suffix")
println(output.joinToString("|"))
}
suffix ← api, first ← Thread[#19,Thread-0,5,main], second ← Thread[#20,Thread-1,5,main]
10fun main() {11 val suffix→ api = "api" //@suffix="ui", "jobs"12 val output = Array(2) { "" }1314 val first→ Thread[#19,Thread-0,5,main] = Thread { collectLabel("read-$suffix", 0, output) }15 val second→ Thread[#20,Thread-1,5,main] = Thread { collectLabel("write-$suffix", 1, output) }1617 firstThread[#19,Thread-0,5,main].start()18 secondThread[#20,Thread-1,5,main].start()19 firstThread[#19,Thread-0,5,main].join()20 second.join()fun collectLabel(label: String, slot: Int, output: Array<String>)
pass 1 of 23fun collectLabel(labelread-api: String, slot0: Int, output: Array<String>) {4 currentLabel.set(label)currentLabel ← ⟨ThreadLocal$SuppliedThreadLocal A⟩, label ← read-api
pass 2 of 23fun collectLabel(labelwrite-api: String, slot1: Int, output: Array<String>) {4 currentLabel→ ⟨ThreadLocal$SuppliedThreadLocal A⟩.set(label→ read-api)5 val local→ write-api = currentLabel→ ⟨ThreadLocal$SuppliedThreadLocal A⟩.get()6 output[slot]→ write-api:9 = "$local→ write-api:${label.length→ 9}"7 currentLabel⟨ThreadLocal$SuppliedThreadLocal A⟩.remove()8}first ← Thread[#19,Thread-0,5,]
18 second.start()19 first→ Thread[#19,Thread-0,5,].join()20 secondThread[#20,Thread-1,5,].join()2122 println("suffix=$suffixapi")23 println(output.joinToString("|"))24}outputsuffix=api read-api:8|write-api:9
suffix ← ui, first ← Thread[#19,Thread-0,5,main], second ← Thread[#20,Thread-1,5,main]
10fun main() {11 val suffix→ ui = "ui"12 val output = Array(2) { "" }1314 val first→ Thread[#19,Thread-0,5,main] = Thread { collectLabel("read-$suffix", 0, output) }15 val second→ Thread[#20,Thread-1,5,main] = Thread { collectLabel("write-$suffix", 1, output) }1617 firstThread[#19,Thread-0,5,main].start()18 secondThread[#20,Thread-1,5,main].start()19 first.join()fun collectLabel(label: String, slot: Int, output: Array<String>)
pass 1 of 23fun collectLabel(labelread-ui: String, slot0: Int, output: Array<String>) {4 currentLabel.set(label)fun collectLabel(label: String, slot: Int, output: Array<String>)
pass 2 of 23fun collectLabel(labelwrite-ui: String, slot1: Int, output: Array<String>) {4 currentLabel⟨ThreadLocal$SuppliedThreadLocal A⟩.set(labelread-ui)5 val local = currentLabel.get()first ← Thread[#19,Thread-0,5,], currentLabel ← ⟨ThreadLocal$SuppliedThreadLocal A⟩
3fun collectLabel(label: String, slot: Int, output: Array<String>) {4 currentLabel→ ⟨ThreadLocal$SuppliedThreadLocal A⟩.set(label→ write-ui)5 val local→ write-ui = currentLabel→ ⟨ThreadLocal$SuppliedThreadLocal A⟩.get()6 output[slot]→ read-ui:7 = "$local→ read-ui:${label.length→ 7}"7 currentLabel⟨ThreadLocal$SuppliedThreadLocal A⟩.remove()8}910fun main() {11 val suffix = "ui"12 val output = Array(2) { "" }1314 val first = Thread { collectLabel("read-$suffix", 0, output) }15 val second = Thread { collectLabel("write-$suffix", 1, output) }1617 first.start()18 second.start()19 first→ Thread[#19,Thread-0,5,].join()20 secondThread[#20,Thread-1,5,].join()2122 println("suffix=$suffixui")23 println(output.joinToString("|"))24}outputsuffix=ui read-ui:7|write-ui:8values this step0slot
suffix ← jobs, first ← Thread[#19,Thread-0,5,main], second ← Thread[#20,Thread-1,5,main]
10fun main() {11 val suffix→ jobs = "jobs"12 val output = Array(2) { "" }1314 val first→ Thread[#19,Thread-0,5,main] = Thread { collectLabel("read-$suffix", 0, output) }15 val second→ Thread[#20,Thread-1,5,main] = Thread { collectLabel("write-$suffix", 1, output) }1617 firstThread[#19,Thread-0,5,main].start()18 secondThread[#20,Thread-1,5,main].start()19 first.join()fun collectLabel(label: String, slot: Int, output: Array<String>)
pass 1 of 23fun collectLabel(labelread-jobs: String, slot0: Int, output: Array<String>) {4 currentLabel⟨ThreadLocal$SuppliedThreadLocal A⟩.set(labelread-jobs)5 val local = currentLabel.get()second.start()
17first.start()18secondThread[#20,Thread-1,5,main].start()19firstThread[#19,Thread-0,5,main].join()20second.join()currentLabel ← ⟨ThreadLocal$SuppliedThreadLocal A⟩, local ← read-jobs
pass 2 of 23fun collectLabel(labelwrite-jobs: String, slot1: Int, output: Array<String>) {4 currentLabel⟨ThreadLocal$SuppliedThreadLocal A⟩.set(labelwrite-jobs)5 val local→ read-jobs = currentLabel→ ⟨ThreadLocal$SuppliedThreadLocal A⟩.get()6 output[slot]→ write-jobs:10 = "$local→ write-jobs:${label.length→ 10}"7 currentLabel⟨ThreadLocal$SuppliedThreadLocal A⟩.remove()8}first ← Thread[#19,Thread-0,5,]
18 second.start()19 first→ Thread[#19,Thread-0,5,].join()20 secondThread[#20,Thread-1,5,].join()2122 println("suffix=$suffixjobs")23 println(output.joinToString("|"))24}outputsuffix=jobs read-jobs:9|write-jobs:10