Unix.write vs. Unix.single_write vs. really_write

The Unix library provides:

val write : file_descr -> bytes -> int -> int -> int
(** [write fd buff ofs len] writes [len] bytes to descriptor [fd],
    taking them from byte sequence [buff], starting at position [ofs]
    in [buff]. Return the number of bytes actually written.  [write]
    repeats the writing operation until all bytes have been written or
    an error occurs.  *)

val single_write : file_descr -> bytes -> int -> int -> int
(** Same as [write], but attempts to write only once.
   Thus, if an error occurs, [single_write] guarantees that no data
   has been written. *)

Both require the caller to deal with exceptions like Unix.EINTR and this has lead to really_write in Unix system programming in OCaml:

let rec really_write fd buffer offset len =
  let n = restart_on_EINTR (single_write fd buffer offset) len in
  if n < len then really_write fd buffer (offset + n) (len - n);;

So it appears that what a typical client would need for writing to a file descriptor is not provided by the standard library. Isn’t that a bit unsatisfying and risks clients providing their own solutions that possibly overlook an edge case?

1 Like

Shouldn’t the Unix library already be a thin wrapper around libc? In which case what you’re really asking is whether libc does (or should) restart interrupted syscalls? I don’t know what the state of play is there, but I do know that back in the day libc did not do so: you could (for instance) crash a tar-pipe-tar running in an xterm, by resizing the xterm window repeatedly (delivering SIGWINCH at the wrong moment, crashing one of the tars due to that unhandled SIGWINCH).

I understand the tension between a minimal wrapper and what you need. But Unix.write already tries to loop over write(2) calls (in its C implementation) but still does not what you need. I do think a minimal interface should be provided but this does not exclude providing something most clients want to use in addition.

Maybe write should have done what really_write does but now the implementation can’t be changed, just deprecated. So, should really_write be part of Unix? (The same problem exists for read calls, too).