hop-2012/thirdparty/lwt-2.3.2/src/unix/lwt_unix.ml

2682 lines
76 KiB
OCaml

(* Lightweight thread library for Objective Caml
* http://www.ocsigen.org/lwt
* Module Lwt_unix
* Copyright (C) 2005-2008 Jérôme Vouillon
* Laboratoire PPS - CNRS Université Paris Diderot
* 2009 Jérémie Dimino
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, with linking exceptions;
* either version 2.1 of the License, or (at your option) any later
* version. See COPYING file for details.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*)
#include "src/unix/lwt_config.ml"
open Lwt
(* +-----------------------------------------------------------------+
| Configuration |
+-----------------------------------------------------------------+ *)
type async_method =
| Async_none
| Async_detach
| Async_switch
let default_async_method_var = ref Async_detach
let () =
try
match Sys.getenv "LWT_ASYNC_METHOD" with
| "none" ->
default_async_method_var := Async_none
| "detach" ->
default_async_method_var := Async_detach
| "switch" ->
default_async_method_var := Async_switch
| str ->
Printf.eprintf
"%s: invalid lwt async method: '%s', must be 'none', 'detach' or 'switch'\n%!"
(Filename.basename Sys.executable_name) str
with Not_found ->
()
let default_async_method () = !default_async_method_var
let set_default_async_method am = default_async_method_var := am
let async_method_key = Lwt.new_key ()
let async_method () =
match Lwt.get async_method_key with
| Some am -> am
| None -> !default_async_method_var
let with_async_none f =
with_value async_method_key (Some Async_none) f
let with_async_detach f =
with_value async_method_key (Some Async_detach) f
let with_async_switch f =
with_value async_method_key (Some Async_switch) f
(* +-----------------------------------------------------------------+
| Notifications management |
+-----------------------------------------------------------------+ *)
(* Informations about a notifier *)
type notifier = {
notify_handler : unit -> unit;
(* The callback *)
notify_once : bool;
(* Whether to remove the notifier after the reception of the first
notification *)
}
module Notifiers = Hashtbl.Make(struct
type t = int
let equal (x : int) (y : int) = x = y
let hash (x : int) = x
end)
let notifiers = Notifiers.create 1024
let current_notification_id = ref 0
let rec find_free_id id =
if Notifiers.mem notifiers id then
find_free_id (id + 1)
else
id
let make_notification ?(once=false) f =
let id = find_free_id (!current_notification_id + 1) in
current_notification_id := id;
Notifiers.add notifiers id { notify_once = once; notify_handler = f };
id
let stop_notification id =
Notifiers.remove notifiers id
let set_notification id f =
let notifier = Notifiers.find notifiers id in
Notifiers.replace notifiers id { notifier with notify_handler = f }
(* +-----------------------------------------------------------------+
| Sleepers |
+-----------------------------------------------------------------+ *)
let sleep delay =
let waiter, wakener = Lwt.task () in
let ev = Lwt_engine.on_timer delay false (fun ev -> Lwt_engine.stop_event ev; Lwt.wakeup wakener ()) in
Lwt.on_cancel waiter (fun () -> Lwt_engine.stop_event ev);
waiter
let yield = Lwt_main.yield
let auto_yield timeout =
let limit = ref (Unix.gettimeofday () +. timeout) in
fun () ->
let current = Unix.gettimeofday () in
if current >= !limit then begin
limit := current +. timeout;
yield ();
end else
return ()
exception Timeout
let timeout d = sleep d >> Lwt.fail Timeout
let with_timeout d f = Lwt.pick [timeout d; Lwt.apply f ()]
(* +-----------------------------------------------------------------+
| Jobs |
+-----------------------------------------------------------------+ *)
type 'a job
external start_job : 'a job -> async_method -> bool = "lwt_unix_start_job"
(* Starts the given job with given parameters. It returns [true]
if the job is already terminated. *)
external check_job : 'a job -> int -> bool = "lwt_unix_check_job" "noalloc"
(* Check whether that a job has terminated or not. If it has not
yet terminated, it is marked so it will send a notification
when it finishes. *)
external cancel_job : 'a job -> unit = "lwt_unix_cancel_job" "noalloc"
(* Cancel the thread of the given job. *)
(* All running jobs. *)
let jobs = Lwt_sequence.create ()
(* Cancel all running jobs. *)
let rec cancel_jobs () =
match Lwt_sequence.take_opt_l jobs with
| Some w -> cancel w; cancel_jobs ()
| None -> ()
let wait_for_jobs () =
join (Lwt_sequence.fold_l (fun w l -> w :: l) jobs [])
let execute_job ?async_method ~job ~result ~free =
let async_method =
match async_method with
| Some am -> am
| None ->
match Lwt.get async_method_key with
| Some am -> am
| None -> !default_async_method_var
in
(* Starts the job. *)
let job_done = start_job job async_method in
let w =
lwt status =
if job_done then
return None
else
(* Create the notification for asynchronous wakeup. *)
let id = make_notification ~once:true ignore in
try_lwt
(* Give some time to the job before we fallback to
asynchronous notification. *)
lwt () = pause () in
if check_job job id then begin
stop_notification id;
return None
end else
return (Some id)
with Canceled as exn ->
cancel_job job;
(* Free resources when the job terminates. *)
if check_job job id then begin
stop_notification id;
free job
end else
set_notification id (fun () -> free job);
raise_lwt exn
in
match status with
| None ->
(* The job has already terminated, read and return the result
immediatly. *)
let thread =
try
return (result job)
with exn ->
fail exn
in
free job;
thread
| Some id ->
(* The job has not terminated, setup the notification for the
asynchronous wakeup. *)
let waiter, wakener = task () in
set_notification id
(fun () ->
begin
try
wakeup wakener (result job);
with exn ->
wakeup_exn wakener exn
end;
free job);
on_cancel waiter
(fun () ->
cancel_job job;
set_notification id (fun () -> free job));
waiter
in
if state w = Sleep then begin
(* Add the job to the sequence of all jobs. *)
let node = Lwt_sequence.add_l (w >> return ()) jobs in
(* Remove it on termination. *)
on_termination w (fun () -> Lwt_sequence.remove node)
end;
w
(* +-----------------------------------------------------------------+
| File descriptor wrappers |
+-----------------------------------------------------------------+ *)
type state = Opened | Closed | Aborted of exn
type file_descr = {
fd : Unix.file_descr;
(* The underlying unix file descriptor *)
mutable state: state;
(* The state of the file descriptor *)
mutable set_flags : bool;
(* Whether to set file flags *)
mutable blocking : bool Lwt.t Lazy.t;
(* Is the file descriptor in blocking or non-blocking mode *)
mutable event_readable : Lwt_engine.event option;
(* The event used to check the file descriptor for readability. *)
mutable event_writable : Lwt_engine.event option;
(* The event used to check the file descriptor for writability. *)
hooks_readable : (unit -> unit) Lwt_sequence.t;
(* Hooks to call when the file descriptor becomes readable. *)
hooks_writable : (unit -> unit) Lwt_sequence.t;
(* Hooks to call when the file descriptor becomes writable. *)
}
#if windows
external is_socket : Unix.file_descr -> bool = "lwt_unix_is_socket" "noalloc"
let is_blocking ?blocking ?(set_flags=true) fd =
if is_socket fd then
match blocking, set_flags with
| Some state, false ->
lazy(return state)
| Some true, true ->
Unix.clear_nonblock fd;
lazy(return true)
| Some false, true ->
Unix.set_nonblock fd;
lazy(return false)
| None, false ->
lazy(return false)
| None, true ->
Unix.set_nonblock fd;
lazy(return false)
else
match blocking with
| Some state ->
lazy(return state)
| None ->
lazy(return true)
#else
external guess_blocking_job : Unix.file_descr -> [ `unix_guess_blocking ] job = "lwt_unix_guess_blocking_job"
external guess_blocking_result : [ `unix_guess_blocking ] job -> bool = "lwt_unix_guess_blocking_result" "noalloc"
external guess_blocking_free : [ `unix_guess_blocking ] job -> unit = "lwt_unix_guess_blocking_free" "noalloc"
let guess_blocking fd =
execute_job (guess_blocking_job fd) guess_blocking_result guess_blocking_free
let is_blocking ?blocking ?(set_flags=true) fd =
match blocking, set_flags with
| Some state, false ->
lazy(return state)
| Some true, true ->
Unix.clear_nonblock fd;
lazy(return true)
| Some false, true ->
Unix.set_nonblock fd;
lazy(return false)
| None, false ->
lazy(guess_blocking fd)
| None, true ->
lazy(guess_blocking fd >>= function
| true ->
Unix.clear_nonblock fd;
return true
| false ->
Unix.set_nonblock fd;
return false)
#endif
let mk_ch ?blocking ?(set_flags=true) fd = {
fd = fd;
state = Opened;
set_flags = set_flags;
blocking = is_blocking ?blocking ~set_flags fd;
event_readable = None;
event_writable = None;
hooks_readable = Lwt_sequence.create ();
hooks_writable = Lwt_sequence.create ();
}
let rec check_descriptor ch =
match ch.state with
| Opened ->
()
| Aborted e ->
raise e
| Closed ->
raise (Unix.Unix_error (Unix.EBADF, "check_descriptor", ""))
let state ch = ch.state
let blocking ch =
check_descriptor ch;
Lazy.force ch.blocking
let set_blocking ?(set_flags=true) ch blocking =
check_descriptor ch;
ch.set_flags <- set_flags;
ch.blocking <- is_blocking ~blocking ~set_flags ch.fd
#if windows
let stub_readable fd = Unix.select [fd] [] [] (-1.0) <> ([], [], [])
let stub_writable fd = Unix.select [] [fd] [] (-1.0) <> ([], [], [])
#else
external stub_readable : Unix.file_descr -> bool = "lwt_unix_readable"
external stub_writable : Unix.file_descr -> bool = "lwt_unix_writable"
#endif
let readable ch =
check_descriptor ch;
stub_readable ch.fd
let writable ch =
check_descriptor ch;
stub_writable ch.fd
let set_state ch st =
ch.state <- st
let clear_events ch =
Lwt_sequence.iter_node_l (fun node -> Lwt_sequence.remove node; Lwt_sequence.get node ()) ch.hooks_readable;
Lwt_sequence.iter_node_l (fun node -> Lwt_sequence.remove node; Lwt_sequence.get node ()) ch.hooks_writable;
begin
match ch.event_readable with
| Some ev ->
ch.event_readable <- None;
Lwt_engine.stop_event ev
| None ->
()
end;
begin
match ch.event_writable with
| Some ev ->
ch.event_writable <- None;
Lwt_engine.stop_event ev
| None ->
()
end
let abort ch e =
if ch.state <> Closed then begin
set_state ch (Aborted e);
clear_events ch
end
let unix_file_descr ch = ch.fd
let of_unix_file_descr = mk_ch
let stdin = of_unix_file_descr ~set_flags:false ~blocking:true Unix.stdin
let stdout = of_unix_file_descr ~set_flags:false ~blocking:true Unix.stdout
let stderr = of_unix_file_descr ~set_flags:false ~blocking:true Unix.stderr
(* +-----------------------------------------------------------------+
| Actions on file descriptors |
+-----------------------------------------------------------------+ *)
type io_event = Read | Write
exception Retry
exception Retry_write
exception Retry_read
type 'a outcome =
| Success of 'a
| Exn of exn
| Requeued of io_event
(* Wait a bit, then stop events that are no more used. *)
let stop_events ch =
on_success
(pause ())
(fun () ->
if Lwt_sequence.is_empty ch.hooks_readable then begin
match ch.event_readable with
| Some ev ->
ch.event_readable <- None;
Lwt_engine.stop_event ev
| None ->
()
end;
if Lwt_sequence.is_empty ch.hooks_writable then begin
match ch.event_writable with
| Some ev ->
ch.event_writable <- None;
Lwt_engine.stop_event ev
| None ->
()
end)
let register_readable ch =
if ch.event_readable = None then
ch.event_readable <- Some(Lwt_engine.on_readable ch.fd (fun _ -> Lwt_sequence.iter_l (fun f -> f ()) ch.hooks_readable))
let register_writable ch =
if ch.event_writable = None then
ch.event_writable <- Some(Lwt_engine.on_writable ch.fd (fun _ -> Lwt_sequence.iter_l (fun f -> f ()) ch.hooks_writable))
(* Retry a queued syscall, [wakener] is the thread to wakeup if the
action succeeds: *)
let rec retry_syscall node event ch wakener action =
let res =
try
check_descriptor ch;
Success(action ())
with
| Retry
| Unix.Unix_error((Unix.EAGAIN | Unix.EWOULDBLOCK | Unix.EINTR), _, _)
| Sys_blocked_io ->
(* EINTR because we are catching SIG_CHLD hence the system
call might be interrupted to handle the signal; this lets
us restart the system call eventually. *)
Requeued event
| Retry_read ->
Requeued Read
| Retry_write ->
Requeued Write
| e ->
Exn e
in
match res with
| Success v ->
Lwt_sequence.remove !node;
stop_events ch;
Lwt.wakeup wakener v
| Exn e ->
Lwt_sequence.remove !node;
stop_events ch;
Lwt.wakeup_exn wakener e
| Requeued event' ->
if event <> event' then begin
Lwt_sequence.remove !node;
stop_events ch;
match event' with
| Read ->
node := Lwt_sequence.add_r (fun () -> retry_syscall node Read ch wakener action) ch.hooks_readable ;
register_readable ch
| Write ->
node := Lwt_sequence.add_r (fun () -> retry_syscall node Write ch wakener action) ch.hooks_writable;
register_writable ch
end
let dummy = Lwt_sequence.add_r ignore (Lwt_sequence.create ())
let register_action event ch action =
let waiter, wakener = Lwt.task () in
match event with
| Read ->
let node = ref dummy in
node := Lwt_sequence.add_r (fun () -> retry_syscall node Read ch wakener action) ch.hooks_readable;
on_cancel waiter (fun () -> Lwt_sequence.remove !node; stop_events ch);
register_readable ch;
waiter
| Write ->
let node = ref dummy in
node := Lwt_sequence.add_r (fun () -> retry_syscall node Write ch wakener action) ch.hooks_writable;
on_cancel waiter (fun () -> Lwt_sequence.remove !node; stop_events ch);
register_writable ch;
waiter
(* Wraps a system call *)
let wrap_syscall event ch action =
try
check_descriptor ch;
lwt blocking = Lazy.force ch.blocking in
if not blocking || (event = Read && stub_readable ch.fd) || (event = Write && stub_writable ch.fd) then
return (action ())
else
register_action event ch action
with
| Retry
| Unix.Unix_error((Unix.EAGAIN | Unix.EWOULDBLOCK | Unix.EINTR), _, _)
| Sys_blocked_io ->
(* The action could not be completed immediatly, register it: *)
register_action event ch action
| Retry_read ->
register_action Read ch action
| Retry_write ->
register_action Write ch action
| e ->
raise_lwt e
(* +-----------------------------------------------------------------+
| Basic file input/output |
+-----------------------------------------------------------------+ *)
type open_flag =
Unix.open_flag =
| O_RDONLY
| O_WRONLY
| O_RDWR
| O_NONBLOCK
| O_APPEND
| O_CREAT
| O_TRUNC
| O_EXCL
| O_NOCTTY
| O_DSYNC
| O_SYNC
| O_RSYNC
#if windows
let openfile name flags perms =
return (of_unix_file_descr (Unix.openfile name flags perms))
#else
external open_job : string -> Unix.open_flag list -> int -> [ `unix_open ] job = "lwt_unix_open_job"
external open_result : [ `unix_open ] job -> Unix.file_descr * bool = "lwt_unix_open_result"
external open_free : [ `unix_open ] job -> unit = "lwt_unix_open_free" "noalloc"
let openfile name flags perms =
lwt fd, blocking =
execute_job
(open_job name flags perms)
open_result
open_free
in
return (of_unix_file_descr ~blocking fd)
#endif
#if windows
let close ch =
if ch.state = Closed then check_descriptor ch;
set_state ch Closed;
clear_events ch;
return (Unix.close ch.fd)
#else
external close_job : Unix.file_descr -> [ `unix_close ] job = "lwt_unix_close_job"
external close_result : [ `unix_close ] job -> unit = "lwt_unix_close_result"
external close_free : [ `unix_close ] job -> unit = "lwt_unix_close_free" "noalloc"
let close ch =
if ch.state = Closed then check_descriptor ch;
set_state ch Closed;
clear_events ch;
execute_job (close_job ch.fd) close_result close_free
#endif
let wait_read ch =
try_lwt
if readable ch then
return ()
else
register_action Read ch ignore
external stub_read : Unix.file_descr -> string -> int -> int -> int = "lwt_unix_read"
external read_job : Unix.file_descr -> int -> [ `unix_read ] job = "lwt_unix_read_job"
external read_result : [ `unix_read ] job -> string -> int -> int = "lwt_unix_read_result"
external read_free : [ `unix_read ] job -> unit = "lwt_unix_read_free" "noalloc"
let read ch buf pos len =
if pos < 0 || len < 0 || pos > String.length buf - len then
invalid_arg "Lwt_unix.read"
else
Lazy.force ch.blocking >>= function
| true ->
lwt () = wait_read ch in
execute_job (read_job ch.fd len) (fun job -> read_result job buf pos) read_free
| false ->
wrap_syscall Read ch (fun () -> stub_read ch.fd buf pos len)
let wait_write ch =
try_lwt
if writable ch then
return ()
else
register_action Write ch ignore
external stub_write : Unix.file_descr -> string -> int -> int -> int = "lwt_unix_write"
external write_job : Unix.file_descr -> string -> int -> int -> [ `unix_write ] job = "lwt_unix_write_job"
external write_result : [ `unix_write ] job -> int = "lwt_unix_write_result"
external write_free : [ `unix_write ] job -> unit = "lwt_unix_write_free" "noalloc"
let write ch buf pos len =
if pos < 0 || len < 0 || pos > String.length buf - len then
invalid_arg "Lwt_unix.write"
else
Lazy.force ch.blocking >>= function
| true ->
lwt () = wait_write ch in
execute_job (write_job ch.fd buf pos len) write_result write_free
| false ->
wrap_syscall Write ch (fun () -> stub_write ch.fd buf pos len)
(* +-----------------------------------------------------------------+
| Seeking and truncating |
+-----------------------------------------------------------------+ *)
type seek_command =
Unix.seek_command =
| SEEK_SET
| SEEK_CUR
| SEEK_END
#if windows
let lseek ch offset whence =
check_descriptor ch;
return (Unix.lseek ch.fd offset whence)
#else
external lseek_job : Unix.file_descr -> int -> Unix.seek_command -> [ `unix_lseek ] job = "lwt_unix_lseek_job"
external lseek_result : [ `unix_lseek ] job -> int = "lwt_unix_lseek_result"
external lseek_free : [ `unix_lseek ] job -> unit = "lwt_unix_lseek_free"
let lseek ch offset whence =
check_descriptor ch;
execute_job (lseek_job ch.fd offset whence) lseek_result lseek_free
#endif
#if windows
let truncate name offset =
return (Unix.truncate name offset)
#else
external truncate_job : string -> int -> [ `unix_truncate ] job = "lwt_unix_truncate_job"
external truncate_result : [ `unix_truncate ] job -> unit = "lwt_unix_truncate_result"
external truncate_free : [ `unix_truncate ] job -> unit = "lwt_unix_truncate_free"
let truncate name offset =
execute_job (truncate_job name offset) truncate_result truncate_free
#endif
#if windows
let ftruncate ch offset =
check_descriptor ch;
return (Unix.ftruncate ch.fd offset)
#else
external ftruncate_job : Unix.file_descr -> int -> [ `unix_ftruncate ] job = "lwt_unix_ftruncate_job"
external ftruncate_result : [ `unix_ftruncate ] job -> unit = "lwt_unix_ftruncate_result"
external ftruncate_free : [ `unix_ftruncate ] job -> unit = "lwt_unix_ftruncate_free"
let ftruncate ch offset =
check_descriptor ch;
execute_job (ftruncate_job ch.fd offset) ftruncate_result ftruncate_free
#endif
(* +-----------------------------------------------------------------+
| Syncing |
+-----------------------------------------------------------------+ *)
external fsync_job : Unix.file_descr -> [ `unix_fsync ] job = "lwt_unix_fsync_job"
external fsync_result : [ `unix_fsync ] job -> unit = "lwt_unix_fsync_result"
external fsync_free : [ `unix_fsync ] job -> unit = "lwt_unix_fsync_free"
let fsync ch =
check_descriptor ch;
execute_job (fsync_job ch.fd) fsync_result fsync_free
#if HAVE_FDATASYNC
external fdatasync_job : Unix.file_descr -> [ `unix_fdatasync ] job = "lwt_unix_fdatasync_job"
external fdatasync_result : [ `unix_fdatasync ] job -> unit = "lwt_unix_fdatasync_result"
external fdatasync_free : [ `unix_fdatasync ] job -> unit = "lwt_unix_fdatasync_free"
let fdatasync ch =
check_descriptor ch;
execute_job (fdatasync_job ch.fd) fdatasync_result fdatasync_free
#else
let fdatasync ch =
fail (Lwt_sys.Not_available "fdatasync")
#endif
(* +-----------------------------------------------------------------+
| File status |
+-----------------------------------------------------------------+ *)
type file_perm = Unix.file_perm
type file_kind =
Unix.file_kind =
| S_REG
| S_DIR
| S_CHR
| S_BLK
| S_LNK
| S_FIFO
| S_SOCK
type stats =
Unix.stats =
{
st_dev : int;
st_ino : int;
st_kind : file_kind;
st_perm : file_perm;
st_nlink : int;
st_uid : int;
st_gid : int;
st_rdev : int;
st_size : int;
st_atime : float;
st_mtime : float;
st_ctime : float;
}
#if windows
let stat name =
return (Unix.stat name)
#else
external stat_job : string -> [ `unix_stat ] job = "lwt_unix_stat_job"
external stat_result : [ `unix_stat ] job -> Unix.stats = "lwt_unix_stat_result"
external stat_free : [ `unix_stat ] job -> unit = "lwt_unix_stat_free"
let stat name =
execute_job (stat_job name) stat_result stat_free
#endif
#if windows
let lstat name =
return (Unix.lstat name)
#else
external lstat_job : string -> [ `unix_lstat ] job = "lwt_unix_lstat_job"
external lstat_result : [ `unix_lstat ] job -> Unix.stats = "lwt_unix_lstat_result"
external lstat_free : [ `unix_lstat ] job -> unit = "lwt_unix_lstat_free"
let lstat name =
execute_job (lstat_job name) lstat_result lstat_free
#endif
#if windows
let fstat ch =
check_descriptor ch;
return (Unix.fstat ch.fd)
#else
external fstat_job : Unix.file_descr -> [ `unix_fstat ] job = "lwt_unix_fstat_job"
external fstat_result : [ `unix_fstat ] job -> Unix.stats = "lwt_unix_fstat_result"
external fstat_free : [ `unix_fstat ] job -> unit = "lwt_unix_fstat_free"
let fstat ch =
check_descriptor ch;
execute_job (fstat_job ch.fd) fstat_result fstat_free
#endif
#if windows
let isatty ch =
check_descriptor ch;
return (Unix.isatty ch.fd)
#else
external isatty_job : Unix.file_descr -> [ `unix_isatty ] job = "lwt_unix_isatty_job"
external isatty_result : [ `unix_isatty ] job -> bool = "lwt_unix_isatty_result"
external isatty_free : [ `unix_isatty ] job -> unit = "lwt_unix_isatty_free"
let isatty ch =
check_descriptor ch;
execute_job (isatty_job ch.fd) isatty_result isatty_free
#endif
(* +-----------------------------------------------------------------+
| File operations on large files |
+-----------------------------------------------------------------+ *)
module LargeFile =
struct
type stats =
Unix.LargeFile.stats =
{
st_dev : int;
st_ino : int;
st_kind : file_kind;
st_perm : file_perm;
st_nlink : int;
st_uid : int;
st_gid : int;
st_rdev : int;
st_size : int64;
st_atime : float;
st_mtime : float;
st_ctime : float;
}
#if windows
let lseek ch offset whence =
check_descriptor ch;
return (Unix.LargeFile.lseek ch.fd offset whence)
#else
external lseek_job : Unix.file_descr -> int64 -> Unix.seek_command -> [ `unix_lseek ] job = "lwt_unix_lseek_64_job"
external lseek_result : [ `unix_lseek ] job -> int64 = "lwt_unix_lseek_64_result"
external lseek_free : [ `unix_lseek ] job -> unit = "lwt_unix_lseek_64_free"
let lseek ch offset whence =
check_descriptor ch;
execute_job (lseek_job ch.fd offset whence) lseek_result lseek_free
#endif
#if windows
let truncate name offset =
return (Unix.LargeFile.truncate name offset)
#else
external truncate_job : string -> int64 -> [ `unix_truncate ] job = "lwt_unix_truncate_64_job"
external truncate_result : [ `unix_truncate ] job -> unit = "lwt_unix_truncate_64_result"
external truncate_free : [ `unix_truncate ] job -> unit = "lwt_unix_truncate_64_free"
let truncate name offset =
execute_job (truncate_job name offset) truncate_result truncate_free
#endif
#if windows
let ftruncate ch offset =
check_descriptor ch;
return (Unix.LargeFile.ftruncate ch.fd offset)
#else
external ftruncate_job : Unix.file_descr -> int64 -> [ `unix_ftruncate ] job = "lwt_unix_ftruncate_64_job"
external ftruncate_result : [ `unix_ftruncate ] job -> unit = "lwt_unix_ftruncate_64_result"
external ftruncate_free : [ `unix_ftruncate ] job -> unit = "lwt_unix_ftruncate_64_free"
let ftruncate ch offset =
check_descriptor ch;
execute_job (ftruncate_job ch.fd offset) ftruncate_result ftruncate_free
#endif
#if windows
let stat name =
return (Unix.LargeFile.stat name)
#else
external stat_job : string -> [ `unix_stat ] job = "lwt_unix_stat_64_job"
external stat_result : [ `unix_stat ] job -> Unix.LargeFile.stats = "lwt_unix_stat_64_result"
external stat_free : [ `unix_stat ] job -> unit = "lwt_unix_stat_64_free"
let stat name =
execute_job (stat_job name) stat_result stat_free
#endif
#if windows
let lstat name =
return (Unix.LargeFile.lstat name)
#else
external lstat_job : string -> [ `unix_lstat ] job = "lwt_unix_lstat_64_job"
external lstat_result : [ `unix_lstat ] job -> Unix.LargeFile.stats = "lwt_unix_lstat_64_result"
external lstat_free : [ `unix_lstat ] job -> unit = "lwt_unix_lstat_64_free"
let lstat name =
execute_job (lstat_job name) lstat_result lstat_free
#endif
#if windows
let fstat ch =
check_descriptor ch;
return (Unix.LargeFile.fstat ch.fd)
#else
external fstat_job : Unix.file_descr -> [ `unix_fstat ] job = "lwt_unix_fstat_64_job"
external fstat_result : [ `unix_fstat ] job -> Unix.LargeFile.stats = "lwt_unix_fstat_64_result"
external fstat_free : [ `unix_fstat ] job -> unit = "lwt_unix_fstat_64_free"
let fstat ch =
check_descriptor ch;
execute_job (fstat_job ch.fd) fstat_result fstat_free
#endif
end
(* +-----------------------------------------------------------------+
| Operations on file names |
+-----------------------------------------------------------------+ *)
#if windows
let unlink name =
return (Unix.unlink name)
#else
external unlink_job : string -> [ `unix_unlink ] job = "lwt_unix_unlink_job"
external unlink_result : [ `unix_unlink ] job -> unit = "lwt_unix_unlink_result"
external unlink_free : [ `unix_unlink ] job -> unit = "lwt_unix_unlink_free"
let unlink name =
execute_job (unlink_job name) unlink_result unlink_free
#endif
#if windows
let rename name1 name2 =
return (Unix.rename name1 name2)
#else
external rename_job : string -> string -> [ `unix_rename ] job = "lwt_unix_rename_job"
external rename_result : [ `unix_rename ] job -> unit = "lwt_unix_rename_result"
external rename_free : [ `unix_rename ] job -> unit = "lwt_unix_rename_free"
let rename name1 name2 =
execute_job (rename_job name1 name2) rename_result rename_free
#endif
#if windows
let link name1 name2 =
return (Unix.link name1 name2)
#else
external link_job : string -> string -> [ `unix_link ] job = "lwt_unix_link_job"
external link_result : [ `unix_link ] job -> unit = "lwt_unix_link_result"
external link_free : [ `unix_link ] job -> unit = "lwt_unix_link_free"
let link name1 name2 =
execute_job (link_job name1 name2) link_result link_free
#endif
(* +-----------------------------------------------------------------+
| File permissions and ownership |
+-----------------------------------------------------------------+ *)
#if windows
let chmod name perms =
return (Unix.chmod name perms)
#else
external chmod_job : string -> Unix.file_perm -> [ `unix_chmod ] job = "lwt_unix_chmod_job"
external chmod_result : [ `unix_chmod ] job -> unit = "lwt_unix_chmod_result"
external chmod_free : [ `unix_chmod ] job -> unit = "lwt_unix_chmod_free"
let chmod name perms =
execute_job (chmod_job name perms) chmod_result chmod_free
#endif
#if windows
let fchmod ch perms =
check_descriptor ch;
return (Unix.fchmod ch.fd perms)
#else
external fchmod_job : Unix.file_descr -> Unix.file_perm -> [ `unix_fchmod ] job = "lwt_unix_fchmod_job"
external fchmod_result : [ `unix_fchmod ] job -> unit = "lwt_unix_fchmod_result"
external fchmod_free : [ `unix_fchmod ] job -> unit = "lwt_unix_fchmod_free"
let fchmod ch perms =
check_descriptor ch;
execute_job (fchmod_job ch.fd perms) fchmod_result fchmod_free
#endif
#if windows
let chown name uid gid =
return (Unix.chown name uid gid)
#else
external chown_job : string -> int -> int -> [ `unix_chown ] job = "lwt_unix_chown_job"
external chown_result : [ `unix_chown ] job -> unit = "lwt_unix_chown_result"
external chown_free : [ `unix_chown ] job -> unit = "lwt_unix_chown_free"
let chown name uid gid =
execute_job (chown_job name uid gid) chown_result chown_free
#endif
#if windows
let fchown ch uid gid =
check_descriptor ch;
return (Unix.fchown ch.fd uid gid)
#else
external fchown_job : Unix.file_descr -> int -> int -> [ `unix_fchown ] job = "lwt_unix_fchown_job"
external fchown_result : [ `unix_fchown ] job -> unit = "lwt_unix_fchown_result"
external fchown_free : [ `unix_fchown ] job -> unit = "lwt_unix_fchown_free"
let fchown ch uid gid =
check_descriptor ch;
execute_job (fchown_job ch.fd uid gid) fchown_result fchown_free
#endif
type access_permission =
Unix.access_permission =
| R_OK
| W_OK
| X_OK
| F_OK
#if windows
let access name perms =
return (Unix.access name perms)
#else
external access_job : string -> Unix.access_permission list -> [ `unix_access ] job = "lwt_unix_access_job"
external access_result : [ `unix_access ] job -> unit = "lwt_unix_access_result"
external access_free : [ `unix_access ] job -> unit = "lwt_unix_access_free"
let access name perms =
execute_job (access_job name perms) access_result access_free
#endif
(* +-----------------------------------------------------------------+
| Operations on file descriptors |
+-----------------------------------------------------------------+ *)
let dup ch =
check_descriptor ch;
let fd = Unix.dup ch.fd in
{
fd = fd;
state = Opened;
set_flags = ch.set_flags;
blocking =
if ch.set_flags then
lazy(Lazy.force ch.blocking >>= function
| true ->
Unix.clear_nonblock fd;
return true
| false ->
Unix.set_nonblock fd;
return false)
else
ch.blocking;
event_readable = None;
event_writable = None;
hooks_readable = Lwt_sequence.create ();
hooks_writable = Lwt_sequence.create ();
}
let dup2 ch1 ch2 =
check_descriptor ch1;
Unix.dup2 ch1.fd ch2.fd;
ch2.set_flags <- ch1.set_flags;
ch2.blocking <- (
if ch2.set_flags then
lazy(Lazy.force ch1.blocking >>= function
| true ->
Unix.clear_nonblock ch2.fd;
return true
| false ->
Unix.set_nonblock ch2.fd;
return false)
else
ch1.blocking
)
let set_close_on_exec ch =
check_descriptor ch;
Unix.set_close_on_exec ch.fd
let clear_close_on_exec ch =
check_descriptor ch;
Unix.clear_close_on_exec ch.fd
(* +-----------------------------------------------------------------+
| Directories |
+-----------------------------------------------------------------+ *)
#if windows
let mkdir name perms =
return (Unix.mkdir name perms)
#else
external mkdir_job : string -> Unix.file_perm -> [ `unix_mkdir ] job = "lwt_unix_mkdir_job"
external mkdir_result : [ `unix_mkdir ] job -> unit = "lwt_unix_mkdir_result"
external mkdir_free : [ `unix_mkdir ] job -> unit = "lwt_unix_mkdir_free"
let mkdir name perms =
execute_job (mkdir_job name perms) mkdir_result mkdir_free
#endif
#if windows
let rmdir name =
return (Unix.rmdir name)
#else
external rmdir_job : string -> [ `unix_rmdir ] job = "lwt_unix_rmdir_job"
external rmdir_result : [ `unix_rmdir ] job -> unit = "lwt_unix_rmdir_result"
external rmdir_free : [ `unix_rmdir ] job -> unit = "lwt_unix_rmdir_free"
let rmdir name =
execute_job (rmdir_job name) rmdir_result rmdir_free
#endif
#if windows
let chdir name =
return (Unix.chdir name)
#else
external chdir_job : string -> [ `unix_chdir ] job = "lwt_unix_chdir_job"
external chdir_result : [ `unix_chdir ] job -> unit = "lwt_unix_chdir_result"
external chdir_free : [ `unix_chdir ] job -> unit = "lwt_unix_chdir_free"
let chdir name =
execute_job (chdir_job name) chdir_result chdir_free
#endif
#if windows
let chroot name =
return (Unix.chroot name)
#else
external chroot_job : string -> [ `unix_chroot ] job = "lwt_unix_chroot_job"
external chroot_result : [ `unix_chroot ] job -> unit = "lwt_unix_chroot_result"
external chroot_free : [ `unix_chroot ] job -> unit = "lwt_unix_chroot_free"
let chroot name =
execute_job (chroot_job name) chroot_result chroot_free
#endif
type dir_handle = Unix.dir_handle
#if windows
let opendir name =
return (Unix.opendir name)
#else
external opendir_job : string -> [ `unix_opendir ] job = "lwt_unix_opendir_job"
external opendir_result : [ `unix_opendir ] job -> Unix.dir_handle = "lwt_unix_opendir_result"
external opendir_free : [ `unix_opendir ] job -> unit = "lwt_unix_opendir_free"
let opendir name =
execute_job (opendir_job name) opendir_result opendir_free
#endif
#if windows
let readdir handle =
return (Unix.readdir handle)
#else
external readdir_job : Unix.dir_handle -> [ `unix_readdir ] job = "lwt_unix_readdir_job"
external readdir_result : [ `unix_readdir ] job -> string = "lwt_unix_readdir_result"
external readdir_free : [ `unix_readdir ] job -> unit = "lwt_unix_readdir_free"
let readdir handle =
execute_job (readdir_job handle) readdir_result readdir_free
#endif
#if windows
let readdir_n handle count =
if count < 0 then
fail (Invalid_argument "Lwt_uinx.readdir_n")
else
let array = Array.make count "" in
let rec fill i =
if i = count then
return array
else
match try array.(i) <- Unix.readdir handle; true with End_of_file -> false with
| true ->
fill (i + 1)
| false ->
return (Array.sub array 0 i)
in
fill 0
#else
external readdir_n_job : Unix.dir_handle -> int -> [ `unix_readdir_n ] job = "lwt_unix_readdir_n_job"
external readdir_n_result : [ `unix_readdir_n ] job -> string array = "lwt_unix_readdir_n_result"
external readdir_n_free : [ `unix_readdir_n ] job -> unit = "lwt_unix_readdir_n_free"
let readdir_n handle count =
if count < 0 then
fail (Invalid_argument "Lwt_uinx.readdir_n")
else
execute_job (readdir_n_job handle count) readdir_n_result readdir_n_free
#endif
#if windows
let rewinddir handle =
return (Unix.rewinddir handle)
#else
external rewinddir_job : Unix.dir_handle -> [ `unix_rewinddir ] job = "lwt_unix_rewinddir_job"
external rewinddir_result : [ `unix_rewinddir ] job -> unit = "lwt_unix_rewinddir_result"
external rewinddir_free : [ `unix_rewinddir ] job -> unit = "lwt_unix_rewinddir_free"
let rewinddir handle =
execute_job (rewinddir_job handle) rewinddir_result rewinddir_free
#endif
#if windows
let closedir handle =
return (Unix.closedir handle)
#else
external closedir_job : Unix.dir_handle -> [ `unix_closedir ] job = "lwt_unix_closedir_job"
external closedir_result : [ `unix_closedir ] job -> unit = "lwt_unix_closedir_result"
external closedir_free : [ `unix_closedir ] job -> unit = "lwt_unix_closedir_free"
let closedir handle =
execute_job (closedir_job handle) closedir_result closedir_free
#endif
type list_directory_state =
| LDS_not_started
| LDS_listing of Unix.dir_handle
| LDS_done
let cleanup_dir_handle state =
match !state with
| LDS_listing handle ->
ignore (closedir handle)
| LDS_not_started | LDS_done ->
()
let files_of_directory path =
let state = ref LDS_not_started in
Lwt_stream.concat
(Lwt_stream.from
(fun () ->
match !state with
| LDS_not_started ->
lwt handle = opendir path in
lwt entries =
try_lwt
readdir_n handle 1024
with exn ->
lwt () = closedir handle in
raise exn
in
if Array.length entries < 1024 then begin
state := LDS_done;
lwt () = closedir handle in
return (Some(Lwt_stream.of_array entries))
end else begin
state := LDS_listing handle;
Gc.finalise cleanup_dir_handle state;
return (Some(Lwt_stream.of_array entries))
end
| LDS_listing handle ->
lwt entries =
try_lwt
readdir_n handle 1024
with exn ->
lwt () = closedir handle in
raise exn
in
if Array.length entries < 1024 then begin
state := LDS_done;
lwt () = closedir handle in
return (Some(Lwt_stream.of_array entries))
end else
return (Some(Lwt_stream.of_array entries))
| LDS_done ->
return None))
(* +-----------------------------------------------------------------+
| Pipes and redirections |
+-----------------------------------------------------------------+ *)
let pipe () =
let (out_fd, in_fd) = Unix.pipe() in
(mk_ch ~blocking:Lwt_sys.windows out_fd, mk_ch ~blocking:Lwt_sys.windows in_fd)
let pipe_in () =
let (out_fd, in_fd) = Unix.pipe() in
(mk_ch ~blocking:Lwt_sys.windows out_fd, in_fd)
let pipe_out () =
let (out_fd, in_fd) = Unix.pipe() in
(out_fd, mk_ch ~blocking:Lwt_sys.windows in_fd)
#if windows
let mkfifo name perms =
return (Unix.mkfifo name perms)
#else
external mkfifo_job : string -> Unix.file_perm -> [ `unix_mkfifo ] job = "lwt_unix_mkfifo_job"
external mkfifo_result : [ `unix_mkfifo ] job -> unit = "lwt_unix_mkfifo_result"
external mkfifo_free : [ `unix_mkfifo ] job -> unit = "lwt_unix_mkfifo_free"
let mkfifo name perms =
execute_job (mkfifo_job name perms) mkfifo_result mkfifo_free
#endif
(* +-----------------------------------------------------------------+
| Symbolic links |
+-----------------------------------------------------------------+ *)
#if windows
let symlink name1 name2 =
return (Unix.symlink name1 name2)
#else
external symlink_job : string -> string -> [ `unix_symlink ] job = "lwt_unix_symlink_job"
external symlink_result : [ `unix_symlink ] job -> unit = "lwt_unix_symlink_result"
external symlink_free : [ `unix_symlink ] job -> unit = "lwt_unix_symlink_free"
let symlink name1 name2 =
execute_job (symlink_job name1 name2) symlink_result symlink_free
#endif
#if windows
let readlink name =
return (Unix.readlink name)
#else
external readlink_job : string -> [ `unix_readlink ] job = "lwt_unix_readlink_job"
external readlink_result : [ `unix_readlink ] job -> string = "lwt_unix_readlink_result"
external readlink_free : [ `unix_readlink ] job -> unit = "lwt_unix_readlink_free"
let readlink name =
execute_job (readlink_job name) readlink_result readlink_free
#endif
(* +-----------------------------------------------------------------+
| Locking |
+-----------------------------------------------------------------+ *)
type lock_command =
Unix.lock_command =
| F_ULOCK
| F_LOCK
| F_TLOCK
| F_TEST
| F_RLOCK
| F_TRLOCK
#if windows
let lockf ch cmd size =
check_descriptor ch;
return (Unix.lockf ch.fd cmd size)
#else
external lockf_job : Unix.file_descr -> Unix.lock_command -> int -> [ `unix_lockf ] job = "lwt_unix_lockf_job"
external lockf_result : [ `unix_lockf ] job -> unit = "lwt_unix_lockf_result"
external lockf_free : [ `unix_lockf ] job -> unit = "lwt_unix_lockf_free"
let lockf ch cmd size =
check_descriptor ch;
execute_job (lockf_job ch.fd cmd size) lockf_result lockf_free
#endif
(* +-----------------------------------------------------------------+
| User id, group id |
+-----------------------------------------------------------------+ *)
type passwd_entry =
Unix.passwd_entry =
{
pw_name : string;
pw_passwd : string;
pw_uid : int;
pw_gid : int;
pw_gecos : string;
pw_dir : string;
pw_shell : string
}
type group_entry =
Unix.group_entry =
{
gr_name : string;
gr_passwd : string;
gr_gid : int;
gr_mem : string array
}
#if windows
let getlogin () =
return (Unix.getlogin ())
#else
external getlogin_job : unit -> [ `unix_getlogin ] job = "lwt_unix_getlogin_job"
external getlogin_result : [ `unix_getlogin ] job -> string = "lwt_unix_getlogin_result"
external getlogin_free : [ `unix_getlogin ] job -> unit = "lwt_unix_getlogin_free"
let getlogin () =
execute_job (getlogin_job ()) getlogin_result getlogin_free
#endif
#if windows
let getpwnam name =
return (Unix.getpwnam name)
#else
external getpwnam_job : string -> [ `unix_getpwnam ] job = "lwt_unix_getpwnam_job"
external getpwnam_result : [ `unix_getpwnam ] job -> Unix.passwd_entry = "lwt_unix_getpwnam_result"
external getpwnam_free : [ `unix_getpwnam ] job -> unit = "lwt_unix_getpwnam_free"
let getpwnam name =
execute_job (getpwnam_job name) getpwnam_result getpwnam_free
#endif
#if windows
let getgrnam name =
return (Unix.getgrnam name)
#else
external getgrnam_job : string -> [ `unix_getgrnam ] job = "lwt_unix_getgrnam_job"
external getgrnam_result : [ `unix_getgrnam ] job -> Unix.group_entry = "lwt_unix_getgrnam_result"
external getgrnam_free : [ `unix_getgrnam ] job -> unit = "lwt_unix_getgrnam_free"
let getgrnam name =
execute_job (getgrnam_job name) getgrnam_result getgrnam_free
#endif
#if windows
let getpwuid uid =
return (Unix.getpwuid uid)
#else
external getpwuid_job : int -> [ `unix_getpwuid ] job = "lwt_unix_getpwuid_job"
external getpwuid_result : [ `unix_getpwuid ] job -> Unix.passwd_entry = "lwt_unix_getpwuid_result"
external getpwuid_free : [ `unix_getpwuid ] job -> unit = "lwt_unix_getpwuid_free"
let getpwuid uid =
execute_job (getpwuid_job uid) getpwuid_result getpwuid_free
#endif
#if windows
let getgrgid gid =
return (Unix.getgrgid gid)
#else
external getgrgid_job : int -> [ `unix_getgrgid ] job = "lwt_unix_getgrgid_job"
external getgrgid_result : [ `unix_getgrgid ] job -> Unix.group_entry = "lwt_unix_getgrgid_result"
external getgrgid_free : [ `unix_getgrgid ] job -> unit = "lwt_unix_getgrgid_free"
let getgrgid gid =
execute_job (getgrgid_job gid) getgrgid_result getgrgid_free
#endif
(* +-----------------------------------------------------------------+
| Sockets |
+-----------------------------------------------------------------+ *)
type msg_flag =
Unix.msg_flag =
| MSG_OOB
| MSG_DONTROUTE
| MSG_PEEK
#if windows
let stub_recv = Unix.recv
#else
external stub_recv : Unix.file_descr -> string -> int -> int -> Unix.msg_flag list -> int = "lwt_unix_recv"
#endif
let recv ch buf pos len flags =
if pos < 0 || len < 0 || pos > String.length buf - len then
invalid_arg "Lwt_unix.recv"
else
wrap_syscall Read ch (fun () -> stub_recv ch.fd buf pos len flags)
#if windows
let stub_send = Unix.send
#else
external stub_send : Unix.file_descr -> string -> int -> int -> Unix.msg_flag list -> int = "lwt_unix_send"
#endif
let send ch buf pos len flags =
if pos < 0 || len < 0 || pos > String.length buf - len then
invalid_arg "Lwt_unix.send"
else
wrap_syscall Write ch (fun () -> stub_send ch.fd buf pos len flags)
#if windows
let stub_recvfrom = Unix.recvfrom
#else
external stub_recvfrom : Unix.file_descr -> string -> int -> int -> Unix.msg_flag list -> int * Unix.sockaddr = "lwt_unix_recvfrom"
#endif
let recvfrom ch buf pos len flags =
if pos < 0 || len < 0 || pos > String.length buf - len then
invalid_arg "Lwt_unix.recvfrom"
else
wrap_syscall Read ch (fun () -> stub_recvfrom ch.fd buf pos len flags)
#if windows
let stub_sendto = Unix.sendto
#else
external stub_sendto : Unix.file_descr -> string -> int -> int -> Unix.msg_flag list -> Unix.sockaddr -> int = "lwt_unix_sendto_byte" "lwt_unix_sendto"
#endif
let sendto ch buf pos len flags addr =
if pos < 0 || len < 0 || pos > String.length buf - len then
invalid_arg "Lwt_unix.sendto"
else
wrap_syscall Write ch (fun () -> stub_sendto ch.fd buf pos len flags addr)
type io_vector = {
iov_buffer : string;
iov_offset : int;
iov_length : int;
}
let io_vector ~buffer ~offset ~length = {
iov_buffer = buffer;
iov_offset = offset;
iov_length = length;
}
let check_io_vectors func_name iovs =
List.iter (fun iov ->
if iov.iov_offset < 0
|| iov.iov_length < 0
|| iov.iov_offset > String.length iov.iov_buffer - iov.iov_length then
invalid_arg func_name) iovs
#if windows
let recv_msg ~socket ~io_vectors =
raise (Lwt_sys.Not_available "recv_msg")
#else
external stub_recv_msg : Unix.file_descr -> int -> io_vector list -> int * Unix.file_descr list = "lwt_unix_recv_msg"
let recv_msg ~socket ~io_vectors =
check_io_vectors "Lwt_unix.recv_msg" io_vectors;
let n_iovs = List.length io_vectors in
wrap_syscall Read socket
(fun () ->
stub_recv_msg socket.fd n_iovs io_vectors)
#endif
#if windows
let send_msg ~socket ~io_vectors ~fds =
raise (Lwt_sys.Not_available "send_msg")
#else
external stub_send_msg : Unix.file_descr -> int -> io_vector list -> int -> Unix.file_descr list -> int = "lwt_unix_send_msg"
let send_msg ~socket ~io_vectors ~fds =
check_io_vectors "Lwt_unix.send_msg" io_vectors;
let n_iovs = List.length io_vectors and n_fds = List.length fds in
wrap_syscall Write socket
(fun () ->
stub_send_msg socket.fd n_iovs io_vectors n_fds fds)
#endif
type inet_addr = Unix.inet_addr
type socket_domain =
Unix.socket_domain =
| PF_UNIX
| PF_INET
| PF_INET6
type socket_type =
Unix.socket_type =
| SOCK_STREAM
| SOCK_DGRAM
| SOCK_RAW
| SOCK_SEQPACKET
type sockaddr = Unix.sockaddr = ADDR_UNIX of string | ADDR_INET of inet_addr * int
let socket dom typ proto =
let s = Unix.socket dom typ proto in
mk_ch ~blocking:false s
type shutdown_command =
Unix.shutdown_command =
| SHUTDOWN_RECEIVE
| SHUTDOWN_SEND
| SHUTDOWN_ALL
let shutdown ch shutdown_command =
check_descriptor ch;
Unix.shutdown ch.fd shutdown_command
let socketpair dom typ proto =
let (s1, s2) = Unix.socketpair dom typ proto in
(mk_ch ~blocking:false s1, mk_ch ~blocking:false s2)
let accept ch =
wrap_syscall Read ch (fun _ -> let (fd, addr) = Unix.accept ch.fd in (mk_ch ~blocking:false fd, addr))
let accept_n ch n =
let l = ref [] in
lwt blocking = Lazy.force ch.blocking in
try_lwt
wrap_syscall Read ch begin fun () ->
begin
try
for i = 1 to n do
if blocking && not (stub_readable ch.fd) then raise Retry;
let fd, addr = Unix.accept ch.fd in
l := (mk_ch ~blocking:false fd, addr) :: !l
done
with
| (Unix.Unix_error((Unix.EAGAIN | Unix.EWOULDBLOCK | Unix.EINTR), _, _) | Retry) when !l <> [] ->
(* Ignore blocking errors if we have at least one file-descriptor: *)
()
end;
(List.rev !l, None)
end
with exn ->
return (List.rev !l, Some exn)
#if windows
let connect ch addr =
(* [in_progress] tell wether connection has started but not
terminated: *)
let in_progress = ref false in
wrap_syscall Write ch begin fun () ->
if !in_progress then
(* Nothing works without this test and i have no idea why... *)
if writable ch then
try
Unix.connect ch.fd addr
with
| Unix.Unix_error (Unix.EISCONN, _, _) ->
(* This is the windows way of telling that the connection
has completed. *)
()
else
raise Retry
else
try
Unix.connect ch.fd addr
with
| Unix.Unix_error (Unix.EWOULDBLOCK, _, _) ->
in_progress := true;
raise Retry
end
#else
let connect ch addr =
(* [in_progress] tell wether connection has started but not
terminated: *)
let in_progress = ref false in
wrap_syscall Write ch begin fun () ->
if !in_progress then
(* If the connection is in progress, [getsockopt_error] tells
wether it succceed: *)
match Unix.getsockopt_error ch.fd with
| None ->
(* The socket is connected *)
()
| Some err ->
(* An error happened: *)
raise (Unix.Unix_error(err, "connect", ""))
else
try
(* We should pass only one time here, unless the system call
is interrupted by a signal: *)
Unix.connect ch.fd addr
with
| Unix.Unix_error (Unix.EINPROGRESS, _, _) ->
in_progress := true;
raise Retry
end
#endif
let setsockopt ch opt v =
check_descriptor ch;
Unix.setsockopt ch.fd opt v
let bind ch addr =
check_descriptor ch;
Unix.bind ch.fd addr
let listen ch cnt =
check_descriptor ch;
Unix.listen ch.fd cnt
let getpeername ch =
check_descriptor ch;
Unix.getpeername ch.fd
let getsockname ch =
check_descriptor ch;
Unix.getsockname ch.fd
type credentials = {
cred_pid : int;
cred_uid : int;
cred_gid : int;
}
#if HAVE_GET_CREDENTIALS
external stub_get_credentials : Unix.file_descr -> credentials = "lwt_unix_get_credentials"
let get_credentials ch =
check_descriptor ch;
stub_get_credentials ch.fd
#else
let get_credentials ch =
raise (Lwt_sys.Not_available "get_credentials")
#endif
(* +-----------------------------------------------------------------+
| Socket options |
+-----------------------------------------------------------------+ *)
type socket_bool_option =
Unix.socket_bool_option =
| SO_DEBUG
| SO_BROADCAST
| SO_REUSEADDR
| SO_KEEPALIVE
| SO_DONTROUTE
| SO_OOBINLINE
| SO_ACCEPTCONN
| TCP_NODELAY
| IPV6_ONLY
type socket_int_option =
Unix.socket_int_option =
| SO_SNDBUF
| SO_RCVBUF
| SO_ERROR
| SO_TYPE
| SO_RCVLOWAT
| SO_SNDLOWAT
type socket_optint_option = Unix.socket_optint_option = SO_LINGER
type socket_float_option =
Unix.socket_float_option =
| SO_RCVTIMEO
| SO_SNDTIMEO
let getsockopt ch opt =
check_descriptor ch;
Unix.getsockopt ch.fd opt
let setsockopt ch opt x =
check_descriptor ch;
Unix.setsockopt ch.fd opt x
let getsockopt_int ch opt =
check_descriptor ch;
Unix.getsockopt_int ch.fd opt
let setsockopt_int ch opt x =
check_descriptor ch;
Unix.setsockopt_int ch.fd opt x
let getsockopt_optint ch opt =
check_descriptor ch;
Unix.getsockopt_optint ch.fd opt
let setsockopt_optint ch opt x =
check_descriptor ch;
Unix.setsockopt_optint ch.fd opt x
let getsockopt_float ch opt =
check_descriptor ch;
Unix.getsockopt_float ch.fd opt
let setsockopt_float ch opt x =
check_descriptor ch;
Unix.setsockopt_float ch.fd opt x
let getsockopt_error ch =
check_descriptor ch;
Unix.getsockopt_error ch.fd
(* +-----------------------------------------------------------------+
| Host and protocol databases |
+-----------------------------------------------------------------+ *)
type host_entry =
Unix.host_entry =
{
h_name : string;
h_aliases : string array;
h_addrtype : socket_domain;
h_addr_list : inet_addr array
}
type protocol_entry =
Unix.protocol_entry =
{
p_name : string;
p_aliases : string array;
p_proto : int
}
type service_entry =
Unix.service_entry =
{
s_name : string;
s_aliases : string array;
s_port : int;
s_proto : string
}
#if windows
let gethostname () =
return (Unix.gethostname ())
#else
external gethostname_job : unit -> [ `unix_gethostname ] job = "lwt_unix_gethostname_job"
external gethostname_result : [ `unix_gethostname ] job -> string = "lwt_unix_gethostname_result"
external gethostname_free : [ `unix_gethostname ] job -> unit = "lwt_unix_gethostname_free"
let gethostname () =
execute_job (gethostname_job ()) gethostname_result gethostname_free
#endif
#if windows
let gethostbyname name =
return (Unix.gethostbyname name)
#else
external gethostbyname_job : string -> [ `unix_gethostbyname ] job = "lwt_unix_gethostbyname_job"
external gethostbyname_result : [ `unix_gethostbyname ] job -> Unix.host_entry = "lwt_unix_gethostbyname_result"
external gethostbyname_free : [ `unix_gethostbyname ] job -> unit = "lwt_unix_gethostbyname_free"
let gethostbyname name =
execute_job (gethostbyname_job name) gethostbyname_result gethostbyname_free
#endif
#if windows
let gethostbyaddr addr =
return (Unix.gethostbyaddr addr)
#else
external gethostbyaddr_job : Unix.inet_addr -> [ `unix_gethostbyaddr ] job = "lwt_unix_gethostbyaddr_job"
external gethostbyaddr_result : [ `unix_gethostbyaddr ] job -> Unix.host_entry = "lwt_unix_gethostbyaddr_result"
external gethostbyaddr_free : [ `unix_gethostbyaddr ] job -> unit = "lwt_unix_gethostbyaddr_free"
let gethostbyaddr addr =
execute_job (gethostbyaddr_job addr) gethostbyaddr_result gethostbyaddr_free
#endif
#if windows
let getprotobyname name =
return (Unix.getprotobyname name)
#else
external getprotobyname_job : string -> [ `unix_getprotobyname ] job = "lwt_unix_getprotobyname_job"
external getprotobyname_result : [ `unix_getprotobyname ] job -> Unix.protocol_entry = "lwt_unix_getprotobyname_result"
external getprotobyname_free : [ `unix_getprotobyname ] job -> unit = "lwt_unix_getprotobyname_free"
let getprotobyname name =
execute_job (getprotobyname_job name) getprotobyname_result getprotobyname_free
#endif
#if windows
let getprotobynumber number =
return (Unix.getprotobynumber number)
#else
external getprotobynumber_job : int -> [ `unix_getprotobynumber ] job = "lwt_unix_getprotobynumber_job"
external getprotobynumber_result : [ `unix_getprotobynumber ] job -> Unix.protocol_entry = "lwt_unix_getprotobynumber_result"
external getprotobynumber_free : [ `unix_getprotobynumber ] job -> unit = "lwt_unix_getprotobynumber_free"
let getprotobynumber number =
execute_job (getprotobynumber_job number) getprotobynumber_result getprotobynumber_free
#endif
#if windows
let getservbyname name x =
return (Unix.getservbyname name x)
#else
external getservbyname_job : string -> string -> [ `unix_getservbyname ] job = "lwt_unix_getservbyname_job"
external getservbyname_result : [ `unix_getservbyname ] job -> Unix.service_entry = "lwt_unix_getservbyname_result"
external getservbyname_free : [ `unix_getservbyname ] job -> unit = "lwt_unix_getservbyname_free"
let getservbyname name x =
execute_job (getservbyname_job name x) getservbyname_result getservbyname_free
#endif
#if windows
let getservbyport port x =
return (Unix.getservbyport port x)
#else
external getservbyport_job : int -> string -> [ `unix_getservbyport ] job = "lwt_unix_getservbyport_job"
external getservbyport_result : [ `unix_getservbyport ] job -> Unix.service_entry = "lwt_unix_getservbyport_result"
external getservbyport_free : [ `unix_getservbyport ] job -> unit = "lwt_unix_getservbyport_free"
let getservbyport port x =
execute_job (getservbyport_job port x) getservbyport_result getservbyport_free
#endif
type addr_info =
Unix.addr_info =
{
ai_family : socket_domain;
ai_socktype : socket_type;
ai_protocol : int;
ai_addr : sockaddr;
ai_canonname : string;
}
type getaddrinfo_option =
Unix.getaddrinfo_option =
| AI_FAMILY of socket_domain
| AI_SOCKTYPE of socket_type
| AI_PROTOCOL of int
| AI_NUMERICHOST
| AI_CANONNAME
| AI_PASSIVE
#if windows
let getaddrinfo host service opts =
return (Unix.getaddrinfo host service opts)
#else
external getaddrinfo_job : string -> string -> Unix.getaddrinfo_option list -> [ `unix_getaddrinfo ] job = "lwt_unix_getaddrinfo_job"
external getaddrinfo_result : [ `unix_getaddrinfo ] job -> Unix.addr_info list = "lwt_unix_getaddrinfo_result"
external getaddrinfo_free : [ `unix_getaddrinfo ] job -> unit = "lwt_unix_getaddrinfo_free"
let getaddrinfo host service opts =
execute_job (getaddrinfo_job host service opts) getaddrinfo_result getaddrinfo_free
#endif
type name_info =
Unix.name_info =
{
ni_hostname : string;
ni_service : string;
}
type getnameinfo_option =
Unix.getnameinfo_option =
| NI_NOFQDN
| NI_NUMERICHOST
| NI_NAMEREQD
| NI_NUMERICSERV
| NI_DGRAM
#if windows
let getnameinfo addr opts =
return (Unix.getnameinfo addr opts)
#else
external getnameinfo_job : Unix.sockaddr -> Unix.getnameinfo_option list -> [ `unix_getnameinfo ] job = "lwt_unix_getnameinfo_job"
external getnameinfo_result : [ `unix_getnameinfo ] job -> Unix.name_info = "lwt_unix_getnameinfo_result"
external getnameinfo_free : [ `unix_getnameinfo ] job -> unit = "lwt_unix_getnameinfo_free"
let getnameinfo addr opts =
execute_job (getnameinfo_job addr opts) getnameinfo_result getnameinfo_free
#endif
(* +-----------------------------------------------------------------+
| Terminal interface |
+-----------------------------------------------------------------+ *)
type terminal_io =
Unix.terminal_io =
{
mutable c_ignbrk : bool;
mutable c_brkint : bool;
mutable c_ignpar : bool;
mutable c_parmrk : bool;
mutable c_inpck : bool;
mutable c_istrip : bool;
mutable c_inlcr : bool;
mutable c_igncr : bool;
mutable c_icrnl : bool;
mutable c_ixon : bool;
mutable c_ixoff : bool;
mutable c_opost : bool;
mutable c_obaud : int;
mutable c_ibaud : int;
mutable c_csize : int;
mutable c_cstopb : int;
mutable c_cread : bool;
mutable c_parenb : bool;
mutable c_parodd : bool;
mutable c_hupcl : bool;
mutable c_clocal : bool;
mutable c_isig : bool;
mutable c_icanon : bool;
mutable c_noflsh : bool;
mutable c_echo : bool;
mutable c_echoe : bool;
mutable c_echok : bool;
mutable c_echonl : bool;
mutable c_vintr : char;
mutable c_vquit : char;
mutable c_verase : char;
mutable c_vkill : char;
mutable c_veof : char;
mutable c_veol : char;
mutable c_vmin : int;
mutable c_vtime : int;
mutable c_vstart : char;
mutable c_vstop : char;
}
type setattr_when =
Unix.setattr_when =
| TCSANOW
| TCSADRAIN
| TCSAFLUSH
type flush_queue =
Unix.flush_queue =
| TCIFLUSH
| TCOFLUSH
| TCIOFLUSH
type flow_action =
Unix.flow_action =
| TCOOFF
| TCOON
| TCIOFF
| TCION
#if windows
let tcgetattr ch =
check_descriptor ch;
return (Unix.tcgetattr ch.fd)
#else
external tcgetattr_job : Unix.file_descr -> [ `unix_tcgetattr ] job = "lwt_unix_tcgetattr_job"
external tcgetattr_result : [ `unix_tcgetattr ] job -> Unix.terminal_io = "lwt_unix_tcgetattr_result"
external tcgetattr_free : [ `unix_tcgetattr ] job -> unit = "lwt_unix_tcgetattr_free"
let tcgetattr ch =
check_descriptor ch;
execute_job (tcgetattr_job ch.fd) tcgetattr_result tcgetattr_free
#endif
#if windows
let tcsetattr ch when_ attrs =
check_descriptor ch;
return (Unix.tcsetattr ch.fd when_ attrs)
#else
external tcsetattr_job : Unix.file_descr -> Unix.setattr_when -> Unix.terminal_io -> [ `unix_tcsetattr ] job = "lwt_unix_tcsetattr_job"
external tcsetattr_result : [ `unix_tcsetattr ] job -> unit = "lwt_unix_tcsetattr_result"
external tcsetattr_free : [ `unix_tcsetattr ] job -> unit = "lwt_unix_tcsetattr_free"
let tcsetattr ch when_ attrs =
check_descriptor ch;
execute_job (tcsetattr_job ch.fd when_ attrs) tcsetattr_result tcsetattr_free
#endif
#if windows
let tcsendbreak ch delay =
check_descriptor ch;
return (Unix.tcsendbreak ch.fd delay)
#else
external tcsendbreak_job : Unix.file_descr -> int -> [ `unix_tcsendbreak ] job = "lwt_unix_tcsendbreak_job"
external tcsendbreak_result : [ `unix_tcsendbreak ] job -> unit = "lwt_unix_tcsendbreak_result"
external tcsendbreak_free : [ `unix_tcsendbreak ] job -> unit = "lwt_unix_tcsendbreak_free"
let tcsendbreak ch delay =
check_descriptor ch;
execute_job (tcsendbreak_job ch.fd delay) tcsendbreak_result tcsendbreak_free
#endif
#if windows
let tcdrain ch =
check_descriptor ch;
return (Unix.tcdrain ch.fd)
#else
external tcdrain_job : Unix.file_descr -> [ `unix_tcdrain ] job = "lwt_unix_tcdrain_job"
external tcdrain_result : [ `unix_tcdrain ] job -> unit = "lwt_unix_tcdrain_result"
external tcdrain_free : [ `unix_tcdrain ] job -> unit = "lwt_unix_tcdrain_free"
let tcdrain ch =
check_descriptor ch;
execute_job (tcdrain_job ch.fd) tcdrain_result tcdrain_free
#endif
#if windows
let tcflush ch q =
check_descriptor ch;
return (Unix.tcflush ch.fd q)
#else
external tcflush_job : Unix.file_descr -> Unix.flush_queue -> [ `unix_tcflush ] job = "lwt_unix_tcflush_job"
external tcflush_result : [ `unix_tcflush ] job -> unit = "lwt_unix_tcflush_result"
external tcflush_free : [ `unix_tcflush ] job -> unit = "lwt_unix_tcflush_free"
let tcflush ch q =
check_descriptor ch;
execute_job (tcflush_job ch.fd q) tcflush_result tcflush_free
#endif
#if windows
let tcflow ch act =
check_descriptor ch;
return (Unix.tcflow ch.fd act)
#else
external tcflow_job : Unix.file_descr -> Unix.flow_action -> [ `unix_tcflow ] job = "lwt_unix_tcflow_job"
external tcflow_result : [ `unix_tcflow ] job -> unit = "lwt_unix_tcflow_result"
external tcflow_free : [ `unix_tcflow ] job -> unit = "lwt_unix_tcflow_free"
let tcflow ch act =
check_descriptor ch;
execute_job (tcflow_job ch.fd act) tcflow_result tcflow_free
#endif
(* +-----------------------------------------------------------------+
| Reading notifications |
+-----------------------------------------------------------------+ *)
(* Buffer used to receive notifications: *)
let notification_buffer = String.create 4
external init_notification : unit -> Unix.file_descr = "lwt_unix_init_notification"
external send_notification : int -> unit = "lwt_unix_send_notification_stub"
external recv_notifications : unit -> int array = "lwt_unix_recv_notifications"
let handle_notification id =
match try Some(Notifiers.find notifiers id) with Not_found -> None with
| Some notifier ->
if notifier.notify_once then
stop_notification id;
notifier.notify_handler ()
| None ->
()
let rec handle_notifications ev =
(* Process available notifications. *)
Array.iter handle_notification (recv_notifications ())
let event_notifications = ref (Lwt_engine.on_readable (init_notification ()) handle_notifications)
(* +-----------------------------------------------------------------+
| Signals |
+-----------------------------------------------------------------+ *)
module Signal_map = Map.Make(struct type t = int let compare a b = a - b end)
let signals = ref Signal_map.empty
let signal_count () =
Signal_map.fold
(fun signum (id, actions) len -> len + Lwt_sequence.length actions)
!signals
0
type signal_handler_id = unit Lazy.t
let on_signal signum handler =
let notification, actions =
try
Signal_map.find signum !signals
with Not_found ->
let actions = Lwt_sequence.create () in
let notification = make_notification (fun () -> Lwt_sequence.iter_l (fun f -> f signum) actions) in
(try
Sys.set_signal signum (Sys.Signal_handle (fun signum -> send_notification notification))
with exn ->
stop_notification notification;
raise exn);
signals := Signal_map.add signum (notification, actions) !signals;
(notification, actions)
in
let node = Lwt_sequence.add_r handler actions in
lazy(Lwt_sequence.remove node;
if Lwt_sequence.is_empty actions then begin
signals := Signal_map.remove signum !signals;
stop_notification notification;
Sys.set_signal signum Sys.Signal_default
end)
let disable_signal_handler = Lazy.force
let reinstall_signal_handler signum =
match try Some (Signal_map.find signum !signals) with Not_found -> None with
| Some (notification, actions) ->
Sys.set_signal signum (Sys.Signal_handle (fun signum -> send_notification notification))
| None ->
()
(* +-----------------------------------------------------------------+
| Processes |
+-----------------------------------------------------------------+ *)
external reset_after_fork : unit -> unit = "lwt_unix_reset_after_fork"
let fork () =
match Unix.fork () with
| 0 ->
(* Reset threading. *)
reset_after_fork ();
(* Stop the old event for notifications. *)
Lwt_engine.stop_event !event_notifications;
(* Reinitialise the notification system. *)
event_notifications := Lwt_engine.on_readable (init_notification ()) handle_notifications;
(* Collect all pending jobs. *)
let l = Lwt_sequence.fold_l (fun w l -> w :: l) jobs [] in
(* Remove them all. *)
Lwt_sequence.iter_node_l Lwt_sequence.remove jobs;
(* And cancel them all. We yield first so that if the program
do an exec just after, it won't be executed. *)
on_termination (Lwt_main.yield ()) (fun () -> List.iter cancel l);
0
| pid ->
pid
type process_status =
Unix.process_status =
| WEXITED of int
| WSIGNALED of int
| WSTOPPED of int
type wait_flag =
Unix.wait_flag =
| WNOHANG
| WUNTRACED
let has_wait4 = not Lwt_sys.windows
type resource_usage = { ru_utime : float; ru_stime : float }
#if windows
let stub_wait4 flags pid =
let pid, status = Unix.waitpid flags pid in
(pid, status, { ru_utime = 0.0; ru_stime = 0.0 })
#else
external stub_wait4 : Unix.wait_flag list -> int -> int * Unix.process_status * resource_usage = "lwt_unix_wait4"
#endif
let wait_children = Lwt_sequence.create ()
let wait_count () = Lwt_sequence.length wait_children
#if not windows
let () =
ignore begin
on_signal Sys.sigchld
(fun _ ->
Lwt_sequence.iter_node_l begin fun node ->
let wakener, flags, pid = Lwt_sequence.get node in
try
let (pid', _, _) as v = stub_wait4 flags pid in
if pid' <> 0 then begin
Lwt_sequence.remove node;
Lwt.wakeup wakener v
end
with e ->
Lwt_sequence.remove node;
Lwt.wakeup_exn wakener e
end wait_children)
end
#endif
let _waitpid flags pid =
try_lwt
return (Unix.waitpid flags pid)
#if windows
let waitpid = _waitpid
#else
let waitpid flags pid =
if List.mem Unix.WNOHANG flags then
_waitpid flags pid
else
let flags = Unix.WNOHANG :: flags in
lwt ((pid', _) as res) = _waitpid flags pid in
if pid' <> 0 then
return res
else begin
let (res, w) = Lwt.task () in
let node = Lwt_sequence.add_l (w, flags, pid) wait_children in
Lwt.on_cancel res (fun _ -> Lwt_sequence.remove node);
lwt (pid, status, _) = res in
return (pid, status)
end
#endif
let _wait4 flags pid =
try_lwt
return (stub_wait4 flags pid)
#if windows
let wait4 = _wait4
#else
let wait4 flags pid =
if List.mem Unix.WNOHANG flags then
_wait4 flags pid
else
let flags = Unix.WNOHANG :: flags in
lwt (pid', _, _) as res = _wait4 flags pid in
if pid' <> 0 then
return res
else begin
let (res, w) = Lwt.task () in
let node = Lwt_sequence.add_l (w, flags, pid) wait_children in
Lwt.on_cancel res (fun _ -> Lwt_sequence.remove node);
res
end
#endif
let wait () = waitpid [] (-1)
let system cmd =
match Unix.fork () with
| 0 ->
begin try
Unix.execv "/bin/sh" [| "/bin/sh"; "-c"; cmd |]
with _ ->
exit 127
end
| id ->
waitpid [] id >|= snd
(* +-----------------------------------------------------------------+
| Misc |
+-----------------------------------------------------------------+ *)
let run = Lwt_main.run
let handle_unix_error f x =
try_lwt
f x
with exn ->
Unix.handle_unix_error (fun () -> raise exn) ()
(* +-----------------------------------------------------------------+
| System thread pool |
+-----------------------------------------------------------------+ *)
external pool_size : unit -> int = "lwt_unix_pool_size" "noalloc"
external set_pool_size : int -> unit = "lwt_unix_set_pool_size" "noalloc"
external thread_count : unit -> int = "lwt_unix_thread_count" "noalloc"
external thread_waiting_count : unit -> int = "lwt_unix_thread_waiting_count" "noalloc"
(* +-----------------------------------------------------------------+
| CPUs |
+-----------------------------------------------------------------+ *)
#if HAVE_GETCPU
external get_cpu : unit -> int = "lwt_unix_get_cpu"
#else
let get_cpu () = raise (Lwt_sys.Not_available "get_cpu")
#endif
#if HAVE_AFFINITY
external stub_get_affinity : int -> int list = "lwt_unix_get_affinity"
external stub_set_affinity : int -> int list -> unit = "lwt_unix_set_affinity"
let get_affinity ?(pid=0) () = stub_get_affinity pid
let set_affinity ?(pid=0) l = stub_set_affinity pid l
#else
let get_affinity ?pid () = raise (Lwt_sys.Not_available "get_affinity")
let set_affinity ?pid l = raise (Lwt_sys.Not_available "set_affinity")
#endif
(* +-----------------------------------------------------------------+
| Error printing |
+-----------------------------------------------------------------+ *)
let () =
Printexc.register_printer
(function
| Unix.Unix_error(error, func, arg) ->
let error =
match error with
| Unix.E2BIG -> "E2BIG"
| Unix.EACCES -> "EACCES"
| Unix.EAGAIN -> "EAGAIN"
| Unix.EBADF -> "EBADF"
| Unix.EBUSY -> "EBUSY"
| Unix.ECHILD -> "ECHILD"
| Unix.EDEADLK -> "EDEADLK"
| Unix.EDOM -> "EDOM"
| Unix.EEXIST -> "EEXIST"
| Unix.EFAULT -> "EFAULT"
| Unix.EFBIG -> "EFBIG"
| Unix.EINTR -> "EINTR"
| Unix.EINVAL -> "EINVAL"
| Unix.EIO -> "EIO"
| Unix.EISDIR -> "EISDIR"
| Unix.EMFILE -> "EMFILE"
| Unix.EMLINK -> "EMLINK"
| Unix.ENAMETOOLONG -> "ENAMETOOLONG"
| Unix.ENFILE -> "ENFILE"
| Unix.ENODEV -> "ENODEV"
| Unix.ENOENT -> "ENOENT"
| Unix.ENOEXEC -> "ENOEXEC"
| Unix.ENOLCK -> "ENOLCK"
| Unix.ENOMEM -> "ENOMEM"
| Unix.ENOSPC -> "ENOSPC"
| Unix.ENOSYS -> "ENOSYS"
| Unix.ENOTDIR -> "ENOTDIR"
| Unix.ENOTEMPTY -> "ENOTEMPTY"
| Unix.ENOTTY -> "ENOTTY"
| Unix.ENXIO -> "ENXIO"
| Unix.EPERM -> "EPERM"
| Unix.EPIPE -> "EPIPE"
| Unix.ERANGE -> "ERANGE"
| Unix.EROFS -> "EROFS"
| Unix.ESPIPE -> "ESPIPE"
| Unix.ESRCH -> "ESRCH"
| Unix.EXDEV -> "EXDEV"
| Unix.EWOULDBLOCK -> "EWOULDBLOCK"
| Unix.EINPROGRESS -> "EINPROGRESS"
| Unix.EALREADY -> "EALREADY"
| Unix.ENOTSOCK -> "ENOTSOCK"
| Unix.EDESTADDRREQ -> "EDESTADDRREQ"
| Unix.EMSGSIZE -> "EMSGSIZE"
| Unix.EPROTOTYPE -> "EPROTOTYPE"
| Unix.ENOPROTOOPT -> "ENOPROTOOPT"
| Unix.EPROTONOSUPPORT -> "EPROTONOSUPPORT"
| Unix.ESOCKTNOSUPPORT -> "ESOCKTNOSUPPORT"
| Unix.EOPNOTSUPP -> "EOPNOTSUPP"
| Unix.EPFNOSUPPORT -> "EPFNOSUPPORT"
| Unix.EAFNOSUPPORT -> "EAFNOSUPPORT"
| Unix.EADDRINUSE -> "EADDRINUSE"
| Unix.EADDRNOTAVAIL -> "EADDRNOTAVAIL"
| Unix.ENETDOWN -> "ENETDOWN"
| Unix.ENETUNREACH -> "ENETUNREACH"
| Unix.ENETRESET -> "ENETRESET"
| Unix.ECONNABORTED -> "ECONNABORTED"
| Unix.ECONNRESET -> "ECONNRESET"
| Unix.ENOBUFS -> "ENOBUFS"
| Unix.EISCONN -> "EISCONN"
| Unix.ENOTCONN -> "ENOTCONN"
| Unix.ESHUTDOWN -> "ESHUTDOWN"
| Unix.ETOOMANYREFS -> "ETOOMANYREFS"
| Unix.ETIMEDOUT -> "ETIMEDOUT"
| Unix.ECONNREFUSED -> "ECONNREFUSED"
| Unix.EHOSTDOWN -> "EHOSTDOWN"
| Unix.EHOSTUNREACH -> "EHOSTUNREACH"
| Unix.ELOOP -> "ELOOP"
| Unix.EOVERFLOW -> "EOVERFLOW"
| Unix.EUNKNOWNERR n -> Printf.sprintf "EUNKNOWNERR %d" n
in
Some(Printf.sprintf "Unix.Unix_error(Unix.%s, %S, %S)" error func arg)
| _ ->
None)