user2347300
user2347300

Reputation: 39

exit 0 not terminating program

I'm developing a client-server application in OCaml using the high-level network connection functions available in OCaml Unix library, following the steps available at https://caml.inria.fr/pub/docs/oreilly-book/html/book-ora187.html. These functions are:

val open_connection : sockaddr -> in_channel * out_channel

val shutdown_connection : in_channel -> unit

val establish_server : (in_channel -> out_channel -> unit) -> sockaddr -> unit

I'm able to successfully build the client and verifier but I cannot terminate the server using the exit OCaml function.

My (minimal) server code is the following:

let handle_service ic oc =
  try while true do
        ...
        if ... then raise Finish_interaction
      done ;
  with
  | Finish_interaction -> raise Sys.Break
  | _ -> ...

let main_server serv_fun =
  if Array.length Sys.argv < 4 then ...
  else try
      let port = int_of_string Sys.argv.(1) in
      ...
      let my_address = Unix.inet_addr_loopback in
      Unix.establish_server serv_fun (Unix.ADDR_INET(my_address, port)) 
    with
    | Sys.Break -> exit 0 (* PROGRAM DOES NOT TERMINATE *)
    | _ -> ...

let go_server () = 
  Unix.handle_unix_error main_server handle_service ;;

go_server ()

I can successfully catch the Sys.Break exception, but the exit 0 code after catching that exception does nothing and the server just keeps running and waiting for another client connection.

OCaml documentation says the following regarding establish_server:

The function Unix.establish_server never returns normally.

I don't know if this implies that I can never terminate the program without user interaction (via Ctrl + C, for example).

In a nutshell, how can I terminate my server? The client does terminate after shutdown_connection but the server keeps waiting for incoming connections. BTW, I'm compiling my code using OCamlbuild.

Upvotes: 0

Views: 429

Answers (2)

user2347300
user2347300

Reputation: 39

I managed to solve my problem, without having the need to send signals between processes.

I tweaked the establish_server function so that it doesn't create a loop that answers all incoming connections. By removing the loop, it will only answer one incoming connection, and then it simply ends its execution.

Here is the code for the new establish_server function:

let establish_server server_fun sockaddr =
  let domain = Unix.domain_of_sockaddr sockaddr in
  let sock = Unix.socket domain Unix.SOCK_STREAM 0 
  in Unix.bind sock sockaddr ;
     Unix.listen sock 3;
     (*while true do*)
     let (s, caller) = Unix.accept sock 
     in match Unix.fork() with
          0 -> if Unix.fork() <> 0 then exit 0 ; 
               let inchan = Unix.in_channel_of_descr s 
               and outchan = Unix.out_channel_of_descr s 
               in server_fun inchan outchan ;
                  close_in inchan ;
                  close_out outchan ;
                  exit 0
          | id -> Unix.close s; ignore(Unix.waitpid [] id)
(*done ;;*)

Removing the two commented lines gives you the original version of it.

Thanks everyone for the answers!

Upvotes: 0

Martin Jambon
Martin Jambon

Reputation: 4939

From the documentation of Unix.establish_server:

A new process is created for each connection

I recommend printing the process IDs (Unix.getpid ()) to make sure the process calling exit is the one you're expecting (the parent).

Another thing you can check is that the program is not stuck in the execution of an at_exit callback. For example, the following program enters an infinite loop during the call to exit:

let () =
  at_exit (fun () -> while true do () done);
  print_endline "all is well!";
  exit 0

(probably not the problem you're having but could be useful to future visitors)

Upvotes: 1

Related Questions