Discussion:
[libvirt] mingw: virsh event loop failure in current git
Eric Blake
2011-03-28 23:27:04 UTC
Permalink
[adding bug-gnulib and Paolo as author of rpl_ioctl]
Commit 2ed6cc7bec41dd344d41ea1531f6760c93099128 "Expose event loop
implementation as a public API" turned a failure to initialize the
default event loop into a fatal error in virsh on Windows. Before that
commit such a failure was ignored.
virEventRegisterDefaultImpl calls virEventPollInit that calls
virSetNonBlock that calls ioctl that is replaced by gnulib and calls
ioctlsocket. ioctlsocket fails because the given FD is a pipe but
ioctlsocket expects a socket.
A version that works on pipes on Windows looks like this. Although the
pipe is actually not a named pipe the call to SetNamedPipeHandleState
doesn't fail at least.
int
virSetPipeNonBlock(int fd)
{
DWORD mode = PIPE_NOWAIT;
HANDLE handle = _get_osfhandle(fd)
BOOL result = SetNamedPipeHandleState(handle, &mode, NULL, NULL);
return result ? 0 : -1;
}
So, is the event loop stuff supposed to work on Windows at all and we
should get it fixed? Or do we just put an #ifndef WIN32 around
virEventRegisterDefaultImpl in virsh, because the only event loop user
in virsh is the console command that is disabled on Windows anyway?
Right now, gnulib's rpl_ioctl doesn't do any inspection of the various
requests handed it, nor does it check whether the request makes sense on
a non-socket. Sniffing the FIONBIO request and handling it for pipes as
well as sockets might make sense.

And while gnulib has rpl_fcntl for mingw, it does not yet support
F_SETFL or F_GETFL to inspect/set O_NONBLOCK on non-file fds. I'm not
sure how much effort that would be to add; I suppose that if F_GETFL
doesn't support all possible flags, but does support enough that we
could detect whether a read-modify-write F_GETFL/F_SETFL sequence is
trying to set or clear just O_NONBLOCK, that it would be sufficient for
the task at hand.

But rather than exposing fcntl, maybe the better thing to do is have
gnulib provide a simple wrapper API module that controls whether an fd
is blocking (similar to how the gnulib cloexec module hides
fcntl(F_GETFD/F_SETFD) or a mingw counterpart).

Paolo, any thoughts on the best approach to take? (I know which way I'm
leaning, but want some feedback before I give away my bias).
--
Eric Blake ***@redhat.com +1-801-349-2682
Libvirt virtualization library http://libvirt.org
Paolo Bonzini
2011-03-29 06:48:03 UTC
Permalink
Post by Eric Blake
Paolo, any thoughts on the best approach to take? (I know which way I'm
leaning, but want some feedback before I give away my bias).
Without guessing what your bias is, I also :) prefer to implement
{g,s}et_nonblock_flag functions. It would use either
SetNamedPipeHandleState or ioctlsocket (using the socket detection trick
in sockets.c to detect sockets, and then GetFileType to detect pipes if
it fails).

This module can then be used to build a fcntl F_GETFL/F_SETFL
implementation, but it is not very important to do so.

Paolo
Bastien ROUCARIES
2011-03-29 07:26:01 UTC
Permalink
Post by Paolo Bonzini
Paolo, any thoughts on the best approach to take?  (I know which way I'm
leaning, but want some feedback before I give away my bias).
Without guessing what your bias is, I also :) prefer to implement
{g,s}et_nonblock_flag functions.  It would use either
SetNamedPipeHandleState or ioctlsocket (using the socket detection trick in
sockets.c to detect sockets, and then GetFileType to detect pipes if it
fails).
This module can then be used to build a fcntl F_GETFL/F_SETFL
implementation, but it is not very important to do so.
Could you also implement SOCK_NONBLOCK ?

Bastien
Post by Paolo Bonzini
Paolo
Bruno Haible
2011-03-29 12:08:32 UTC
Permalink
Hi Paolo, Eric,
prefer to implement {g,s}et_nonblock_flag functions. ...
This module can then be used to build a fcntl F_GETFL/F_SETFL
implementation, but it is not very important to do so.
Nevertheless in gnulib we try to offer the POSIX APIs, if possible.

There are three APIs for non-blocking I/O on Unix:
1) fcntl F_SETFL O_NONBLOCK (POSIX, SysV)
2) fcntl F_SETFL O_NDELAY (older SysV)
3) ioctl FIONBIO (BSD 4.2)

According to the include files the support is the following:

- glibc, MacOS X, FreeBSD, NetBSD, OpenBSD, AIX, HP-UX, IRIX, OSF/1, Solaris,
Cygwin, Interix have all three APIs,
- mingw lacks F_GETFL, O_NONBLOCK, O_NDELAY
- BeOS lacks O_NDELAY, FIONBIO
- Haiku lacks O_NDELAY

So, barring bugs that we find during unit testing, we need
- a replacement for fcntl F_SETFL O_NONBLOCK that supports mingw,
- extend lib/open.c so that it handles O_NONBLOCK on mingw (presumably
by failing, because Win32 does not support non-blocking I/O on
regular files).

I agree that get/set_nonblocking functions are a good step towards this.

Bruno
--
In memoriam Rachel Levy <http://en.wikipedia.org/wiki/Rachel_Levy>
Eric Blake
2011-03-29 12:45:15 UTC
Permalink
Post by Bruno Haible
- glibc, MacOS X, FreeBSD, NetBSD, OpenBSD, AIX, HP-UX, IRIX, OSF/1, Solaris,
Cygwin, Interix have all three APIs,
- mingw lacks F_GETFL, O_NONBLOCK, O_NDELAY
- BeOS lacks O_NDELAY, FIONBIO
- Haiku lacks O_NDELAY
Thanks for the research. It looks like everyone except mingw supports
the standardized O_NONBLOCK, so I see no reason to implement O_NDELAY
(modern code shouldn't be using it) or ioctl FIONBIO.
Post by Bruno Haible
So, barring bugs that we find during unit testing, we need
- a replacement for fcntl F_SETFL O_NONBLOCK that supports mingw,
- extend lib/open.c so that it handles O_NONBLOCK on mingw (presumably
by failing, because Win32 does not support non-blocking I/O on
regular files).
Mingw supports named pipes (witness the mkfifo gnulib function), and you
can open named pipes with O_NONBLOCK; so supporting O_NONBLOCK would be
a useful addition for those. For all other file types, POSIX recommends
that O_NONBLOCK is silently ignored rather than making open() fail
(http://austingroupbugs.net/view.php?id=141); likewise, using fcntl
F_SETFL with O_NONBLOCK on regular files and directories is recommended
to silently ignore the bit (either mask it out on set, or the bit is set
on F_GETFL but has no effect) rather than failing.
--
Eric Blake ***@redhat.com +1-801-349-2682
Libvirt virtualization library http://libvirt.org
Eric Blake
2011-03-29 12:49:38 UTC
Permalink
Post by Eric Blake
Mingw supports named pipes (witness the mkfifo gnulib function),
Oops, hit send too soon. Mingw has named pipes, but they appear to
reside in a different namespace than the normal file system, and the
gnulib mkfifo() implementation current always fails with ENOSYS on
mingw. Unless we find a way to expose named pipes in the normal file
system on mingw, then you are right that there's no work to do for
O_NONBLOCK on any file that mingw can open.
--
Eric Blake ***@redhat.com +1-801-349-2682
Libvirt virtualization library http://libvirt.org
Bruno Haible
2011-03-29 15:16:03 UTC
Permalink
Post by Eric Blake
Mingw has named pipes, but they appear to
reside in a different namespace than the normal file system, and the
gnulib mkfifo() implementation current always fails with ENOSYS on
mingw. Unless we find a way to expose named pipes in the normal file
system on mingw, then you are right that there's no work to do for
O_NONBLOCK on any file that mingw can open.
More precisely the named pipes reside in a special portion of the file system.
See [1][2][3].

When run I a program that calls
CreateNamedPipe ("\\\\.\\pipe\\foo", PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE,
3, 512, 512, 0, NULL)
and then sleeps, and while the program sleeps, I run a cmd.exe with
type \\.\pipe\foo
then it finds the pipe and tries to read from it. But as soon as the creator
process is terminated, the pipe is closed and disappears from the file system.
[4]

So it's OK to leave gnulib's mkfifo() as it as, because on Windows, a program
like 'mkfifo' that terminates immediately after creating a named pipe would
be useless.

Bruno

[1] http://msdn.microsoft.com/en-us/library/aa365137.aspx
[2] http://msdn.microsoft.com/en-us/library/aa365783.aspx
[3] http://msdn.microsoft.com/en-us/library/aa365150.aspx
[4] http://en.wikipedia.org/wiki/Named_pipe#Named_pipes_in_Windows
--
In memoriam Rachel Levy <http://en.wikipedia.org/wiki/Rachel_Levy>
Bastien ROUCARIES
2011-03-29 12:38:16 UTC
Permalink
Post by Bruno Haible
Hi Paolo, Eric,
prefer to implement {g,s}et_nonblock_flag functions. ...
This module can then be used to build a fcntl F_GETFL/F_SETFL
implementation, but it is not very important to do so.
Nevertheless in gnulib we try to offer the POSIX APIs, if possible.
 1) fcntl F_SETFL O_NONBLOCK  (POSIX, SysV)
 2) fcntl F_SETFL O_NDELAY  (older SysV)
 3) ioctl FIONBIO  (BSD 4.2)
4) SOCK_NONBLOCKL

Bastien
Post by Bruno Haible
 - glibc, MacOS X, FreeBSD, NetBSD, OpenBSD, AIX, HP-UX, IRIX, OSF/1, Solaris,
   Cygwin, Interix   have all three APIs,
 - mingw             lacks F_GETFL, O_NONBLOCK, O_NDELAY
 - BeOS              lacks O_NDELAY, FIONBIO
 - Haiku             lacks O_NDELAY
So, barring bugs that we find during unit testing, we need
 - a replacement for fcntl F_SETFL O_NONBLOCK that supports mingw,
 - extend lib/open.c so that it handles O_NONBLOCK on mingw (presumably
   by failing, because Win32 does not support non-blocking I/O on
   regular files).
Or fail silently ?

Bastien
Post by Bruno Haible
I agree that get/set_nonblocking functions are a good step towards this.
Bruno
--
In memoriam Rachel Levy <http://en.wikipedia.org/wiki/Rachel_Levy>
Bruno Haible
2011-03-29 15:16:20 UTC
Permalink
Post by Paolo Bonzini
Without guessing what your bias is, I also :) prefer to implement
{g,s}et_nonblock_flag functions. It would use either
SetNamedPipeHandleState or ioctlsocket (using the socket detection trick
in sockets.c to detect sockets, and then GetFileType to detect pipes if
it fails).
Here's proposed code to that effect. Note that the getter function cannot
be implemented: How to determine whether a Woe32 socket is non-blocking?


================================ nonblocking.h ================================
/* Non-blocking I/O for pipe or socket descriptors.
Copyright (C) 2011 Free Software Foundation, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */

#ifndef _NONBLOCKING_H
#define _NONBLOCKING_H

#include <stdbool.h>

/* Non-blocking I/O is an I/O mode by which read(), write() calls avoid
blocking the current thread. When non-blocking is enabled:
- A read() call returns -1 with errno set to EAGAIN when no data or EOF
information is immediately available.
- A write() call returns -1 with errno set to EAGAIN when it cannot
transport the requested amount of data (but at most one pipe buffer)
without blocking.

There are two modern alternatives to non-blocking I/O:
- use select() or poll() followed by read() or write() if the descriptor
is ready,
- call read() or write() in separate threads. */


#ifdef __cplusplus
extern "C" {
#endif


#if 0 /* cannot be portably implemented */
/* Return true if I/O to the descriptor DESC is currently non-blocking,
or false if it is blocking. */
extern bool get_nonblocking_flag (int desc);
#endif

/* Specify the non-blocking flag for the descriptor DESC.
Return 0 upon success, or -1 with errno set upon failure.
The default depends on the presence of the O_NONBLOCK flag for files
or pipes opened with open() or on the presence of the SOCK_NONBLOCK
flag for pipes. */
extern int set_nonblocking_flag (int desc, bool value);


#ifdef __cplusplus
}
#endif

#endif /* _NONBLOCKING_H */
================================ nonblocking.c ================================
/* Non-blocking I/O for pipe or socket descriptors.
Copyright (C) 2011 Free Software Foundation, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */

#include <config.h>

/* Specification. */
#include "nonblocking.h"

#include <errno.h>

#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
/* Native Woe32 API. */

# if 0
bool
get_nonblocking_flag (int desc)
{
HANDLE h = (HANDLE) _get_osfhandle (desc);
if (GetFileType (h) == FILE_TYPE_PIPE)
{
/* h is a pipe or socket. */
DWORD state;
if (GetNamedPipeHandleState (h, &state, NULL, NULL, NULL, NULL, 0))
/* h is a pipe. */
return (state & PIPE_NOWAIT) != 0;
else
/* h is a socket. */
?? How can this be implemented ??
}
else
return false;
}
# endif

int
set_nonblocking_flag (int desc, bool value)
{
HANDLE h = (HANDLE) _get_osfhandle (desc);
if (GetFileType (h) == FILE_TYPE_PIPE)
{
/* h is a pipe or socket. */
DWORD state;
if (GetNamedPipeHandleState (h, &state, NULL, NULL, NULL, NULL, 0))
{
/* h is a pipe. */
if ((state & PIPE_NOWAIT) != 0)
{
if (value)
return 0;
state &= ~PIPE_NOWAIT;
}
else
{
if (!value)
return 0;
state |= PIPE_NOWAIT;
}
if (SetNamedPipeHandleState (h, &state, NULL, NULL))
return 0;
errno = EINVAL;
return -1;
}
else
{
/* h is a socket. */
int v = value;
return ioctl ((SOCKET) h, FIONBIO, &v);
}
}
else
{
/* Win32 does not support non-blocking on regular files. */
errno = ENOTSUP;
return -1;
}
}

#else
/* Unix API. */

# include <fcntl.h>

/* We don't need the gnulib replacement of fcntl() here. */
# undef fcntl

# if 0
bool
get_nonblocking_flag (int desc)
{
int fcntl_flags;

fcntl_flags = fcntl (desc, F_GETFL, 0);
if (fcntl_flags < 0)
return false;
return (fcntl_flags & O_NONBLOCK) != 0;
}
# endif

int
set_nonblocking_flag (int desc, bool value)
{
int fcntl_flags;

fcntl_flags = fcntl (desc, F_GETFL, 0);
if (fcntl_flags < 0)
return -1;
if ((O_NONBLOCK & ~fcntl_flags) == 0)
return 0;
return fcntl (desc, F_SETFL, fcntl_flags | O_NONBLOCK);
}

#endif
===============================================================================
--- lib/ioctl.c.orig Tue Mar 29 17:15:16 2011
+++ lib/ioctl.c Tue Mar 29 16:08:53 2011
@@ -63,6 +63,10 @@
buf = va_arg (args, void *);
va_end (args);

+ /* We don't support FIONBIO on pipes here. If you want to make pipe fds
+ non-blocking, use the gnulib 'nonblocking' module, until gnulib implements
+ fcntl F_GETFL / F_SETFL with O_NONBLOCK. */
+
sock = FD_TO_SOCKET (fd);
r = ioctlsocket (sock, req, buf);
if (r < 0)
--
In memoriam Rachel Levy <http://en.wikipedia.org/wiki/Rachel_Levy>
Bastien ROUCARIES
2011-03-29 16:34:58 UTC
Permalink
Post by Paolo Bonzini
Without guessing what your bias is, I also :) prefer to implement
{g,s}et_nonblock_flag functions.  It would use either
SetNamedPipeHandleState or ioctlsocket (using the socket detection trick
in sockets.c to detect sockets, and then GetFileType to detect pipes if
it fails).
Here's proposed code to that effect.  Note that the getter function cannot
be implemented: How to determine whether a Woe32 socket is non-blocking?
according to crappy doc:
The WSAEventSelect function automatically sets a socket to nonblocking
mode. If WSAEventSelect has been issued on a socket, then any attempt
to use ioctlsocket to set the socket back to blocking mode will fail
with WSAEINVAL.

To set the socket back to blocking mode, an application must first
disable WSAEventSelect by calling WSAEventSelect with the
lNetworkEvents parameter equal to zero.

Using this trick will allow to ask the socket (but not thread safe :S)

Bastien
================================ nonblocking.h ================================
/* Non-blocking I/O for pipe or socket descriptors.
  Copyright (C) 2011 Free Software Foundation, Inc.
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 3 of the License, or
  (at your option) any later version.
  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 General Public License for more details.
  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
#ifndef _NONBLOCKING_H
#define _NONBLOCKING_H
#include <stdbool.h>
/* Non-blocking I/O is an I/O mode by which read(), write() calls avoid
  - A read() call returns -1 with errno set to EAGAIN when no data or EOF
    information is immediately available.
  - A write() call returns -1 with errno set to EAGAIN when it cannot
    transport the requested amount of data (but at most one pipe buffer)
    without blocking.
    - use select() or poll() followed by read() or write() if the descriptor
      is ready,
    - call read() or write() in separate threads.  */
#ifdef __cplusplus
extern "C" {
#endif
#if 0 /* cannot be portably implemented */
/* Return true if I/O to the descriptor DESC is currently non-blocking,
  or false if it is blocking.  */
extern bool get_nonblocking_flag (int desc);
#endif
/* Specify the non-blocking flag for the descriptor DESC.
  Return 0 upon success, or -1 with errno set upon failure.
  The default depends on the presence of the O_NONBLOCK flag for files
  or pipes opened with open() or on the presence of the SOCK_NONBLOCK
  flag for pipes.  */
extern int set_nonblocking_flag (int desc, bool value);
#ifdef __cplusplus
}
#endif
#endif /* _NONBLOCKING_H */
================================ nonblocking.c ================================
/* Non-blocking I/O for pipe or socket descriptors.
  Copyright (C) 2011 Free Software Foundation, Inc.
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 3 of the License, or
  (at your option) any later version.
  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 General Public License for more details.
  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
#include <config.h>
/* Specification.  */
#include "nonblocking.h"
#include <errno.h>
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
/* Native Woe32 API.  */
# if 0
bool
get_nonblocking_flag (int desc)
{
 HANDLE h = (HANDLE) _get_osfhandle (desc);
 if (GetFileType (h) == FILE_TYPE_PIPE)
   {
     /* h is a pipe or socket.  */
     DWORD state;
     if (GetNamedPipeHandleState (h, &state, NULL, NULL, NULL, NULL, 0))
       /* h is a pipe.  */
       return (state & PIPE_NOWAIT) != 0;
     else
       /* h is a socket.  */
       ?? How can this be implemented ??
   }
 else
   return false;
}
# endif
int
set_nonblocking_flag (int desc, bool value)
{
 HANDLE h = (HANDLE) _get_osfhandle (desc);
 if (GetFileType (h) == FILE_TYPE_PIPE)
   {
     /* h is a pipe or socket.  */
     DWORD state;
     if (GetNamedPipeHandleState (h, &state, NULL, NULL, NULL, NULL, 0))
       {
         /* h is a pipe.  */
         if ((state & PIPE_NOWAIT) != 0)
           {
             if (value)
               return 0;
             state &= ~PIPE_NOWAIT;
           }
         else
           {
             if (!value)
               return 0;
             state |= PIPE_NOWAIT;
           }
         if (SetNamedPipeHandleState (h, &state, NULL, NULL))
           return 0;
         errno = EINVAL;
         return -1;
       }
     else
       {
         /* h is a socket.  */
         int v = value;
         return ioctl ((SOCKET) h, FIONBIO, &v);
       }
   }
 else
   {
     /* Win32 does not support non-blocking on regular files.  */
     errno = ENOTSUP;
     return -1;
   }
}
#else
/* Unix API.  */
# include <fcntl.h>
/* We don't need the gnulib replacement of fcntl() here.  */
# undef fcntl
# if 0
bool
get_nonblocking_flag (int desc)
{
 int fcntl_flags;
 fcntl_flags = fcntl (desc, F_GETFL, 0);
 if (fcntl_flags < 0)
   return false;
 return (fcntl_flags & O_NONBLOCK) != 0;
}
# endif
int
set_nonblocking_flag (int desc, bool value)
{
 int fcntl_flags;
 fcntl_flags = fcntl (desc, F_GETFL, 0);
 if (fcntl_flags < 0)
   return -1;
 if ((O_NONBLOCK & ~fcntl_flags) == 0)
   return 0;
 return fcntl (desc, F_SETFL, fcntl_flags | O_NONBLOCK);
}
#endif
===============================================================================
--- lib/ioctl.c.orig    Tue Mar 29 17:15:16 2011
+++ lib/ioctl.c Tue Mar 29 16:08:53 2011
@@ -63,6 +63,10 @@
  buf = va_arg (args, void *);
  va_end (args);
+  /* We don't support FIONBIO on pipes here.  If you want to make pipe fds
+     non-blocking, use the gnulib 'nonblocking' module, until gnulib implements
+     fcntl F_GETFL / F_SETFL with O_NONBLOCK.  */
+
  sock = FD_TO_SOCKET (fd);
  r = ioctlsocket (sock, req, buf);
  if (r < 0)
--
In memoriam Rachel Levy <http://en.wikipedia.org/wiki/Rachel_Levy>
Bastien ROUCARIES
2011-03-29 16:43:27 UTC
Permalink
On Tue, Mar 29, 2011 at 6:34 PM, Bastien ROUCARIES
Post by Bastien ROUCARIES
Post by Paolo Bonzini
Without guessing what your bias is, I also :) prefer to implement
{g,s}et_nonblock_flag functions.  It would use either
SetNamedPipeHandleState or ioctlsocket (using the socket detection trick
in sockets.c to detect sockets, and then GetFileType to detect pipes if
it fails).
Here's proposed code to that effect.  Note that the getter function cannot
be implemented: How to determine whether a Woe32 socket is non-blocking?
The WSAEventSelect function automatically sets a socket to nonblocking
mode. If WSAEventSelect has been issued on a socket, then any attempt
to use ioctlsocket to set the socket back to blocking mode will fail
with WSAEINVAL.
To set the socket back to blocking mode, an application must first
disable WSAEventSelect by calling WSAEventSelect with the
lNetworkEvents parameter equal to zero.
Using this trick will allow to ask the socket (but not thread safe :S)
I am stupid It will not work

Bastien
Post by Bastien ROUCARIES
Bastien
================================ nonblocking.h ================================
/* Non-blocking I/O for pipe or socket descriptors.
  Copyright (C) 2011 Free Software Foundation, Inc.
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 3 of the License, or
  (at your option) any later version.
  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 General Public License for more details.
  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
#ifndef _NONBLOCKING_H
#define _NONBLOCKING_H
#include <stdbool.h>
/* Non-blocking I/O is an I/O mode by which read(), write() calls avoid
  - A read() call returns -1 with errno set to EAGAIN when no data or EOF
    information is immediately available.
  - A write() call returns -1 with errno set to EAGAIN when it cannot
    transport the requested amount of data (but at most one pipe buffer)
    without blocking.
    - use select() or poll() followed by read() or write() if the descriptor
      is ready,
    - call read() or write() in separate threads.  */
#ifdef __cplusplus
extern "C" {
#endif
#if 0 /* cannot be portably implemented */
/* Return true if I/O to the descriptor DESC is currently non-blocking,
  or false if it is blocking.  */
extern bool get_nonblocking_flag (int desc);
#endif
/* Specify the non-blocking flag for the descriptor DESC.
  Return 0 upon success, or -1 with errno set upon failure.
  The default depends on the presence of the O_NONBLOCK flag for files
  or pipes opened with open() or on the presence of the SOCK_NONBLOCK
  flag for pipes.  */
extern int set_nonblocking_flag (int desc, bool value);
#ifdef __cplusplus
}
#endif
#endif /* _NONBLOCKING_H */
================================ nonblocking.c ================================
/* Non-blocking I/O for pipe or socket descriptors.
  Copyright (C) 2011 Free Software Foundation, Inc.
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 3 of the License, or
  (at your option) any later version.
  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 General Public License for more details.
  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
#include <config.h>
/* Specification.  */
#include "nonblocking.h"
#include <errno.h>
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
/* Native Woe32 API.  */
# if 0
bool
get_nonblocking_flag (int desc)
{
 HANDLE h = (HANDLE) _get_osfhandle (desc);
 if (GetFileType (h) == FILE_TYPE_PIPE)
   {
     /* h is a pipe or socket.  */
     DWORD state;
     if (GetNamedPipeHandleState (h, &state, NULL, NULL, NULL, NULL, 0))
       /* h is a pipe.  */
       return (state & PIPE_NOWAIT) != 0;
     else
       /* h is a socket.  */
       ?? How can this be implemented ??
   }
 else
   return false;
}
# endif
int
set_nonblocking_flag (int desc, bool value)
{
 HANDLE h = (HANDLE) _get_osfhandle (desc);
 if (GetFileType (h) == FILE_TYPE_PIPE)
   {
     /* h is a pipe or socket.  */
     DWORD state;
     if (GetNamedPipeHandleState (h, &state, NULL, NULL, NULL, NULL, 0))
       {
         /* h is a pipe.  */
         if ((state & PIPE_NOWAIT) != 0)
           {
             if (value)
               return 0;
             state &= ~PIPE_NOWAIT;
           }
         else
           {
             if (!value)
               return 0;
             state |= PIPE_NOWAIT;
           }
         if (SetNamedPipeHandleState (h, &state, NULL, NULL))
           return 0;
         errno = EINVAL;
         return -1;
       }
     else
       {
         /* h is a socket.  */
         int v = value;
         return ioctl ((SOCKET) h, FIONBIO, &v);
       }
   }
 else
   {
     /* Win32 does not support non-blocking on regular files.  */
     errno = ENOTSUP;
     return -1;
   }
}
#else
/* Unix API.  */
# include <fcntl.h>
/* We don't need the gnulib replacement of fcntl() here.  */
# undef fcntl
# if 0
bool
get_nonblocking_flag (int desc)
{
 int fcntl_flags;
 fcntl_flags = fcntl (desc, F_GETFL, 0);
 if (fcntl_flags < 0)
   return false;
 return (fcntl_flags & O_NONBLOCK) != 0;
}
# endif
int
set_nonblocking_flag (int desc, bool value)
{
 int fcntl_flags;
 fcntl_flags = fcntl (desc, F_GETFL, 0);
 if (fcntl_flags < 0)
   return -1;
 if ((O_NONBLOCK & ~fcntl_flags) == 0)
   return 0;
 return fcntl (desc, F_SETFL, fcntl_flags | O_NONBLOCK);
}
#endif
===============================================================================
--- lib/ioctl.c.orig    Tue Mar 29 17:15:16 2011
+++ lib/ioctl.c Tue Mar 29 16:08:53 2011
@@ -63,6 +63,10 @@
  buf = va_arg (args, void *);
  va_end (args);
+  /* We don't support FIONBIO on pipes here.  If you want to make pipe fds
+     non-blocking, use the gnulib 'nonblocking' module, until gnulib implements
+     fcntl F_GETFL / F_SETFL with O_NONBLOCK.  */
+
  sock = FD_TO_SOCKET (fd);
  r = ioctlsocket (sock, req, buf);
  if (r < 0)
--
In memoriam Rachel Levy <http://en.wikipedia.org/wiki/Rachel_Levy>
Bastien ROUCARIES
2011-03-29 16:57:28 UTC
Permalink
On Tue, Mar 29, 2011 at 6:43 PM, Bastien ROUCARIES
Post by Bastien ROUCARIES
On Tue, Mar 29, 2011 at 6:34 PM, Bastien ROUCARIES
Post by Bastien ROUCARIES
Post by Paolo Bonzini
Without guessing what your bias is, I also :) prefer to implement
{g,s}et_nonblock_flag functions.  It would use either
SetNamedPipeHandleState or ioctlsocket (using the socket detection trick
in sockets.c to detect sockets, and then GetFileType to detect pipes if
it fails).
Here's proposed code to that effect.  Note that the getter function cannot
be implemented: How to determine whether a Woe32 socket is non-blocking?
At least they achieve to do it in C#
http://msdn.microsoft.com/en-us/library/system.net.sockets.socketinformation.aspx
Post by Bastien ROUCARIES
Post by Bastien ROUCARIES
The WSAEventSelect function automatically sets a socket to nonblocking
mode. If WSAEventSelect has been issued on a socket, then any attempt
to use ioctlsocket to set the socket back to blocking mode will fail
with WSAEINVAL.
To set the socket back to blocking mode, an application must first
disable WSAEventSelect by calling WSAEventSelect with the
lNetworkEvents parameter equal to zero.
Using this trick will allow to ask the socket (but not thread safe :S)
I am stupid It will not work
Bastien
Post by Bastien ROUCARIES
Bastien
================================ nonblocking.h ================================
/* Non-blocking I/O for pipe or socket descriptors.
  Copyright (C) 2011 Free Software Foundation, Inc.
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 3 of the License, or
  (at your option) any later version.
  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 General Public License for more details.
  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
#ifndef _NONBLOCKING_H
#define _NONBLOCKING_H
#include <stdbool.h>
/* Non-blocking I/O is an I/O mode by which read(), write() calls avoid
  - A read() call returns -1 with errno set to EAGAIN when no data or EOF
    information is immediately available.
  - A write() call returns -1 with errno set to EAGAIN when it cannot
    transport the requested amount of data (but at most one pipe buffer)
    without blocking.
    - use select() or poll() followed by read() or write() if the descriptor
      is ready,
    - call read() or write() in separate threads.  */
#ifdef __cplusplus
extern "C" {
#endif
#if 0 /* cannot be portably implemented */
/* Return true if I/O to the descriptor DESC is currently non-blocking,
  or false if it is blocking.  */
extern bool get_nonblocking_flag (int desc);
#endif
/* Specify the non-blocking flag for the descriptor DESC.
  Return 0 upon success, or -1 with errno set upon failure.
  The default depends on the presence of the O_NONBLOCK flag for files
  or pipes opened with open() or on the presence of the SOCK_NONBLOCK
  flag for pipes.  */
extern int set_nonblocking_flag (int desc, bool value);
#ifdef __cplusplus
}
#endif
#endif /* _NONBLOCKING_H */
================================ nonblocking.c ================================
/* Non-blocking I/O for pipe or socket descriptors.
  Copyright (C) 2011 Free Software Foundation, Inc.
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 3 of the License, or
  (at your option) any later version.
  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 General Public License for more details.
  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
#include <config.h>
/* Specification.  */
#include "nonblocking.h"
#include <errno.h>
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
/* Native Woe32 API.  */
# if 0
bool
get_nonblocking_flag (int desc)
{
 HANDLE h = (HANDLE) _get_osfhandle (desc);
 if (GetFileType (h) == FILE_TYPE_PIPE)
   {
     /* h is a pipe or socket.  */
     DWORD state;
     if (GetNamedPipeHandleState (h, &state, NULL, NULL, NULL, NULL, 0))
       /* h is a pipe.  */
       return (state & PIPE_NOWAIT) != 0;
     else
       /* h is a socket.  */
       ?? How can this be implemented ??
   }
 else
   return false;
}
# endif
int
set_nonblocking_flag (int desc, bool value)
{
 HANDLE h = (HANDLE) _get_osfhandle (desc);
 if (GetFileType (h) == FILE_TYPE_PIPE)
   {
     /* h is a pipe or socket.  */
     DWORD state;
     if (GetNamedPipeHandleState (h, &state, NULL, NULL, NULL, NULL, 0))
       {
         /* h is a pipe.  */
         if ((state & PIPE_NOWAIT) != 0)
           {
             if (value)
               return 0;
             state &= ~PIPE_NOWAIT;
           }
         else
           {
             if (!value)
               return 0;
             state |= PIPE_NOWAIT;
           }
         if (SetNamedPipeHandleState (h, &state, NULL, NULL))
           return 0;
         errno = EINVAL;
         return -1;
       }
     else
       {
         /* h is a socket.  */
         int v = value;
         return ioctl ((SOCKET) h, FIONBIO, &v);
       }
   }
 else
   {
     /* Win32 does not support non-blocking on regular files.  */
     errno = ENOTSUP;
     return -1;
   }
}
#else
/* Unix API.  */
# include <fcntl.h>
/* We don't need the gnulib replacement of fcntl() here.  */
# undef fcntl
# if 0
bool
get_nonblocking_flag (int desc)
{
 int fcntl_flags;
 fcntl_flags = fcntl (desc, F_GETFL, 0);
 if (fcntl_flags < 0)
   return false;
 return (fcntl_flags & O_NONBLOCK) != 0;
}
# endif
int
set_nonblocking_flag (int desc, bool value)
{
 int fcntl_flags;
 fcntl_flags = fcntl (desc, F_GETFL, 0);
 if (fcntl_flags < 0)
   return -1;
 if ((O_NONBLOCK & ~fcntl_flags) == 0)
   return 0;
 return fcntl (desc, F_SETFL, fcntl_flags | O_NONBLOCK);
}
#endif
===============================================================================
--- lib/ioctl.c.orig    Tue Mar 29 17:15:16 2011
+++ lib/ioctl.c Tue Mar 29 16:08:53 2011
@@ -63,6 +63,10 @@
  buf = va_arg (args, void *);
  va_end (args);
+  /* We don't support FIONBIO on pipes here.  If you want to make pipe fds
+     non-blocking, use the gnulib 'nonblocking' module, until gnulib implements
+     fcntl F_GETFL / F_SETFL with O_NONBLOCK.  */
+
  sock = FD_TO_SOCKET (fd);
  r = ioctlsocket (sock, req, buf);
  if (r < 0)
--
In memoriam Rachel Levy <http://en.wikipedia.org/wiki/Rachel_Levy>
Eric Blake
2011-03-30 17:46:06 UTC
Permalink
Post by Bruno Haible
Post by Paolo Bonzini
Without guessing what your bias is, I also :) prefer to implement
{g,s}et_nonblock_flag functions. It would use either
SetNamedPipeHandleState or ioctlsocket (using the socket detection trick
in sockets.c to detect sockets, and then GetFileType to detect pipes if
it fails).
Here's proposed code to that effect.
Let's get it checked in.
Post by Bruno Haible
Note that the getter function cannot
be implemented: How to determine whether a Woe32 socket is non-blocking?
I don't know.
Post by Bruno Haible
/* Non-blocking I/O is an I/O mode by which read(), write() calls avoid
- A read() call returns -1 with errno set to EAGAIN when no data or EOF
information is immediately available.
- A write() call returns -1 with errno set to EAGAIN when it cannot
transport the requested amount of data (but at most one pipe buffer)
without blocking.
three
Post by Bruno Haible
- use select() or poll() followed by read() or write() if the descriptor
is ready,
- call read() or write() in separate threads. */
- use aio_*, although that is not as widely portable
Post by Bruno Haible
#if 0 /* cannot be portably implemented */
/* Return true if I/O to the descriptor DESC is currently non-blocking,
or false if it is blocking. */
extern bool get_nonblocking_flag (int desc);
#endif
Should we make this tri-state?

-1 for unable to determine (such as mingw sockets for now), 0 for
blocking (such as mingw regular files, or Unix platforms where
fcntl(F_GETFL) works), 1 for non-blocking.
Post by Bruno Haible
/* Specify the non-blocking flag for the descriptor DESC.
Return 0 upon success, or -1 with errno set upon failure.
The default depends on the presence of the O_NONBLOCK flag for files
or pipes opened with open() or on the presence of the SOCK_NONBLOCK
flag for pipes. */
extern int set_nonblocking_flag (int desc, bool value);
Should we document this function's behavior on fd's exempt by POSIX?
For regular files and directories, non-blocking has no effect, but it is
implementation-defined whether fcntl(F_SETFL) can modify the O_NONBLOCK
flag or whether the flag is silently ignored. Having consistent
behavior (such as making this function always return -1 EBADF on a
non-pipe non-socket, rather than triggering implementation-defined
behavior with subtle differences across platforms) might be worth the
extra effort of an fstat(). I'm not sure whether block and character
special devices can usefully be set non-blocking, or whether that is a
per-device setting.
Post by Bruno Haible
#else
/* Unix API. */
# include <fcntl.h>
/* We don't need the gnulib replacement of fcntl() here. */
# undef fcntl
# if 0
bool
get_nonblocking_flag (int desc)
{
int fcntl_flags;
fcntl_flags = fcntl (desc, F_GETFL, 0);
if (fcntl_flags < 0)
return false;
This would be a case for returning -1 in a tri-state implementation.
Post by Bruno Haible
return (fcntl_flags & O_NONBLOCK) != 0;
}
# endif
int
set_nonblocking_flag (int desc, bool value)
{
int fcntl_flags;
fcntl_flags = fcntl (desc, F_GETFL, 0);
if (fcntl_flags < 0)
return -1;
if ((O_NONBLOCK & ~fcntl_flags) == 0)
return 0;
return fcntl (desc, F_SETFL, fcntl_flags | O_NONBLOCK);
This doesn't honor the 'value' parameter, but blindly assumes it was
true. Sometimes it is also desirable to clear non-blocking, by passing
false.
--
Eric Blake ***@redhat.com +1-801-349-2682
Libvirt virtualization library http://libvirt.org
Eric Blake
2011-03-30 18:01:35 UTC
Permalink
Post by Eric Blake
Post by Bruno Haible
Post by Paolo Bonzini
Without guessing what your bias is, I also :) prefer to implement
{g,s}et_nonblock_flag functions. It would use either
SetNamedPipeHandleState or ioctlsocket (using the socket detection trick
in sockets.c to detect sockets, and then GetFileType to detect pipes if
it fails).
Here's proposed code to that effect.
Let's get it checked in.
Post by Bruno Haible
Note that the getter function cannot
be implemented: How to determine whether a Woe32 socket is non-blocking?
I don't know.
Then again, since gnulib is _already_ wrapping all Woe32 sockets into a
nicer fd, we could maintain an external table of the blocking status of
all sockets that we have created (similar to how we already create an
external table of directory fds for emulating fchdir()). [Or even
modify the existing fchdir table to serve multiple purposes, since we've
already hooked dup(), fcntl(), and other functions into updating that
table as necessary]
--
Eric Blake ***@redhat.com +1-801-349-2682
Libvirt virtualization library http://libvirt.org
Paolo Bonzini
2011-03-31 06:44:36 UTC
Permalink
Post by Eric Blake
I'm not sure whether block and character
special devices can usefully be set non-blocking
I believe at least /dev/random supports it:

#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
char buf[256];
int fd = open ("/dev/random", O_RDONLY | O_NONBLOCK);
if (fd == -1) {
perror ("/dev/random");
exit (1);
}
alarm (5);
while (read (fd, buf, 256) != -1 || errno != EAGAIN)
;
puts ("works");
exit (0);
}

Paolo
Bruno Haible
2011-03-31 10:39:12 UTC
Permalink
Hi Eric,

Thanks for taking up and improving on my proposal.
Post by Eric Blake
Post by Bruno Haible
- use select() or poll() followed by read() or write() if the descriptor
is ready,
- call read() or write() in separate threads. */
- use aio_*, although that is not as widely portable
Yes, good point.
Post by Eric Blake
Post by Bruno Haible
#if 0 /* cannot be portably implemented */
/* Return true if I/O to the descriptor DESC is currently non-blocking,
or false if it is blocking. */
extern bool get_nonblocking_flag (int desc);
#endif
Should we make this tri-state?
-1 for unable to determine (such as mingw sockets for now), 0 for
blocking (such as mingw regular files, or Unix platforms where
fcntl(F_GETFL) works), 1 for non-blocking.
Good idea.
Post by Eric Blake
since gnulib is _already_ wrapping all Woe32 sockets into a
nicer fd, we could maintain an external table of the blocking status of
all sockets that we have created (similar to how we already create an
external table of directory fds for emulating fchdir()).
I don't think this is worth doing, because
- Most programs which need non-blocking can keep track of it by themselves.
- On Woe32, for socket descriptors inherited from the parent process (via
stdin, stdout, stderr), we would not know the initial value to put into
the table.
Post by Eric Blake
Post by Bruno Haible
/* Specify the non-blocking flag for the descriptor DESC.
Return 0 upon success, or -1 with errno set upon failure.
The default depends on the presence of the O_NONBLOCK flag for files
or pipes opened with open() or on the presence of the SOCK_NONBLOCK
flag for pipes. */
extern int set_nonblocking_flag (int desc, bool value);
Should we document this function's behavior on fd's exempt by POSIX?
For regular files and directories, non-blocking has no effect
Oh really? Even on regular files on NFS, CIFS, or CODA file systems,
reading from non-blocking file descriptors does block??
Post by Eric Blake
Having consistent
behavior (such as making this function always return -1 EBADF on a
non-pipe non-socket, rather than triggering implementation-defined
behavior with subtle differences across platforms) might be worth the
extra effort of an fstat().
On the contrary, if some kernels implement non-blocking on regular files
correctly, I wouldn't want this feature to be taken from me through an
extra fstat().
Post by Eric Blake
I'm not sure whether block and character
special devices can usefully be set non-blocking
Yes, terminal devices (such as stdin in an xterm) implement non-blocking.
The GNU readline library makes extensive use of it.
Post by Eric Blake
Post by Bruno Haible
return fcntl (desc, F_SETFL, fcntl_flags | O_NONBLOCK);
This doesn't honor the 'value' parameter
Oops. Thanks for catching that.

Bruno
--
In memoriam Selena Quintanilla <http://en.wikipedia.org/wiki/Selena>
Eric Blake
2011-03-31 04:07:20 UTC
Permalink
This still lacks the ability to query non-blocking status of mingw
sockets; but that should be possible in a future patch in much the
same manner as fchdir.

* modules/nonblocking: New module.
* modules/nonblocking-tests: Likewise.
* lib/nonblocking.h: New file.
* lib/nonblocking.c: Likewise.
* tests/test-nonblocking.c: New test.
* lib/ioctl.c (ioctl) [mingw]: Update comment.

Signed-off-by: Eric Blake <***@redhat.com>
---

I've touched up your proposal, and done more testing with this.
I'll probably commit this tomorrow.

ChangeLog | 11 ++++
lib/ioctl.c | 4 +
lib/nonblocking.c | 137 +++++++++++++++++++++++++++++++++++++++++++++
lib/nonblocking.h | 60 ++++++++++++++++++++
modules/nonblocking | 26 +++++++++
modules/nonblocking-tests | 16 +++++
tests/test-nonblocking.c | 115 +++++++++++++++++++++++++++++++++++++
7 files changed, 369 insertions(+), 0 deletions(-)
create mode 100644 lib/nonblocking.c
create mode 100644 lib/nonblocking.h
create mode 100644 modules/nonblocking
create mode 100644 modules/nonblocking-tests
create mode 100644 tests/test-nonblocking.c

diff --git a/ChangeLog b/ChangeLog
index 5bc9b73..daaf7bc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,15 @@
2011-03-30 Bruno Haible <***@clisp.org>
+ and Eric Blake <***@redhat.com>
+
+ nonblocking: new module
+ * modules/nonblocking: New module.
+ * modules/nonblocking-tests: Likewise.
+ * lib/nonblocking.h: New file.
+ * lib/nonblocking.c: Likewise.
+ * tests/test-nonblocking.c: New test.
+ * lib/ioctl.c (ioctl) [mingw]: Update comment.
+
+2011-03-30 Bruno Haible <***@clisp.org>

stdio: Avoid GCC >= 4.4 warnings when using %lld and similar on mingw.
* lib/stdio.in.h (_GL_ATTRIBUTE_FORMAT_PRINTF): Use 'gnu_printf' format
diff --git a/lib/ioctl.c b/lib/ioctl.c
index 7c09d95..4bbed76 100644
--- a/lib/ioctl.c
+++ b/lib/ioctl.c
@@ -63,6 +63,10 @@ ioctl (int fd, int req, ...)
buf = va_arg (args, void *);
va_end (args);

+ /* We don't support FIONBIO on pipes here. If you want to make pipe
+ fds non-blocking, use the gnulib 'nonblocking' module, until
+ gnulib implements fcntl F_GETFL / F_SETFL with O_NONBLOCK. */
+
sock = FD_TO_SOCKET (fd);
r = ioctlsocket (sock, req, buf);
if (r < 0)
diff --git a/lib/nonblocking.c b/lib/nonblocking.c
new file mode 100644
index 0000000..61ea3ca
--- /dev/null
+++ b/lib/nonblocking.c
@@ -0,0 +1,137 @@
+/* Non-blocking I/O for pipe or socket descriptors.
+ Copyright (C) 2011 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+/* Specification. */
+#include "nonblocking.h"
+
+#include <errno.h>
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+/* Native Woe32 API. */
+
+# include <sys/socket.h>
+# include <unistd.h>
+
+/* Get declarations of the Win32 API functions. */
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+
+int
+get_nonblocking_flag (int desc)
+{
+ HANDLE h = (HANDLE) _get_osfhandle (desc);
+ if (GetFileType (h) == FILE_TYPE_PIPE)
+ {
+ /* h is a pipe or socket. */
+ DWORD state;
+ if (GetNamedPipeHandleState (h, &state, NULL, NULL, NULL, NULL, 0))
+ /* h is a pipe. */
+ return (state & PIPE_NOWAIT) != 0;
+ else
+ /* h is a socket. */
+ errno = ENOSYS;
+ return -1;
+ }
+ else
+ return 0;
+}
+
+int
+set_nonblocking_flag (int desc, bool value)
+{
+ HANDLE h = (HANDLE) _get_osfhandle (desc);
+ if (GetFileType (h) == FILE_TYPE_PIPE)
+ {
+ /* h is a pipe or socket. */
+ DWORD state;
+ if (GetNamedPipeHandleState (h, &state, NULL, NULL, NULL, NULL, 0))
+ {
+ /* h is a pipe. */
+ if ((state & PIPE_NOWAIT) != 0)
+ {
+ if (value)
+ return 0;
+ state &= ~PIPE_NOWAIT;
+ }
+ else
+ {
+ if (!value)
+ return 0;
+ state |= PIPE_NOWAIT;
+ }
+ if (SetNamedPipeHandleState (h, &state, NULL, NULL))
+ return 0;
+ errno = EINVAL;
+ return -1;
+ }
+ else
+ {
+ /* h is a socket. */
+ int v = value;
+ return ioctl (desc, FIONBIO, &v);
+ }
+ }
+ else
+ {
+ /* Win32 does not support non-blocking on regular files. */
+ errno = ENOTSUP;
+ return -1;
+ }
+}
+
+#else
+/* Unix API. */
+
+# include <fcntl.h>
+
+# if !O_NONBLOCK
+# error Please port nonblocking to your platform
+# endif
+
+/* We don't need the gnulib replacement of fcntl() here. */
+# undef fcntl
+
+int
+get_nonblocking_flag (int desc)
+{
+ int fcntl_flags;
+
+ fcntl_flags = fcntl (desc, F_GETFL, 0);
+ if (fcntl_flags < 0)
+ return -1;
+ return (fcntl_flags & O_NONBLOCK) != 0;
+}
+
+int
+set_nonblocking_flag (int desc, bool value)
+{
+ int fcntl_flags;
+
+ fcntl_flags = fcntl (desc, F_GETFL, 0);
+ if (fcntl_flags < 0)
+ return -1;
+ if (!!(O_NONBLOCK & fcntl_flags) == value)
+ return 0;
+ if (value)
+ fcntl_flags |= O_NONBLOCK;
+ else
+ fcntl_flags &= ~O_NONBLOCK;
+ return fcntl (desc, F_SETFL, fcntl_flags);
+}
+
+#endif
diff --git a/lib/nonblocking.h b/lib/nonblocking.h
new file mode 100644
index 0000000..ed1f317
--- /dev/null
+++ b/lib/nonblocking.h
@@ -0,0 +1,60 @@
+/* Non-blocking I/O for pipe or socket descriptors.
+ Copyright (C) 2011 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _NONBLOCKING_H
+#define _NONBLOCKING_H
+
+#include <stdbool.h>
+
+/* Non-blocking I/O is an I/O mode by which read(), write() calls avoid
+ blocking the current thread. When non-blocking is enabled:
+ - A read() call returns -1 with errno set to EAGAIN when no data or EOF
+ information is immediately available.
+ - A write() call returns -1 with errno set to EAGAIN when it cannot
+ transport the requested amount of data (but at most one pipe buffer)
+ without blocking.
+
+ There are three modern alternatives to non-blocking I/O:
+ - use select() or poll() followed by read() or write() if the descriptor
+ is ready,
+ - call read() or write() in separate threads,
+ - use <aio.h> interfaces. */
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Return 1 if I/O to the descriptor DESC is currently non-blocking, 0
+ it is blocking, or -1 with errno set if fd is invalid or blocking
+ status cannot be determined (such as with sockets on mingw). */
+extern int get_nonblocking_flag (int desc);
+
+/* Specify the non-blocking flag for the descriptor DESC.
+ Return 0 upon success, or -1 with errno set upon failure.
+ The default depends on the presence of the O_NONBLOCK flag for files
+ or pipes opened with open() or on the presence of the SOCK_NONBLOCK
+ flag for sockets. Regular files and directories cannot be made
+ non-blocking; block and character devices depend on the system. */
+extern int set_nonblocking_flag (int desc, bool value);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _NONBLOCKING_H */
diff --git a/modules/nonblocking b/modules/nonblocking
new file mode 100644
index 0000000..44ca33e
--- /dev/null
+++ b/modules/nonblocking
@@ -0,0 +1,26 @@
+Description:
+Read, set or clear the non-blocking file descriptor flag.
+
+Files:
+lib/nonblocking.c
+lib/nonblocking.h
+
+Depends-on:
+fcntl-h
+ioctl
+stdbool
+sys_socket
+
+configure.ac:
+AC_LIBOBJ([nonblocking])
+
+Makefile.am:
+
+Include:
+"nonblocking.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+Bruno Haible, Eric Blake
diff --git a/modules/nonblocking-tests b/modules/nonblocking-tests
new file mode 100644
index 0000000..94fccb2
--- /dev/null
+++ b/modules/nonblocking-tests
@@ -0,0 +1,16 @@
+Files:
+tests/test-nonblocking.c
+tests/macros.h
+
+Depends-on:
+close
+open
+pipe-posix
+socket
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-nonblocking
+check_PROGRAMS += test-nonblocking
+test_nonblocking_LDADD = $(LDADD) @LIBSOCKET@
diff --git a/tests/test-nonblocking.c b/tests/test-nonblocking.c
new file mode 100644
index 0000000..8ba4c9c
--- /dev/null
+++ b/tests/test-nonblocking.c
@@ -0,0 +1,115 @@
+/* Test manipulation of non-blocking flag.
+ Copyright (C) 2011 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Written by Eric Blake <***@byu.net>, 2011. */
+
+#include <config.h>
+
+#include "nonblocking.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "macros.h"
+
+int
+main (void)
+{
+ const char *file = "test-nonblock.tmp";
+ int fd_file = creat (file, 0600);
+ int fd_pipe[2];
+ int fd_sock;
+ bool sock_works = true;
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+ /* For now, we can't get nonblock status of windows sockets. */
+ sock_works = false;
+#endif
+
+ /* Assume std descriptors were provided by invoker. */
+ ASSERT (STDERR_FILENO < fd_file);
+
+ /* Test regular files; setting nonblock on file is unspecified. */
+ ASSERT (get_nonblocking_flag (fd_file) == 0);
+ ASSERT (set_nonblocking_flag (fd_file, false) == 0);
+ ASSERT (get_nonblocking_flag (fd_file) == 0);
+ ASSERT (close (fd_file) == 0);
+ ASSERT (unlink (file) == 0);
+
+ /* Test directories; setting nonblock is unspecified. */
+ fd_file = open (".", O_RDONLY);
+ ASSERT (STDERR_FILENO < fd_file);
+ ASSERT (get_nonblocking_flag (fd_file) == 0);
+ ASSERT (set_nonblocking_flag (fd_file, false) == 0);
+ ASSERT (get_nonblocking_flag (fd_file) == 0);
+ ASSERT (close (fd_file) == 0);
+
+ /* Test pipes. */
+ ASSERT (pipe (fd_pipe) == 0);
+ ASSERT (get_nonblocking_flag (fd_pipe[0]) == 0);
+ ASSERT (get_nonblocking_flag (fd_pipe[1]) == 0);
+ ASSERT (set_nonblocking_flag (fd_pipe[0], true) == 0);
+ ASSERT (get_nonblocking_flag (fd_pipe[0]) == 1);
+ ASSERT (get_nonblocking_flag (fd_pipe[1]) == 0);
+ ASSERT (set_nonblocking_flag (fd_pipe[1], true) == 0);
+ ASSERT (set_nonblocking_flag (fd_pipe[0], false) == 0);
+ ASSERT (get_nonblocking_flag (fd_pipe[0]) == 0);
+ ASSERT (get_nonblocking_flag (fd_pipe[1]) == 1);
+ ASSERT (close (fd_pipe[0]) == 0);
+ ASSERT (close (fd_pipe[1]) == 0);
+
+#if GNULIB_TEST_PIPE2
+ /* mingw still lacks O_NONBLOCK replacement. */
+ ASSERT (pipe2 (fd_pipe, O_NONBLOCK) == 0);
+ ASSERT (get_nonblocking_flag (fd_pipe[0]) == !!O_NONBLOCK);
+ ASSERT (get_nonblocking_flag (fd_pipe[1]) == !!O_NONBLOCK);
+ ASSERT (close (fd_pipe[0]) == 0);
+ ASSERT (close (fd_pipe[1]) == 0);
+#endif /* GNULIB_TEST_PIPE2 */
+
+ /* Test sockets. */
+ fd_sock = socket (AF_INET, SOCK_STREAM, 0);
+ ASSERT (get_nonblocking_flag (fd_sock) == sock_works ? 0 : -1);
+ ASSERT (set_nonblocking_flag (fd_sock, true) == 0);
+ ASSERT (get_nonblocking_flag (fd_sock) == sock_works ? 1 : -1);
+ ASSERT (set_nonblocking_flag (fd_sock, false) == 0);
+ ASSERT (get_nonblocking_flag (fd_sock) == sock_works ? 0 : -1);
+ ASSERT (close (fd_sock) == 0);
+
+#if SOCK_NONBLOCK
+ fd_sock = socket (AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
+ ASSERT (get_nonblocking_flag (fd_sock) == sock_works ? 1 : -1);
+ ASSERT (close (fd_sock) == 0);
+#endif /* SOCK_NONBLOCK */
+
+ /* Test error handling. */
+ errno = 0;
+ ASSERT (get_nonblocking_flag (-1) == -1);
+ ASSERT (errno == EBADF);
+ errno = 0;
+ ASSERT (set_nonblocking_flag (-1, false) == -1);
+ ASSERT (errno == EBADF);
+ errno = 0;
+ ASSERT (set_nonblocking_flag (-1, true) == -1);
+ ASSERT (errno == EBADF);
+ errno = 0;
+ ASSERT (set_nonblocking_flag (10000000, false) == -1);
+ ASSERT (errno == EBADF);
+
+ return 0;
+}
--
1.7.4
Bruno Haible
2011-03-31 11:12:25 UTC
Permalink
Post by Eric Blake
I've touched up your proposal, and done more testing with this.
Great, thanks!

I've done three more touch-ups:
- In the module description, prefer a lib_SOURCES augmentation to an
AC_LIBOBJ invocation when possible. In this case both are equivalent,
but AC_LIBOBJ has some restrictions that make lib_SOURCES a better
choice in general.
- Fix ASSERT expressions: recall that == has higher precedence than ? :.
- A little more structure in the tests file, to separate independent tests.

Bruno


--- lib/nonblocking.c.orig Thu Mar 31 13:06:41 2011
+++ lib/nonblocking.c Thu Mar 31 12:53:36 2011
@@ -48,6 +48,7 @@
return -1;
}
else
+ /* Win32 does not support non-blocking on regular files. */
return 0;
}

@@ -125,7 +126,7 @@
fcntl_flags = fcntl (desc, F_GETFL, 0);
if (fcntl_flags < 0)
return -1;
- if (!!(O_NONBLOCK & fcntl_flags) == value)
+ if (((fcntl_flags & O_NONBLOCK) != 0) == value)
return 0;
if (value)
fcntl_flags |= O_NONBLOCK;
--- lib/nonblocking.h.orig Thu Mar 31 13:06:41 2011
+++ lib/nonblocking.h Thu Mar 31 12:51:19 2011
@@ -26,6 +26,9 @@
- A write() call returns -1 with errno set to EAGAIN when it cannot
transport the requested amount of data (but at most one pipe buffer)
without blocking.
+ Non-blocking I/O is most useful for character devices, pipes, and sockets.
+ Whether it also works on regular files and block devices is platform
+ dependent.

There are three modern alternatives to non-blocking I/O:
- use select() or poll() followed by read() or write() if the descriptor
@@ -48,8 +50,7 @@
Return 0 upon success, or -1 with errno set upon failure.
The default depends on the presence of the O_NONBLOCK flag for files
or pipes opened with open() or on the presence of the SOCK_NONBLOCK
- flag for sockets. Regular files and directories cannot be made
- non-blocking; block and character devices depend on the system. */
+ flag for sockets. */
extern int set_nonblocking_flag (int desc, bool value);


--- modules/nonblocking.orig Thu Mar 31 13:06:41 2011
+++ modules/nonblocking Thu Mar 31 12:43:41 2011
@@ -12,9 +12,9 @@
sys_socket

configure.ac:
-AC_LIBOBJ([nonblocking])

Makefile.am:
+lib_SOURCES += nonblocking.c

Include:
"nonblocking.h"
--- tests/test-nonblocking.c.orig Thu Mar 31 13:06:41 2011
+++ tests/test-nonblocking.c Thu Mar 31 13:02:40 2011
@@ -31,27 +31,29 @@
main (void)
{
const char *file = "test-nonblock.tmp";
- int fd_file = creat (file, 0600);
+ int fd_file;
int fd_pipe[2];
int fd_sock;
bool sock_works = true;

#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
- /* For now, we can't get nonblock status of windows sockets. */
+ /* For now, we can't get nonblocking status of windows sockets. */
sock_works = false;
#endif

+ fd_file = creat (file, 0600);
+
/* Assume std descriptors were provided by invoker. */
ASSERT (STDERR_FILENO < fd_file);

- /* Test regular files; setting nonblock on file is unspecified. */
+ /* Test regular files; setting nonblocking on file is unspecified. */
ASSERT (get_nonblocking_flag (fd_file) == 0);
ASSERT (set_nonblocking_flag (fd_file, false) == 0);
ASSERT (get_nonblocking_flag (fd_file) == 0);
ASSERT (close (fd_file) == 0);
ASSERT (unlink (file) == 0);

- /* Test directories; setting nonblock is unspecified. */
+ /* Test directories; setting nonblocking is unspecified. */
fd_file = open (".", O_RDONLY);
ASSERT (STDERR_FILENO < fd_file);
ASSERT (get_nonblocking_flag (fd_file) == 0);
@@ -84,32 +86,40 @@

/* Test sockets. */
fd_sock = socket (AF_INET, SOCK_STREAM, 0);
- ASSERT (get_nonblocking_flag (fd_sock) == sock_works ? 0 : -1);
+ ASSERT (get_nonblocking_flag (fd_sock) == (sock_works ? 0 : -1));
ASSERT (set_nonblocking_flag (fd_sock, true) == 0);
- ASSERT (get_nonblocking_flag (fd_sock) == sock_works ? 1 : -1);
+ ASSERT (get_nonblocking_flag (fd_sock) == (sock_works ? 1 : -1));
ASSERT (set_nonblocking_flag (fd_sock, false) == 0);
- ASSERT (get_nonblocking_flag (fd_sock) == sock_works ? 0 : -1);
+ ASSERT (get_nonblocking_flag (fd_sock) == (sock_works ? 0 : -1));
ASSERT (close (fd_sock) == 0);

#if SOCK_NONBLOCK
fd_sock = socket (AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
- ASSERT (get_nonblocking_flag (fd_sock) == sock_works ? 1 : -1);
+ ASSERT (get_nonblocking_flag (fd_sock) == (sock_works ? 1 : -1));
ASSERT (close (fd_sock) == 0);
#endif /* SOCK_NONBLOCK */

/* Test error handling. */
- errno = 0;
- ASSERT (get_nonblocking_flag (-1) == -1);
- ASSERT (errno == EBADF);
- errno = 0;
- ASSERT (set_nonblocking_flag (-1, false) == -1);
- ASSERT (errno == EBADF);
- errno = 0;
- ASSERT (set_nonblocking_flag (-1, true) == -1);
- ASSERT (errno == EBADF);
- errno = 0;
- ASSERT (set_nonblocking_flag (10000000, false) == -1);
- ASSERT (errno == EBADF);
+ {
+ errno = 0;
+ ASSERT (get_nonblocking_flag (-1) == -1);
+ ASSERT (errno == EBADF);
+ }
+ {
+ errno = 0;
+ ASSERT (set_nonblocking_flag (-1, false) == -1);
+ ASSERT (errno == EBADF);
+ }
+ {
+ errno = 0;
+ ASSERT (set_nonblocking_flag (-1, true) == -1);
+ ASSERT (errno == EBADF);
+ }
+ {
+ errno = 0;
+ ASSERT (set_nonblocking_flag (10000000, false) == -1);
+ ASSERT (errno == EBADF);
+ }

return 0;
}
--
In memoriam Selena Quintanilla <http://en.wikipedia.org/wiki/Selena>
Loading...