<br>Hello,<br><br> I'm having trouble using the foreign function interface. The<br>bug I have is strange, as I can only get it to occur in the context<br>of a larger program. I have spent some time simplifying the code
<br>in the hope that someone may explain why I am getting regular <br>segmentation faults.<br> <br> I wanted to use a C program called CFSQP, a nonlinear optimization<br>package. Roughly, you give the program a function pointer representing
<br>the objective function and a function pointer representing the<br>constraints and some bounds, and CFSQP will return the optimum value<br>of the function satisfying the constraints within those bounds.<br><br> I implemented bindings for CFSQP in OCaml with little trouble,
<br>and thus have confidence that in principle the SML-CFSQP connection<br>should work. The problem I face with MLton is that the functions<br>need to take arrays of doubles. Since these must be allocated<br>in the ML heap, I'm not sure how to create such arrays in C.
<br>The solution I came up with is to allocate a global array for<br>the arguments in ML, and to set the values from C, finally calling<br>the SML function on the global array. <br><br> I kept getting segfaults though, so I simplified the code until
<br>it no longer does anything except modify that global argument array.<br>I figure it's simple enough now for someone to see my mistake.<br><br>structure Cfsqp :> CFSQP =<br>struct <br><br> structure A = Array<br>
<br> type opt = {obj_fun : real array -> real,<br> constr_fun : int * real array -> real,<br> num_constrs : int,<br> lower_bnds : real array,<br> upper_bnds : real array}
<br><br> (* Here is the global array *)<br> val function_args : real array ref = ref (A.array(0,0.0))<br> (* Here is how we set the values from C *)<br> val mlton_set_arg = _export "mlton_set_arg" : (int * real -> unit) -> unit;
<br> fun set_fun (i,x) = A.update(!function_args,i,x)<br><br> (* The imported C function *)<br> val cfsqp_minimize = _import "cfsqp_minimize" : int -> unit;<br><br> (* Calling it once *)<br> fun minimize_once {obj_fun,constr_fun,num_constrs,lower_bnds,upper_bnds} =
<br> let<br> val num_args = A.length lower_bnds<br> val () = function_args := A.array(num_args,0.0)<br> val _ = mlton_set_arg set_fun (* I tried this line both outside and inside this function *)
<br> val () = print "minimizing sml...\n"<br> val () = cfsqp_minimize num_args (* segfault is always here, after the C function returns successfully *)<br> val () = print "done minimizing sml...\n"
<br> in<br> (0.0,!function_args)<br> end<br><br> (* Calling it lots of times exposes the segfault, usually around <br> the 100th iteration *)<br> fun minimize x : real * real array =<br> let<br>
val repeat = 10000<br> in<br> A.sub (A.tabulate(repeat,(fn _ => minimize_once x)),0)<br> end<br><br>end<br><br>Here is the C code:<br><br><br>#include "stdio.h"<br>// the name of the exported header from MLton -stop ...
<br>#include "cfsqp-sml.h"<br><br>// copy a bunch of 0s to the MLton global real array<br>void copy_array_to_mlton_heap(int numargs)<br>{<br> int i;<br> for(i=0;i<numargs;i++){<br> mlton_set_arg(i,0.0);<br>
}<br> return;<br>}<br><br>// do that 10 times, to expose the segfault<br>void cfsqp_minimize(Int32 numargs) <br>{<br> printf("minimizing C...\n");<br> int i;<br> for(i=1;i<=10;i++){<br> copy_array_to_mlton_heap(numargs);
<br> }<br> printf("done minimizing C...\n");<br> return;<br>}<br><br>My only guess as to what may be happening is that<br>at some point the garbage collector is moving the<br>[function_args] ref or the actual array. That
<br>would make the call to [mlton_set_arg] mess things<br>up if [mlton_set_arg] didn't get updated with the<br>new address. But it seems that the garbage collector<br>should inform, at least [set_fun], and thus this<br>
doesn't seem possible. <br><br>Any ideas?<br><br>Thanks,<br><br>Sean<br><br>