Skip to content

Commit

Permalink
Improve stacktrace of errors thrown from within a callback (#233)
Browse files Browse the repository at this point in the history
  • Loading branch information
odow authored May 15, 2024
1 parent 596b3a8 commit c7ff4d6
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 5 deletions.
19 changes: 14 additions & 5 deletions src/MOI_wrapper/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ function _internal_callback(tree::Ptr{Cvoid}, info::Ptr{Cvoid})
cb_data.callback_function(cb_data)
catch ex
glp_ios_terminate(tree)
cb_data.exception = ex
cb_data.exception = CapturedException(ex, catch_backtrace())
end
return Cint(0)
end
Expand Down Expand Up @@ -1402,10 +1402,19 @@ function _solve_mip_problem(model::Optimizer)
#
# This next bit is _very_ important! See the note associated with
# set_callback.
if model.callback_data.exception isa InterruptException
model.solver_status = GLP_ESTOP
elseif model.callback_data.exception !== nothing
throw(model.callback_data.exception)
if model.callback_data.exception !== nothing
model.callback_data.exception::CapturedException
inner = model.callback_data.exception.ex
if inner isa InterruptException
model.solver_status = GLP_ESTOP
elseif inner isa MOI.InvalidCallbackUsage
# Special-case throwing InvalidCallbackUsage to preserve
# backwards compatibility. But it does mean that they don't have
# good backtraces...
throw(inner)
else
throw(model.callback_data.exception)
end
end
finally
for (column, lower, upper) in bounds_to_reset
Expand Down
19 changes: 19 additions & 0 deletions test/MOI_callbacks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,25 @@ function test_no_cache_InterruptException()
return
end

function test_no_cache_issue_232()
model = GLPK.Optimizer()
x = MOI.add_variable(model)
MOI.add_constraint(model, x, MOI.ZeroOne())
my_callback(args...) = error("FooBar")
MOI.set(model, GLPK.CallbackFunction(), my_callback)
try
MOI.optimize!(model)
# To ensure that if an error is not thrown from the callback we still
# hit the catch block
@assert false
catch ex
@test ex isa CapturedException
@test occursin("my_callback", "$ex")
@test occursin("FooBar", "$ex")
end
return
end

end

TestCallbacks.runtests()

0 comments on commit c7ff4d6

Please sign in to comment.