--[[
	Instead of doing lots of task.spawns for running on seperate threads	
	use this which is more performant & is better for memory management.
	
	`FreeThread` idea from derived from `Signal`. I want to point how the way
	it operates is the same but is improved.
	
	:::note
	1. Garbage collection is all about references.
	2. Only use this module if you know the enviroments wont always yield (this can severly slow down code)
	:::
]]


local FreeThread: thread?

local function AquireFreeThreadAndCall(fn: (...unknown) -> (), ...)
	local aquiredThread = FreeThread
	FreeThread = nil
	fn(...)
	FreeThread = aquiredThread
end

local function CreateResumptionForFreeThread()
	while true do
		AquireFreeThreadAndCall(coroutine.yield()) 
	end
end

local function Call<T...>(func: (T...) -> (), ...: T...)
	assert(type(func) == 'function', `Function expected, got {typeof(func)}`)

	if not FreeThread then
		FreeThread = task.spawn(CreateResumptionForFreeThread)
	end

	task.spawn(FreeThread :: thread, func, ...)
end

local function All(tbl: {(...unknown) -> ()}, ...: unknown)
	for _, fn in tbl do
		Call(fn, ...)
	end
end

local function List(tbl: {}, func: (key: any, value: any) -> ())
	for k, v in tbl do
		Call(func, k, v)
	end
end

local function Empty()
	FreeThread = nil
end

local class = {
	Empty = Empty,
	Call = Call,
	List = List,
	All = All,
}

return class
