From 679de094952e9589da4c01465e7caa0aa4572a05 Mon Sep 17 00:00:00 2001 From: "Killian Q. Zhuo" Date: Thu, 27 Jan 2022 16:40:47 +0800 Subject: [PATCH] Control flow support (#107) * control flow support * revert the infinite loops * bugfix * Update CI scripts (#111) * Add refs to integration tests (#106) * Add refs to integration tests * Update .github/workflows/IntegrationTest.yml Co-authored-by: Hong Ge * Update IntegrationTest.yml (#109) Co-authored-by: Rik Huijzer * revise types * Update IntegrationTest.yml * Update BenchmarksAndMicroIntegration.yml * only try to eval Symbol and GlobalRef * refactor * fix Turing branch * Update IntegrationTest.yml * minor update * remove used var * code refactor * remove `step_in`, move copying functions * fix perf code * move cache mechanism into TapedFunction's constructor * Update Project.toml Co-authored-by: Hong Ge Co-authored-by: Rik Huijzer --- .../BenchmarksAndMicroIntegration.yml | 2 +- .github/workflows/IntegrationTest.yml | 2 +- Project.toml | 2 +- perf/p0.jl | 8 +- perf/p2.jl | 2 +- src/tapedfunction.jl | 383 +++++++++++------- src/tapedtask.jl | 115 +----- test/ctask.jl | 16 +- test/tarray.jl | 2 +- test/tf.jl | 2 +- 10 files changed, 275 insertions(+), 259 deletions(-) diff --git a/.github/workflows/BenchmarksAndMicroIntegration.yml b/.github/workflows/BenchmarksAndMicroIntegration.yml index a3914a39..1fab6eba 100644 --- a/.github/workflows/BenchmarksAndMicroIntegration.yml +++ b/.github/workflows/BenchmarksAndMicroIntegration.yml @@ -25,7 +25,7 @@ jobs: using Pkg try # force it to use this PR's version of the package - pkg"add Turing#master" # TODO: remove this when Turing is updated + pkg"add Turing#libtask-integration" Pkg.develop(PackageSpec(path=".")) # resolver may fail with main deps Pkg.update() catch err diff --git a/.github/workflows/IntegrationTest.yml b/.github/workflows/IntegrationTest.yml index 00633ce1..f92d67b8 100644 --- a/.github/workflows/IntegrationTest.yml +++ b/.github/workflows/IntegrationTest.yml @@ -15,7 +15,7 @@ jobs: matrix: package: - {user: TuringLang, repo: AdvancedPS.jl, ref: releases} - - {user: TuringLang, repo: Turing.jl, ref: hg/new-libtask2} + - {user: TuringLang, repo: Turing.jl, ref: libtask-integration} steps: - uses: actions/checkout@v2 diff --git a/Project.toml b/Project.toml index 544bc91c..23cdbd91 100644 --- a/Project.toml +++ b/Project.toml @@ -3,7 +3,7 @@ uuid = "6f1fad26-d15e-5dc8-ae53-837a1d7b8c9f" license = "MIT" desc = "Tape based task copying in Turing" repo = "https://github.com/TuringLang/Libtask.jl.git" -version = "0.6.5" +version = "0.6.6" [deps] IRTools = "7869d1d1-7146-5819-86e3-90919afe41df" diff --git a/perf/p0.jl b/perf/p0.jl index 02f20015..c1f1ef00 100644 --- a/perf/p0.jl +++ b/perf/p0.jl @@ -30,8 +30,8 @@ args = m.evaluator[2:end]; t = @btime Libtask.CTask(f, args...) # schedule(t.task) # work fine! # @show Libtask.result(t.tf) -@show "Step in a tape..." -@btime Libtask.step_in(t.tf, args) +@show "Run a tape..." +@btime t.tf(args...) # Case 2: SMC sampler @@ -43,5 +43,5 @@ m = Turing.Core.TracedModel(gdemo(1.5, 2.), Sampler(SMC(50)), VarInfo()); t = @btime Libtask.CTask(m.evaluator[1], m.evaluator[2:end]...); # schedule(t.task) # @show Libtask.result(t.tf.tape) -@show "Step in a tape..." -@btime Libtask.step_in(t.tf, m.evaluator[2:end]) +@show "Run a tape..." +@btime t.tf(m.evaluator[2:end]...) diff --git a/perf/p2.jl b/perf/p2.jl index 5d1a4454..a5a4357d 100644 --- a/perf/p2.jl +++ b/perf/p2.jl @@ -58,6 +58,6 @@ args = m.evaluator[2:end] t = Libtask.CTask(f, args...) -Libtask.step_in(t.tf, args) +t.tf(args...) @show Libtask.result(t.tf) diff --git a/src/tapedfunction.jl b/src/tapedfunction.jl index a9043ce2..7f7c8c8b 100644 --- a/src/tapedfunction.jl +++ b/src/tapedfunction.jl @@ -1,3 +1,7 @@ +mutable struct Box{T} + val::T +end + abstract type AbstractInstruction end abstract type Taped end const RawTape = Vector{AbstractInstruction} @@ -7,34 +11,78 @@ const RawTape = Vector{AbstractInstruction} An `Instruction` stands for a function call """ -mutable struct Instruction{F, T<:Taped} <: AbstractInstruction +mutable struct Instruction{F, TI<:Tuple, TO, T<:Taped} <: AbstractInstruction func::F - input::Tuple - output + input::TI + output::Box{TO} + tape::T +end + +mutable struct BlockInstruction{TA, T<:Taped} <: AbstractInstruction + block_id::Int + args::Vector{TA} + tape::T +end + +mutable struct BranchInstruction{TA, T<:Taped} <: AbstractInstruction + condition::Box{Any} + block_id::Int + args::Vector{TA} tape::T end +mutable struct ReturnInstruction{TA, T<:Taped} <: AbstractInstruction + arg::TA + tape::T +end + +const TRCache = LRU{Any, Any}(maxsize=10) + mutable struct TapedFunction{F} <: Taped - func::F # maybe a function or a callable obejct + func::F # maybe a function, a constructor, or a callable obejct arity::Int - ir::Union{Nothing, IRTools.IR} + ir::IRTools.IR tape::RawTape counter::Int + # map from BlockInstruction.block_id to its index on tape + block_map::Dict{Int, Int} + retval owner - function TapedFunction(f::F; arity::Int=-1) where {F} - new{F}(f, arity, nothing, RawTape(), 1, nothing) - end -end -mutable struct Box{T} - val::T + function TapedFunction(f::F, args...; cache=false, init=true) where {F} + cache_key = (f, typeof.(args)...) + + if cache && haskey(TRCache, cache_key) # use cache + cached_tf = TRCache[cache_key] + tf = copy(cached_tf) + tf.counter = 1 + return tf + end + + tf = new{F}() # leave some fields to be undef + tf.func = f + tf.tape = RawTape() + tf.counter = 1 + tf.block_map = Dict{Int, Int}() + + if init # init + tf.arity = length(args) + ir = IRTools.@code_ir tf.func(args...) + tf.ir = ir + translate!(tf, ir) + # set cache + TRCache[cache_key] = tf + end + return tf + end end ## methods for Box val(x) = x val(x::Box) = x.val -val(x::TapedFunction) = x.func val(x::GlobalRef) = getproperty(x.mod, x.name) +val(x::QuoteNode) = eval(x) +val(x::TapedFunction) = x.func box(x) = Box(x) box(x::Box) = x Base.show(io::IO, box::Box) = print(io, "Box(", box.val, ")") @@ -43,39 +91,20 @@ Base.show(io::IO, box::Box) = print(io, "Box(", box.val, ")") MacroTools.@forward TapedFunction.tape Base.iterate, Base.length MacroTools.@forward TapedFunction.tape Base.push!, Base.getindex, Base.lastindex -result(t::RawTape) = isempty(t) ? nothing : val(t[end].output) -result(t::TapedFunction) = result(t.tape) +result(t::TapedFunction) = t.retval -function increase_counter!(t::TapedFunction) - t.counter > length(t) && return - # instr = t[t.counter] - t.counter += 1 - return t -end - -function reset!(tf::TapedFunction, ir::IRTools.IR, tape::RawTape) - tf.ir = ir - tf.tape = tape - return tf -end +function (tf::TapedFunction)(args...; callback=nothing) + # run the raw tape + if(tf.counter <= 1 && length(args) > 0) + input = map(Box{Any}, args) + tf.tape[1].input = input + end -function (tf::TapedFunction)(args...) - if isempty(tf.tape) - ir = IRTools.@code_ir tf.func(args...) - ir = intercept(ir; recorder=:track!) - tf.ir = ir - tf.tape = RawTape() - tf2 = IRTools.evalir(ir, tf, args...) - @assert tf === tf2 - else - # run the raw tape - if length(args) > 0 - input = map(box, args) - tf.tape[1].input = input - end - for instruction in tf.tape - instruction() - end + while true + ins = tf[tf.counter] + ins() + callback !== nothing && callback() + isa(ins, ReturnInstruction) && break end return result(tf) end @@ -112,7 +141,7 @@ function Base.show(io::IO, tp::RawTape) end ## methods for Instruction -Base.show(io::IO, instruction::AbstractInstruction) = print(io, "A ", typeof(instruction)) +Base.show(io::IO, instruction::AbstractInstruction) = println(io, "A ", typeof(instruction)) function Base.show(io::IO, instruction::Instruction) func = instruction.func @@ -120,11 +149,24 @@ function Base.show(io::IO, instruction::Instruction) println(io, "Instruction($(func)$(map(val, instruction.input)), tape=$(objectid(tape)))") end +function Base.show(io::IO, instruction::BlockInstruction) + id = instruction.block_id + tape = instruction.tape + println(io, "BlockInstruction($(id)->$(map(val, instruction.args)), tape=$(objectid(tape)))") +end + +function Base.show(io::IO, instruction::BranchInstruction) + tape = instruction.tape + println(io, "BranchInstruction($(val(instruction.condition)), tape=$(objectid(tape)))") +end + function (instr::Instruction{F})() where F # catch run-time exceptions / errors. try - output = instr.func(map(val, instr.input)...) + func = val(instr.func) + output = func(map(val, instr.input)...) instr.output.val = output + instr.tape.counter += 1 catch e println(e, catch_backtrace()); rethrow(e); @@ -138,124 +180,183 @@ function (instr::Instruction{typeof(_new)})() expr = Expr(:new, map(val, instr.input)...) output = eval(expr) instr.output.val = output + instr.tape.counter += 1 catch e println(e, catch_backtrace()); rethrow(e); end end -## internal functions +function (instr::BlockInstruction)() + instr.tape.counter += 1 +end -function track!(tape::Taped, f, args...) - f = val(f) # f maybe a Boxed closure - output = try - box(f(map(val, args)...)) - catch e - @warn e - Box{Any}(nothing) +function (instr::BranchInstruction)() + if instr.condition === nothing || !val(instr.condition) # unless + target = instr.block_id + target_idx = instr.tape.block_map[target] + blk_ins = instr.tape[target_idx] + @assert isa(blk_ins, BlockInstruction) + @assert length(instr.args) == length(blk_ins.args) + for i in 1:length(instr.args) + blk_ins.args[i].val = val(instr.args[i]) + end + instr.tape.counter = target_idx + else + instr.tape.counter += 1 end - ins = Instruction(f, args, output, tape) - push!(tape, ins) - return output end -function track!(tape::Taped, ::typeof(_new), args...) - output = try - expr = Expr(:new, map(val, args)...) - box(eval(expr)) - catch e - @warn e - Box{Any}(nothing) +function (instr::ReturnInstruction)() + instr.tape.retval = val(instr.arg) +end + + +## internal functions + +arg_boxer(var, boxes::Dict{IRTools.Variable, Box{Any}}) = var +arg_boxer(var::IRTools.Variable, boxes::Dict{IRTools.Variable, Box{Any}}) = + get!(boxes, var, Box{Any}(nothing)) +function args_initializer(ins::BlockInstruction) + return (args...) -> begin + @assert length(args) + 1 == length(ins.args) + ins.args[1].val = ins.tape.func + for i in 1:length(args) + ins.args[i + 1].val = val(args[i]) # fill the boxes + end end - ins = Instruction(_new, args, output, tape) - push!(tape, ins) - return output end -function unbox_condition(ir) - for blk in IRTools.blocks(ir) - vars = keys(blk) - brs = IRTools.branches(blk) - for (i, br) in enumerate(brs) - IRTools.isconditional(br) || continue - cond = br.condition - new_cond = IRTools.push!( - blk, - IRTools.xcall(@__MODULE__, :val, cond)) - brs[i] = IRTools.Branch(br; condition=new_cond) +function translate!(taped::Taped, ir::IRTools.IR) + tape = taped.tape + boxes = Dict{IRTools.Variable, Box{Any}}() + _box = (x) -> arg_boxer(x, boxes) + for (blk_id, blk) in enumerate(IRTools.blocks(ir)) + # blocks + blk_args = IRTools.arguments(blk) + push!(tape, BlockInstruction(blk_id, map(_box, blk_args), taped)) + # `+ 1` because we will have an extra ins at the beginning + taped.block_map[blk_id] = length(tape) + 1 + # normal instructions + for (x, st) in blk + if Meta.isexpr(st.expr, :call) + args = map(_box, st.expr.args) + # args[1] is the function + f = args[1] + ins = Instruction(f, args[2:end] |> Tuple, + _box(x), taped) + push!(tape, ins) + elseif Meta.isexpr(st.expr, :new) + args = map(_box, st.expr.args) + ins = Instruction(_new, args |> Tuple, _box(x), taped) + push!(tape, ins) + elseif isa(st.expr, GlobalRef) + ins = Instruction(val, (st.expr,), _box(x), taped) + push!(tape, ins) + else + @error "Unknown IR code: " st + end + end + + # branches (including `return`) + for br in IRTools.branches(blk) + if br.condition === nothing && br.block == 0 # a return + ins = ReturnInstruction(_box(br.args[1]), taped) + push!(tape, ins) + else + cond = br.condition === nothing ? false : _box(br.condition) + isa(cond, Bool) && (cond = Box{Any}(cond)) # unify the condiftion type + ins = BranchInstruction( + cond, br.block, map(_box, br.args), taped) + push!(tape, ins) + end end end + init_ins = Instruction(args_initializer(tape[1]), tuple(tape[1].args[2:end]...), + Box{Any}(nothing), taped) + insert!(tape, 1, init_ins) end -box_args() = nothing -box_args(x) = x -box_args(args...) = args -function _replace_args(args, pairs::Dict) - map(args) do x - haskey(pairs, x) ? pairs[x] : x +## copy Box, RawTape, and TapedFunction + +""" + tape_copy(x) + +Function `tape_copy` is used to copy data while copying a TapedTask, the +default behavior is: 1. for `Array` and `Dict`, we do `deepcopy`; 2. for +other data types, we do not copy and share the data between tasks, i.e., +`tape_copy(x) = x`. If one wants some kinds of data to be copied, or +deeply copied, one can add a method to this function. +""" +function tape_copy end +tape_copy(x) = x +# tape_copy(x::Array) = deepcopy(x) +# tape_copy(x::Dict) = deepcopy(x) + +function copy_box(old_box::Box{T}, roster::Dict{UInt64, Any}) where T + oid = objectid(old_box) + haskey(roster, oid) && (return roster[oid]) + + # We don't know the type of box.val now, so we use Box{Any} + new_box = Box{T}(tape_copy(old_box.val)) + roster[oid] = new_box + return new_box +end +copy_box(o, roster::Dict{UInt64, Any}) = o + +function Base.copy(x::Instruction, on_tape::Taped, roster::Dict{UInt64, Any}) + # func may also be a boxed variable + func = copy_box(x.func, roster) + input = map(x.input) do ob + copy_box(ob, roster) end + output = copy_box(x.output, roster) + Instruction(func, input, output, on_tape) end -function intercept(ir; recorder=:track!) - ir == nothing && return - # we use tf instead of the original function as the first argument - # get the TapedFunction - tape = pushfirst!(ir, IRTools.xcall(Base, :identity, IRTools.arguments(ir)[1])) - - # box the args - first_blk = IRTools.blocks(ir)[1] - args = IRTools.arguments(first_blk) - arity = length(args) - 1 - arg_pairs= Dict() - - args_var = args[1] - if arity == 0 - args_var = IRTools.insertafter!(ir, tape, IRTools.xcall(@__MODULE__, :box_args)) - elseif arity == 1 - args_var = IRTools.insertafter!(ir, tape, IRTools.xcall(@__MODULE__, :box_args, args[2])) - arg_pairs = Dict(args[2] => args_var) - else # arity > 1 - args_var = IRTools.insertafter!(ir, tape, IRTools.xcall(@__MODULE__, :box_args, args[2:end]...)) - args_new, last_pos = [], args_var - - iter_state = [] - - for i in 1:arity - last_pos = IRTools.insertafter!(ir, last_pos, IRTools.xcall(Base, :indexed_iterate, args_var, i, iter_state...)) - args_iter = last_pos - last_pos = IRTools.insertafter!(ir, last_pos, IRTools.xcall(Core, :getfield, args_iter, 1)) - push!(args_new, last_pos) - if i != arity - last_pos = IRTools.insertafter!(ir, last_pos, IRTools.xcall(Core, :getfield, args_iter, 2)) - iter_state = [last_pos] - end - end - arg_pairs = Dict(zip(args[2:end], args_new)) +function Base.copy(x::BlockInstruction, on_tape::Taped, roster::Dict{UInt64, Any}) + args = map(x.args) do ob + copy_box(ob, roster) end + BlockInstruction(x.block_id, args, on_tape) +end - # here we assumed the ir only has a return statement at its last block, - # and we make sure the return value is from a function call (to `identity`) - last_blk = IRTools.blocks(ir)[end] - retv = IRTools.returnvalue(last_blk) - IRTools.return!(last_blk, IRTools.xcall(Base, :identity, retv)) - - for (x, st) in ir - x == tape && continue - if Meta.isexpr(st.expr, :call) - new_args = (x == args_var) ? st.expr.args : _replace_args(st.expr.args, arg_pairs) - ir[x] = IRTools.xcall(@__MODULE__, recorder, tape, new_args...) - elseif Meta.isexpr(st.expr, :new) - args = st.expr.args - ir[x] = IRTools.xcall(@__MODULE__, recorder, tape, _new, args...) - elseif isa(st.expr, GlobalRef) - ir[x] = IRTools.xcall(@__MODULE__, recorder, tape, val, st.expr) - else - @warn "Unknown IR code: " st - end +function Base.copy(x::BranchInstruction, on_tape::Taped, roster::Dict{UInt64, Any}) + cond = copy_box(x.condition, roster) + args = map(x.args) do ob + copy_box(ob, roster) end - # the real return value will be in the last instruction on the tape - IRTools.return!(ir, tape) - unbox_condition(ir) - return ir + + BranchInstruction(cond, x.block_id, args, on_tape) +end + +function Base.copy(x::ReturnInstruction, on_tape::Taped, roster::Dict{UInt64, Any}) + arg = copy_box(x.arg, roster) + ReturnInstruction(arg, on_tape) +end + +function Base.copy(old_tape::RawTape, on_tape::Taped, roster::Dict{UInt64, Any}) + new_tape = RawTape(undef, length(old_tape)) + for (i, x) in enumerate(old_tape) + new_ins = copy(x, on_tape, roster) + new_tape[i] = new_ins + end + + init_ins = Instruction(args_initializer(new_tape[2]), tuple(new_tape[2].args[2:end]...), + Box{Any}(nothing), on_tape) + new_tape[1] = init_ins + return new_tape +end + +function Base.copy(tf::TapedFunction) + # create a new uninitialized TapedFunction + new_tf = TapedFunction(tf.func; cache=false, init=false) + new_tf.block_map = tf.block_map + new_tf.ir = tf.ir + roster = Dict{UInt64, Any}() + new_tape = copy(tf.tape, new_tf, roster) + new_tf.tape = new_tape + new_tf.counter = tf.counter + return new_tf end diff --git a/src/tapedtask.jl b/src/tapedtask.jl index cb7799fb..e1e387a9 100644 --- a/src/tapedtask.jl +++ b/src/tapedtask.jl @@ -16,26 +16,19 @@ struct TapedTask end end -const TRCache = LRU{Any, Any}(maxsize=10) - function TapedTask(tf::TapedFunction, args...) - tf.owner !== nothing && error("TapedFunction is owned by another task.") - if isempty(tf.tape) - cache_key = (tf.func, typeof.(args)...) - if haskey(TRCache, cache_key) - ir, tape = TRCache[cache_key] - # Here we don't need change the initial arguments of the tape, - # it will be set when we `step_in` to the tape. - reset!(tf, ir, copy(tape, tf, Dict{UInt64, Any}(); start=1)) - else - tf(args...) - TRCache[cache_key] = (tf.ir, tf.tape) - end - end produce_ch = Channel() consume_ch = Channel{Int}() task = @task try - step_in(tf, args) + producer = () -> begin + ttask = current_task().storage[:tapedtask] + if length(ttask.produced_val) > 0 + val = pop!(ttask.produced_val) + put!(ttask.produce_ch, val) + take!(ttask.consume_ch) # wait for next consumer + end + end + tf(args...; callback=producer) catch e bt = catch_backtrace() put!(produce_ch, TapedTaskException(e, bt)) @@ -59,35 +52,15 @@ function TapedTask(tf::TapedFunction, args...) return t end -# Issue: evaluating model without a trace, see +# NOTE: evaluating model without a trace, see # https://github.com/TuringLang/Turing.jl/pull/1757#diff-8d16dd13c316055e55f300cd24294bb2f73f46cbcb5a481f8936ff56939da7ceR329 -TapedTask(f, args...) = TapedTask(TapedFunction(f, arity=length(args)), args...) -TapedTask(t::TapedTask, args...) = TapedTask(func(t), args...) -func(t::TapedTask) = t.tf.func - -function step_in(tf::TapedFunction, args) - len = length(tf) - if(tf.counter <= 1 && length(args) > 0) - input = map(box, args) - tf[1].input = input - end - while tf.counter <= len - tf[tf.counter]() - # produce and wait after an instruction is done - ttask = tf.owner - if length(ttask.produced_val) > 0 - val = pop!(ttask.produced_val) - put!(ttask.produce_ch, val) - take!(ttask.consume_ch) # wait for next consumer - end - increase_counter!(tf) - end +function TapedTask(f, args...) + tf = TapedFunction(f, args...; cache=true, init=true) + TapedTask(tf, args...) end -function next_step!(t::TapedTask) - increase_counter!(t.tf) - return t -end +TapedTask(t::TapedTask, args...) = TapedTask(func(t), args...) +func(t::TapedTask) = t.tf.func #= # ** Approach (A) to implement `produce`: @@ -177,68 +150,10 @@ Base.IteratorEltype(::Type{TapedTask}) = Base.EltypeUnknown() # copy the task -""" - tape_copy(x) - -Function `tape_copy` is used to copy data while copying a TapedTask, the -default behavior is: 1. for `Array` and `Dict`, we do `deepcopy`; 2. for -other data types, we do not copy and share the data between tasks, i.e., -`tape_copy(x) = x`. If one wants some kinds of data to be copied, or -deeply copied, one can add a method to this function. -""" -function tape_copy end -tape_copy(x) = x -# tape_copy(x::Array) = deepcopy(x) -# tape_copy(x::Dict) = deepcopy(x) - -function copy_box(old_box::Box{T}, roster::Dict{UInt64, Any}) where T - oid = objectid(old_box) - haskey(roster, oid) && (return roster[oid]) - - # We don't know the type of box.val now, so we use Box{Any} - new_box = Box{T}(tape_copy(old_box.val)) - roster[oid] = new_box - return new_box -end -copy_box(o, roster::Dict{UInt64, Any}) = o - -function Base.copy(x::Instruction, on_tape::Taped, roster::Dict{UInt64, Any}) - input = map(x.input) do ob - copy_box(ob, roster) - end - output = copy_box(x.output, roster) - Instruction(x.func, input, output, on_tape) -end - -function Base.copy(t::RawTape, on_tape::Taped, roster::Dict{UInt64, Any}; start::Int=1) - old_data = t - len = length(old_data) - start + 1 - new_data = RawTape(undef, len) - - for (i, x) in enumerate(old_data[start:end]) - new_ins = copy(x, on_tape, roster) - new_data[i] = new_ins - end - - return new_data -end - -function Base.copy(tf::TapedFunction) - new_tf = TapedFunction(tf.func; arity=tf.arity) - new_tf.ir = tf.ir - roster = Dict{UInt64, Any}() - new_tape = copy(tf.tape, new_tf, roster; start=tf.counter) - new_tf.tape = new_tape - new_tf.counter = 1 - return new_tf -end - function Base.copy(t::TapedTask) - # t.counter[] <= 1 && error("Can't copy a TapedTask which is not running.") tf = copy(t.tf) new_t = TapedTask(tf) new_t.task.storage = copy(t.task.storage) new_t.task.storage[:tapedtask] = new_t - next_step!(new_t) return new_t end diff --git a/test/ctask.jl b/test/ctask.jl index 87b9011b..625b1b01 100644 --- a/test/ctask.jl +++ b/test/ctask.jl @@ -3,7 +3,7 @@ @testset "stack allocated objects" begin function f() t = 0 - while t < 4 + while true produce(t) t = 1 + t end @@ -23,7 +23,7 @@ @testset "heap allocated objects" begin function f() t = [0 1 2] - for _ in 1:10 + while true produce(t[1]) t[1] = 1 + t[1] end @@ -44,7 +44,7 @@ @testset "iteration" begin function f() t = 1 - for _ in 1:12 + while true produce(t) t = 1 + t end @@ -73,7 +73,7 @@ @testset "method error" begin function f() t = 0 - for _ in 1:3 + while true t[3] = 1 produce(t) t = t + 1 @@ -94,7 +94,7 @@ @testset "error test" begin function f() x = 1 - for _ in 1:3 + while true error("error test") produce(x) x += 1 @@ -115,7 +115,7 @@ @testset "OutOfBounds Test Before" begin function f() x = zeros(2) - for _ in 1:3 + while true x[1] = 1 x[2] = 2 x[3] = 3 @@ -137,7 +137,7 @@ @testset "OutOfBounds Test After `produce`" begin function f() x = zeros(2) - for _ in 1:3 + while true x[1] = 1 x[2] = 2 produce(x[2]) @@ -160,7 +160,7 @@ @testset "OutOfBounds Test After `copy`" begin function f() x = zeros(2) - for _ in 1:3 + while true x[1] = 1 x[2] = 2 produce(x[2]) diff --git a/test/tarray.jl b/test/tarray.jl index 7f1c3fb3..7f3eca30 100644 --- a/test/tarray.jl +++ b/test/tarray.jl @@ -123,7 +123,7 @@ function f() t = TArray(Int, 1) t[1] = 0 - for _ in 1:10 + while true produce(t[1]) t[1] t[1] = 1 + t[1] diff --git a/test/tf.jl b/test/tf.jl index 8a3822ac..1fccd7c8 100644 --- a/test/tf.jl +++ b/test/tf.jl @@ -8,7 +8,7 @@ using Libtask S(x, y) = new(x + y) end - tf = Libtask.TapedFunction(S) + tf = Libtask.TapedFunction(S, 1, 2) s1 = tf(1, 2) @test s1.i == 3 newins = findall(x -> isa(x, Libtask.Instruction{typeof(Libtask._new)}), tf.tape)