Fix captured range bounds in launched for-loops

This commit is contained in:
Sergey Chernov 2026-04-09 17:05:00 +03:00
parent fbb1683ba3
commit 840cd32574
3 changed files with 47 additions and 31 deletions

View File

@ -2,70 +2,55 @@ import lyng.io.net
val host = "127.0.0.1" val host = "127.0.0.1"
val clientCount = 1000 val clientCount = 1000
val server: TcpServer = Net.tcpListen(0, host, clientCount, true) as TcpServer val server = Net.tcpListen(0, host, clientCount, true) as TcpServer
val port: Int = server.localAddress().port val port = server.localAddress().port
fun payloadFor(index: Int): String { fun payloadFor(index: Int): String {
"$index:${Random.nextInt()}:${Random.nextInt()}" "$index:${Random.nextInt()}:${Random.nextInt()}"
} }
fun handleClient(client: TcpSocket): String { fun handleClient(client: TcpSocket) {
try { try {
val source = client.readLine() val source = client.readLine()
if( source == null ) { if( source != null ) {
return "server-eof" client.writeUtf8("pong: $source\n")
client.flush()
} }
val reply = "pong: $source"
client.writeUtf8(reply + "\n")
client.flush()
reply
} finally { } finally {
client.close() client.close()
} }
} }
val serverJob: Deferred = launch { launch {
var handlers: List<Deferred> = List()
try { try {
for( i in 0..<1000 ) { for( i in 0..<clientCount ) {
val client: TcpSocket = server.accept() as TcpSocket val client = server.accept() as TcpSocket
handlers += launch { launch {
handleClient(client) handleClient(client)
} }
} }
handlers.joinAll()
} finally { } finally {
server.close() server.close()
} }
} }
val clientJobs: List<Deferred> = (0..<clientCount).map { index -> val replies = (0..<clientCount).map { index ->
val payload = payloadFor(index) val payload = payloadFor(index)
launch { launch {
val socket: TcpSocket = Net.tcpConnect(host, port) as TcpSocket val socket = Net.tcpConnect(host, port) as TcpSocket
try { try {
socket.writeUtf8(payload + "\n") socket.writeUtf8(payload + "\n")
socket.flush() socket.flush()
val reply = socket.readLine() val reply = socket.readLine()
if( reply == null ) { assertEquals("pong: $payload", reply)
"client-eof:$payload" reply
}
else {
assertEquals("pong: $payload", reply)
reply
}
} finally { } finally {
socket.close() socket.close()
} }
} }
} }.joinAll()
val replies = clientJobs.joinAll()
val serverReplies = serverJob.await() as List<Object>
assertEquals(clientCount, replies.size) assertEquals(clientCount, replies.size)
assertEquals(clientCount, serverReplies.size)
assertEquals(replies.toSet, serverReplies.toSet)
val summary = "OK: $clientCount concurrent tcp clients" val summary = "OK: $clientCount concurrent tcp clients"
println(summary) println(summary)

View File

@ -7820,7 +7820,9 @@ class BytecodeCompiler(
scopeSlotRefPosByKey[scopeKey] = ref.pos() scopeSlotRefPosByKey[scopeKey] = ref.pos()
} }
} }
return resolved if (resolved != null) return resolved
localSlotIndexByName[ref.name]?.let { return scopeSlotCount + it }
return null
} }
private fun resolveLocalSlotByRefOrName(ref: LocalSlotRef): Int? { private fun resolveLocalSlotByRefOrName(ref: LocalSlotRef): Int? {
@ -8672,6 +8674,11 @@ class BytecodeCompiler(
collectLoopVarNamesRef(ref.targetRef) collectLoopVarNamesRef(ref.targetRef)
collectLoopVarNamesRef(ref.indexRef) collectLoopVarNamesRef(ref.indexRef)
} }
is RangeRef -> {
ref.left?.let { collectLoopVarNamesRef(it) }
ref.right?.let { collectLoopVarNamesRef(it) }
ref.step?.let { collectLoopVarNamesRef(it) }
}
else -> {} else -> {}
} }
} }
@ -8798,6 +8805,11 @@ class BytecodeCompiler(
collectScopeSlotsRef(ref.targetRef) collectScopeSlotsRef(ref.targetRef)
collectScopeSlotsRef(ref.indexRef) collectScopeSlotsRef(ref.indexRef)
} }
is RangeRef -> {
ref.left?.let { collectScopeSlotsRef(it) }
ref.right?.let { collectScopeSlotsRef(it) }
ref.step?.let { collectScopeSlotsRef(it) }
}
is ClassOperatorRef -> { is ClassOperatorRef -> {
collectScopeSlotsRef(ref.target) collectScopeSlotsRef(ref.target)
} }

View File

@ -199,6 +199,25 @@ class TestCoroutines {
) )
} }
@Test
fun testLaunchCanUseCapturedRangeBoundInForLoop() = runTest {
eval(
"""
val count = 8
val outer = launch {
val jobs = []
for( i in 0..<count ) {
val x = i
jobs += launch { x }
}
jobs.joinAll()
}
assertEquals([0,1,2,3,4,5,6,7], outer.await())
""".trimIndent()
)
}
@Test @Test
fun testFlows() = runTest { fun testFlows() = runTest {
eval(""" eval("""