Mercurial > hg > octave-kai > gnulib-hg
changeset 14513:d6c2edffe836
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.
Signed-off-by: Eric Blake <eblake@redhat.com>
author | Eric Blake <eblake@redhat.com> |
---|---|
date | Wed, 30 Mar 2011 22:07:20 -0600 |
parents | e8bb1db3efbb |
children | c71d17cff81b |
files | ChangeLog lib/ioctl.c lib/nonblocking.c lib/nonblocking.h modules/nonblocking modules/nonblocking-tests tests/test-nonblocking.c |
diffstat | 7 files changed, 381 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +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.
--- a/lib/ioctl.c +++ b/lib/ioctl.c @@ -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)
new file mode 100644 --- /dev/null +++ b/lib/nonblocking.c @@ -0,0 +1,138 @@ +/* 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
new file mode 100644 --- /dev/null +++ b/lib/nonblocking.h @@ -0,0 +1,61 @@ +/* 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 */
new file mode 100644 --- /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: + +Makefile.am: +lib_SOURCES += nonblocking.c + +Include: +"nonblocking.h" + +License: +LGPLv2+ + +Maintainer: +Bruno Haible, Eric Blake
new file mode 100644 --- /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@
new file mode 100644 --- /dev/null +++ b/tests/test-nonblocking.c @@ -0,0 +1,125 @@ +/* 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; +}