7

As is described in SUSv4 or POSIX.1-2008
http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html#tag_16_685_08
The write() call may return a value less than nbytes if write()ing to a NONBLOCK pipe/FIFO. Thus, it's necessary to check the return value and write() the rest of the buffer in a loop demonstrated below:

while (bytes_to_write > 0) {
    select(...);                 // Or poll()
    retv = write(...);
    if (retv < 0)
        ...                      // Error
    bytes_to_write -= retv;
}

The standard says nothing about regular files, special files (aka. devices) and sockets, especially stream-based sockets (TCP sockets and UNIX Domain ones, for example).

Then, I have the following two questions:

  • Will partial write() (or partial send()) possibly occur on regular files (or sockets with O_NONBLOCK unset) ?
  • How about writev() and sendmsg() on NONBLOCK sockets? This is very important, since dealing with partially written vector (struct iovec []) is a bit of trouble.

Sorry for broken English.

Lv Zheng
  • 316
  • 2
  • 6
  • 1
    To avoid this,many peoply just use a `writeall()` wrapper that continues to write any remaining bytes until everything is written. – technosaurus Jul 31 '15 at 04:03
  • writeall() is just the loop I gave. But something like writev_all() is more complex. Of course it's not too much work to implement a writev_all(), but is it necessary for either NONBLOCK ones or BLOCK files? – Lv Zheng Jul 31 '15 at 04:17
  • here is an example that is in regular use: https://github.com/landley/toybox/blob/master/lib/lib.c#L79 – technosaurus Jul 31 '15 at 04:46
  • What about writing to a normal file and getting "disk full" or "quota exceeded" errors? I think you get a partial success first. – o11c Jul 31 '15 at 07:02
  • 1
    Here is good stuff for handling partial `writev`: http://stackoverflow.com/questions/5853675/techniques-for-handling-short-reads-writes-with-scatter-gather – Zan Lynx Jan 31 '17 at 23:03

3 Answers3

5

Ok. Since the standard does't provide any guarantee, we can't assume full write()s.

I Googled partial writev and got an answer:

http://developerweb.net/viewtopic.php?id=4154

Yes, I've seen that behavior before as well (though, with sendmsg() and its iovecs)... And, actually, no it's NOT incorrect/unexpected behavior... Both read()/recv() and write()/send() (and all permutations of the I/O funcs) can return short reads/writes, and all sockets code needs to be prepared to deal with that... It doesn't matter if they're blocking or non-blocking mode sockets, either... All that controls is what happens when the buffer is totally empty (in the case of input) or totally full (in the case of output)... But, when the send buffer isn't quite full, any write to it (via a blocking or non-blocking socket) of more than the amount of free space left will write as much as it can, and then return the short write count... And, you are expected to handle calling it again, to send the remaining amount... With normal write()/send(), it's easy to do, but with writev()/sendmsg() iovecs, it does become tricky to handle, and a real pain... But, you still MUST do it

writev_all() can't be avoided.

Thanks.

Lv Zheng
  • 316
  • 2
  • 6
  • 1
    http://stackoverflow.com/questions/5853675/techniques-for-handling-short-reads-writes-with-scatter-gather – Zan Lynx Jan 31 '17 at 23:05
-1

Will partial write() (or partial send()) possibly occur on regular files

Yes.

(or sockets with O_NONBLOCK unset) ?

Yes.

How about writev() and sendmsg() on NONBLOCK sockets?

Yes.

This is very important, since dealing with partially written vector (struct iovec []) is a bit of trouble.

Not really. You know how many bytes were written, you just have to advance the pointers and decrement the sizes accordingly. If it's too much trouble for you, use blocking mode.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • EJP, a partial [`write()`](http://man7.org/linux/man-pages/man2/write.2.html) is possible even on regular files in Linux, for example when the process exceeds its resource limit (`RLIMIT_FSIZE`), or the underlying media runs out of space. – Nominal Animal Jul 31 '15 at 07:14
  • @Nominal Yes, thank you, I misread the question to apply to files in non-blocking mode. – user207421 Jul 31 '15 at 08:12
-1

Will partial write() (or partial send()) possibly occur on regular files (or sockets with O_NONBLOCK unset) ?

Possibly. Signals can interrupt any I/O operation, but the library will restart automatically depending on the SA_RESTART flag:

This flag affects the behavior of interruptible functions; that is, those specified to fail with errno set to [EINTR]. If set, and a function specified as interruptible is interrupted by this signal, the function shall restart and shall not fail with [EINTR] unless otherwise specified. If an interruptible function which uses a timeout is restarted, the duration of the timeout following the restart is set to an unspecified value that does not exceed the original timeout value. If the flag is not set, interruptible functions interrupted by this signal shall fail with errno set to [EINTR].

This flag is set by default on Linux, so you don't need to worry unless custom signal handling is in effect.

How about writev() and sendmsg() on NONBLOCK sockets? This is very important, since dealing with partially written vector (struct iovec []) is a bit of trouble.

More complex system calls are handled the same. The user-space library code does the heavy lifting of restarting the interrupted call, after the signal handler (if any) has returned.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • `writev()` and `sendmsg()` are not 'user-space library code'. They are system calls with minimal C wrappers. What 'heavy lifting' do they do for you exactly? – user207421 Jul 31 '15 at 04:35
  • @EJP The C language does not define kernel bindings. You call a C function which calls the kernel, waits, and then decides whether or not to return `EINTR`. Implementation of `SA_RESTART` requires catching `EINTR` conditions, somehow, but that catch can only happen in user space because the kernel doesn't know about signal handling flags. – Potatoswatter Jul 31 '15 at 04:39
  • The 'heavy lifting' described in your edit is not correct. The system call is not restarted. The function returns -1 with `errno` set to `EINTR`. Otherwise the *man* page wouldn't list `EINTR` as a possible error. – user207421 Jul 31 '15 at 06:13
  • @EJP This answer explains that already. `EINTR` is a possibility only if `SA_RESTART` is unset. What is your assertion, anyway? You're denying any such thing as restarted calls? – Potatoswatter Jul 31 '15 at 06:57
  • @Potatoswatter The kernel does know about signal handling flags on many platforms. – fuz Jul 31 '15 at 13:17
  • @FUZxxl Yeah, but most platforms don't want a trip to the kernel after completing a signal handler. Anyway, it's pretty immaterial. If the system call is interrupted, or it would have been interrupted, it gets restarted, regardless of its complexity. – Potatoswatter Jul 31 '15 at 13:45
  • @Potatoswatter But `SA_RESTART` is only set when calling `sigaction` or am I wrong in this regard? – fuz Jul 31 '15 at 13:52
  • @FUZxxl As I mentioned, "This flag is set by default on Linux." The info is surprisingly obscure, but it's mentioned on the [siginterrupt(3) manpage](http://man7.org/linux/man-pages/man3/siginterrupt.3.html) — `siginterrupt` is a dedicated interface to the `SA_RESTART` flag, but it's deprecated in favor of `sigaction`. – Potatoswatter Aug 01 '15 at 01:32
  • AFAIK, SA_RESTART doesn't restart partially completed long blocking IO operations. – Petr Skocik Apr 05 '17 at 22:51