caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* Strange buffering interaction: stdout / file_descr
@ 2009-05-23 21:17 Alexander Fuchs
  2009-05-24 10:55 ` [Caml-list] " Gerd Stolpmann
  0 siblings, 1 reply; 2+ messages in thread
From: Alexander Fuchs @ 2009-05-23 21:17 UTC (permalink / raw)
  To: caml-list

Hi,

I am trying to use Unix.create_process to create a process and redirect
its input/output channels. Actually, as a first step I want to determine
if process creation succeeded. I couldn't find any straightforward way
to figure this out, so I am just making the program output its version,
and them I am trying to read from its output channel. If the output
channel is ready according to Unix.select, but returns 0 bytes with
Unix.read, I can conclude that process creation failed (e.g. the wrong
path was given). I hope I am just missing something obvious here and
someone can point me to a better solution.

Anyway, when doing this, I get some strange behavior. First the main
program outputs something on stdout, without flushing stdout. Then a
process is created, with redirecting it input/output channels to new
socket pairs, but process creation fails (intentionally). But now
reading from the process' stdout, which should be completely independent
from the main program's stdout, yields what was previously output on the
main program's stdout. If instead I flush stdout before calling
Unix.create_process, this doesn't happen, and nothing can be read from
the failed process' stdout, as expected.

Is this a bug in the interaction of channels / file_descr, or should
this be make sense? 

For illustration I attached a sample program, compile with ocamlc
unix.cma <file_name>. It first outputs "main" on its stdout, then tries
to create a process, fails, reads "main" from the process' stdout, and
prints it.

Tested with ocaml 3.11.0 native and byte code, on x86_64 GNU/Linux.


Thanks,
	Alex




let () =
  print_string "main";

  let (stdout_r, stdout_w) = Unix.socketpair Unix.PF_UNIX
Unix.SOCK_STREAM 0 in
  let (stdin_r, stdin_w) = Unix.socketpair Unix.PF_UNIX Unix.SOCK_STREAM
0 in

  let cmd = "none" in

  (* without flush reading from stdout_r yields "main" *)
  (* flush stdout; *)
  let pid = Unix.create_process cmd [| cmd; "-version" |] stdin_r
stdout_w stdout_w in

  Unix.close stdin_r;
  Unix.close stdout_w;

  begin

    let (outs, _, _) = Unix.select [stdout_r] [] [] (-1.0) in

    match outs with
    | [out] ->
        let buffer = String.make 100 ' ' in
        let size =  Unix.read out buffer 0 100 in

        if size > 0 then begin
          print_endline (buffer)
        end

        else begin
          print_endline "create_process failed"
        end

    | _ ->
        assert false;
  end;

  Unix.close stdout_r;
  Unix.close stdin_w;
  Unix.kill pid Sys.sigkill;
  let (_, _status) = Unix.waitpid [] pid in
  ()



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

* Re: [Caml-list] Strange buffering interaction: stdout / file_descr
  2009-05-23 21:17 Strange buffering interaction: stdout / file_descr Alexander Fuchs
@ 2009-05-24 10:55 ` Gerd Stolpmann
  0 siblings, 0 replies; 2+ messages in thread
From: Gerd Stolpmann @ 2009-05-24 10:55 UTC (permalink / raw)
  To: fuchs; +Cc: caml-list

This is the somewhat expected behavior, although a bit surprising at the
first glance. create_process isn't an atomic operation. Actually, it
forks the process (and thus implicitly duplicates the stdout buffer). In
the forked child it tries to exec the new executable. If this fails, it
does a regular exit(), and the buffers are flushed.

One can argue that this is wrong - the implementation should better do
an _exit (i.e. immediately exit the child process without doing any sort
of "cleanup"). AFAIK, system() implementations do this.

The standard way of checking for exec errors is to test the exit code,
and if it is 127, exec or the code immediately before exec failed.
That's a convention only, i.e. nothing enforces this.

Gerd

Am Samstag, den 23.05.2009, 16:17 -0500 schrieb Alexander Fuchs:
> let () =
>   print_string "main";
> 
>   let (stdout_r, stdout_w) = Unix.socketpair Unix.PF_UNIX
> Unix.SOCK_STREAM 0 in
>   let (stdin_r, stdin_w) = Unix.socketpair Unix.PF_UNIX
> Unix.SOCK_STREAM
> 0 in
> 
>   let cmd = "none" in
> 
>   (* without flush reading from stdout_r yields "main" *)
>   (* flush stdout; *)
>   let pid = Unix.create_process cmd [| cmd; "-version" |] stdin_r
> stdout_w stdout_w in
> 
>   Unix.close stdin_r;
>   Unix.close stdout_w;
> 
>   begin
> 
>     let (outs, _, _) = Unix.select [stdout_r] [] [] (-1.0) in
> 
>     match outs with
>     | [out] ->
>         let buffer = String.make 100 ' ' in
>         let size =  Unix.read out buffer 0 100 in
> 
>         if size > 0 then begin
>           print_endline (buffer)
>         end
> 
>         else begin
>           print_endline "create_process failed"
>         end
> 
>     | _ ->
>         assert false;
>   end;
> 
>   Unix.close stdout_r;
>   Unix.close stdin_w;
>   Unix.kill pid Sys.sigkill;
>   let (_, _status) = Unix.waitpid [] pid in
>   ()
> 
-- 
------------------------------------------------------------
Gerd Stolpmann * Viktoriastr. 45 * 64293 Darmstadt * Germany 
gerd@gerd-stolpmann.de          http://www.gerd-stolpmann.de
Phone: +49-6151-153855                  Fax: +49-6151-997714
------------------------------------------------------------



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

end of thread, other threads:[~2009-05-24 10:53 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-05-23 21:17 Strange buffering interaction: stdout / file_descr Alexander Fuchs
2009-05-24 10:55 ` [Caml-list] " Gerd Stolpmann

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