Question About coroutine.running()

I have this module script with a bunch of coroutines in it for this lets say npc system controlling multiple objects in one script.

This grand loop doesn’t go forever. A stop and go sort of system. Turning loops off when they are not needed type of thing. The only thing I can’t turn off is the coroutines. I can halt all functions and actions in them but I read even then the coroutine is still taking up memory? Even though in theory it’s dead. In application it’s really dead? And not doing anything?

All my options require turning my functions into threads. I do not want to turn everything into task functions as the system just won’t work as smoothly I fear.

I read this and tried it and I just don’t know if it’s actually stopping anything.

Code Example

My question is what is this thread generated exactly and does it have any tie to the coroutines in my module? I adapted this method into my function that turns everything off. I see no difference in how it is performing. Also the threads are always random, never the same thread? Are the threads even being canceled whether or not they are connected to my coroutines?

I also want to add, if I remove task,defer the thread cannot be canceled. Based on reading what defer does this in theory is perfect. At the end of the coroutine cycle do something…(task.cancel, thread) but is it actually canceling the thread. What is this mysterious thread that is never the same? Is it a chain reaction. I assume threads are randomly generated in terms of the ID but it’s actually the same thing(x). My mind is confused.
EDIT****
I just did a test with task.spawn(func) and the output says cannot resume dead coroutine. So I’m so confused. I have no code manually closing the coroutines but the engine says it’s dead…IS IT?

I don’t think I understood this well
but you can close a coroutine directly without task.defer ???

local cor = coroutine.create(function()
	while true do
		print("Running")
		task.wait()
	end
end)

coroutine.resume(cor)

coroutine.close(cor)

print(coroutine.status(cor))

If it wasn’t in a module script yes I could do this.

Whenever I use any of those methods in a module script I get an error that says “cor” needs to be a thread, not a function.

Also wondering is this even important given if I turn the operations done in the coroutines off and try to task.spawn the coroutines. The output says that the engine considers them dead, but are they?

1 Like

I’ll mark my own comment as a solution.

I can’t find the post anymore but I saw a thread last night that clearly said if you don’t manually close a coroutine it’ll still take up memory or something overtime and cause memory leaks. The more I look into coroutines I see a lot of conflicting information. I was up all night maybe I was hallucinating things idk. I did dream about a solution of made up code that has no application to real life so maybe I was dreaming about this aforementioned post.

I was up all night researching because of that one conflicting post, maybe I misread idk.

I have slept and woken up. From what I can gather you don’t need to manually close coroutines. As long as whatever is happening inside them is stopped the coroutine will naturally be collected by the garbage service. courutine.running() is essentially useless from what i can gather as there are no topics on it really. If you are trying to understand it save yourself the headache and move on to other methods.

TBH, coroutine.running() has it’s very niche use cases. It returns the thread that the code is currently running in. So you can do pretty funky stuff like completely custom binds and wait events. So I thought I could give a niche way of using it:

local yielding = {} :: { thread }
local function yieldForSomething()
	-- // The current thread, that this code is running in, into the yielding thread array
	table.insert(yielding, coroutine.running())

	return coroutine.yield()
end

-- // Some script
-- blah
-- blah
-- // Just so you can run this yourself, if you wanted
task.spawn(function()
	-- // So now this current thread is yielding for a result
	local x, y = yieldForSomething()
	print(x, y) -- Prints 1, 2
end)

-- // Some executer, where it runs the yielding threads
for i = #yielding, 1, -1 do
	local thread = yielding[i]
	task.spawn(thread, 1, 2)
	yielding[i] = nil
end

So this is really just a completely custom method of a bindable event when doing bindable.Event:Wait(). Which, if you wanted to not create an instance for something small, this could work. You can also use this method of waiting for async tasks when finishing (outside of modules like promises).

The link to the code you sent, I’ve personally used this to short out a thread that I did not want running anymore, and was too lazy to just short return everything, or have an immense amount of if not success then return end’s. As example, a conditional wait:

local function conditionalWait(duration: number)
	local currentThread = coroutine.running()
	task.delay(duration, function()
		if not false then
			return task.cancel(currentThread)
		end
		task.spawn(currentThread)
	end)
	return coroutine.yield()
end

conditionalWait(4)
print("Hello") -- // Won't print because the condition inside the wait failed

Presumably, all this stuff in the thread just goes into GC, I haven’t studied up on that enough to tell you for sure.

Now, I won’t write the code for this next one, but you can have an incremental coroutine.yield()
that will run over and over inside of a function, and you can put the coroutine.running() inside of a specific list which returns updated values or something which will incrementally change. But, I don’t particularly know a use cases for this atm.

So that’s my mini ted-talk on coroutine.running(). It’s not something very special, but can be useful or more optimized if you know how or when to use it over other alternative methods.

3 Likes