[MLton-user] ffi newbie

Sean McLaughlin seanmcl at gmail.com
Wed Sep 5 11:17:20 PDT 2007


Hello,

  I'm having trouble using the foreign function interface.  The
bug I have is strange, as I can only get it to occur in the context
of a larger program.  I have spent some time simplifying the code
in the hope that someone may explain why I am getting regular
segmentation faults.

  I wanted to use a C program called CFSQP, a nonlinear optimization
package.  Roughly, you give the program a function pointer representing
the objective function and a function pointer representing the
constraints and some bounds, and CFSQP will return the optimum value
of the function satisfying the constraints within those bounds.

  I implemented bindings for CFSQP in OCaml with little trouble,
and thus have confidence that in principle the SML-CFSQP connection
should work.  The problem I face with MLton is that the functions
need to take arrays of doubles.  Since these must be allocated
in the ML heap, I'm not sure how to create such arrays in C.
The solution I came up with is to allocate a global array for
the arguments in ML, and to set the values from C, finally calling
the SML function on the global array.

  I kept getting segfaults though, so I simplified the code until
it no longer does anything except modify that global argument array.
I figure it's simple enough now for someone to see my mistake.

structure Cfsqp :> CFSQP =
struct

  structure A = Array

  type opt = {obj_fun : real array -> real,
              constr_fun : int * real array -> real,
              num_constrs : int,
              lower_bnds : real array,
              upper_bnds : real array}

  (* Here is the global array *)
  val function_args : real array ref = ref (A.array(0,0.0))
  (* Here is how we set the values from C *)
  val mlton_set_arg = _export "mlton_set_arg" : (int * real -> unit) ->
unit;
  fun set_fun (i,x) = A.update(!function_args,i,x)

  (* The imported C function *)
  val cfsqp_minimize = _import "cfsqp_minimize" : int -> unit;

  (* Calling it once *)
  fun minimize_once {obj_fun,constr_fun,num_constrs,lower_bnds,upper_bnds} =
      let
        val num_args = A.length lower_bnds
        val () = function_args := A.array(num_args,0.0)
        val _ = mlton_set_arg set_fun (* I tried this line both outside and
inside this function *)
        val () = print "minimizing sml...\n"
        val () = cfsqp_minimize num_args (* segfault is always here, after
the C function returns successfully *)
        val () = print "done minimizing sml...\n"
      in
        (0.0,!function_args)
      end

  (* Calling it lots of times exposes the segfault, usually around
     the 100th iteration *)
  fun minimize x : real * real array =
      let
        val repeat = 10000
      in
        A.sub (A.tabulate(repeat,(fn _ => minimize_once x)),0)
      end

end

Here is the C code:


#include "stdio.h"
// the name of the exported header from MLton -stop ...
#include "cfsqp-sml.h"

// copy a bunch of 0s to the MLton global real array
void copy_array_to_mlton_heap(int numargs)
{
  int i;
  for(i=0;i<numargs;i++){
    mlton_set_arg(i,0.0);
  }
  return;
}

// do that 10 times, to expose the segfault
void cfsqp_minimize(Int32 numargs)
{
  printf("minimizing C...\n");
  int i;
  for(i=1;i<=10;i++){
    copy_array_to_mlton_heap(numargs);
  }
  printf("done minimizing C...\n");
  return;
}

My only guess as to what may be happening is that
at some point the garbage collector is moving the
[function_args] ref or the actual array.  That
would make the call to [mlton_set_arg] mess things
up if [mlton_set_arg] didn't get updated with the
new address.  But it seems that the garbage collector
should inform, at least [set_fun], and thus this
doesn't seem possible.

Any ideas?

Thanks,

Sean
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mlton.org/pipermail/mlton-user/attachments/20070905/76f22097/attachment.htm


More information about the MLton-user mailing list