Timeouts in OCaml

Timeouts are important for programs that connect to external resources or that otherwise need to bound execution time. Implementing timeouts in OCaml can be done using the Unix module and the select function.

open Unix

let () =
  (* For our example, suppose we're executing an external
     call that returns its result after 2 seconds. We'll
     use a pipe to simulate this behavior. *)
  let (read_fd, write_fd) = pipe () in
  let _ = Thread.create (fun () ->
    Thread.delay 2.0;
    let _ = write write_fd "result 1" 0 8 in
    close write_fd
  ) () in

  (* Here's the select implementing a timeout.
     We'll wait for either data to be available on the pipe
     or for the timeout to occur. *)
  match select [read_fd] [] [] 1.0 with
  | [], _, _ ->
      print_endline "timeout 1"
  | [ready_fd], _, _ ->
      let buffer = Bytes.create 8 in
      let _ = read ready_fd buffer 0 8 in
      print_endline (Bytes.to_string buffer)
  | _ -> failwith "Unexpected select result";

  close read_fd;

  (* If we allow a longer timeout of 3s, then the read
     from the pipe will succeed and we'll print the result. *)
  let (read_fd, write_fd) = pipe () in
  let _ = Thread.create (fun () ->
    Thread.delay 2.0;
    let _ = write write_fd "result 2" 0 8 in
    close write_fd
  ) () in

  match select [read_fd] [] [] 3.0 with
  | [], _, _ ->
      print_endline "timeout 2"
  | [ready_fd], _, _ ->
      let buffer = Bytes.create 8 in
      let _ = read ready_fd buffer 0 8 in
      print_endline (Bytes.to_string buffer)
  | _ -> failwith "Unexpected select result";

  close read_fd

Running this program shows the first operation timing out and the second succeeding.

$ ocamlc unix.cma threads.cma timeouts.ml -o timeouts
$ ./timeouts
timeout 1
result 2

In this OCaml version:

  1. We use the Unix.pipe function to create a pipe, which simulates an external call.
  2. We use Thread.create to start a new thread that writes to the pipe after a delay.
  3. The Unix.select function is used to implement the timeout. It waits for either data to be available on the pipe or for the timeout to occur.
  4. We use pattern matching to handle the result of select.
  5. The Thread.delay function is used to simulate the delay in the “external call”.

Note that OCaml’s standard library doesn’t have a direct equivalent to Go’s channels, so we use pipes and threads to achieve similar functionality. The select function in OCaml works with file descriptors rather than channels.