(*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *)

(** Type-safe versions of the channels in Pervasives/Stdlib. *)

type 'a in_channel

type 'a out_channel

type ('in_, 'out) channel_pair = 'in_ in_channel * 'out out_channel

val to_channel : 'a out_channel -> ?flags:Marshal.extern_flags list -> ?flush:bool -> 'a -> unit

val from_channel : ?timeout:Timeout.t -> 'a in_channel -> 'a

val flush : 'a out_channel -> unit

(* This breaks the type safety, but is necessary in order to allow select() *)
val descr_of_in_channel : 'a in_channel -> Unix.file_descr

val descr_of_out_channel : 'a out_channel -> Unix.file_descr

val cast_in : 'a in_channel -> Timeout.in_channel

val cast_out : 'a out_channel -> Stdlib.out_channel

val close_out : 'a out_channel -> unit

val output_string : 'a out_channel -> string -> unit

val close_in : 'a in_channel -> unit

val input_char : 'a in_channel -> char

val input_value : 'a in_channel -> 'b

(** Spawning new process *)

(* In the absence of 'fork' on Windows, its usage must be restricted
   to Unix specifics parts.

   This module provides a mechanism to "spawn" new instance of the
   current program, but with a custom entry point (e.g. DfindServer,
   ...). Then, alternate entry points should not depend on global
   references that may not have been (re)initialised in the new
   process.

   All required data must be passed through the typed channels.
   associated to the spawned process.
*)

(* Alternate entry points *)
type ('param, 'input, 'output) entry

(* Alternate entry points must be registered at toplevel, i.e.
   every call to `Daemon.register_entry_point` must have been
   evaluated when `Daemon.check_entry_point` is called at the
   beginning of `ServerMain.start`. *)
val register_entry_point :
  string -> ('param -> ('input, 'output) channel_pair -> unit) -> ('param, 'input, 'output) entry

val name_of_entry : ('param, 'input, 'output) entry -> string

(* Handler upon spawn and forked process. *)
type ('in_, 'out) handle = {
  channels: ('in_, 'out) channel_pair;
  pid: int;
}

(* for unit tests *)
val devnull : unit -> ('a, 'b) handle

val fd_of_path : string -> Unix.file_descr

val null_fd : unit -> Unix.file_descr

(* Spawn a new instance of the current process, and execute the
   alternate entry point. *)
val spawn :
  ?channel_mode:[ `pipe | `socket ] ->
  ?name:string ->
  (* Where the daemon's input and output should go *)
  Unix.file_descr * Unix.file_descr * Unix.file_descr ->
  ('param, 'input, 'output) entry ->
  'param ->
  ('output, 'input) handle

(** Close the typed channels associated to a 'spawned' child,
  flushing all buffered write operations. Does nothing when applied to an
  already closed channel. May raise [Sys_error] if the operating system
  signals an error when flushing or closing. *)
val close : ('a, 'b) handle -> unit

(** Close the typed channels associated to a 'spawned' child,
  flushing all buffered write operations. Does nothing when applied to an
  already closed channel. Same as [close] but ignore all errors. *)
val close_noerr : ('a, 'b) handle -> unit

(* Kill a 'spawned' child and close the associated typed channels. *)
val kill : ('a, 'b) handle -> unit

(* Main function, that execute a alternate entry point.
   It should be called only once. Just before the main entry point.
   This function does not return when a custom entry point is selected. *)
val check_entry_point : unit -> unit
