* 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 <eblake@redhat.com>
+2011-03-31 Bruno Haible <bruno@clisp.org>
+ and Eric Blake <eblake@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 <bruno@clisp.org>
stdio: Avoid GCC >= 4.4 warnings when using %lld and similar on mingw.
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)
--- /dev/null
+/* 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
+ /* Win32 does not support non-blocking on regular files. */
+ 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 (((fcntl_flags & O_NONBLOCK) != 0) == value)
+ return 0;
+ if (value)
+ fcntl_flags |= O_NONBLOCK;
+ else
+ fcntl_flags &= ~O_NONBLOCK;
+ return fcntl (desc, F_SETFL, fcntl_flags);
+}
+
+#endif
--- /dev/null
+/* 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.
+ Non-blocking I/O is most useful for character devices. 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
+ 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. */
+extern int set_nonblocking_flag (int desc, bool value);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _NONBLOCKING_H */
--- /dev/null
+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:
+
+Makefile.am:
+lib_SOURCES += nonblocking.c
+
+Include:
+"nonblocking.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+Bruno Haible, Eric Blake
--- /dev/null
+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@
--- /dev/null
+/* 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 <ebb9@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;
+ int fd_pipe[2];
+ int fd_sock;
+ bool sock_works = true;
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+ /* 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 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 nonblocking 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;
+}