Skip to content

-sNODERAWSOCKETS DNS resolution, with general readiness#27182

Open
guybedford wants to merge 5 commits into
emscripten-core:mainfrom
guybedford:async-dns
Open

-sNODERAWSOCKETS DNS resolution, with general readiness#27182
guybedford wants to merge 5 commits into
emscripten-core:mainfrom
guybedford:async-dns

Conversation

@guybedford

@guybedford guybedford commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

Async DNS, built on top of #27207 for async readiness. Implementation diff is the last commit - b5af97b.

This adds real DNS resolution under -sNODERAWSOCKETS via node:dns, along with an async syscall variant of getaddrinfo for non-JSPI environments.

getaddrinfo() resolves numeric addresses and /etc/hosts entries (read fresh through emscripten's FS) synchronously, and returns a full addrinfo list. For a real hostname: without JSPI it returns EAI_AGAIN (resolve via the async API); under JSPI it suspends the wasm stack on the node:dns lookup and returns the addresses directly.

Async API (available in all builds):

  • emscripten_dns_lookup_async(node, service, hint) — same inputs as getaddrinfo(), returns a pollable fd that becomes readable when resolution completes.
  • emscripten_dns_lookup_result(fd, struct addrinfo **res) — reads the outcome: 0 on success (writing the addrinfo list to *res, freed with freeaddrinfo), or an EAI_* code.

The original PR in #27162 allowed the async callback for DNS lookup to be registered via emscripten_set_socket_message_callback. Instead of "hijacking" that mechanism, we can now directly use an epoll'able file descriptor to represent the dns lookup operation. On readiness, the actual emscripten_dns_lookup_result() just gets called again.

freeaddrinfo now frees the whole ai_next chain; EAI_AGAIN is added to the generated struct info.

Tested with test_dns_async, test_dns_callback (completion via emscripten_poll_with_callback), test_dns_async_net, test_dns_async_default, and test_dns_jspi, including PROXY_TO_PTHREAD variants.

@guybedford guybedford force-pushed the async-dns branch 2 times, most recently from 5bae5d9 to 917c563 Compare June 25, 2026 19:03
@guybedford guybedford changed the title Add asynchronous DNS resolution (emscripten_dns_lookup_async) Support proper DNS resolution, with readiness Jun 25, 2026
@guybedford guybedford changed the title Support proper DNS resolution, with readiness -sNODERAWSOCKETS DNS resolution, with shared readiness Jun 25, 2026
@guybedford guybedford changed the title -sNODERAWSOCKETS DNS resolution, with shared readiness -sNODERAWSOCKETS DNS resolution, with shared readiness callback Jun 25, 2026
@guybedford guybedford changed the title -sNODERAWSOCKETS DNS resolution, with shared readiness callback -sNODERAWSOCKETS DNS resolution, with general readiness callback Jun 25, 2026
@guybedford guybedford force-pushed the async-dns branch 3 times, most recently from d5328e2 to 4f24376 Compare June 26, 2026 23:29
@guybedford guybedford changed the title -sNODERAWSOCKETS DNS resolution, with general readiness callback -sNODERAWSOCKETS DNS resolution, with general readiness Jun 27, 2026
@guybedford guybedford force-pushed the async-dns branch 4 times, most recently from 77cbb2d to d5e266b Compare June 30, 2026 22:56
Move the readiness wait-queue onto FSNode (addListener/notifyListeners) so
dup'd fds share one queue, and rewrite the suspending poll() path
(readPollfds/writePollfds/pollWait) on top of it. Producers (SOCKFS, PIPEFS)
now notify the node on readiness transitions, and close() wakes waiters with
POLLNVAL.

The stream_ops.poll backend handler signature changes from
poll(stream, timeout) to poll(stream) returning the current readiness mask.
Add epoll_create1/epoll_ctl/epoll_wait/epoll_pwait on the legacy (non-WASMFS)
JS syscall layer, built on the per-inode readiness wait-queue: level- and
edge-triggered modes, EPOLLONESHOT, EPOLLEXCLUSIVE, EPOLLRDHUP, nesting, and
blocking waits under PROXY_TO_PTHREAD, ASYNCIFY, and JSPI.

Also add emscripten_epoll_set_callback (new experimental <emscripten/epoll.h>),
a non-blocking variant that delivers an epoll set's readiness to a JS callback
without ASYNCIFY/JSPI.
Under -sNODERAWSOCKETS, getaddrinfo() previously fabricated fake addresses via
DNS.lookup_name. This adds real resolution backed by node:dns, plus an async
getaddrinfo so names can be resolved without blocking.

getaddrinfo() now resolves numeric addresses and /etc/hosts entries (read fresh
through emscripten's FS) synchronously, and returns a full addrinfo list (one
node per address). For a real hostname:

- without JSPI it returns EAI_AGAIN (no synchronous DNS); resolve it via the
  async API below.
- under JSPI it suspends the wasm stack on the node:dns lookup and returns the
  addresses directly (gated on ASYNCIFY == 2).

The async API (available in all builds):

- emscripten_dns_lookup_async(node, service, hint) takes the same inputs as
  getaddrinfo() and returns a pollable fd that becomes readable when resolution
  completes.
- emscripten_dns_lookup_result(fd, struct addrinfo **res) reads the outcome: 0
  on success (writing the addrinfo list to *res, freed with freeaddrinfo), or an
  EAI_* code on failure.

The completion fd is a plain pollable descriptor: wait on it with
epoll/poll/select.

freeaddrinfo now frees the whole ai_next chain; EAI_AGAIN is added to the
generated struct info.

Tested with test_dns_async, test_dns_callback (completion via
emscripten_poll_with_callback), test_dns_async_net, test_dns_async_default, and
test_dns_jspi, including PROXY_TO_PTHREAD variants.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant