[MLton] interrupted system call
Matthew Fluet
fluet@cs.cornell.edu
Fri, 26 Mar 2004 22:08:21 -0500 (EST)
> > Don't forget !MLton.Signal.restart.
>
> I wasn't sure about the interaction of that with the new restart
> argument to syscall. I thought that since you now explicitly passed
> restart that maybe you didn't want the bool ref any more. If you
> still have the bool ref, then maybe it makes sense to get rid of the
> restart argument, and use fluidLet when you need it? I don't know.
No, there is a big difference. !MLton.Signal.restart determines
(dynamically) the Basis Library's treatment of system calls that may error
with EINTR or ERESTART. But, there are a lot of system calls that can't
return with those errors. The restart loop is a lot of overhead to keep
around for control flow paths that can never be reached. The
{restart:bool} argument to syscall asserts that a particular system call
will never need to be restarted. It should be the case that every syscall
call passes either true or false (the constants). Of course, we can
enforce that by exporting two functions that correspond to the two
instances, and hiding the more general function.
I think we've uncovered enough corner cases in system calls to warrant
having one workhorse function with all the logic and special instances for
the most common cases.
> > val syscall: (unit -> int * (unit -> 'a)) * {restart: bool} -> 'a
> ...
> > I understand that this is equivalent to my signature.
>
> I don't think they are equivalent. I think mine is more powerful.
I don't agree with your abstraction. I proposed:
signature M =
sig
val m : (('a -> 'b) * ('b -> 'c)) -> ('a -> 'c)
end
signature S =
sig
val s : ('a -> 'b * (unit -> 'c)) -> ('a -> 'c)
end
functor SToM (S: S) : M =
struct
val m = fn (pre, post) =>
S.s (fn a => let val b = pre a
in (b, fn () => post b)
end)
end
functor MToS (M: M) : S =
struct
val s = fn f =>
M.m (f, fn (b, g) => g ())
end
The point is, I want the 'a -> 'c function; I don't care if the 'b types
necessarily line up.
> OK, but it also seems nice read the entire behavior of the call in one
> place, pretending that it is atomic. I find it hard to see how
>
> syscall
> {restart = true,
> pre = fn (x, {a, b}) => (F_Foo_setA(a)
> ; F_Foo_setB(b)
> ; x)),
> call = fn x => MLton_F x,
> return = fn n => n,
> post = fn _ => {y = F_Bar_getY (),
> z = F_Bar_getZ ()}}
>
> is more readable than
>
> syscall (fn () => (F_Foo_setA a
> ; F_Foo_setB b
> ; (MLton_F x,
> fn () => {y = F_Bar_getY (),
> z = F_Bar_getZ ()})),
>
> {restart = true})
I just think its easier to identify components. I agree that threading of
arguments could be painful, although I think rare.
> Furthermore, going back to your type
>
> {wrapAtomic: bool,
> wrapRestart: bool,
> pre: 'a -> 'aa,
> call: 'aa -> 'bb,
> return: 'bb -> int,
> post: 'bb -> 'b} ->
> ('a -> 'b)
>
> there are a couple things that seem arbitrary to me. What is the
> reason for separating pre from call and return from post? What is the
> reason for passing 'a to pre?
Simply because I believe they correspond to distinct phases in the
execution of a system call. You're right that nothing interesting happens
at the boundaries; the execution goes right from pre to call, and from
call right to return or post.
> Taking those decisions the other way
> gives
>
> {wrapAtomic: bool,
> wrapRestart: bool,
> call: unit -> 'a,
> return: 'a -> int,
> post: 'a -> 'b} -> 'b
>
> That's seems better to me, although it still suffers from the
> inexpressiveness problem (and still has one too many type variables
> :-).
>
>
> Overall, this seems like a classic case of currying = staged
> computation, and it seems wrong not to use it.
>
> _______________________________________________
> MLton mailing list
> MLton@mlton.org
> http://www.mlton.org/mailman/listinfo/mlton
>