[MLton-devel] calling SML from C; reentrance
Matthew Fluet
fluet@cs.cornell.edu
Fri, 16 May 2003 11:50:17 -0400 (EDT)
I was thinking about proposing a MLton.Callback structure that would
replace MLton.FFI.handleCallFromC and alleviate some of the burden of
interoperability. However, I was also thinking about the fact
MLton.FFI.handleCallFromC isn't reentrant (and I don't see how to
make it so from the client side) -- in the sense that the callFromC
handler can't call a C function that calls back into SML. Slightly
modifying Steve's original example:
------------------------------------------------------------
/* f.c */
#include <stdio.h>
void MLton_callFromC ();
void f () {
fprintf (stderr, "f calling SML\n");
MLton_callFromC ();
fprintf (stderr, "f calling SML again\n");
MLton_callFromC ();
fprintf (stderr, "f done calling SML\n");
}
------------------------------------------------------------
------------------------------------------------------------
(* z.sml *)
local
val tab = 3
val indent = ref 0
in
fun push () = indent := (!indent) + tab
fun pop () = indent := (!indent) - tab
val print = fn s =>
(print (CharVector.tabulate (!indent, fn _ => #" "))
; print s)
end
val f = _ffi "f": unit -> unit;
val f = fn () =>
(print "calling f\n"; push ()
; f ()
; pop (); print "done calling f\n")
val h = ref true
val handler =
fn () =>
(print "handling C\n"; push ()
; print "C called me\n"
; if !h then (h := false ; f ()) else ()
; pop (); print "done handling C\n")
val _ = MLton.FFI.handleCallFromC handler
val _ = f ()
val _ = f ()
-------------------------------------------------------
[fluet@lennon test]$ mlton.cvs.HEAD z.sml f.c ; ./z
calling f
f calling SML
handling C
C called me
calling f
f calling SML
done calling f
done handling C
f calling SML again
handling C
C called me
done handling C
f done calling SML
Segmentation fault
I understand this is "bad", although I don't quite understand why the
segmentation fault. When handler executes f () the handleCallFromC
thread is suspended right there, so when we reenter SML, we resume that
thread and continue with print "done handling C\n". Thus, we get the
"error" on lines 6-7, where f calls SML but doesn't see "handling C".
After that, I guess we just have the threads in a bad enough state that
things don't work.
>From the client side, I would (naively) think that the following
modification would work: i.e., every time the handler gets run,
immediately reinstall it as the handler:
val h = ref true
val rec handler =
fn () =>
(print "handling C\n"; push ()
; MLton.FFI.handleCallFromC handler
; print "C called me\n"
; if !h then (h := false ; f ()) else ()
; pop (); print "done handling C\n")
val _ = MLton.FFI.handleCallFromC handler
Now, because I know the implementation of handleCallFromC, I know this
won't work. The "original" handler is still the thread that's suspended
at the call to C and we won't see the newly installed handler until the
handleCallFromC loop get's back to the top and derefs the ref holding the
handler. Interestingly, though, here is how the code fails:
[fluet@lennon test]$ mlton.cvs.HEAD z.sml f.c ; ./z
calling f
f calling SML
handling C
C called me
calling f
f calling SML
done calling f
done handling C
f calling SML again
handling C
C called me
done handling C
f done calling SML
Out of memory: 1,073,854,960 bytes live.
I have no idea why the runtime looking for a gig (and clearly a gig is not
live, as this machine has 512Meg RAM + 128Meg Swap).
So, it seems like we need to go deeper. Switch z.sml's handler back to
the original (i.e., don't attempt to reinstall the handler on the client
side) and change the basis-library/mlton/thread.sml to:
val setCallFromCHandler =
let
val r: (unit -> unit) ref =
ref (fn () => raise Fail "no handler for C calls")
val _ =
let
fun install () =
Prim.setCallFromCHandler
(toPrimitive
(new (fn () =>
let
val t = Prim.saved ()
in
install ()
; !r () handle e => MLtonExn.topLevelHandler e
; Prim.setSaved t
; Prim.returnToC ()
; raise Fail "Thread.setCallFromCHandler"
end)))
in
install ()
end
in
fn f => r := f
end
Note, we remove the loop, as the Fail exception is unreachable;
Prim.returnToC doesn't return here. Now, I get:
[fluet@lennon test]$ mlton.cvs.HEAD z.sml f.c ; ./z
calling f
f calling SML
handling C
C called me
calling f
f calling SML
handling C
C called me
done handling C
f calling SML again
handling C
C called me
done handling C
f done calling SML
done calling f
done handling C
f calling SML again
handling C
C called me
done handling C
f done calling SML
done calling f
calling f
f calling SML
handling C
C called me
done handling C
f calling SML again
handling C
C called me
done handling C
f done calling SML
done calling f
which is "more" correct from my point of view. I had always envisioned C
calling SML as always starting "at the top" of the handler function. I
don't think there are any major problems with the above. In a "single
threaded" main program, there will really only ever be one stack of
execution, just chained together through C functions. Interestingly, this
is precisely what happens on the C side, where the same stack is used for
all the C functions, just peppered with returns to MLton_callFromC that
side-track the execution into ML before returning. In a "multi threaded"
main program, this should be more robust, since while handling a call from
C to SML in one thread, we might switch to another ML thread that also
calls C that calls SML.
It also seems that we could further simplify setCallFromCHandler as
follows:
val setCallFromCHandler =
let
fun install f =
Prim.setCallFromCHandler
(toPrimitive
(new (fn () =>
let
val t = Prim.saved ()
in
install f
; f () handle e => MLtonExn.topLevelHandler e
; Prim.setSaved t
; Prim.returnToC ()
; raise Fail "Thread.setCallFromCHandler"
end)))
val _ = install (fn () => raise Fail "no handler for C calls")
in
fn f => install f
end
-------------------------------------------------------
Enterprise Linux Forum Conference & Expo, June 4-6, 2003, Santa Clara
The only event dedicated to issues related to Linux enterprise solutions
www.enterpriselinuxforum.com
_______________________________________________
MLton-devel mailing list
MLton-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mlton-devel