From 752c0482c3f201dc37ffba3340e7758ec50fe9db Mon Sep 17 00:00:00 2001 From: Kirill Ignatiev Date: Fri, 22 Jan 2021 09:44:25 +0000 Subject: [PATCH 1/7] Fix #156, exceptions from FORCED_STOP not rethrown --- src/NLopt.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NLopt.jl b/src/NLopt.jl index 5705edd..ce1bccb 100644 --- a/src/NLopt.jl +++ b/src/NLopt.jl @@ -219,7 +219,7 @@ function chk(o::Opt, result::Result) e = nlopt_exception if e !== nothing && !isa(e, ForcedStop) nlopt_exception = nothing - rethrow(e) + throw(e) end else error("nlopt failure $result", _errmsg(o)) @@ -623,7 +623,7 @@ function optimize!(o::Opt, x::Vector{Cdouble}) ret = ccall((:nlopt_optimize,libnlopt), Result, (_Opt, Ptr{Cdouble}, Ptr{Cdouble}), o, x, opt_f) - ret == INVALID_ARGS && chk(o, ret) + chk(o, ret) return (opt_f[1], x, Symbol(ret)) end From a33c1fa9e291d20106abffc7f473b111e62712ae Mon Sep 17 00:00:00 2001 From: Kirill Ignatiev Date: Sat, 23 Jan 2021 06:30:02 +0000 Subject: [PATCH 2/7] Clear saved exception correctly --- src/NLopt.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NLopt.jl b/src/NLopt.jl index ce1bccb..672c30d 100644 --- a/src/NLopt.jl +++ b/src/NLopt.jl @@ -217,8 +217,8 @@ function chk(o::Opt, result::Result) elseif result == FORCED_STOP global nlopt_exception e = nlopt_exception + nlopt_exception = nothing if e !== nothing && !isa(e, ForcedStop) - nlopt_exception = nothing throw(e) end else From 1674571ac4703d4e0ae1ac7a30e5c1fb159f9ba0 Mon Sep 17 00:00:00 2001 From: Kirill Ignatiev Date: Sat, 23 Jan 2021 06:31:46 +0000 Subject: [PATCH 3/7] Add exception wrapper for rethrowing exceptions with backtraces --- src/NLopt.jl | 16 ++++++++++++++-- test/exceptions.jl | 37 +++++++++++++++++++++++++++++++++++++ test/runtests.jl | 1 + 3 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 test/exceptions.jl diff --git a/src/NLopt.jl b/src/NLopt.jl index 672c30d..16e7f95 100644 --- a/src/NLopt.jl +++ b/src/NLopt.jl @@ -197,6 +197,18 @@ struct ForcedStop <: Exception end # cache current exception for forced stop nlopt_exception = nothing +struct SavedException{E} <: Exception + e :: E + bt :: Vector +end + +import Base: show +function Base.show(io::IO, e::SavedException) + show(io, e.e) + println(io, "\nOriginal stack trace:") + Base.show_backtrace(io, e.bt) +end + function errmsg(o::Opt) msg = ccall((:nlopt_get_errmsg,libnlopt), Ptr{UInt8}, (_Opt,), o) return msg == C_NULL ? nothing : unsafe_string(msg) @@ -394,7 +406,7 @@ function nlopt_callback_wrapper(n::Cuint, x::Ptr{Cdouble}, return res::Cdouble catch e global nlopt_exception - nlopt_exception = e + nlopt_exception = nlopt_exception = isa(e, ForcedStop) ? e : SavedException(e, catch_backtrace()) force_stop!(d.o::Opt) return 0.0 # ignored by nlopt end @@ -454,7 +466,7 @@ function nlopt_vcallback_wrapper(m::Cuint, res::Ptr{Cdouble}, : unsafe_wrap(Array, grad, (convert(Int, n),convert(Int, m)))) catch e global nlopt_exception - nlopt_exception = e + nlopt_exception = isa(e, ForcedStop) ? e : SavedException(e, catch_backtrace()) force_stop!(d.o::Opt) end nothing diff --git a/test/exceptions.jl b/test/exceptions.jl new file mode 100644 index 0000000..39031d7 --- /dev/null +++ b/test/exceptions.jl @@ -0,0 +1,37 @@ +using NLopt, Test + +# Issue #156 +let + f(x, g=[]) = (error("test error"); x[1]^2) + opt = Opt(:LN_SBPLX, 1) + opt.min_objective = f + @test_throws NLopt.SavedException{ErrorException} optimize(opt, [0.1234]) + @test NLopt.nlopt_exception === nothing + try + optimize(opt, [0.1234]) + catch e + io = IOBuffer() + show(io, e) + # Check that the backtrace is being printed + @test length(String(take!(io))) > 100 + end +end + +let + f(x, g=[]) = (throw(NLopt.ForcedStop()); x[1]^2) + opt = Opt(:LN_SBPLX, 1) + opt.min_objective = f + fmin, xmin, ret = optimize(opt, [0.1234]) + @test ret == :FORCED_STOP + @test NLopt.nlopt_exception === nothing +end + +# check nlopt_exception is cleared correctly +let + f(x, g=[]) = (x[1]^2) + opt = Opt(:LN_SBPLX, 1) + opt.min_objective = f + fmin, xmin, ret = optimize(opt, [0.1234]) + @test ret ∈ (:SUCCESS, :FTOL_REACHED, :XTOL_REACHED) + @test NLopt.nlopt_exception === nothing +end diff --git a/test/runtests.jl b/test/runtests.jl index 0b0c614..06a9eb0 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -6,6 +6,7 @@ include("tutorial.jl") include("fix133.jl") include("MOI_wrapper.jl") +include("exceptions.jl") using NLopt using Test From 536a9859ea2006534ca891f660f2eedb4065fd46 Mon Sep 17 00:00:00 2001 From: odow Date: Wed, 25 Jan 2023 14:57:36 +1300 Subject: [PATCH 4/7] Rethrow errors instead of returning FORCED_STOP --- src/NLopt.jl | 35 ++++++++++++++++++++++++----------- test/exceptions.jl | 37 ------------------------------------- test/runtests.jl | 33 ++++++++++++++++++++++++++++++++- 3 files changed, 56 insertions(+), 49 deletions(-) delete mode 100644 test/exceptions.jl diff --git a/src/NLopt.jl b/src/NLopt.jl index 16e7f95..750e71b 100644 --- a/src/NLopt.jl +++ b/src/NLopt.jl @@ -197,16 +197,23 @@ struct ForcedStop <: Exception end # cache current exception for forced stop nlopt_exception = nothing -struct SavedException{E} <: Exception - e :: E - bt :: Vector +""" + SavedException{E,B} <: Exception + +This exception wraps trapped exceptions during the optimize call, along with +their backtrace, so that they can be rethrown once we exit optimize. + +It isn't sufficient just to re-throw because this will lose the backtrace +information. +""" +struct SavedException{E,B} <: Exception + error::E + backtrace::Vector{B} end -import Base: show function Base.show(io::IO, e::SavedException) - show(io, e.e) - println(io, "\nOriginal stack trace:") - Base.show_backtrace(io, e.bt) + show(io, e.error) + return Base.show_backtrace(io, e.backtrace) end function errmsg(o::Opt) @@ -405,8 +412,11 @@ function nlopt_callback_wrapper(n::Cuint, x::Ptr{Cdouble}, : unsafe_wrap(Array, grad, (convert(Int, n),)))) return res::Cdouble catch e - global nlopt_exception - nlopt_exception = nlopt_exception = isa(e, ForcedStop) ? e : SavedException(e, catch_backtrace()) + if e isa ForcedStop + global nlopt_exception = e + else + global nlopt_exception = SavedException(e, catch_backtrace()) + end force_stop!(d.o::Opt) return 0.0 # ignored by nlopt end @@ -465,8 +475,11 @@ function nlopt_vcallback_wrapper(m::Cuint, res::Ptr{Cdouble}, grad == C_NULL ? empty_jac : unsafe_wrap(Array, grad, (convert(Int, n),convert(Int, m)))) catch e - global nlopt_exception - nlopt_exception = isa(e, ForcedStop) ? e : SavedException(e, catch_backtrace()) + if e isa ForcedStop + global nlopt_exception = e + else + global nlopt_exception = SavedException(e, catch_backtrace()) + end force_stop!(d.o::Opt) end nothing diff --git a/test/exceptions.jl b/test/exceptions.jl deleted file mode 100644 index 39031d7..0000000 --- a/test/exceptions.jl +++ /dev/null @@ -1,37 +0,0 @@ -using NLopt, Test - -# Issue #156 -let - f(x, g=[]) = (error("test error"); x[1]^2) - opt = Opt(:LN_SBPLX, 1) - opt.min_objective = f - @test_throws NLopt.SavedException{ErrorException} optimize(opt, [0.1234]) - @test NLopt.nlopt_exception === nothing - try - optimize(opt, [0.1234]) - catch e - io = IOBuffer() - show(io, e) - # Check that the backtrace is being printed - @test length(String(take!(io))) > 100 - end -end - -let - f(x, g=[]) = (throw(NLopt.ForcedStop()); x[1]^2) - opt = Opt(:LN_SBPLX, 1) - opt.min_objective = f - fmin, xmin, ret = optimize(opt, [0.1234]) - @test ret == :FORCED_STOP - @test NLopt.nlopt_exception === nothing -end - -# check nlopt_exception is cleared correctly -let - f(x, g=[]) = (x[1]^2) - opt = Opt(:LN_SBPLX, 1) - opt.min_objective = f - fmin, xmin, ret = optimize(opt, [0.1234]) - @test ret ∈ (:SUCCESS, :FTOL_REACHED, :XTOL_REACHED) - @test NLopt.nlopt_exception === nothing -end diff --git a/test/runtests.jl b/test/runtests.jl index 06a9eb0..2a86d30 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -6,7 +6,6 @@ include("tutorial.jl") include("fix133.jl") include("MOI_wrapper.jl") -include("exceptions.jl") using NLopt using Test @@ -27,3 +26,35 @@ end ) @test_throws err opt.initial_step end + +@testset "Fix #156" begin + @testset "Test that SavedException is thrown" begin + f(x, g=[]) = (error("test error"); x[1]^2) + opt = Opt(:LN_SBPLX, 1) + opt.min_objective = f + @test_throws NLopt.SavedException{ErrorException} optimize(opt, [0.1234]) + @test NLopt.nlopt_exception === nothing + try + optimize(opt, [0.1234]) + catch e + # Check that the backtrace is being printed + @test length(sprint(show, e)) > 100 + end + end + @testset "Test that ForcedStop does not rethrow" begin + f(x, g=[]) = (throw(NLopt.ForcedStop()); x[1]^2) + opt = Opt(:LN_SBPLX, 1) + opt.min_objective = f + fmin, xmin, ret = optimize(opt, [0.1234]) + @test ret == :FORCED_STOP + @test NLopt.nlopt_exception === nothing + end + @testset "Test that no error works correctly" begin + f(x, g=[]) = (x[1]^2) + opt = Opt(:LN_SBPLX, 1) + opt.min_objective = f + fmin, xmin, ret = optimize(opt, [0.1234]) + @test ret ∈ (:SUCCESS, :FTOL_REACHED, :XTOL_REACHED) + @test NLopt.nlopt_exception === nothing + end +end From 0f2208d4377d8249ca253774d0a041fabc883d63 Mon Sep 17 00:00:00 2001 From: odow Date: Wed, 25 Jan 2023 15:43:26 +1300 Subject: [PATCH 5/7] Fix typo --- ext/NLoptMathOptInterfaceExt.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/NLoptMathOptInterfaceExt.jl b/ext/NLoptMathOptInterfaceExt.jl index dc6e1b8..3c01e77 100644 --- a/ext/NLoptMathOptInterfaceExt.jl +++ b/ext/NLoptMathOptInterfaceExt.jl @@ -659,7 +659,7 @@ end function fill_gradient!(grad, x, var::MOI.VariableIndex) fill!(grad, 0.0) grad[var.value] = 1.0 - return retur + return end function fill_gradient!(grad, x, aff::MOI.ScalarAffineFunction{Float64}) From 1a1c191c1224d41dfea5b821bc8265820ba6051e Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 15 Jan 2024 14:09:02 +1300 Subject: [PATCH 6/7] Use CapturedException --- src/NLopt.jl | 23 ++--------------------- test/runtests.jl | 4 ++-- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/src/NLopt.jl b/src/NLopt.jl index 750e71b..2092300 100644 --- a/src/NLopt.jl +++ b/src/NLopt.jl @@ -197,25 +197,6 @@ struct ForcedStop <: Exception end # cache current exception for forced stop nlopt_exception = nothing -""" - SavedException{E,B} <: Exception - -This exception wraps trapped exceptions during the optimize call, along with -their backtrace, so that they can be rethrown once we exit optimize. - -It isn't sufficient just to re-throw because this will lose the backtrace -information. -""" -struct SavedException{E,B} <: Exception - error::E - backtrace::Vector{B} -end - -function Base.show(io::IO, e::SavedException) - show(io, e.error) - return Base.show_backtrace(io, e.backtrace) -end - function errmsg(o::Opt) msg = ccall((:nlopt_get_errmsg,libnlopt), Ptr{UInt8}, (_Opt,), o) return msg == C_NULL ? nothing : unsafe_string(msg) @@ -415,7 +396,7 @@ function nlopt_callback_wrapper(n::Cuint, x::Ptr{Cdouble}, if e isa ForcedStop global nlopt_exception = e else - global nlopt_exception = SavedException(e, catch_backtrace()) + global nlopt_exception = CapturedException(e, catch_backtrace()) end force_stop!(d.o::Opt) return 0.0 # ignored by nlopt @@ -478,7 +459,7 @@ function nlopt_vcallback_wrapper(m::Cuint, res::Ptr{Cdouble}, if e isa ForcedStop global nlopt_exception = e else - global nlopt_exception = SavedException(e, catch_backtrace()) + global nlopt_exception = CapturedException(e, catch_backtrace()) end force_stop!(d.o::Opt) end diff --git a/test/runtests.jl b/test/runtests.jl index 2a86d30..bae7953 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -28,11 +28,11 @@ end end @testset "Fix #156" begin - @testset "Test that SavedException is thrown" begin + @testset "Test that CapturedException is thrown" begin f(x, g=[]) = (error("test error"); x[1]^2) opt = Opt(:LN_SBPLX, 1) opt.min_objective = f - @test_throws NLopt.SavedException{ErrorException} optimize(opt, [0.1234]) + @test_throws CapturedException{ErrorException} optimize(opt, [0.1234]) @test NLopt.nlopt_exception === nothing try optimize(opt, [0.1234]) From 22ae5b4430f772b42318db1f99f9a48592dfebc0 Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 15 Jan 2024 14:22:18 +1300 Subject: [PATCH 7/7] Update --- test/runtests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index bae7953..0371327 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -32,7 +32,7 @@ end f(x, g=[]) = (error("test error"); x[1]^2) opt = Opt(:LN_SBPLX, 1) opt.min_objective = f - @test_throws CapturedException{ErrorException} optimize(opt, [0.1234]) + @test_throws CapturedException optimize(opt, [0.1234]) @test NLopt.nlopt_exception === nothing try optimize(opt, [0.1234])