Skip to content

Commit

Permalink
Preserve data handed to callbacks with pointer_from_objref
Browse files Browse the repository at this point in the history
  • Loading branch information
lassepe committed Apr 26, 2024
1 parent d6fc14e commit 26c1ad1
Showing 1 changed file with 68 additions and 62 deletions.
130 changes: 68 additions & 62 deletions src/C_API.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
###
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -760,77 +770,73 @@ 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

Check warning on line 780 in src/C_API.jl

View check run for this annotation

Codecov / codecov/patch

src/C_API.jl#L780

Added line #L780 was not covered by tests
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
# something to do with the timing of when things need to get freed on the
# 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

Expand Down

0 comments on commit 26c1ad1

Please sign in to comment.