Does :WaitForChild() leak memory in streaming enabled environment?

Does :WaitForChild() leak memory in streaming enabled environment?

Specifically, I have this code:

--- Warps the WaitForChild API with a promise
-- @module promiseChild

local require = require(game:GetService("ReplicatedStorage"):WaitForChild("Nevermore"))

local Promise = require("Promise")

--- Wraps the :WaitForChild API with a promise
return function(parent, name, timeOut)
	local result = parent:FindFirstChild(name)
	if result then
		return Promise.resolved(result)
	end

	return Promise.new(function(resolve, reject)
		-- Cheaper to do spawn() here than fastSpawn, and we aren't going to get the
		-- resource for another tick anyway
		spawn(function()
			local child = parent:WaitForChild(name, timeOut)

			if child then
				resolve(child)
			else
				reject("Timed out")
			end
		end)
	end)
end

This code my never resolve in the case that an object streams out before it exists. Does anyone know if this leaks memory on Roblox?

3 Likes

I don’t really use WaitForChild, but it should just result in nil after it times out, or error after a while if there’s no timeOut.

The ability to disconnect things (like promises) while they’re running makes code really robust, so I’d probably go with .ChildAdded. Maid objects can make this really easy, so I generally avoid yielding or creating creating coroutines unless I’m using an ‘Async’ API.

1 Like

Each call to WaitForChild does two things if the child doesn’t currently exist:

  • Registers a timer delay for the potential infinite yield warning or “resume with nil” timeout
  • Adds the waiting thread to the “threads waiting for children” set on the parent instance.

With no timeout the warning will fire after 5 seconds and be gone. If the thing you’re waiting on never arrives the only thing that “leaks” is the waiting thread set entry and potentially the waiting thread itself. Unless you have a large number of these the overhead should be mild.

2 Likes

How about these situations?

  • Waiting thread is garbage collected (Don’t think this can happen because the WaitForChild() closure keeps it alive
  • You call :Destroy() on the parent (This should GC, right?)
  • The parent you’re listening on streams out (This should also GC, right?)

I’m especially curious about the latter 2, because that means a waiting thread would GC.

1 Like

The waiting thread ref on the parent is a weak reference, so it should be collected in this case.

4 Likes