Version française
Home     About     Download     Resources     Contact us    
Browse thread
va_arg values
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: -- (:)
From: Chris King <colanderman@g...>
Subject: Re: [Caml-list] va_arg values
Short answer: your function is okay, but not for the reasons you
state.  Long answer:

On 1/13/07, Bob Matcuk <Hamartiology@squeg.net> wrote:
> I guess the real question is: is it even necessary to worry about this?
> The function, as I said, will only ever be called from other C
> functions (who have already designated these values as being
> params/local to themselves, assuming they are written correctly).

This doesn't matter.  The purpose of CAMLparam/CAMLlocal is twofold:
first, to declare that a particular value shouldn't be garbage
collected (which, as you point out, the other C functions take care
of).  Secondly (and more importantly in this case), they inform the
garbage collector of the all roots which it may need to update in the
event that it relocates a block.  If you don't use CAMLparam/CAMLlocal
and a GC cycle occurs, there's a chance that a block which one of your
values points to will be moved, and your value will become invalid.

> I
> seem to recall reading somewhere that if you write a function that
> will only ever be called from other C functions that have already
> registered the values (via CAMLparam/CAMLlocal), then it's
> unnecessary to do it again.

AFAIK this is incorrect (because blocks can be relocated).  The only
time it is safe not to register values is if your code does not assume
that the contents of a value are valid after a call which could trip
the GC.

> The function doesn't allocate any new
> values either, so it shouldn't trip the GC anyway, right?

True, false.  caml_callbackN executes arbitrary code, which may or may
not trip the GC.  hash_variant and caml_get_public_method are
questionable also (since they return values), but looking at the Caml
source code, it seems that they are safe (but I don't think the docs
guarantee this).

(BTW you should use caml_hash_variant rather than hash_variant; the
comment for caml_get_public_method in caml/mlvalues.h should probably
be updated to this effect also.)

> Should probably check calloc for success and maybe throw an exception
> if it failed...

You could do this with caml_stat_alloc and caml_stat_free (in
caml/memory.h).  These are equivalent to malloc/free but throw Caml's
out-of-memory exception if they fail.  However in this case, I would
simply declare args as an array.  Otherwise, if the callback throws an
exception, args will not be freed unless you explicitly catch
exceptions via caml_callbackN_exn, free it, and then re-raise the
exception.

Note that if you have no control over the C functions higher up the
call chain (say an external library which calls your function), they
could exhibit similar problems if they are unaware of the possibility
of your function raising an exception.  The best thing to do in such a
case would be to return an error condition if possible, or at the very
least, print a warning and return or exit gracefully (the functions in
caml/printexc.h help here).

> Which actually brings me to another quick question: if
> I throw an exception, say caml_failwith("..."), is it necessary to
> still call CAMLreturn after it? Or will the exception cause the
> function to exit?

The exception causes the function to exit.  You can see which
functions act like this in the header files by looking for the
"Noreturn" attribute at the end of their declaration.

> Is it an invalid assumption that it is unnecessary to bother with the
> CAMLparam/CAMLlocal stuff since there's nothing to trip the GC? If so,
> what is the best way to handle all the CAMLparam/CAMLlocal stuff?

Yes, it is an invalid assumption, because your code may in fact trip the GC.

BUT

Look over the function you wrote carefully.  Notice that values obj
and *args are used only before the call to caml_callbackN, and that
the value r is used only after that call.  Your function is indeed
safe, only because you don't use after the "unsafe" call any value
which was initialized before.

Of course, for the sake of maintainability, I wouldn't in general
endorse such eliding of CAMLparam/CAMLlocal.  I'd recommend putting a
big ol' warning in there :)

> For
> example, CAMLlocalN(args, n + 1) is invalid because C does not allow
> you to declare dynamic arrays.

K&R C doesn't, but GCC does.  If you're using another compiler or some
compatibility flag, then the alloca function (usually found in
alloca.h) should do the trick.  It allocates space on the stack
exactly like an array declaration does, so the guts of CAMLlocalN
should apply to it.

> I know values are just pointers so it is syntactically correct, but
> what I'm asking is: is it safe to do? Should I be using some function
> instead to create a copy of the value?

Copying values with assignment is perfectly legal, provided the
locations to which they are copied are registered with the GC first
(just like any other value).

Hope this was able to clear things up, I've hit many of these bumps
myself while learning to write extensions.  The best thing to remember
is that Caml's GC is not a reference counter but a generational
collector and can move blocks from right under your nose.  Then the
reasons to use CAMLlocal/CAMLparam become clear.

- Chris