Skip to content

AOT: Promise resolve() called via object-property/method access doesn't wake await site from socket 'data' callback #87

Description

@proggeramlug

Environment

  • perry 0.5.97
  • macOS arm64 (darwin 25.4.0)

Problem

When a Promise's resolve function is stored as a property on an object and later invoked via property access (e.g. obj.resolve(val)) from inside a net.Socket 'data' event handler, the outer await never wakes. Calling resolve(val) directly (with the bare captured identifier) works correctly.

Related to #77 (which fixed the setTimeout path), but this variant involves property-access dispatch rather than a captured closure binding — and it still hangs under 0.5.97.

Minimal repro

// repro.ts
import * as net from 'node:net';

async function readOneChunk(host: string, port: number): Promise<number> {
    return new Promise<number>((resolve, reject) => {
        const sock = net.createConnection(host as never, port as never);
        const box = { resolve: resolve };  // store on an object
        sock.on('data', (chunk: Buffer) => {
            console.log('[data] len=' + chunk.length + ' before resolve');
            box.resolve(chunk.length);       // <-- call via property access
            console.log('[data] after resolve');
            sock.destroy();
        });
        sock.on('error', (e: Error) => { reject(e); });
    });
}

async function main() {
    // Any TCP server that sends ≥ 1 chunk works; MySQL's HandshakeV10
    // is a convenient test target.
    const len = await readOneChunk('84.32.98.163', 3306);
    console.log('main: resolved len=' + len);
}
main().catch(e => { console.error('err:', e.message); });
perry compile repro.ts -o /tmp/repro && /tmp/repro
# Observed:
#   [data] len=95 before resolve
#   [data] after resolve
#   (hangs indefinitely — "main: resolved len=..." never prints)
#
# Expected (Node/Bun):
#   [data] len=95 before resolve
#   [data] after resolve
#   main: resolved len=95

The same program with resolve(chunk.length) in place of box.resolve(chunk.length) completes correctly under 0.5.97.

Hit in real code

@perry/mysql's runTextQuery() stores resolve on a PendingQuery struct that hangs off the connection state map; finishResultset() (invoked from the socket 'data' callback) calls pending.resolve(result). Under AOT the driver authenticates correctly and receives the query response, but the await conn.query(...) hangs forever. Under Bun / Node the same code resolves instantly.

I've captured the trace from inside the driver showing resolve() returns normally from the callback, but the awaiting coroutine never resumes:

[onData] packet[0..3] seq=1..4 — all parsed correctly
[finishRS] statusFlags=0x2
[finishRS] before resolve
[finishRS] after resolve
<hang>

Expected

box.resolve(val) from a socket 'data' callback behaves identically to the bare resolve(val) — the awaiting coroutine resumes on the next microtask tick.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugConfirmed defect or regression

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions