If this is a server script:
and it has this ModuleScript as its child:
Then the output will be
The ‘after’ is never printed, while it should be.
If anyone happens to know a workaround, please let me know.
The require function doesn’t support yields like this, but if you want to yield immediately upon requiring a module, you could put the logic into a function and call it right after requiring, like so:
module = require(script.ModuleScript)
module.init = function()
Why does wait work?
|Where did the coroutine come from
||Can I use
coroutine.yield/resume on it
|Can I use
|It's a "ROBLOX thread" (from
delay/spawn/event:connect()/a script's main thread/etc)
*Maybe, but treat the coroutine as being a “ROBLOX thread” from then on
The core problem is that ROBLOX internally uses (the C side equivalent of)
coroutine.create/yield/resume to implement its “threads”. Therefore when you use coroutine functions on ROBLOX threads, you interfere with what the engine does to its coroutines. And this works sometimes, but I wouldn’t rely on it. You might break assumptions the engine makes about the states its threads are in (like in this case), and even if you get it working, engine changes could make it break on you.
wait works because it’s coded to play nice with ROBLOX threads, including in a ModuleScript. But you manually yielding the ModuleScript’s thread probably causes the engine to lose track of that thread, meaning scripts waiting on the ModuleScript’s initialisation aren’t woken.
If you must yield in this context, you need to use a ROBLOX yielding function (like
event:wait() on a BindableEvent).
Are you sure this is true? I use
coroutine.yield in event connections after [Live] Changes to coroutine.yield.
CC @woot3 (I cc them in all coroutine/Roblox thread scheduler related posts)
My knowledge might be dated.
I think that change might actually be the problem here, kind of. Seems like calling coroutine.yield() now causes the task scheduler to relinquish control of the thread, and that leaves the ModuleScript in limbo.
(obviously, it still wouldn’t have done what OP wanted before the change).
So maybe this can be fixed. But things like this are why I recommend avoiding it.
So strangely this works… I thought that after this change [Live] Changes to coroutine.yield there became no difference between Roblox threads and coroutine threads
On the 10th of September I enabled a change which allowed you to call
coroutine.yield in any sort of context, essentially making a user-owned thread and Roblox thread synonymous.
coroutine.yield is not at fault.
The problem is related to continuations, an internal mechanism used to pass information between a thread and the Roblox scheduler. In this specific case, the continuation is used to resume all threads requiring the module.
coroutine.resume does not support continuations as it exists outside of the Roblox scheduler. Therefore, the script which called
require will never resume.
You can see this in action with the following example (note how
coroutine.yield is not called).
If the thread is resumed by the Roblox scheduler, continuations will run.
I have every intention of fixing this at some point in the future, however it’s not as trivial as it may seem. By rerouting
coroutine.resume to use the same resume mechanism as other threads, there is a significant performance impact which I would like to avoid.
Will you implement a temporary fix(that makes coroutine.resume slower)? Or how long will it be until the fix is live? Thanks
At present I am not working at Roblox due to my studies therefore I am unable to give you a timescale in which this will be fixed. Another member of staff could pick this up before I return. I can definitely ping somebody about this, however I would not expect a fix in the near future.
A short-term fix which makes
coroutine.resume slower is unlikely to happen due to the significant impact it would have on performance.
For now, you might be better off just using a
Are bindableevents slower than coroutines?
Not with regards to any metric worth caring about (instantiating a bindable event, connecting it, and firing it takes microseconds more than a coroutine).
Then why is using their implementation for coroutines such a worry : /
Or why dont they use coroutine’s buggy one :((
I believe you mean the Roblox implementation of
resume. @Kampfkarren is saying that performance is unlikely to be a concern for you, however that does not mean the Roblox engine should not be performant.
require would fail 100% of the time.
The slowdown you get from using a
BindableEvent is comparable to that of the temporary fix for
coroutine.resume. It seems reasonable, that until a performant fix is shipped, to use a
BindableEvent as an alternative.
I will be using bindableevents because thats the only way for me to continue scripting in my framework(and working on Roblox at all for that matter)
But this means everywhere i use coroutine resume i have to use bindableevents
I very much appreciate the Roblox engine being performant but I just don’t understand why buggy performant code was chosen over stable code…and why nothing will be done about this either (until months when you can return to fix it :/)
At some point I am going to put together an alternative to the
coroutine library which handles all of this for you.
Before the change to
coroutine.yield this bug was less prevalent as there was no real reason to resume a thread with continuations. We definitely appreciate that stable code is a priority, however if we’re going to change it then we should just do it right the first time.
Thanks, I just have a couple of questions about it. Will it be a part of the Roblox engine or an auxiliary lua module? Also is this the only coroutine related bug it would solve or are there others I should be aware of? I’ve been having incomplete stack traces in my games too, would this be fixed? (I don’t have a repro atm but I suspect it’s to do with coroutine resume because I never did replicate it when trying to with coroutine wrap)
I guess the issue isn’t important enough/doesn’t affect enough people to necessitate a temporary fix
The library will be a Lua module, as a temporary alternative to the
Unfortunately I do not have a copy of the source code to hand to answer those questions. I believe there are some other related bugs this may fix, I am not certain if stack traces is one of them.
It’d be nice if this could be revisited
the problem for me is I can’t think how to avoid memory leaks when using BindableEvents to replace coroutines because even
repeat wait()until not t
leaks in lua
Sorry to revive this topic, but the new task library solves this problem. You should use task.spawn ( Takes a thread or function and resumes it immediately through the engine’s scheduler.) instead of coroutine.resume and it will work.
New version of your code (only changed the ModuleScript):