That doesn’t satisfy the
No, I think it should work fine since you’re calling wait().
Okay, but then you can just make multiple generators and keep track of when they were started. That’s just how I would do it, although I guess there are multiple ways to skin a cat. In general, I feel that it’s good practice to avoid a large number of concurrent loops, although ROBLOX’s task scheduler takes care of it all in the end.
local c = coroutine.create(function()
wait(5)
end)
print(coroutine.status(c))
coroutine.resume(c)
print(coroutine.status(c))
wait(5)
print(coroutine.status(c))
For first two it returns suspended and last it returns dead, when would it ever return running?
Also I think I was misusing wrap in some of my code above where I should have been using create (to get the thread) and then resume (instead of calling the function)
What do you mean by multiple generators though?
I’m having a hard time understanding how your code would scale without turning into my stepper with one universal loop example
If you don’t care about errors propagating from the coroutine to the main thread, then coroutine.wrap is fine; otherwise, use coroutine.create/resume, which calls the function in protected mode (like pcall).
As for when it would return running, presumably the only time that would happen is if you’re calling coroutine.status from inside that coroutine.
Well, I don’t see why there’s a problem with one universal loop. You could store the threads in a more specific manner if it suits you. For example, you could collect a list of threads into another thread which operates on their combined output, then pass that to a “universal loop”.
I assume the reason there’s not already a coroutine.kill method is that nobody really ever uses coroutines in that way.
I just don’t think it’d ever be a good idea to give every single thread its own loop using wait(). Personally I want more control over things like what order my threads run in, and how I handle their combined output. Also, if I only have one loop, then I can control the refresh rate with only one variable. I feel like a general coroutine.kill method should never really be necessary, but do correct me if I’m wrong.
Coroutine “killing” is generally a bad idea. Depending on the implementation the coroutine could be doing anything at the moment it’s “killed”. Not to mention state and gc cleanup that also needs to happen (which I’m pretty sure simply yielding it will cause it to memleak). You’re better off having a failsafe inside the coroutine to jump out of the loop and let it die on its own.
Are non-yielding blocks of code split up by the task scheduler?
This message says it will be auto gc-ed:
http://lua-users.org/lists/lua-l/2005-08/msg00552.html
Roblox uses their own thread scheduler so I can’t guarantee anything.
O ok
What about first question though?
Again, I’m not sure how they handle task scheduling. They shouldn’t behave that way, but it’s entirely possible they might.
Here’s a way I used to kill a thread:
local function foo()
while true do
wait(0.1)
print("working")
end
end
local function kill(thread, f)
local env = getfenv(f)
function env:__index(k)
if type(env[k]) == "function" and coroutine.running() == thread then
return function()
coroutine.yield()
error("Killed " .. tostring(thread), 0)
end
else
return env[k]
end
end
setfenv(f, setmetatable({}, env))
coroutine.resume(thread)
end
local thread = coroutine.create(foo)
coroutine.resume(thread)
print(coroutine.status(thread)) --> suspended
kill(thread, foo)
wait(0.1)
print(coroutine.status(thread)) --> dead
You should then be able to build your own manager and check if the thread running is blacklisted with a dictionary, instead of just coroutine.running() == thread
.
What hapens when you don’t reference a global within the thread though?
E.g: add
local print = print
local wait = wait
to the beginning of the script (I always do it for speed)
It’s a workaround to inject the killing code, it won’t kill if you don’t reference global, that’s why you should have it’s environment set by your coroutine manager, or implement a function that does the same job within the function.
Since it requires referencing a global why would I want to use it over one of the previous solutions?
Your code would either crash or die if it doesn’t reference a global anywhere, most functions don’t localize everything, the thread can only be killed when it’s running, if you don’t want to add checks while it’s running, your coroutine manager should set the environment before it localizes.
What do you mean by set the environment before it localizes?
The stack is different from the static globals
this is kind of late but i thought it was very interesting
after experimenting in this post
i noticed that script:Destroy() basically acts like coroutine.kill()~it GCs and everything
so is this the ultimate coroutine.kill()? and could it possibly be implemented into Roblox then since script:Destroy() already does it?
here is the test place I used: corouitnekill.rbxl (14.2 KB)
@AMD_chan I know this is very late lol but I also noticed that if you replace the wait(5)
in the kill script with coroutine.yield()
instead, the GC cleans up table t before the script is destroyed (since the script main thread has no references and isn’t running it gets GCed)
I remember playing around with this behavior a while back and facing some issues regarding the consistency at which the Lua threads get killed on :Destroy()
; back then simply parenting the script to nil
would disconnect all its connections and mess with some other stuff while leaving its main thread running, so I wouldn’t rely on the behavior.
if you can remember some of the things you did to break this behavior, I’d very much like to see
Edit: If you :Destroy() and set to nil inside of the script, the script will still run, but I think thats a special case because the (or a) script thread is still running
Edit 2: if a modulescript is destroyed from an outside script then the code will still run : (
I think this could help you. @FieryEvent already said this but I think you were looking for this though.
shared.FunTables = {} shared.CreateKillableFun = function(foo) local index = tostring(foo)..#shared.FunTables local thread = coroutine.create(foo) coroutine.resume(thread) shared.FunTables[index] = {foo,thread} return index,shared.FunTables[index][1] end shared.kill = function(Index, f) local env = getfenv(f) local thread = shared.FunTables[Index][2] function env:__index(k) if type(env[k]) == "function" and coroutine.running() == thread then return function() coroutine.yield() end else return env[k] end end setfenv(f, setmetatable({}, env)) coroutine.resume(thread) end-
this can go in the second script im guessing you can tell how to use it
local index,Funck = shared.CreateKillableFun(function() print("On") wait(1) print("Stopped?") end) wait(.1) shared.kill(index, Funck)
It lets you stop a running function from outside the function.
Hi guys, I used the code offer by @FieryEvent (very thanks) to kill all the events “wait()” en LocalScript, but crash the memory , What can I do to not crashing…?
local function foo()
while true do
wait(0.1)
print("working")
end
end
local function kill(thread, f)
local env = getfenv(f)
function env:__index(k)
if type(env[k]) == "function" and coroutine.running() == thread then
return function()
coroutine.yield()
error("Killed " .. tostring(thread), 0)
end
else
return env[k]
end
end
setfenv(f, setmetatable({}, env))
coroutine.resume(thread)
end
local thread = coroutine.create(foo)
coroutine.resume(thread)
print(coroutine.status(thread)) --> suspended
kill(thread, foo)
wait(0.1)
print(coroutine.status(thread)) --> dead
My LocalScript execute 2 animations, and the crashing occurs when press the key “x” several times:
UIS.InputBegan:Connect(function(tecla,isTyping)
if tecla.KeyCode == Enum.KeyCode.X then
function kill(thread, f)
local env = getfenv(f)
function env:__index(k)
if type(env[k]) == "function" and coroutine.running() == thread then
return function()
coroutine.yield()
print("Killed " .. tostring(thread), 0)
end
else
return env[k]
end
end
setfenv(f, setmetatable({}, env))
coroutine.resume(thread)
end
giro = true
function animacion()
solte = false
Track1.Stopped:wait()
if solte == false then
Track2 = game.Players.LocalPlayer.Character.Humanoid:LoadAnimation(script.Holding)
Track2:Play()
print("play track2")
--********************************Inicio Remolino*******************************
local function remolino()
local remolino = script.remolino:Clone()
print("Crea remolino")
remolino.Parent = Character.UpperTorso
remolino.CFrame = Character.UpperTorso.CFrame * CFrame.Angles(0.4,0,0) --porque? se crea chueco??
remolino.Transparency = 1
wait(0.5)
remolino.Transparency = 0.5
local remolino = Character.UpperTorso:FindFirstChild("remolino")
spawn(function()
print("antes de girar")
while giro do
print("girando")
remolino.CFrame = remolino.CFrame * CFrame.Angles(0,0.5,0)
wait()
end
end)
spawn(function()
while UIS:IsKeyDown(Enum.KeyCode.X) do
print("apareciendo")
remolino.Transparency -= 0.05
wait()
end
end)
end
--********************************Fin Remolino**********************************
--********************************Inicio Aura*******************************
local function aura()
local aura = game.ReplicatedStorage:WaitForChild("aura"):Clone()
print("aura creada", aura)
local weld = Instance.new("Weld")
weld.Parent = Character.HumanoidRootPart
weld.Part0 = Character.HumanoidRootPart
aura.CanCollide = false
aura.Anchored = false
aura.Parent = Character.HumanoidRootPart
weld.Part1 = Character.HumanoidRootPart.aura
end
--********************************Fin Aura**********************************
--********************************Inicio ShakeCamera**********************************
local function shake()
wait(0.7)
while UIS:IsKeyDown(Enum.KeyCode.X) do
local x = math.random(-10,20)/50
local y = math.random(-10,20)/50
local z = math.random(-10,20)/50
Hum.CameraOffset = Vector3.new(x,y,z)
wait()
end
Hum.CameraOffset = Vector3.new(0,0,0)
end
--********************************Fin ShakeCamera**********************************
spawn(aura)
spawn(remolino)
spawn(shake)
end
end
local thread = coroutine.create(animacion)
coroutine.resume(thread)
local con
con = tecla.Changed:Connect(function(prop)
if prop == "UserInputState" then
if Track2 then
Track2:Stop()
print("1 detuve Track2",Track2)
end
kill(thread, animacion)
end
end)
end
end)
UIS.InputEnded:Connect(function(tecla,isTyping)
if tecla.KeyCode == Enum.KeyCode.X then
solte = true
print("solte",solte)
if Track2 then
Track2:Stop()
print("2 detuve Track2",Track2)
end
--******************************** Inicio Desvanece y borra Remolino**************************************
local remolino = Character.UpperTorso:FindFirstChild("remolino")
if remolino then
spawn(function()
while true do
remolino.Transparency += 0.1
wait()
end
end)
wait(1)
remolino:Destroy()
giro = false
--******************************** Fin Desvanece y borra Remolino**************************************
--******************************** Inicio borra Aura**************************************
if Character.HumanoidRootPart:FindFirstChild("aura") then
Character.HumanoidRootPart.aura:Destroy()
Character.HumanoidRootPart.Weld:Destroy()
end
--******************************** Fin borra Aura**************************************
end
end
end)