daemonize_wait with async

49 views
Skip to first unread message

Ashish Agarwal

unread,
Oct 27, 2015, 1:01:19 PM10/27/15
to ocaml...@googlegroups.com
I can't get Daemon.daemonize_wait to work as intended. Here's a test program. The intention is to report the PID of the daemon process to the calling shell, but it keeps getting printed to the redirected file. What am I doing wrong?

---foo.ml---
open Core.Std
open Async.Std

let daemon_work () : unit Deferred.t =
  let pid = Unix.getpid() in
  printf "hello from daemon with pid %s\n" (Pid.to_string pid);
  Deferred.unit

let () =
  let release_parent =
    Daemon.daemonize_wait
      ~redirect_stdout:(`File_truncate "/tmp/stdout")
      ()
  in
  let pid = Unix.getpid() in
  printf "want this printed to calling shell: daemon started with pid %s\n" (Pid.to_string pid)
  ;unstage release_parent ()
  ;don't_wait_for (daemon_work())
  ;never_returns (Scheduler.go())

$ ocamlfind ocamlopt -package async -thread -linkpkg foo.ml -o foo

$ ./foo
(* no output*)

$ cat /tmp/stdout 
want this printed to calling shell: daemon started with pid 12191
hello from daemon with pid 12191

Malcolm Matalka

unread,
Oct 27, 2015, 3:25:16 PM10/27/15
to ocaml...@googlegroups.com

Fwiw, self daemonizing code is finally being accepted as an antipattern. If possible, you should avoid doing it and let an init system take care of it for you.

That being said, I'm completely useless in answering your  question, sorry.

--
You received this message because you are subscribed to the Google Groups "ocaml-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ocaml-core+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Paolo Donadeo

unread,
Oct 27, 2015, 7:11:25 PM10/27/15
to Core mailing list
daemonize_wait by default redirects stdin, stdout and stderr to /dev/null, after che release closure has been called. See this link:


You can decide not to redirect at all, like in this example:

open Core.Std
open Async.Std

let main_loop release_parent () =
  let pid = Unix.getpid () |> Pid.to_int in
  let () = Staged.unstage release_parent () in

  (* HERE WE START MANY OTHER ASYNC THREADS USING THE USUAL OPERATORS *)

  return pid

let main () =
  let release_parent =
    Daemon.daemonize_wait ~redirect_stdout:`Do_not_redirect ~cd:(Core.Std.Sys.getcwd ()) () in

  main_loop release_parent () >>| (fun pid ->
      Printf.printf "The PID is %d\n%!" pid)
  |> Deferred.don't_wait_for;
  never_returns (Scheduler.go ())

let command =
  Command.async_basic
    ~summary:"daemonize_wait example"
    Command.Spec.empty
    main

let () = Command.run command

or to print and then release:

open Core.Std
open Async.Std

let main_loop release_parent () =
  let pid = Unix.getpid () |> Pid.to_int in
  Printf.printf "The PID is %d\n%!" pid;
  let () = Staged.unstage release_parent () in

  (* HERE WE START MANY OTHER ASYNC THREADS USING THE USUAL OPERATORS *)

  return ()

let main () =
  let release_parent =
    Daemon.daemonize_wait ~cd:(Core.Std.Sys.getcwd ()) () in

  main_loop release_parent () |> Deferred.don't_wait_for;
  never_returns (Scheduler.go ())

let command =
  Command.async_basic
    ~summary:"daemonize_wait example"
    Command.Spec.empty
    main

let () = Command.run command

Hope this fits your needs.




--
You received this message because you are subscribed to the Google Groups "ocaml-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ocaml-core+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Paolo

Ashish Agarwal

unread,
Oct 30, 2015, 4:52:43 PM10/30/15
to ocaml...@googlegroups.com
Hi. Thanks for the reply. I'm aiming for this:

- print the PID of the daemon process to stderr, so I get some feedback that the daemon started correctly
- redirect all stdout in the daemon process to some file, so that the output is not lost

Your first example prints everything to stdout. Your second example loses everything to /dev/null. I tried some more permutations of your code, but I still can't get the desired behavior, which I thought is the whole point of daemonize_wait.

On the other hand, this example is my entire use case. Right now, I have all output redirected using plain `daemonize`, and I have to take the extra step of checking a log file to see if the daemon started. Not too big of a deal, just a bit annoying during development when I'm starting a daemon frequently. It's not important in production, as Malcolm points out.

Paolo Donadeo

unread,
Oct 30, 2015, 6:49:14 PM10/30/15
to Core mailing list
On Fri, Oct 30, 2015 at 9:52 PM, Ashish Agarwal <agarw...@gmail.com> wrote:
Your first example prints everything to stdout.

Yes.

 
Your second example loses everything to /dev/null.

Actually, not on my computer, "It works on my PC © ®" :-)

The second example print on stdout the PID of the daemon (the child process), then redirects everything else on /dev/null and the child remains in memory (inactive) forever.

The following code does the same but the child quits after 30 seconds:

open Core.Std
open Async.Std

let main_loop release_parent () =
  let pid = Unix.getpid () |> Pid.to_int in
  Printf.printf "The PID is %d\n%!" pid;
  let () = Staged.unstage release_parent () in

  (* HERE WE START MANY OTHER ASYNC THREADS USING THE USUAL OPERATORS *)
  Printf.printf "This is hidden!\n%!";

  after (sec 30.0) >>= fun () ->
  return (Shutdown.shutdown 0)

let main () =
  let release_parent =
    Daemon.daemonize_wait ~cd:(Core.Std.Sys.getcwd ()) () in

  main_loop release_parent () |> Deferred.don't_wait_for;
  never_returns (Scheduler.go ())

let command =
  Command.async_basic
    ~summary:"daemonize_wait example"
    Command.Spec.empty
    main

let () = Command.run command

Compile and run with:

$ ocamlfind ocamlopt -package async -thread -linkpkg main.ml -o main && ./main
The PID is 6599

The program starts and immediately print the PID of the child, quits and remain in memory for 30 seconds. See the following screenshots:

Inline image 1

Inline image 2

In the actual daemon I have in production I don't print anything on stdout. Instead I create a PID file, as usual for this kind of programs, and I use the logging modules provided by the Log.Global module of async_extra:


On shutdown (I trap SIGTERM with Signal.handle) I remove the PID file and close the logging file.


--
Paolo

Ashish Agarwal

unread,
Nov 4, 2015, 12:48:49 PM11/4/15
to ocaml...@googlegroups.com
Actually your code doesn't compile for me. I copied your latest example exactly and get:

$ ocamlfind ocamlopt -package async -thread -linkpkg main.ml -o main
File "main.ml", line 6, characters 2-15:
Error: This expression has type
         [> `Probably_should_not_use_blocking_Core_Printf_functions_with_Async ]
       This is not a function; it cannot be applied.

Previously I assumed your use of `Printf.printf` was an irrelevant stylistic choice, and had replaced it with `printf`. But now I recall that Async overrides Printf.printf to cause this type error, so I'm not sure how you're compiling at all. Are you on a very old version of Async? However, I think this implementation of Printf.printf has been around for a long time.

Since I can't compile your example, I'm not yet sure if your choice of blocking print functions is the reason for the different behavior.



--

Ashish Agarwal

unread,
Nov 4, 2015, 12:51:45 PM11/4/15
to ocaml...@googlegroups.com
Since I can't compile your example, I'm not yet sure if your choice of blocking print functions is the reason for the different behavior.

Of course I can call Core's functions, so yes confirmed that is the reason. It's not exactly the solution I was looking for, but maybe I can work with that.

Paolo Donadeo

unread,
Nov 5, 2015, 9:46:10 AM11/5/15
to Core mailing list
No, this is really strange. I can compile the exact source I posted, with:

$ ocamlfind ocamlopt -package async -thread -linkpkg main.ml -o main

without a warning!

What's your toolchain? I'm using an OCaml compiler version 4.02.1 and async 112.35.00, everything installed using OPAM. Maybe I'm a bit outdated...



--
Paolo

Ashish Agarwal

unread,
Nov 5, 2015, 10:31:11 AM11/5/15
to ocaml...@googlegroups.com
> async 112.35.00

Yes, that is the difference. I'm on the latest Async, and the change to Async.Std.Printf is more recent than I thought (I haven't used the Printf prefix for years, so never noticed).

But is the use of blocking functions intentional? Are you saying that's the key to making my original request work?

Paolo Donadeo

unread,
Nov 5, 2015, 12:22:48 PM11/5/15
to Core mailing list
On Thu, Nov 5, 2015 at 4:30 PM, Ashish Agarwal <agarw...@gmail.com> wrote:
But is the use of blocking functions intentional?

No, sorry it's not intentional at all :-/

 
Are you saying that's the key to making my original request work?

No, it's not, and it's not advisable, in my opinion. As I told you, in a server I have in production I create a PID file and write a log file, I don't print anything to stdout.

Sorry for the misunderstanding...


--
Paolo

Ashish Agarwal

unread,
Nov 5, 2015, 12:42:43 PM11/5/15
to ocaml...@googlegroups.com
Okay, thanks for all the suggestions nonetheless. Yes, I also avoid printing to stdout/stderr in my server. This is mostly now for curiosity and understanding more deeply daemonization, async, and their interaction.

--
Reply all
Reply to author
Forward
0 new messages