fix recursive queue flush (#28)

* fix recursive queue flush

* remove print from test

* swap order of flush

* simplified test case and remove error

* write more in-depth tests

* change approach flush_update_queue

* double diamond test case

* fix indent

* change naming of function

* subsequent updates do not batch test case

* simplify test case
This commit is contained in:
alicesaidhi 2024-08-20 13:44:56 +02:00 committed by GitHub
parent b85419088c
commit fbe2f01bb9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 219 additions and 16 deletions

View file

@ -6,17 +6,18 @@ local graph = require(script.Parent.graph)
local function batch(setter: () -> ()) local function batch(setter: () -> ())
local already_batching = flags.batch local already_batching = flags.batch
local from
if not already_batching then
flags.batch = true flags.batch = true
from = graph.get_update_queue_length()
end
local ok, err: string? = pcall(setter) local ok, err: string? = pcall(setter)
if not already_batching then if not already_batching then
flags.batch = false flags.batch = false
graph.flush_update_queue(from)
if not already_batching then
graph.flush_update_queue()
end
end end
if not ok then throw(`error occured while batching updates: {err}`) end if not ok then throw(`error occured while batching updates: {err}`) end

View file

@ -184,14 +184,12 @@ local function queue_children_for_update<T>(node: SourceNode<T>)
update_queue.n = i update_queue.n = i
end end
local _flushing = false local function get_update_queue_length()
local function flush_update_queue() return update_queue.n
assert(not _flushing, "recursive queue flush occured") -- todo end
_flushing = true
local n0 = 0 local function flush_update_queue(from: number)
local i = from + 1
local i = n0 + 1
while i <= update_queue.n do while i <= update_queue.n do
local node = update_queue[i] local node = update_queue[i]
--assert(node.effect) --assert(node.effect)
@ -204,9 +202,7 @@ local function flush_update_queue()
i += 1 i += 1
end end
update_queue.n = n0 update_queue.n = from
_flushing = false
end end
local function update_descendants<T>(root: SourceNode<T>) local function update_descendants<T>(root: SourceNode<T>)
@ -286,5 +282,6 @@ return table.freeze {
create_source_node = create_source_node, create_source_node = create_source_node,
get_children = get_children, get_children = get_children,
flush_update_queue = flush_update_queue, flush_update_queue = flush_update_queue,
get_update_queue_length = get_update_queue_length,
scopes = scopes scopes = scopes
} }

View file

@ -1842,6 +1842,7 @@ end))
TEST("batch()", wrap_root(function() TEST("batch()", wrap_root(function()
local source = vide.source local source = vide.source
local derive = vide.derive local derive = vide.derive
local effect = vide.effect
local batch = vide.batch local batch = vide.batch
do CASE "evaluation deferred" do CASE "evaluation deferred"
@ -1920,6 +1921,210 @@ TEST("batch()", wrap_root(function()
CHECK(b2() == 3) CHECK(b2() == 3)
CHECK(b3() == 4) CHECK(b3() == 4)
end end
do CASE "subsequent updates do not batch"
local a = source(0)
local b = source(0)
local c = source(0)
local d_n = 0
effect(function()
b()
c()
d_n += 1
end)
effect(function()
b(a())
c(a())
end)
batch(function()
a(1)
end)
CHECK(d_n == 3)
end
do CASE "recursive queue flush diamond A,B,C,D"
--[[
a > b > d
> c >
]]
local a = source(0)
local b = source(0)
local c = source(0)
local d = source(0)
local count = { b = 0, c = 0, d = 0 }
effect(function()
batch(function()
b(a() % 2 == 0 and 1 or 0)
c(a() * 2)
end)
count.b += 1
count.c += 1
end)
effect(function()
batch(function()
d(b() + c())
end)
count.d += 1
end)
a(1)
CHECK(count.b == 2)
CHECK(count.c == 2)
CHECK(count.d == 2)
CHECK(d() == 2)
a(3)
CHECK(count.b == 3)
CHECK(count.c == 3)
CHECK(count.d == 3)
CHECK(d() == 6)
end
do CASE "recursive queue flush diamond A,B,C,D,E"
--[[
where b and c batches d
a > b > e
> c > d >
]]
local a = source(0)
local b = source(0)
local c = source(0)
local d = source(0)
local e = source(0)
local count = { b = 0, c = 0, d = 0, e = 0 }
effect(function()
batch(function()
b(a() % 2 == 0 and 1 or 0)
c(a() * 2)
end)
count.b += 1
count.c += 1
end)
effect(function()
batch(function()
d(c() * 2)
end)
count.d += 1
end)
effect(function()
batch(function()
e(b() + d())
end)
count.e += 1
end)
CHECK(e() == 1)
a(1)
CHECK(count.b == 2)
CHECK(count.c == 2)
CHECK(count.d == 2)
CHECK(count.e == 3)
CHECK(e() == 4)
a(3)
CHECK(count.b == 3)
CHECK(count.c == 3)
CHECK(count.d == 3)
CHECK(count.e == 4)
CHECK(e() == 12)
end
do CASE "recursive queue flush diamond A,B,C,D,E,F,G"
--[[
a > b > d > E > G
> c ^ > F
]]
local a = source(0)
local b = source(0)
local c = source(0)
local d = source(0)
local e = source(0)
local f = source(0)
local g = source(0)
local count = { b = 0, c = 0, d = 0, e = 0, f = 0, g = 0 }
effect(function()
batch(function()
b(a() % 2 == 0 and 1 or 0)
c(a() * 2)
end)
count.b += 1
count.c += 1
end)
effect(function()
batch(function()
d(b() + c())
end)
count.d += 1
end)
effect(function()
batch(function()
e(d() % 2 == 0 and 1 or 0)
f(d() * 2)
end)
count.e += 1
count.f += 1
end)
effect(function()
batch(function()
g(e() + f())
end)
count.g += 1
end)
a(1)
CHECK(count.b == 2)
CHECK(count.c == 2)
CHECK(count.d == 2)
CHECK(count.e == 2)
CHECK(count.f == 2)
CHECK(count.g == 2)
CHECK(d() == 2)
CHECK(g() == 5)
a(3)
CHECK(count.b == 3)
CHECK(count.c == 3)
CHECK(count.d == 3)
CHECK(count.e == 3)
CHECK(count.f == 3)
CHECK(count.g == 3)
CHECK(d() == 6)
CHECK(g() == 13)
end
end)) end))
TEST("read()", wrap_root(function() TEST("read()", wrap_root(function()