Ran into this with https://wpt.live/scroll-to-text-fragment/redirects.html running locally on macOS. It might require my changes to reproduce, but the gist is that it opens 14 new tabs and then for each of them, does repeated fetches until they are finished loading, with no delays or backoff.
In one run I had locally, the parent WebContent accumulated roughly 21,900 Unix socket/fileport attachments from continuous fetches, and then failed because it couldn't allocate any more.
According to Codex's analysis:
WebContent retains completed response pipes, represented as Unix sockets/fileports, until the corresponding Requests::Request is destroyed by GC.
The lifecycle currently appears to be:
- RequestServer removes completed requests and closes its
RequestPipe side.
RequestClient removes the request from its active-request map.
- But the Fetch
Response retains a RefPtr<Requests::Request>.
- On normal completion,
Requests::Request does not call defer_teardown(), so its ReadStream, notifier, and pipe descriptor remain alive until the response/request is collected.
defer_teardown() currently runs for stopped or transferred requests, but not ordinary successful completion.
The WPT’s immediate recursive polling creates completed Fetch responses far faster than GC notices memory pressure. Because the scarce resource is external fileports rather than heap memory, thousands accumulate.
Ran into this with https://wpt.live/scroll-to-text-fragment/redirects.html running locally on macOS. It might require my changes to reproduce, but the gist is that it opens 14 new tabs and then for each of them, does repeated fetches until they are finished loading, with no delays or backoff.
In one run I had locally, the parent WebContent accumulated roughly 21,900 Unix socket/fileport attachments from continuous fetches, and then failed because it couldn't allocate any more.
According to Codex's analysis: