From 26c1ad1ed18aefed013599f10a1a312770014427 Mon Sep 17 00:00:00 2001 From: lassepe Date: Fri, 26 Apr 2024 21:22:00 +0200 Subject: [PATCH] Preserve data handed to callbacks with pointer_from_objref --- src/C_API.jl | 130 +++++++++++++++++++++++++++------------------------ 1 file changed, 68 insertions(+), 62 deletions(-) diff --git a/src/C_API.jl b/src/C_API.jl index e9da455..95e7def 100644 --- a/src/C_API.jl +++ b/src/C_API.jl @@ -6,6 +6,12 @@ # PATH uses the Float64 value 1e20 to represent +infinity. const INFINITY = 1e20 +const _PRESERVED_CONTEXT = IdDict() +function _preserved_pointer_from_objref(object) + push!(_PRESERVED_CONTEXT, object => true) + return pointer_from_objref(object) +end + ### ### License.h ### @@ -62,7 +68,11 @@ end function OutputInterface(output_data) _C_PRINT = @cfunction(_c_print, Cvoid, (Ptr{Cvoid}, Cint, Ptr{Cchar})) _C_FLUSH = C_NULL # flush argument is optional - return OutputInterface(pointer_from_objref(output_data), _C_PRINT, _C_FLUSH) + return OutputInterface( + _preserved_pointer_from_objref(output_data), + _C_PRINT, + _C_FLUSH, + ) end function c_api_Output_SetInterface(o::OutputInterface) @@ -150,7 +160,7 @@ mutable struct Presolve_Interface function Presolve_Interface(presolve_data::PresolveData) return new( - pointer_from_objref(presolve_data), + _preserved_pointer_from_objref(presolve_data), C_NULL, C_NULL, C_NULL, @@ -363,7 +373,7 @@ mutable struct MCP_Interface end return new( - pointer_from_objref(interface_data), + _preserved_pointer_from_objref(interface_data), _C_PROBLEM_SIZE, _C_BOUNDS, _C_FUNCTION_EVALUATION, @@ -760,70 +770,65 @@ function solve_mcp( @assert length(z) == length(lb) == length(ub) out_io = silent ? IOBuffer() : stdout output_data = OutputData(out_io) - GC.@preserve output_data begin - c_api_Output_SetInterface(OutputInterface(output_data)) + c_api_Output_SetInterface(OutputInterface(output_data)) - n = length(z) - if n == 0 - return MCP_Solved, nothing, nothing - end - if nnz > typemax(Cint) - return MCP_Error, nothing, nothing - end - o = c_api_Options_Create() - c_api_Path_AddOptions(o) - c_api_Options_Default(o) - m = c_api_MCP_Create(n, nnz) - if jacobian_structure_constant - c_api_MCP_Jacobian_Structure_Constant(m, true) - end - if jacobian_data_contiguous - c_api_MCP_Jacobian_Data_Contiguous(m, true) - end - m.id_data = InterfaceData( - Cint(n), - Cint(nnz), - F, - J, - lb, - ub, - z, - variable_names, - constraint_names, - ) - m_interface = MCP_Interface(m.id_data) - c_api_MCP_SetInterface(m, m_interface) - if jacobian_structure_constant && !isempty(jacobian_linear_elements) - m.presolve_data = PresolveData() do nnz, types - for i in jacobian_linear_elements - types[i] = PRESOLVE_LINEAR - end - return + n = length(z) + if n == 0 + return MCP_Solved, nothing, nothing + end + if nnz > typemax(Cint) + return MCP_Error, nothing, nothing + end + o = c_api_Options_Create() + c_api_Path_AddOptions(o) + c_api_Options_Default(o) + m = c_api_MCP_Create(n, nnz) + if jacobian_structure_constant + c_api_MCP_Jacobian_Structure_Constant(m, true) + end + if jacobian_data_contiguous + c_api_MCP_Jacobian_Data_Contiguous(m, true) + end + m.id_data = InterfaceData( + Cint(n), + Cint(nnz), + F, + J, + lb, + ub, + z, + variable_names, + constraint_names, + ) + m_interface = MCP_Interface(m.id_data) + c_api_MCP_SetInterface(m, m_interface) + if jacobian_structure_constant && !isempty(jacobian_linear_elements) + m.presolve_data = PresolveData() do nnz, types + for i in jacobian_linear_elements + types[i] = PRESOLVE_LINEAR end - presolve_interface = Presolve_Interface(m.presolve_data) - c_api_MCP_SetPresolveInterface(m, presolve_interface) + return end - if length(kwargs) > 0 - mktemp() do path, io - println( - io, - "* Automatically generated by PATH.jl. Do not edit.", - ) - for (key, val) in kwargs - println(io, key, " ", val) - end - close(io) - return c_api_Options_Read(o, path) + presolve_interface = Presolve_Interface(m.presolve_data) + c_api_MCP_SetPresolveInterface(m, presolve_interface) + end + if length(kwargs) > 0 + mktemp() do path, io + println(io, "* Automatically generated by PATH.jl. Do not edit.") + for (key, val) in kwargs + println(io, key, " ", val) end + close(io) + return c_api_Options_Read(o, path) end - c_api_Options_Display(o) - info = Information(; - generate_output = generate_output, - use_start = use_start, - use_basics = use_basics, - ) - status = c_api_Path_Solve(m, info) - end # GC.@preserve + end + c_api_Options_Display(o) + info = Information(; + generate_output = generate_output, + use_start = use_start, + use_basics = use_basics, + ) + status = c_api_Path_Solve(m, info) X = c_api_MCP_GetX(m) # TODO(odow): I don't know why, but manually calling MCP_Destroy was # necessary to avoid a segfault on Julia 1.0 when using LUSOL. I guess it's @@ -831,6 +836,7 @@ function solve_mcp( # PATH side? i.e., MCP_Destroy before other things? c_api_MCP_Destroy(m) m.ptr = C_NULL + empty!(_PRESERVED_CONTEXT) return MCP_Termination(status), X, info end