write()ing to a blocking socket

write()ing to a blocking socket

Post by Rene Herma » Tue, 01 May 2001 01:08:58



Dear everyone,

each and every socket programming introduction/reference I come across
says that I should loop on write(). Upto now I've been pigheaded about
this, and have refused to believe this to be true for blocking sockets.

Surely, write() will simply block if it can't accept everything
immediately, will it not?

This is (a small part of) the SUSv2 description of write():

===
ssize_t write(int fildes, const void *buf, size_t nbyte);

[ ... for pipes and FIFOs ... ]

* If the O_NONBLOCK flag is clear, a write request may cause the
  thread to block, but on normal completion it will return nbyte.

[ ... ]

When attempting to write to a file descriptor (other than a pipe or
FIFO) that supports non-blocking writes and cannot accept the
data  immediately:

* If the O_NONBLOCK flag is clear, write() will block the calling
  thread until the data can be accepted.

* If the O_NONBLOCK flag is set, write() will not block the process.
  If some data can be written without blocking the process, write()
  will write what it can and return the number of bytes written.
  Otherwise, it will return -1 and errno will be set to [EAGAIN].

===

"If the O_NONBLOCK flag is clear, write() will block the calling thread
unti the data can be accepted" is the one I should be relying on, is it
not?

Similarly, from the glibc info manual entry for write():

===

`EAGAIN'
        Normally, `write' blocks until the write operation is
        complete.  But if the `O_NONBLOCK' flag is set for the file
        (note: Control Operations), it returns immediately without
        writing any data and reports this error

[ ... but just a bit above that ... ]

The return value is the number of bytes actually written.  This
may be SIZE, but can always be smaller.  Your program should
always call `write' in a loop, iterating until all the data is written

===

In addition, my own old winsock code doesn't loop on send(), explicitly
commenting that this isn't necessary since I'm using blocking sockets,
and since I quite clearly remember being very careful about having
verified that to be true back then, I can't believe that UNIX (I'm on
Linux, just in case it makes a difference) would behave differently.

The example code for this group's socket FAQ loops on write(), as does
my "Linux Programming Bible", as does a reference I came across this
afternoon, as does ....

Non of these ever comment on blocking versus non-blocking in this
respect, they just loop. What's the deal with this?

Rene.

 
 
 

write()ing to a blocking socket

Post by Andrew Giert » Tue, 01 May 2001 07:02:55


 Rene> Dear everyone,

 Rene> each and every socket programming introduction/reference I come
 Rene> across says that I should loop on write(). Upto now I've been
 Rene> pigheaded about this, and have refused to believe this to be
 Rene> true for blocking sockets.

 Rene> Surely, write() will simply block if it can't accept everything
 Rene> immediately, will it not?

yes, but you may have to allow for interruptions due to signals (if a
write call has transferred some data and then blocked, and it gets
interrupted by a non-restarting signal, then it will return how much
data has been written so far).

--
Andrew.

comp.unix.programmer FAQ: see <URL: http://www.erlenstar.demon.co.uk/unix/>
                           or <URL: http://www.whitefang.com/unix/>

 
 
 

write()ing to a blocking socket

Post by Rene Herma » Tue, 01 May 2001 08:37:57



>  Rene> Surely, write() will simply block if it can't accept everything
>  Rene> immediately, will it not?

> yes, but

Thank you.

Quote:> you may have to allow for interruptions due to signals (if a
> write call has transferred some data and then blocked, and it gets
> interrupted by a non-restarting signal, then it will return how much
> data has been written so far).

Yes... I'm still very shaky on the subject of signals. If I haven't
installed any signal handlers, or if I have but the handler is SIG_IGN
or SIG_DFL, I never have to worry about being interrupted by signals,
right?

If I, on the other hand, am handling some signals, and my system is
using BSD style signal delivery, any interrupted function will be
restarted, and I'm still safe, no? Ehm. I seem to recall that this is
not right in the case of write, but I can't find any relevant passages
anywhere right now. Even with BSD semantics, write() will return
successfully, but with fewer bytes written, right?

But generally, when using BSD semantics, I don't worry about being
interrupted by signals? If my system is using SysV style delivery, I
worry always and will usually check for a failure return with errno ==
EINTR or do something else appropriate, such as check for fewer bytes
sent in the case of write(). Right?

My particular system, Linux-glibc, allows either method, dependant on
how I install the handler and/or some feature test macros (_BSD_SOURCE
and _GNU_SOURCE select BSD style, default is SysV) being defined prior
to calling signal().

In any case, being as shaky on the subject as I am, I've been steering
clear of signals for now, but when writing socket code it's more or
less mandatory to do something about SIGPIPE. So let's say that the
only thing I do in my program that involves signals is signal(SIGPIPE,
SIG_IGN), and then handle write() returning -1 with errno == EPIPE by
doing something appropriate. Am I in that case correct in not looping
on write()?

I do hope you don't too much mind this many questions, but I just
remembered having another concern regarding signals. My Linux
programming book is happily calling all kinds of library functions from
its signal handlers. When I was using interrupt handlers on DOS, I
quickly learned that calling basically anything from an interrupt
handler was a recipe for disaster, and allthough I of course understand
that signals handlers are different beasts all together, don't I need
to have a reentrant libc to make sure this will always work? Anything
regarding this that I should be aware of?

Rene.

 
 
 

write()ing to a blocking socket

Post by Udo A. Steinber » Tue, 01 May 2001 09:31:14



> I do hope you don't too much mind this many questions, but I just
> remembered having another concern regarding signals. My Linux
> programming book is happily calling all kinds of library functions from
> its signal handlers.

You should get a better book.

Quote:> When I was using interrupt handlers on DOS, I
> quickly learned that calling basically anything from an interrupt
> handler was a recipe for disaster, and allthough I of course understand
> that signals handlers are different beasts all together, don't I need
> to have a reentrant libc to make sure this will always work? Anything
> regarding this that I should be aware of?

The only thing that can safely be done inside a signal handler is modifying
a variable of type volatile sig_atomic_t (which is usually defined as an
integer type) or calling abort. Anything else is undefined behaviour.

-Udo.

 
 
 

write()ing to a blocking socket

Post by Kenneth Ch » Tue, 01 May 2001 10:54:13




Quote:>I do hope you don't too much mind this many questions, but I just
>remembered having another concern regarding signals. My Linux
>programming book is happily calling all kinds of library functions from
>its signal handlers. When I was using interrupt handlers on DOS, I
>quickly learned that calling basically anything from an interrupt
>handler was a recipe for disaster, and allthough I of course understand
>that signals handlers are different beasts all together, don't I need
>to have a reentrant libc to make sure this will always work? Anything
>regarding this that I should be aware of?

If you are using a platform that claims SUSv2 compliance, then you
can call anything listed in:

    http://www.opengroup.org/onlinepubs/7908799/xsh/sigaction.html

 
 
 

write()ing to a blocking socket

Post by Andrew Giert » Tue, 01 May 2001 14:25:34


 Udo> The only thing that can safely be done inside a signal handler
 Udo> is modifying a variable of type volatile sig_atomic_t (which is
 Udo> usually defined as an integer type) or calling abort. Anything
 Udo> else is undefined behaviour.

That's true of C in general, but Unix has other rules (at least partly
because plain standard C has no mechanism for blocking signals).

A signal handler can do almost anything if it _knows_ (usually because
its signal is blocked at all other times) that the main program is not
inside a signal-unsafe function or modifying data structures that the
signal handler uses. Otherwise, the signal handler can only call
signal-safe functions, which are defined to be:

  _exit() access() alarm() cfgetispeed() cfgetospeed() cfsetispeed()
  cfsetospeed() chdir() chmod() chown() close() creat() dup() dup2()
  execle() execve() fcntl() fork() fpathconf() fstat() fsync()
  getegid() geteuid() getgid() getgroups() getpgrp() getpid()
  getppid() getuid() kill() link() lseek() mkdir() mkfifo() open()
  pathconf() pause() pipe() raise() read() rename() rmdir() setgid()
  setpgid() setsid() setuid() sigaction() sigaddset() sigdelset()
  sigemptyset() sigfillset () sigismember() signal() sigpending()
  sigprocmask() sigsuspend() sleep() stat() sysconf() tcdrain()
  tcflow() tcflush() tcgetattr() tcgetpgrp() tcsendbreak() tcsetattr()
  tcsetpgrp() time() times() umask() uname() unlink() utime() wait()
  waitpid() write()

  aio_error() clock_gettime() sigpause() timer_getoverrun()
  aio_return() fdatasync() sigqueue() timer_gettime() aio_suspend()
  sem_post() sigset() timer_settime()

A signal handler can also call longjmp()/siglongjmp(), but if it does
so and the signal handler interrupted a signal-unsafe function, then
any further use of any signal-unsafe functions in the program causes
undefined behaviour.

--
Andrew.

comp.unix.programmer FAQ: see <URL: http://www.erlenstar.demon.co.uk/unix/>
                           or <URL: http://www.whitefang.com/unix/>