caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* C binding and GC interaction: storing values outside the heap
@ 2010-09-07 20:58 Paolo Donadeo
  2010-09-07 21:12 ` [Caml-list] " Stéphane Glondu
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Paolo Donadeo @ 2010-09-07 20:58 UTC (permalink / raw)
  To: OCaml mailing list, OCaml-Lua devel ML

[-- Attachment #1: Type: text/plain, Size: 4331 bytes --]

I'm writing a Lua API binding <http://ocaml-lua.forge.ocamlcore.org/> and I
have a problem regarding the interaction with the garbage collector. The
situation is rather canonical: a particular C data type, the Lua
state<http://www.lua.org/manual/5.1/manual.html#lua_state>,
is used as argument in all the C functions of the API. A pointer to a
lua_State is wrapped inside an OCaml custom block, in the very same way
presented in the official documentation (in the ncurses example). Like the
WINDOW* example, the lua_State is allocated via caml_stat_alloc and the
resulting pointer is wrapped in a value obtained by caml_alloc_custom using
a macro:

#define lua_State_val(L) (*((lua_State **) Data_custom_val(L))) /* also
l-value */
... ... ...
lua_State *L = lua_newstate(custom_alloc, NULL); // the actual allocation
made by caml_stat_resize
... ... ...
v_L = caml_alloc_custom(&lua_State_ops, sizeof(lua_State *), 1, 10);
lua_State_val(v_L) = L;
CAMLreturn(v_L);

So far so good.

The problem is that, for several good reasons, I need a copy, or a
reference, to the OCaml value representing the lua_State (v_L in the code
above) *inside* the Lua state (I mean the C data structure). This is
possible because the Lua API provides for a way to bind a user data inside
the state. So I wrote:

typedef struct ocaml_data
{
 value state_value;
 value panic_callback;
} ocaml_data;

CAMLprim
value luaL_newstate__stub (value unit)
{
   CAMLparam1(unit);
   CAMLlocal1(v_L);

   value *default_panic_v = caml_named_value("default_panic");

   /* create a fresh new Lua state */
   lua_State *L = lua_newstate(custom_alloc, NULL);
   lua_atpanic(L, &default_panic);

   /* alloc space for the register entry */
   ocaml_data *data = (ocaml_data*)caml_stat_alloc(sizeof(ocaml_data));
   caml_register_global_root(&(data->panic_callback));
   data->panic_callback = *default_panic_v;

   /* create a new Lua table for binding informations */
   set_ocaml_data(L, data);  // puts "data" inside L

   /* wrap the lua_State* in a custom object */
   v_L = caml_alloc_custom(&lua_State_ops, sizeof(lua_State *), 1, 10);
   lua_State_val(v_L) = L;
   data->state_value = v_L;  // also v_L inside L but BIG PROBLEM HERE!!!

   /* return the lua_State value */
   CAMLreturn(v_L);
}

The problem here is that I'm storing an OCaml value (v_L) inside a malloc-ed
area. Result: segfault.

Is there a safe way to store a reference to a value outside the heap?

As a temporary workaround I removed the "value state_value" field from the
ocaml_data struct, replacing it with a reference counter:

typedef struct ocaml_data
{
   value panic_callback;
   int ref_counter;
} ocaml_data;

and the previous "luaL_newstate__stub" function sets the counter to 1:

... ... ...
   /* alloc space for the register entry */
   ocaml_data *data = (ocaml_data*)caml_stat_alloc(sizeof(ocaml_data));
   caml_register_global_root(&(data->panic_callback));
   data->panic_callback = *default_panic_v;
   data->ref_counter = 1;
... ... ...

In other parts of the code, where I have the original lua_State pointer, but
I need the corresponding OCaml value, and where I previously used the
retrieve it from the lua_State, now I create *another* OCaml value with the
same lua_State, incrementing the reference counter, for example:

static int panic_wrapper(lua_State *L)
{
   CAMLlocal1(v_L);
   ocaml_data *data = get_ocaml_data(L);

   /* wrap the lua_State* in a custom object */
   v_L = caml_alloc_custom(&lua_State_ops, sizeof(lua_State *), 1, 10);
   lua_State_val(v_L) = L;
   data->ref_counter++;

   return Int_val(caml_callback(data->panic_callback, v_L));
}

In the finalization function I free() the C data structures only if
ref_counter reaches 0:

static void finalize_lua_State(value L)
{
   lua_State *state = lua_State_val(L);
   ocaml_data *data = get_ocaml_data(state);

   if (data->ref_counter == 1)
   {
       caml_remove_global_root(&(data->panic_callback));
       caml_stat_free(data);
       lua_close(state);  // this calls free()
   }
   else
   {
       data->ref_counter--;
   }
}

What I don't like here is that several OCaml values, representing the same C
data structure, are simultaneously present in the program, and the reference
counting is not exactly the best way to collect memory garbage.

Any ideas or suggestions?


-- 
*Paolo*

[-- Attachment #2: Type: text/html, Size: 5473 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Caml-list] C binding and GC interaction: storing values outside the heap
  2010-09-07 20:58 C binding and GC interaction: storing values outside the heap Paolo Donadeo
@ 2010-09-07 21:12 ` Stéphane Glondu
  2010-09-07 21:23   ` Paolo Donadeo
  2010-09-07 22:53 ` Damien Doligez
  2010-09-08  5:06 ` Romain Beauxis
  2 siblings, 1 reply; 8+ messages in thread
From: Stéphane Glondu @ 2010-09-07 21:12 UTC (permalink / raw)
  To: Paolo Donadeo; +Cc: OCaml mailing list, OCaml-Lua devel ML

Le 07/09/2010 22:58, Paolo Donadeo a écrit :
> I'm writing a Lua API binding
> [...]
> typedef struct ocaml_data
> {
>  value state_value;
>  value panic_callback;
> } ocaml_data;
> [...]
>    /* alloc space for the register entry */
>    ocaml_data *data = (ocaml_data*)caml_stat_alloc(sizeof(ocaml_data));
>    caml_register_global_root(&(data->panic_callback));
>    data->panic_callback = *default_panic_v;

Why don't you call caml_register_global_root on &(data->state_value) as
well?

-- 
Stéphane


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Caml-list] C binding and GC interaction: storing values outside the heap
  2010-09-07 21:12 ` [Caml-list] " Stéphane Glondu
@ 2010-09-07 21:23   ` Paolo Donadeo
  2010-09-07 21:48     ` Stéphane Glondu
  0 siblings, 1 reply; 8+ messages in thread
From: Paolo Donadeo @ 2010-09-07 21:23 UTC (permalink / raw)
  To: OCaml mailing list, OCaml-Lua devel ML

On Tue, Sep 7, 2010 at 23:12, Stéphane Glondu wrote:
> Why don't you call caml_register_global_root on &(data->state_value) as well?

This was a solution I tried, but with the additional global root the
finalization function was never called by the GC, so it solved the
segfault with a memory leak :-)


-- 
Paolo ⠠⠵


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Caml-list] C binding and GC interaction: storing values outside the heap
  2010-09-07 21:23   ` Paolo Donadeo
@ 2010-09-07 21:48     ` Stéphane Glondu
  2010-09-09  8:11       ` Paolo Donadeo
  0 siblings, 1 reply; 8+ messages in thread
From: Stéphane Glondu @ 2010-09-07 21:48 UTC (permalink / raw)
  To: Paolo Donadeo; +Cc: OCaml mailing list, OCaml-Lua devel ML

Le 07/09/2010 23:23, Paolo Donadeo a écrit :
>> Why don't you call caml_register_global_root on &(data->state_value) as well?
> 
> This was a solution I tried, but with the additional global root the
> finalization function was never called by the GC, so it solved the
> segfault with a memory leak :-)

Oh, I see.

Well... could you provide a full self-contained example? In your
original mail, there is:

  lua_atpanic(L, &default_panic);

whereas everything around talks about default_panic_v.

My idea would be to add an indirection: luaL_newstate__stub would return
a simple custom block (OCaml-heap-allocated) with a field which is an
abstract block. The Lua structure would be in the inner block. And in
the Lua structure, add a pointer to the inner block, and register the
inner block as a global root (the inner block should probably not be
allocated in the OCaml heap...). In the finalization function of the
outer block, un-register the global root and free the Lua structure.

How does that sound?

-- 
Stéphane


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Caml-list] C binding and GC interaction: storing values outside the heap
  2010-09-07 20:58 C binding and GC interaction: storing values outside the heap Paolo Donadeo
  2010-09-07 21:12 ` [Caml-list] " Stéphane Glondu
@ 2010-09-07 22:53 ` Damien Doligez
  2010-09-09  8:16   ` Paolo Donadeo
  2010-09-08  5:06 ` Romain Beauxis
  2 siblings, 1 reply; 8+ messages in thread
From: Damien Doligez @ 2010-09-07 22:53 UTC (permalink / raw)
  To: Paolo Donadeo; +Cc: OCaml mailing list, OCaml-Lua devel ML


On 2010-09-07, at 22:58, Paolo Donadeo wrote:

> The problem is that, for several good reasons, I need a copy, or a reference, to the OCaml value representing the lua_State (v_L in the code above) inside the Lua state (I mean the C data structure).

That creates a cross-heap reference loop and it's very bad news for the GC.

> As a temporary workaround I removed the "value state_value" field from the ocaml_data struct, replacing it with a reference counter:

Maybe you could try something similar, with a weak hash table to recover the
OCaml value corresponding to a given lua_State, thus eliminating the need for
a reference counter.  Use the address of the lua_State for hashing and for
comparison.

Drawback: the weak hash table has to be global.

-- Damien


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Caml-list] C binding and GC interaction: storing values outside the heap
  2010-09-07 20:58 C binding and GC interaction: storing values outside the heap Paolo Donadeo
  2010-09-07 21:12 ` [Caml-list] " Stéphane Glondu
  2010-09-07 22:53 ` Damien Doligez
@ 2010-09-08  5:06 ` Romain Beauxis
  2 siblings, 0 replies; 8+ messages in thread
From: Romain Beauxis @ 2010-09-08  5:06 UTC (permalink / raw)
  To: caml-list

	Hi,

Le mardi 7 septembre 2010 15:58:04, Paolo Donadeo a écrit :
> Any ideas or suggestions?

I'm sorry if I missed something obvious, but why don't you simply require the 
callback in the CAMLprim function's arguments and then wrap it in the ML code?

Something like:

.ml:

  type handler
  type callback = foo -> bar

  type t = handler * callback

  val foo : handler -> callback -> bar = "caml_lua_foo"

  let foo (h,c) = foo h c

.mli:

  type t

  val foo : t -> bar


Romain


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Caml-list] C binding and GC interaction: storing values outside the heap
  2010-09-07 21:48     ` Stéphane Glondu
@ 2010-09-09  8:11       ` Paolo Donadeo
  0 siblings, 0 replies; 8+ messages in thread
From: Paolo Donadeo @ 2010-09-09  8:11 UTC (permalink / raw)
  To: OCaml mailing list, OCaml-Lua devel ML

On Tue, Sep 7, 2010 at 23:48, Stéphane Glondu wrote:
> Well... could you provide a full self-contained example?

Yes, but it could be confusing due to many other details. In any case:

$ git clone http://git.ocamlcore.org/ocaml-lua/ocaml-lua.git
$ git checkout remotes/origin/paolo_donadeo/segfault_hunting
$ cd src
$ ocamlbuild main.d.byte main.native

(You must have installed the Lua library, liblua5.1-0-dev in Debian/Ubuntu)


> How does that sound?

It could work, in the next few days I'll try this solution too.


-- 
Paolo


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Caml-list] C binding and GC interaction: storing values outside the heap
  2010-09-07 22:53 ` Damien Doligez
@ 2010-09-09  8:16   ` Paolo Donadeo
  0 siblings, 0 replies; 8+ messages in thread
From: Paolo Donadeo @ 2010-09-09  8:16 UTC (permalink / raw)
  To: OCaml mailing list, OCaml-Lua devel ML

On Wed, Sep 8, 2010 at 00:53, Damien Doligez wrote:
> Drawback: the weak hash table has to be global.

In the beginning I had a global data structure (not a weak hash table,
but a simple linked list on the C side) and I removed it because it
requires an extra effort to recover thread safety for the binding.


-- 
Paolo


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2010-09-09  8:16 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-09-07 20:58 C binding and GC interaction: storing values outside the heap Paolo Donadeo
2010-09-07 21:12 ` [Caml-list] " Stéphane Glondu
2010-09-07 21:23   ` Paolo Donadeo
2010-09-07 21:48     ` Stéphane Glondu
2010-09-09  8:11       ` Paolo Donadeo
2010-09-07 22:53 ` Damien Doligez
2010-09-09  8:16   ` Paolo Donadeo
2010-09-08  5:06 ` Romain Beauxis

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).