Basically how can I make it so ‘new’ outputs after ‘cur’ WITHOUT using some form of wait()? Its fine if the new thread is the last one executed in the current scheduler cycle (as opposed to running immediately when the current thread yields) but I do not want it to wait until the next cycle starts before running (as spawn does)
Currently resuming a thread will always push it to the front to execute and wait for it to yield before continuing with the rest of the task scheduler’s tasks (which in most cases is desired)
My specific use case is that I have a signal module which whenever some sort of change is made fires its own Changed remote (this definition is not recursive though xd)
In my network module I wait for the remote’s corresponding signal to be Bind-ed or Wait-ed to before unloading the accumulated data (Roblox does this internally too but their queue size is 250, I ran into a scenario where I needed more so I had to make my own queue)
In my Signal module’s Wait, Changed is fired BEFORE the thread yields, causing the Network to unload the queue without actually resuming the Wait
If I could instead first yield and then call Changed everything would work perfectly
Here’s the relevant code snippets:
In Signal.Wait:
if sig.Changed then
sig.Changed('Wait',t)
end
return coroutine.yield()
In Network:
r.Changed:Bind(function(status)
if status=='Bind'then
while q.N>0 do
fire(r,unpack(q:Pop()))
end
elseif status=='Wait'and q.N>0 then
fire(r,unpack(q:Pop()))
end
end)
Of course this could be solved with a wait() but that is messy so I’d like to avoid it
If it were still supported, would we be guaranteed the gc is aggressive enough to catch it in the next sleep cycle? (Sleep cycle might be the wrong term here)
local event = {}
event.__index = event
function event.new()
return setmetatable({Connections = {}, Waiting = {}}, event)
end
function event:Fire(...)
if #self.Connections > 0 or #self.Waiting > 0 then
if self.Queue then
local queue = self.Queue
self.Queue = nil
for _, request in pairs(queue) do
self:Fire(unpack(request))
end
end
for connection in pairs(self.Connections) do
coroutine.resume(connection, ...)
end
for connection in pairs(self.Waiting) do
self.Wait[connection] = nil
coroutine.resume(connection, ...)
end
else
self.Queue = self.Queue or {}
table.insert(self.Queue, {...})
end
end
function event:Connect(callback)
coroutine.wrap(function()
local thread = coroutine.running()
self.Connections[thread] = true
while self.Connections[thread] do
coroutine.wrap(callback)(coroutine.yield())
end
end)()
end
function event:Wait()
local thread = coroutine.running()
self.Waiting[thread] = true
return coroutine.yield()
end
function event:Disconnect()
self.Connections = {}
self:Fire()
self.Waiting = {}
end
Your :Wait() does not resume immediately if there is an item in the queue, but I guess this could be easily solved with an if statement there
But wow; I can’t believe I missed this, I guess it’s because I didn’t think about implementing it inside of the signal: for me my signal didn’t hold a queue, I added that functionality in my network module while still using the old signal methods
function event:SolveQueue()
local queue = self.Queue
self.Queue = nil
for i=#queue, 1, -1 do
self:Fire(unpack(queue[i]))
end
end
function event:Fire(...)
if #self.Connections > 0 or #self.Waiting > 0 then
if self.Queue then
self:SolveQueue()
end
for connection in pairs(self.Connections) do
coroutine.resume(connection, ...)
end
for connection in pairs(self.Waiting) do
self.Wait[connection] = nil
coroutine.resume(connection, ...)
end
else
self.Queue = self.Queue or {}
table.insert(self.Queue, {...})
end
end
function event:Connect(callback)
coroutine.wrap(function()
local thread = coroutine.running()
self.Connections[thread] = true
while self.Connections[thread] do
coroutine.wrap(callback)(coroutine.yield())
end
end)()
self:SolveQueue()
end
function event:Wait()
if self.Queue then
return unpack(table.remove(self.Queue))
else
local thread = coroutine.running()
self.Waiting[thread] = true
return coroutine.yield()
end
end