I’m encountering some strange problems with interfacing C, Ocaml, and Python.
The overall goal is to use some parsing functions written in ocaml in Python Django, bridging by way of C.
If I startup my django site and go to a page which calls these ocaml functions in order to render, it works perfectly fine as long as I stay on the same page.
If I change pages or wait a few minutes and try to reload, the server will silently crash with no error messages.
Earlier I would get the “No domain lock” crash error under the same circumstances which according to the ocaml c interfacing documentation is something that comes up in a multithreading context – which I’m not using in either the python, c, or ocaml levels. I added manually acquiring the runtime lock to the code and that error message went away, but the behavior remains essentially the same.
I’m thinking I must be screwing something up with the Ocaml garbage collector or making a C error unwittingly. I also am wondering if the ocaml runtime might be silently getting de-initialized somehow.
Here’s the C code:
#include <assert.h>
#include <caml/alloc.h>
#include <caml/callback.h>
#include <caml/fail.h>
#include <caml/memory.h>
#include <caml/misc.h>
#include <caml/mlvalues.h>
#include <caml/osdeps.h>
#include <caml/threads.h>
#include <caml/unixsupport.h>
#include <stdlib.h>
#include <string.h>
void __caml_init() {
static int once = 0;
if (once == 0) {
char *argv[] = {"ocaml_startup", NULL};
caml_startup(argv);
once = 1;
}
}
char *parse_lml_intern(char *str_in) {
CAMLparam0();
static const value *_parse_lml = NULL;
if (_parse_lml == NULL)
_parse_lml = caml_named_value("parse_lml");
value ocaml_str_in = caml_copy_string(str_in);
CAMLlocal1(result);
result = caml_callback_exn(*_parse_lml, ocaml_str_in);
assert(Tag_val(result) == String_tag);
size_t result_len = caml_string_length(result);
char *str_out = malloc(result_len);
memcpy(str_out, String_val(result), result_len);
str_out[result_len] = '\0';
CAMLreturnT(char *, str_out);
}
char *parse_lml(char *str_in) {
__caml_init();
if (Caml_state_opt == NULL)
caml_acquire_runtime_system();
char *out = parse_lml_intern(str_in);
caml_release_runtime_system();
return out;
}
Relevant Ocaml code:
let parse_lml_c lml =
try
Parse.parse_lml lml
with
Document.SyntaxError msg ->
Printf.sprintf "Error: %s" msg
| _ -> ""
let () = Callback.register "parse_lml" parse_lml_c
Python code:
def render_lml(lml, template):
lml = bytes(lml, "utf-8")
parsed_lml = lib.parse_lml(lml)
print(ffi.string(parsed_lml))
result = ffi.string(parsed_lml).decode("utf-8")
# Need to explicitly free the c string now that we are done with it
lib.free(parsed_lml)
return result