caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
From: "Chris King" <colanderman@gmail.com>
To: "Bob Matcuk" <Hamartiology@squeg.net>
Cc: caml-list@yquem.inria.fr
Subject: Re: [Caml-list] va_arg values
Date: Mon, 15 Jan 2007 23:18:51 -0500	[thread overview]
Message-ID: <875c7e070701152018s4eca17b1i72a10440e0c1edef@mail.gmail.com> (raw)
In-Reply-To: <200701132310.l0DNArug026315@discorde.inria.fr>

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


  parent reply	other threads:[~2007-01-16  4:18 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-01-14  0:18 Bob Matcuk
2007-01-15 10:44 ` [Caml-list] " Richard Jones
2007-01-15 23:46   ` Bob Matcuk
2007-01-16  4:18 ` Chris King [this message]
2007-01-16  7:50   ` Bob Matcuk
2007-01-16 10:47   ` Mattias Engdegård
2007-01-21 17:25 ` Xavier Leroy

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=875c7e070701152018s4eca17b1i72a10440e0c1edef@mail.gmail.com \
    --to=colanderman@gmail.com \
    --cc=Hamartiology@squeg.net \
    --cc=caml-list@yquem.inria.fr \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).