caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* Catching Break?
@ 1999-01-25 18:43 Ian T Zimmerman
  1999-01-26  0:24 ` Xavier Leroy
  0 siblings, 1 reply; 12+ messages in thread
From: Ian T Zimmerman @ 1999-01-25 18:43 UTC (permalink / raw)
  To: caml-list


Why doesn't this work:

let main() =
  Sys.catch_break true;
  try Unix.kill (Unix.getpid()) Sys.sigint
  with Sys.Break -> prerr_endline "CAUGHT!"; exit 0

let _ = main()

This program just prints "Fatal error: Uncaught exception Sys.Break"
as if the try block weren't there.  Am I overlooking something really
obvious?

Ocaml 2.01, Unix (Linux 2.0), libc5, linuxthreads.

-- 
Ian T Zimmerman                        <itz@transbay.net>
I came to the conclusion that what was wrong about the guillotine
was that the condemned man had no chance at all, absolutely none.
Albert Camus, _The Outsider_




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

* Re: Catching Break?
  1999-01-25 18:43 Catching Break? Ian T Zimmerman
@ 1999-01-26  0:24 ` Xavier Leroy
  1999-01-26  6:46   ` Ian T Zimmerman
  1999-02-05 18:00   ` Ian T Zimmerman
  0 siblings, 2 replies; 12+ messages in thread
From: Xavier Leroy @ 1999-01-26  0:24 UTC (permalink / raw)
  To: Ian T Zimmerman, caml-list

> Why doesn't this work:
> 
> let main() =
>   Sys.catch_break true;
>   try Unix.kill (Unix.getpid()) Sys.sigint
>   with Sys.Break -> prerr_endline "CAUGHT!"; exit 0
> 
> let _ = main()
> 
> This program just prints "Fatal error: Uncaught exception Sys.Break"
> as if the try block weren't there.  Am I overlooking something really
> obvious?

No, it's fairly subtle, actually.  For various reasons related to the
Caml runtime system, signals in OCaml are not necessarily handled at
the program point where they are received: the signal handler is
called only at the next "safe" program point.

In the case of the ocamlc bytecode interpreter, "safe" program points
are at function application and at the beginning of each loop iteration.
So, in your example above, we leave the "try..with" before the handler
for the signal is called, and that handler thus raises the Sys.Break
exception outside of the "try..with".

If you add a function call after the Unix.kill, everything should work
as expected:

> let main() =
>   Sys.catch_break true;
>   try Unix.kill (Unix.getpid()) Sys.sigint; prerr_endline "Sent signal"
>   with Sys.Break -> prerr_endline "CAUGHT!"; exit 0

This is actually a minor OCaml bug; it would be better to check for
pending signals just before leaving a "try..with" block.  I'll see
what can be done about it.

- Xavier Leroy




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

* Re: Catching Break?
  1999-01-26  0:24 ` Xavier Leroy
@ 1999-01-26  6:46   ` Ian T Zimmerman
  1999-01-28 11:14     ` Xavier Leroy
  1999-02-05 18:00   ` Ian T Zimmerman
  1 sibling, 1 reply; 12+ messages in thread
From: Ian T Zimmerman @ 1999-01-26  6:46 UTC (permalink / raw)
  To: caml-list

> Date: Tue, 26 Jan 1999 01:24:35 +0100
> From: Xavier Leroy <Xavier.Leroy@inria.fr>
> X-Gnus-Article-Number: 93   Mon Jan 25 17:11:10 1999
> 
> > Why doesn't this work:
> >  let main() =
> >   Sys.catch_break true; try Unix.kill (Unix.getpid()) Sys.sigint
> >   with Sys.Break -> prerr_endline "CAUGHT!"; exit 0
> >  let _ = main()
> >  This program just prints "Fatal error: Uncaught exception
> > Sys.Break" as if the try block weren't there.  Am I overlooking
> > something really obvious?

>  No, it's fairly subtle, actually.  For various reasons related to
> the Caml runtime system, signals in OCaml are not necessarily
> handled at the program point where they are received: the signal
> handler is called only at the next "safe" program point.
>  In the case of the ocamlc bytecode interpreter, "safe" program
> points are at function application and at the beginning of each loop
> iteration.  So, in your example above, we leave the "try..with"
> before the handler for the signal is called, and that handler thus
> raises the Sys.Break exception outside of the "try..with".

I understand the kind of consideration which leads to deferring the
handler.  I may be in the middle of a garbage collection etc.

>  If you add a function call after the Unix.kill, everything should
> work as expected

When I have read your mail I thought this would be trivial to work
around.  But it isn't.  First it isn't that obvious what counts as
a function application.  Given the functional nature of ML I'd like to
say "everything".  But then I start to have doubts.  What about basic
arithmetic operators for instance?

let suicide() =
    Unix.kill (Unix.getpid()) Sys.sigint

let suicidal = try 
    begin suicide(); ~-1 end
with Sys.break -> 0

Or how about an assertion?  Is "assert" just a core library symbol, or
a keyword?

let suicidal = try
    begin suicide(); assert true end
with Sys.break -> ()

Finally, and more seriously, this is just a toy example.  In my real
program, I need to return a _value_ from the expression that
corresponds to suicide().  I tried

let suicide() =
    begin Unix.kill (Unix.getpid()) Sys.sigint; 1 end

let id x = x

let suicidal = try
    (suicide(), id 0)
with Sys.break -> (0, 0)


and then

let suicidal = try
    id (suicide())
with Sys.break -> 0


Neither works the way I need, although in both cases there is (in my
naive opinion) a function application between the signal and the end
of the try block.

Do I really have to use _sequencing_ to force a `safe point'?  That
throws away the value from suicide(), so I'll have to invent a
reference to assign to - eeek!

-- 
Ian T Zimmerman                        <itz@transbay.net>
I came to the conclusion that what was wrong about the guillotine
was that the condemned man had no chance at all, absolutely none.
Albert Camus, _The Outsider_




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

* Re: Catching Break?
  1999-01-26  6:46   ` Ian T Zimmerman
@ 1999-01-28 11:14     ` Xavier Leroy
  1999-01-28 17:33       ` Ian T Zimmerman
  0 siblings, 1 reply; 12+ messages in thread
From: Xavier Leroy @ 1999-01-28 11:14 UTC (permalink / raw)
  To: Ian T Zimmerman, caml-list

> When I have read your mail I thought this would be trivial to work
> around.  But it isn't.  First it isn't that obvious what counts as
> a function application.  Given the functional nature of ML I'd like to
> say "everything".  But then I start to have doubts.  What about basic
> arithmetic operators for instance?

These do not check for signals.  Worse, applications of primitive
functions (defined by an "external" clause) don't check either.
So, yes, it's kind of hard to know when signals will be tested exactly.

> Finally, and more seriously, this is just a toy example.  In my real
> program, I need to return a _value_ from the expression that
> corresponds to suicide().  I tried
> 
> let suicide() =
>     begin Unix.kill (Unix.getpid()) Sys.sigint; 1 end
> 
> let id x = x
> 
> let suicidal = try
>     (suicide(), id 0)
> with Sys.break -> (0, 0)

First of all, that should be Sys.Break (an exception constructor), not
Sys.break.  It doesn't work because of the right-to-left evaluation order
(id 0 is evaluated before suicide()).

> let suicidal = try
>     id (suicide())
> with Sys.break -> 0

Works fine here (with Sys.Break of course).

> Do I really have to use _sequencing_ to force a `safe point'?

Not at all.

> That
> throws away the value from suicide(), so I'll have to invent a
> reference to assign to - eeek!

A sequencing operator that doesn't throw away the value of its left
subexpression exists: it's called "let".

One more comment: rather than sending yourself a signal, then turn the
signal into an exception via the signal handler (as Sys.catch_break does),
why not throw the exception directly?  E.g. just raise Sys.Break when
your program wishes to commit suicide.  Exceptions are much nicer for
synchronous notification; signals are a pain and only required for
asynchronous notification (such as the user pressing ctrl-C).

All the best,

- Xavier Leroy




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

* Re: Catching Break?
  1999-01-28 11:14     ` Xavier Leroy
@ 1999-01-28 17:33       ` Ian T Zimmerman
  1999-02-06  8:24         ` Ian T Zimmerman
  0 siblings, 1 reply; 12+ messages in thread
From: Ian T Zimmerman @ 1999-01-28 17:33 UTC (permalink / raw)
  To: caml-list

> Date: Thu, 28 Jan 1999 12:14:11 +0100
> From: Xavier Leroy <Xavier.Leroy@inria.fr>
> X-Gnus-Article-Number: 122   Thu Jan 28 07:47:34 1999
> 
> >  let suicide() =
> >     begin Unix.kill (Unix.getpid()) Sys.sigint; 1 end
> >  let id x = x
> >  let suicidal = try
> >     (suicide(), id 0)
> > with Sys.break -> (0, 0)

>  First of all, that should be Sys.Break (an exception constructor),
> not Sys.break.  It doesn't work because of the right-to-left
> evaluation order (id 0 is evaluated before suicide()).
>
> > let suicidal = try
> >     id (suicide())
> > with Sys.break -> 0
>  Works fine here (with Sys.Break of course).
>
>  One more comment: rather than sending yourself a signal, then turn
> the signal into an exception via the signal handler (as
> Sys.catch_break does), why not throw the exception directly?
> E.g. just raise Sys.Break when your program wishes to commit
> suicide.  Exceptions are much nicer for synchronous notification;
> signals are a pain and only required for asynchronous notification
> (such as the user pressing ctrl-C).

Ok, I see that I'll have to start posting my real code, because I
still can't make it work despite (imperfect) analogy with the example.

let remove_pair prefix pid = 
  let spid = string_of_int pid in
  let iname = prefix ^ spid ^ ".i"
  and oname = prefix ^ spid ^ ".o" in
  Sys.remove iname; Sys.remove oname

let open_pair prefix pid respond =
  let spid = string_of_int pid in
  let iname = prefix ^ spid ^ ".i"
  and oname = prefix ^ spid ^ ".o" 
  and iflags = [Unix.O_RDONLY ; Unix.O_NONBLOCK]
  and oflags = [Unix.O_WRONLY] in
  let id x = x in
  Sys.catch_break true;
  let (ind, outd) = try if respond then
    let outd = Unix.openfile iname oflags 0 in
    let ind = Unix.openfile oname iflags 0 in
    id (ind, outd)
  else
    let ind = Unix.openfile iname iflags 0 in
    let outd = Unix.openfile oname oflags 0 in   (* HERE! *)
    id (ind, outd)
  with Sys.Break ->
    if not respond then remove_pair prefix pid;
    Sys.catch_break false; exit 0;
    assert false in
  Sys.catch_break false; (ind, outd)


The files referred to by [io]name are named pipes; the write open
hangs until the other side is opened, which may never happen.  So as
to have a chance to clean up (namely remove the pipes), I must allow
_and_ catch Control-C as I'm trying to.  But it doesn't work; if I hit
Control-C while blocked in the open marked, I never get into the with
handler.

-- 
Ian T Zimmerman                        <itz@transbay.net>
I came to the conclusion that what was wrong about the guillotine
was that the condemned man had no chance at all, absolutely none.
Albert Camus, _The Outsider_




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

* Re: Catching Break?
  1999-01-26  0:24 ` Xavier Leroy
  1999-01-26  6:46   ` Ian T Zimmerman
@ 1999-02-05 18:00   ` Ian T Zimmerman
  1999-02-08 17:16     ` Xavier Leroy
  1 sibling, 1 reply; 12+ messages in thread
From: Ian T Zimmerman @ 1999-02-05 18:00 UTC (permalink / raw)
  To: Xavier Leroy; +Cc: caml-list

Xavier Leroy <Xavier.Leroy@inria.fr> writes:

>
> > Why doesn't this work:
> >  let main() =
> >   Sys.catch_break true; try Unix.kill (Unix.getpid()) Sys.sigint
> >   with Sys.Break -> prerr_endline "CAUGHT!"; exit 0
> >  let _ = main()
> >  This program just prints "Fatal error: Uncaught exception
> > Sys.Break" as if the try block weren't there.  Am I overlooking
> > something really obvious?

>  No, it's fairly subtle, actually.  For various reasons related to
> the Caml runtime system, signals in OCaml are not necessarily
> handled at the program point where they are received: the signal
> handler is called only at the next "safe" program point.
>  In the case of the ocamlc bytecode interpreter, "safe" program
> points are at function application and at the beginning of each loop
             ^^^^^^^^^^^^^^^^^^^^^^^^
> iteration.  So, in your example above, we leave the "try..with"
> before the handler for the signal is called, and that handler thus
> raises the Sys.Break exception outside of the "try..with".
>  If you add a function call after the Unix.kill, everything should
> work as expected:
>
> > let main() =
> >   Sys.catch_break true; try Unix.kill (Unix.getpid()) Sys.sigint;
> >   prerr_endline "Sent signal" with Sys.Break -> prerr_endline
> >   "CAUGHT!"; exit 0
>  This is actually a minor OCaml bug; it would be better to check for
> pending signals just before leaving a "try..with" block.  I'll see
> what can be done about it.

As I didn't get any further followups on this topic, I looked at the
bytecode compiler and interpreter.  (As of Ocaml 2.01) I cannot see
the "function application" case being done:

kronstadt:/usr/src/ocaml-2.01/bytecomp$ grep -B 8 -e Kcheck_signals *.ml
bytegen.ml-            comp_expr (add_var id (sz+1) env) handler (sz+1) (add_pop 1 cont1))
bytegen.ml-  | Lifthenelse(cond, ifso, ifnot) ->
bytegen.ml-      comp_binary_test env cond ifso ifnot sz cont
bytegen.ml-  | Lsequence(exp1, exp2) ->
bytegen.ml-      comp_expr env exp1 sz (comp_expr env exp2 sz cont)
bytegen.ml-  | Lwhile(cond, body) ->
bytegen.ml-      let lbl_loop = new_label() in
bytegen.ml-      let lbl_test = new_label() in
bytegen.ml:      Kbranch lbl_test :: Klabel lbl_loop :: Kcheck_signals ::
--
bytegen.ml-  | Lfor(param, start, stop, dir, body) ->
bytegen.ml-      let lbl_loop = new_label() in
bytegen.ml-      let lbl_test = new_label() in
bytegen.ml-      let offset = match dir with Upto -> 1 | Downto -> -1 in
bytegen.ml-      let comp = match dir with Upto -> Cle | Downto -> Cge in
bytegen.ml-      comp_expr env start sz
bytegen.ml-        (Kpush :: comp_expr env stop (sz+1)
bytegen.ml-          (Kpush :: Kbranch lbl_test ::
bytegen.ml:           Klabel lbl_loop :: Kcheck_signals ::
--
emitcode.ml-      out_int (Array.length tbl_const + (Array.length tbl_block lsl 16));
emitcode.ml-      let org = !out_position in
emitcode.ml-      Array.iter (out_label_with_orig org) tbl_const;
emitcode.ml-      Array.iter (out_label_with_orig org) tbl_block
emitcode.ml-  | Kboolnot -> out opBOOLNOT
emitcode.ml-  | Kpushtrap lbl -> out opPUSHTRAP; out_label lbl
emitcode.ml-  | Kpoptrap -> out opPOPTRAP
emitcode.ml-  | Kraise -> out opRAISE
emitcode.ml:  | Kcheck_signals -> out opCHECK_SIGNALS
--
instruct.ml-  | Kbranchifnot of label
instruct.ml-  | Kstrictbranchif of label
instruct.ml-  | Kstrictbranchifnot of label
instruct.ml-  | Kswitch of label array * label array
instruct.ml-  | Kboolnot
instruct.ml-  | Kpushtrap of label
instruct.ml-  | Kpoptrap
instruct.ml-  | Kraise
instruct.ml:  | Kcheck_signals
--
printinstr.ml-      Array.iter (fun lbl -> print_space(); print_int lbl) consts;
printinstr.ml-      print_string "/";
printinstr.ml-      Array.iter (fun lbl -> print_space(); print_int lbl) blocks;
printinstr.ml-      close_box()
printinstr.ml-  | Kboolnot -> print_string "\tboolnot"
printinstr.ml-  | Kpushtrap lbl -> print_string "\tpushtrap L"; print_int lbl
printinstr.ml-  | Kpoptrap -> print_string "\tpoptrap"
printinstr.ml-  | Kraise -> print_string "\traise"
printinstr.ml:  | Kcheck_signals -> print_string "\tcheck_signals"


So I'll see if inserting a dummy loop after the slow system call
helps.  But yes something should be done about this.

I haven't even start to think what to do in the native code case...

-- 
Ian T Zimmerman                        <itz@transbay.net>
I came to the conclusion that what was wrong about the guillotine
was that the condemned man had no chance at all, absolutely none.
Albert Camus, _The Outsider_




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

* Re: Catching Break?
  1999-01-28 17:33       ` Ian T Zimmerman
@ 1999-02-06  8:24         ` Ian T Zimmerman
  1999-02-09  7:02           ` Ian T Zimmerman
  0 siblings, 1 reply; 12+ messages in thread
From: Ian T Zimmerman @ 1999-02-06  8:24 UTC (permalink / raw)
  To: Ian T Zimmerman; +Cc: caml-list

Ian T Zimmerman <itz@transbay.net> writes:

>  Ok, I see that I'll have to start posting my real code, because I
> still can't make it work despite (imperfect) analogy with the
> example.
>  let remove_pair prefix pid =
>   let spid = string_of_int pid in let iname = prefix ^ spid ^ ".i"
>   and oname = prefix ^ spid ^ ".o" in Sys.remove iname; Sys.remove
>   oname
>  let open_pair prefix pid respond =
>   let spid = string_of_int pid in let iname = prefix ^ spid ^ ".i"
>   and oname = prefix ^ spid ^ ".o" and iflags = [Unix.O_RDONLY ;
>   Unix.O_NONBLOCK] and oflags = [Unix.O_WRONLY] in let id x = x in
>   Sys.catch_break true; let (ind, outd) = try if respond then
>     let outd = Unix.openfile iname oflags 0 in let ind =
>     Unix.openfile oname iflags 0 in id (ind, outd)
>   else
>     let ind = Unix.openfile iname iflags 0 in let outd =
>     Unix.openfile oname oflags 0 in (* HERE! *) id (ind, outd)
>   with Sys.Break ->
>     if not respond then remove_pair prefix pid; Sys.catch_break
>     false; exit 0; assert false in
>   Sys.catch_break false; (ind, outd)
>
>  The files referred to by [io]name are named pipes; the write open
> hangs until the other side is opened, which may never happen.  So as
> to have a chance to clean up (namely remove the pipes), I must allow
> _and_ catch Control-C as I'm trying to.  But it doesn't work; if I
> hit Control-C while blocked in the open marked, I never get into the
> with handler.
>

I tried this:

let remove_pair prefix pid = 
  let spid = string_of_int pid in
  let iname = prefix ^ spid ^ ".i"
  and oname = prefix ^ spid ^ ".o" in
  Sys.remove iname; Sys.remove oname

let open_pair prefix pid respond =
  let spid = string_of_int pid in
  let iname = prefix ^ spid ^ ".i"
  and oname = prefix ^ spid ^ ".o" 
  and iflags = [Unix.O_RDONLY ; Unix.O_NONBLOCK]
  and oflags = [Unix.O_WRONLY] in
  Sys.catch_break true;
  let (ind, outd) = try if respond then
    let outd = Unix.openfile iname oflags 0 in
    let ind = Unix.openfile oname iflags 0 in
    for i = 0 to 1 do () done; (ind, outd)
  else
    let ind = Unix.openfile iname iflags 0 in
    let outd = Unix.openfile oname oflags 0 in
    for i = 0 to 1 do () done; (ind, outd)
  with Sys.Break ->
    if not respond then remove_pair prefix pid;
    Sys.catch_break false; exit 0;
    assert false in
  Sys.catch_break false; (ind, outd)


... and it doesn't work, either :-(
I am really stumped, please help.

-- 
Ian T Zimmerman                        <itz@transbay.net>
I came to the conclusion that what was wrong about the guillotine
was that the condemned man had no chance at all, absolutely none.
Albert Camus, _The Outsider_




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

* Re: Catching Break?
  1999-02-05 18:00   ` Ian T Zimmerman
@ 1999-02-08 17:16     ` Xavier Leroy
  0 siblings, 0 replies; 12+ messages in thread
From: Xavier Leroy @ 1999-02-08 17:16 UTC (permalink / raw)
  To: Ian T Zimmerman; +Cc: caml-list

> As I didn't get any further followups on this topic, I looked at the
> bytecode compiler and interpreter.  (As of Ocaml 2.01) I cannot see
> the "function application" case being done:

All function application instructions (APPLY*) perform a check for signal.
The CHECK_SIGNAL instruction is needed only when a check must be
performed elsewhere (i.e. at the head of a loop body).

- Xavier Leroy




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

* Re: Catching Break?
  1999-02-06  8:24         ` Ian T Zimmerman
@ 1999-02-09  7:02           ` Ian T Zimmerman
  1999-02-10 15:48             ` Xavier Leroy
  0 siblings, 1 reply; 12+ messages in thread
From: Ian T Zimmerman @ 1999-02-09  7:02 UTC (permalink / raw)
  To: caml-list

Ian T Zimmerman <itz@transbay.net> writes:

> 
> Ian T Zimmerman <itz@transbay.net> writes:
> 
> >  Ok, I see that I'll have to start posting my real code, because I
> > still can't make it work despite (imperfect) analogy with the
> > example.
> >  let remove_pair prefix pid =
> >   let spid = string_of_int pid in let iname = prefix ^ spid ^ ".i"
> >   and oname = prefix ^ spid ^ ".o" in Sys.remove iname; Sys.remove
> >   oname
> >  let open_pair prefix pid respond =
> >   let spid = string_of_int pid in let iname = prefix ^ spid ^ ".i"
> >   and oname = prefix ^ spid ^ ".o" and iflags = [Unix.O_RDONLY ;
> >   Unix.O_NONBLOCK] and oflags = [Unix.O_WRONLY] in let id x = x in
> >   Sys.catch_break true; let (ind, outd) = try if respond then
> >     let outd = Unix.openfile iname oflags 0 in let ind =
> >     Unix.openfile oname iflags 0 in id (ind, outd)
> >   else
> >     let ind = Unix.openfile iname iflags 0 in let outd =
> >     Unix.openfile oname oflags 0 in (* HERE! *) id (ind, outd)
> >   with Sys.Break ->
> >     if not respond then remove_pair prefix pid; Sys.catch_break
> >     false; exit 0; assert false in
> >   Sys.catch_break false; (ind, outd)
> >
> >  The files referred to by [io]name are named pipes; the write open
> > hangs until the other side is opened, which may never happen.  So as
> > to have a chance to clean up (namely remove the pipes), I must allow
> > _and_ catch Control-C as I'm trying to.  But it doesn't work; if I
> > hit Control-C while blocked in the open marked, I never get into the
> > with handler.
> >
> 
> I tried this:
> 
> let remove_pair prefix pid = 
>   let spid = string_of_int pid in
>   let iname = prefix ^ spid ^ ".i"
>   and oname = prefix ^ spid ^ ".o" in
>   Sys.remove iname; Sys.remove oname
> 
> let open_pair prefix pid respond =
>   let spid = string_of_int pid in
>   let iname = prefix ^ spid ^ ".i"
>   and oname = prefix ^ spid ^ ".o" 
>   and iflags = [Unix.O_RDONLY ; Unix.O_NONBLOCK]
>   and oflags = [Unix.O_WRONLY] in
>   Sys.catch_break true;
>   let (ind, outd) = try if respond then
>     let outd = Unix.openfile iname oflags 0 in
>     let ind = Unix.openfile oname iflags 0 in
>     for i = 0 to 1 do () done; (ind, outd)
>   else
>     let ind = Unix.openfile iname iflags 0 in
>     let outd = Unix.openfile oname oflags 0 in
>     for i = 0 to 1 do () done; (ind, outd)
>   with Sys.Break ->
>     if not respond then remove_pair prefix pid;
>     Sys.catch_break false; exit 0;
>     assert false in
>   Sys.catch_break false; (ind, outd)
> 
> 
> ... and it doesn't work, either :-(
> I am really stumped, please help.
> 

I think I have figured this out, at last.  I _have_ overlooked
something quite simple, so I'm a bit ashamed, but maybe it also
qualifies as a minor bug in Ocaml and that's why I'm getting over my
shame and posting this.

The trouble is that there are _two_ exceptions involved here: one
(Sys.Break) set up by the signal handler, and _another_
(Unix.Unix_error) raised by the openfile primitive when the true
open() fails with errno == EINTR.  There was no handler for Unix_error
in my code and that's the reason for the "Uncaught exception"
behaviour; OTOH, the runtime system doesn't seem to be prepared for
this situation and raises Break regardless, at the next "safe point"
which happens to be in the catch_all exception handler.

The only solution I have found is to set up my own sigint handler:

let remove_pair prefix pid = 
  let spid = string_of_int pid in
  let iname = prefix ^ spid ^ ".i"
  and oname = prefix ^ spid ^ ".o" in
  Sys.remove iname; Sys.remove oname

let safe_open name flags mode =
  let break_handler _ = () in
  let old_handler = Sys.signal Sys.sigint (Sys.Signal_handle break_handler) in
  let d = Unix.openfile name flags mode in
  Sys.set_signal Sys.sigint old_handler; d

let open_pair prefix pid respond =
  let spid = string_of_int pid in
  let iname = prefix ^ spid ^ ".i"
  and oname = prefix ^ spid ^ ".o" 
  and iflags = [Unix.O_RDONLY ; Unix.O_NONBLOCK]
  and oflags = [Unix.O_WRONLY] in
  let (ind, outd) = try if respond then
    let outd = safe_open iname oflags 0 in
    let ind = safe_open oname iflags 0 in
    (ind, outd)
  else
    let ind = safe_open iname iflags 0 in
    let outd = safe_open oname oflags 0 in
    (ind, outd) 
  with any ->
    if not respond then remove_pair prefix pid;
    raise any in (ind, outd)


Now I receive _only_ the Unix_error exception, and I clean up as
intended.


IMO there are two possible changes to Ocaml that can fix this
confusing situation:

o	Do not run the delayed ML signal handlers while in a 
        "with .." block.  This is like considering ML synchronous
        exceptions as "highest priority" signals. I can see how this
        could be difficult, though, with a global state flag needed,
        interaction with threads etc.

o       Within unix_error (in otherlibs/unix/unixsupport.c) check if
        Sys.sigint is pending and Sys.catch_break has been set, and if
        so, do nothing (rather than raise Unix_error).  I can see how
        this could be difficult as well - is there any way for C
        primitive code to access the runtime internals like this?

Best,

-- 
Ian T Zimmerman                        <itz@transbay.net>
I came to the conclusion that what was wrong about the guillotine
was that the condemned man had no chance at all, absolutely none.
Albert Camus, _The Outsider_




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

* Re: Catching Break?
  1999-02-09  7:02           ` Ian T Zimmerman
@ 1999-02-10 15:48             ` Xavier Leroy
  0 siblings, 0 replies; 12+ messages in thread
From: Xavier Leroy @ 1999-02-10 15:48 UTC (permalink / raw)
  To: Ian T Zimmerman, caml-list

I'm glad you've figured out the problem, because I couldn't see it
from your sources...

In your example, it's indeed hard to guess which of the exceptions
Unix_error(EINTR) and Sys.Break will be raised.  Unix_error is raised
when the system call returns an "interrupted" error code, while
Sys.Break is raised when the handler associated with the signal is
executed.  Depending on the system call in question and on whether it
is restartable or not in case of signal, either of the two exceptions
can be raised first.  Moreover, if Unix_error occurs first and is
caught, Sys.Break can still occur at the next safe point, so it's
certainly very confusing.

If you're certain that all the system calls used are not restartable
(i.e. return EINTR immediately when interrupted by a signal),
a perhaps cleaner solution is to register an empty handler for SIGINT:

        Sys.handle Sys.sigint (fun _ -> ())

However, this is not portable (different Unixes have different restart
behaviors).

Perhaps pending signals should also be tested when an exception is
raised.  I'll have to think about this.

All the best,

- Xavier Leroy




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

* Re: Catching Break?
@ 1999-01-26 11:23 Toby Moth
  0 siblings, 0 replies; 12+ messages in thread
From: Toby Moth @ 1999-01-26 11:23 UTC (permalink / raw)
  To: caml-list, itz

Ian T Zimmerman writes:
> 
> Why doesn't this work:
> 
> let main() =
>   Sys.catch_break true;
>   try Unix.kill (Unix.getpid()) Sys.sigint
>   with Sys.Break -> prerr_endline "CAUGHT!"; exit 0
> 
> let _ = main()
> 
> This program just prints "Fatal error: Uncaught exception Sys.Break"
> as if the try block weren't there.  Am I overlooking something really
> obvious?

On the other hand - if one hard-codes in a value for sigint..

let interrupt_handle _ = prerr_endline "Caught C-c"; exit 0;;
Sys.set_signal 2 ( Sys.Signal_handle interrupt_handle );
print_string "You have 10 seconds to stop me with C-c";
print_newline ();
Unix.sleep 10;
print_endline "You failed."

then everything works perfectly. I don't understand why there is
a discrepancy between the interrupt numbers in
sys.ml and those in /usr/include/sys/signal.h ?


On the other hand, I know next to nothing about these sorts of things.



Toby




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

* Re: Catching Break?
@ 1999-01-26  5:17 Frank A. Christoph
  0 siblings, 0 replies; 12+ messages in thread
From: Frank A. Christoph @ 1999-01-26  5:17 UTC (permalink / raw)
  To: Ian T Zimmerman, caml-list

>Why doesn't this work:
>
>let main() =
>  Sys.catch_break true;
>  try Unix.kill (Unix.getpid()) Sys.sigint
>  with Sys.Break -> prerr_endline "CAUGHT!"; exit 0
>
>let _ = main()
>
>This program just prints "Fatal error: Uncaught exception Sys.Break"
>as if the try block weren't there.  Am I overlooking something really
>obvious?
>
>Ocaml 2.01, Unix (Linux 2.0), libc5, linuxthreads.

Bytecode or native?  For the native compiler, chapter 10.4 of the manual says:

  Signals are detected only when the program performs an allocation
  in the heap.  That is, if a signal is delivered while in a piece of
  code that does not allocate, its handler will not be called until
  the next heap allocation.

--FC




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

end of thread, other threads:[~1999-02-10 16:01 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1999-01-25 18:43 Catching Break? Ian T Zimmerman
1999-01-26  0:24 ` Xavier Leroy
1999-01-26  6:46   ` Ian T Zimmerman
1999-01-28 11:14     ` Xavier Leroy
1999-01-28 17:33       ` Ian T Zimmerman
1999-02-06  8:24         ` Ian T Zimmerman
1999-02-09  7:02           ` Ian T Zimmerman
1999-02-10 15:48             ` Xavier Leroy
1999-02-05 18:00   ` Ian T Zimmerman
1999-02-08 17:16     ` Xavier Leroy
1999-01-26  5:17 Frank A. Christoph
1999-01-26 11:23 Toby Moth

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).