Code works when debugging but not otherwise in repl

I have the following code:

!/usr/bin/env julia                                                                                                                                                                                        

using Revise
using Sockets
using .Threads

function loadRuleset(path::String)::Vector{Function}
    println("Testing path:"*path)
    println("file found: "*path)
    rules = Vector{Function}(undef, 0)
    open(path) do f
	empty!(rules)
        while ! eof(f)
            s = readline(f)
            iv = Meta.parse(s)
            iw = eval(iv)
            push!(rules, iw)
        end
    end
    return rules
end

function applyRules(context::Dict{String,Any}, ruleset::Vector{Function})
    for i=1:length(ruleset)
        ruleset[i](context)
    end
end

function main()
    rules::Vector{Function} = loadRuleset("rules.txt")
    server = listen(8080)
    while true
        conn = accept(server)
        Threads.@spawn begin
            try
                while true
                    line = readline(conn)
                    #empty!(context)                                                                                                                                                                        
                    context = Dict{String, Any}("x" => 0)
                    context["x"] = parse(Int64, line)
                    applyRules(context, rules)
                    result::String = context["result"]
                    write(conn, result*"\n")
                end
            catch err
                print("connection ended with error $err")
            end
        end
    end
end

The rules file contains the following:

function (context::Dict{String, Any}) if context["x"] > 50 context["type"]="type1" else context["type"]="type2" end; end
function (context::Dict{String, Any}) if context["x"] > 75 context["type"]="over75"; end; end;
function (context::Dict{String, Any}) if context["x"] > 50 && context["x"] <= 75 context["type"]="50-75" end; end
function (context::Dict{String, Any}) if context["x"] <= 25 context["type"]="under25"; end; end;
function (context::Dict{String, Any}) if context["x"] > 25 && context["x"] <= 50 context["type"]="25-50" end; end
function (context::Dict{String, Any}) if context["type"] == "over75" context["result"]="hi" end; end
function (context::Dict{String, Any}) if context["type"] == "50-75" context["result"]="hi-low" end; end
function (context::Dict{String, Any}) if context["type"] == "under25" context["result"]="low" end; end
function (context::Dict{String, Any}) if context["type"] == "25-50" 
context["result"]="low-hi" end; end

If I open a repl, and run main() connect a client and send a number between 1 and 100 i get an error similar to:

MethodError(var"#5#6"(), (Dict{String, Any}("x" => 40),), 0x0000000000007aeb)

If I use Debugger.jl and @enter main() instead, the code works as expected and the correct response is returned to the client indicating that all the functions in the list of rules has been executed.
Is anyone able to explain what is happening here and how I can update my code to run correctly outside of the debugger.
(NB if I create a test and push random integers into the rules without trying to involve the socket server then it all works fine. Also I’m using a simple C++ socket client but nc works as well)
Many thanks for your time!

If you remove the @spawn and the try inside main, you’ll get the full stack dump:

MethodError(var"#5#6"(), (Dict{String, Any}("x" => 40),), 0x00000000000067f3)ERROR: TaskFailedException

    nested task error: MethodError: no method matching (::var"#5#6")(::Dict{String, Any})
    The applicable method may be too new: running in world age 26611, while current world is 26620.
    

The problem is that the methods are too new. This will always happen when you create functions with eval and try to use the created functions right away. You should create the methods before calling main, and let main take an argument:

function main(rules) 
...
end

julia> rules = loadRuleset("rules.txt")
julia> main(rules)

I don’t know the internals of the debugger, but it likely does something special so this particular problem does not show up.
Creating functions from text in this way is almost always a bad idea.

1 Like