diff options
Diffstat (limited to 'gl/tests')
38 files changed, 6129 insertions, 455 deletions
diff --git a/gl/tests/Makefile.am b/gl/tests/Makefile.am index d66ab49a3d..f1e68b6b58 100644 --- a/gl/tests/Makefile.am +++ b/gl/tests/Makefile.am @@ -22,7 +22,7 @@ # # Generated by gnulib-tool. -AUTOMAKE_OPTIONS = 1.5 foreign +AUTOMAKE_OPTIONS = 1.5 foreign subdir-objects SUBDIRS = . TESTS = @@ -85,42 +85,6 @@ EXTRA_DIST += test-argp.c test-argp-2.sh ## end gnulib module argp-tests -## begin gnulib module arpa_inet - -BUILT_SOURCES += arpa/inet.h - -# We need the following in order to create <arpa/inet.h> when the system -# doesn't have one. -arpa/inet.h: arpa_inet.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(WARN_ON_USE_H) $(ARG_NONNULL_H) - $(AM_V_at)$(MKDIR_P) arpa - $(AM_V_GEN)rm -f $@-t $@ && \ - { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \ - sed -e 's|@''GUARD_PREFIX''@|GL|g' \ - -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \ - -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \ - -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \ - -e 's|@''HAVE_FEATURES_H''@|$(HAVE_FEATURES_H)|g' \ - -e 's|@''NEXT_ARPA_INET_H''@|$(NEXT_ARPA_INET_H)|g' \ - -e 's|@''HAVE_ARPA_INET_H''@|$(HAVE_ARPA_INET_H)|g' \ - -e 's/@''GNULIB_INET_NTOP''@/$(GNULIB_INET_NTOP)/g' \ - -e 's/@''GNULIB_INET_PTON''@/$(GNULIB_INET_PTON)/g' \ - -e 's|@''HAVE_DECL_INET_NTOP''@|$(HAVE_DECL_INET_NTOP)|g' \ - -e 's|@''HAVE_DECL_INET_PTON''@|$(HAVE_DECL_INET_PTON)|g' \ - -e 's|@''REPLACE_INET_NTOP''@|$(REPLACE_INET_NTOP)|g' \ - -e 's|@''REPLACE_INET_PTON''@|$(REPLACE_INET_PTON)|g' \ - -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \ - -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \ - -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \ - < $(srcdir)/arpa_inet.in.h; \ - } > $@-t && \ - mv $@-t $@ -MOSTLYCLEANFILES += arpa/inet.h arpa/inet.h-t -MOSTLYCLEANDIRS += arpa - -EXTRA_DIST += arpa_inet.in.h - -## end gnulib module arpa_inet - ## begin gnulib module arpa_inet-tests TESTS += test-arpa_inet @@ -187,6 +151,24 @@ EXTRA_DIST += test-close.c signature.h macros.h ## end gnulib module close-tests +## begin gnulib module connect + + +EXTRA_DIST += connect.c w32sock.h + +EXTRA_libtests_a_SOURCES += connect.c + +## end gnulib module connect + +## begin gnulib module connect-tests + +TESTS += test-connect +check_PROGRAMS += test-connect +test_connect_LDADD = $(LDADD) @LIBSOCKET@ $(INET_PTON_LIB) +EXTRA_DIST += test-connect.c signature.h macros.h + +## end gnulib module connect-tests + ## begin gnulib module crypto/hmac-md5-tests TESTS += test-hmac-md5 @@ -407,6 +389,23 @@ EXTRA_DIST += test-ftello.c test-ftello.sh test-ftello2.sh test-ftello3.c test-f ## end gnulib module ftello-tests +## begin gnulib module ftruncate + + +EXTRA_DIST += ftruncate.c + +EXTRA_libtests_a_SOURCES += ftruncate.c + +## end gnulib module ftruncate + +## begin gnulib module ftruncate-tests + +TESTS += test-ftruncate.sh +check_PROGRAMS += test-ftruncate +EXTRA_DIST += test-ftruncate.c test-ftruncate.sh signature.h macros.h + +## end gnulib module ftruncate-tests + ## begin gnulib module func-tests TESTS += test-func @@ -510,15 +509,6 @@ EXTRA_DIST += test-ignore-value.c ## end gnulib module ignore-value-tests -## begin gnulib module inet_pton - - -EXTRA_DIST += inet_pton.c - -EXTRA_libtests_a_SOURCES += inet_pton.c - -## end gnulib module inet_pton - ## begin gnulib module inet_pton-tests TESTS += test-inet_pton @@ -586,6 +576,23 @@ EXTRA_DIST += test-inttypes.c ## end gnulib module inttypes-tests +## begin gnulib module ioctl + + +EXTRA_DIST += ioctl.c w32sock.h + +EXTRA_libtests_a_SOURCES += ioctl.c + +## end gnulib module ioctl + +## begin gnulib module ioctl-tests + +TESTS += test-ioctl +check_PROGRAMS += test-ioctl +EXTRA_DIST += test-ioctl.c signature.h macros.h + +## end gnulib module ioctl-tests + ## begin gnulib module isnand-nolibm-tests TESTS += test-isnand-nolibm @@ -622,6 +629,21 @@ EXTRA_DIST += test-listen.c signature.h macros.h ## end gnulib module listen-tests +## begin gnulib module lock + +libtests_a_SOURCES += glthread/lock.h glthread/lock.c + +## end gnulib module lock + +## begin gnulib module lock-tests + +TESTS += test-lock +check_PROGRAMS += test-lock +test_lock_LDADD = $(LDADD) @LIBMULTITHREAD@ @YIELD_LIB@ +EXTRA_DIST += test-lock.c + +## end gnulib module lock-tests + ## begin gnulib module lstat @@ -728,6 +750,40 @@ EXTRA_DIST += test-pathmax.c ## end gnulib module pathmax-tests +## begin gnulib module perror + + +EXTRA_DIST += perror.c + +EXTRA_libtests_a_SOURCES += perror.c + +## end gnulib module perror + +## begin gnulib module perror-tests + +TESTS += test-perror.sh test-perror2 +check_PROGRAMS += test-perror test-perror2 +EXTRA_DIST += init.sh macros.h signature.h test-perror.c test-perror2.c test-perror.sh + +## end gnulib module perror-tests + +## begin gnulib module pipe-posix + + +EXTRA_DIST += pipe.c + +EXTRA_libtests_a_SOURCES += pipe.c + +## end gnulib module pipe-posix + +## begin gnulib module pipe-posix-tests + +TESTS += test-pipe +check_PROGRAMS += test-pipe +EXTRA_DIST += test-pipe.c signature.h macros.h + +## end gnulib module pipe-posix-tests + ## begin gnulib module printf-frexp-tests TESTS += test-printf-frexp @@ -785,6 +841,18 @@ EXTRA_DIST += same-inode.h ## end gnulib module same-inode +## begin gnulib module select-tests + +TESTS += test-select test-select-in.sh test-select-out.sh +# test-select-stdin has to be run by hand. +check_PROGRAMS += test-select test-select-fd test-select-stdin +test_select_LDADD = $(LDADD) @LIB_SELECT@ @LIBSOCKET@ $(INET_PTON_LIB) +test_select_fd_LDADD = $(LDADD) @LIB_SELECT@ +test_select_stdin_LDADD = $(LDADD) @LIB_SELECT@ +EXTRA_DIST += macros.h signature.h test-select.c test-select.h test-select-fd.c test-select-in.sh test-select-out.sh test-select-stdin.c + +## end gnulib module select-tests + ## begin gnulib module sendto-tests TESTS += test-sendto @@ -829,6 +897,14 @@ EXTRA_DIST += test-shutdown.c signature.h macros.h ## end gnulib module shutdown-tests +## begin gnulib module signal-h-tests + +TESTS += test-signal-h +check_PROGRAMS += test-signal-h +EXTRA_DIST += test-signal-h.c + +## end gnulib module signal-h-tests + ## begin gnulib module signbit-tests TESTS += test-signbit @@ -1019,6 +1095,23 @@ EXTRA_DIST += test-strerror.c signature.h macros.h ## end gnulib module strerror-tests +## begin gnulib module strerror_r-posix + + +EXTRA_DIST += strerror_r.c + +EXTRA_libtests_a_SOURCES += strerror_r.c + +## end gnulib module strerror_r-posix + +## begin gnulib module strerror_r-posix-tests + +TESTS += test-strerror_r +check_PROGRAMS += test-strerror_r +EXTRA_DIST += test-strerror_r.c signature.h macros.h + +## end gnulib module strerror_r-posix-tests + ## begin gnulib module string-tests TESTS += test-string @@ -1068,6 +1161,54 @@ EXTRA_DIST += test-symlink.h test-symlink.c signature.h macros.h ## end gnulib module symlink-tests +## begin gnulib module sys_ioctl + +BUILT_SOURCES += sys/ioctl.h + +# We need the following in order to create <sys/ioctl.h> when the system +# does not have a complete one. +sys/ioctl.h: sys_ioctl.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(WARN_ON_USE_H) + $(AM_V_at)$(MKDIR_P) sys + $(AM_V_GEN)rm -f $@-t $@ && \ + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \ + sed -e 's|@''GUARD_PREFIX''@|GL|g' \ + -e 's|@''HAVE_SYS_IOCTL_H''@|$(HAVE_SYS_IOCTL_H)|g' \ + -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \ + -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \ + -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \ + -e 's|@''NEXT_SYS_IOCTL_H''@|$(NEXT_SYS_IOCTL_H)|g' \ + -e 's/@''GNULIB_IOCTL''@/$(GNULIB_IOCTL)/g' \ + -e 's|@''SYS_IOCTL_H_HAVE_WINSOCK2_H''@|$(SYS_IOCTL_H_HAVE_WINSOCK2_H)|g' \ + -e 's|@''SYS_IOCTL_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS''@|$(SYS_IOCTL_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS)|g' \ + -e 's|@''REPLACE_IOCTL''@|$(REPLACE_IOCTL)|g' \ + -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \ + -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \ + < $(srcdir)/sys_ioctl.in.h; \ + } > $@-t && \ + mv $@-t $@ +MOSTLYCLEANFILES += sys/ioctl.h sys/ioctl.h-t +MOSTLYCLEANDIRS += sys + +EXTRA_DIST += sys_ioctl.in.h + +## end gnulib module sys_ioctl + +## begin gnulib module sys_ioctl-tests + +TESTS += test-sys_ioctl +check_PROGRAMS += test-sys_ioctl +EXTRA_DIST += test-sys_ioctl.c + +## end gnulib module sys_ioctl-tests + +## begin gnulib module sys_select-tests + +TESTS += test-sys_select +check_PROGRAMS += test-sys_select +EXTRA_DIST += test-sys_select.c signature.h + +## end gnulib module sys_select-tests + ## begin gnulib module sys_socket-tests TESTS += test-sys_socket @@ -1116,6 +1257,30 @@ EXTRA_DIST += test-sysexits.c ## end gnulib module sysexits-tests +## begin gnulib module thread + +libtests_a_SOURCES += glthread/thread.h glthread/thread.c + +## end gnulib module thread + +## begin gnulib module thread-tests + +TESTS += test-thread_self test-thread_create +check_PROGRAMS += test-thread_self test-thread_create +test_thread_self_LDADD = $(LDADD) @LIBTHREAD@ +test_thread_create_LDADD = $(LDADD) @LIBMULTITHREAD@ +EXTRA_DIST += test-thread_self.c test-thread_create.c macros.h + +## end gnulib module thread-tests + +## begin gnulib module threadlib + +libtests_a_SOURCES += glthread/threadlib.c + +EXTRA_DIST += $(top_srcdir)/build-aux/config.rpath + +## end gnulib module threadlib + ## begin gnulib module time-tests TESTS += test-time @@ -1235,6 +1400,12 @@ EXTRA_DIST += test-wchar.c ## end gnulib module wchar-tests +## begin gnulib module yield + +libtests_a_SOURCES += glthread/yield.h + +## end gnulib module yield + # Clean up after Solaris cc. clean-local: rm -rf SunWS_cache diff --git a/gl/tests/arpa_inet.in.h b/gl/tests/arpa_inet.in.h deleted file mode 100644 index 5264853b59..0000000000 --- a/gl/tests/arpa_inet.in.h +++ /dev/null @@ -1,141 +0,0 @@ -/* A GNU-like <arpa/inet.h>. - - Copyright (C) 2005-2006, 2008-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, 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, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - -#ifndef _@GUARD_PREFIX@_ARPA_INET_H - -#if __GNUC__ >= 3 -@PRAGMA_SYSTEM_HEADER@ -#endif -@PRAGMA_COLUMNS@ - -#if @HAVE_FEATURES_H@ -# include <features.h> /* for __GLIBC__ */ -#endif - -/* Gnulib's sys/socket.h is responsible for defining socklen_t (used below) and - for pulling in winsock2.h etc. under MinGW. - But avoid namespace pollution on glibc systems. */ -#ifndef __GLIBC__ -# include <sys/socket.h> -#endif - -/* On NonStop Kernel, inet_ntop and inet_pton are declared in <netdb.h>. - But avoid namespace pollution on glibc systems. */ -#if defined __TANDEM && !defined __GLIBC__ -# include <netdb.h> -#endif - -#if @HAVE_ARPA_INET_H@ - -/* The include_next requires a split double-inclusion guard. */ -# @INCLUDE_NEXT@ @NEXT_ARPA_INET_H@ - -#endif - -#ifndef _@GUARD_PREFIX@_ARPA_INET_H -#define _@GUARD_PREFIX@_ARPA_INET_H - -/* The definitions of _GL_FUNCDECL_RPL etc. are copied here. */ - -/* The definition of _GL_ARG_NONNULL is copied here. */ - -/* The definition of _GL_WARN_ON_USE is copied here. */ - - -#if @GNULIB_INET_NTOP@ -/* Converts an internet address from internal format to a printable, - presentable format. - AF is an internet address family, such as AF_INET or AF_INET6. - SRC points to a 'struct in_addr' (for AF_INET) or 'struct in6_addr' - (for AF_INET6). - DST points to a buffer having room for CNT bytes. - The printable representation of the address (in numeric form, not - surrounded by [...], no reverse DNS is done) is placed in DST, and - DST is returned. If an error occurs, the return value is NULL and - errno is set. If CNT bytes are not sufficient to hold the result, - the return value is NULL and errno is set to ENOSPC. A good value - for CNT is 46. - - For more details, see the POSIX:2001 specification - <http://www.opengroup.org/susv3xsh/inet_ntop.html>. */ -# if @REPLACE_INET_NTOP@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef inet_ntop -# define inet_ntop rpl_inet_ntop -# endif -_GL_FUNCDECL_RPL (inet_ntop, const char *, - (int af, const void *restrict src, - char *restrict dst, socklen_t cnt) - _GL_ARG_NONNULL ((2, 3))); -_GL_CXXALIAS_RPL (inet_ntop, const char *, - (int af, const void *restrict src, - char *restrict dst, socklen_t cnt)); -# else -# if !@HAVE_DECL_INET_NTOP@ -_GL_FUNCDECL_SYS (inet_ntop, const char *, - (int af, const void *restrict src, - char *restrict dst, socklen_t cnt) - _GL_ARG_NONNULL ((2, 3))); -# endif -/* Need to cast, because on NonStop Kernel, the fourth parameter is - size_t cnt. */ -_GL_CXXALIAS_SYS_CAST (inet_ntop, const char *, - (int af, const void *restrict src, - char *restrict dst, socklen_t cnt)); -# endif -_GL_CXXALIASWARN (inet_ntop); -#elif defined GNULIB_POSIXCHECK -# undef inet_ntop -# if HAVE_RAW_DECL_INET_NTOP -_GL_WARN_ON_USE (inet_ntop, "inet_ntop is unportable - " - "use gnulib module inet_ntop for portability"); -# endif -#endif - -#if @GNULIB_INET_PTON@ -# if @REPLACE_INET_PTON@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef inet_pton -# define inet_pton rpl_inet_pton -# endif -_GL_FUNCDECL_RPL (inet_pton, int, - (int af, const char *restrict src, void *restrict dst) - _GL_ARG_NONNULL ((2, 3))); -_GL_CXXALIAS_RPL (inet_pton, int, - (int af, const char *restrict src, void *restrict dst)); -# else -# if !@HAVE_DECL_INET_PTON@ -_GL_FUNCDECL_SYS (inet_pton, int, - (int af, const char *restrict src, void *restrict dst) - _GL_ARG_NONNULL ((2, 3))); -# endif -_GL_CXXALIAS_SYS (inet_pton, int, - (int af, const char *restrict src, void *restrict dst)); -# endif -_GL_CXXALIASWARN (inet_pton); -#elif defined GNULIB_POSIXCHECK -# undef inet_pton -# if HAVE_RAW_DECL_INET_PTON -_GL_WARN_ON_USE (inet_pton, "inet_pton is unportable - " - "use gnulib module inet_pton for portability"); -# endif -#endif - - -#endif /* _@GUARD_PREFIX@_ARPA_INET_H */ -#endif /* _@GUARD_PREFIX@_ARPA_INET_H */ diff --git a/gl/tests/connect.c b/gl/tests/connect.c new file mode 100644 index 0000000000..afd13b9f8d --- /dev/null +++ b/gl/tests/connect.c @@ -0,0 +1,56 @@ +/* connect.c --- wrappers for Windows connect function + + Copyright (C) 2008-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 Paolo Bonzini */ + +#include <config.h> + +#define WIN32_LEAN_AND_MEAN +/* Get winsock2.h. */ +#include <sys/socket.h> + +/* Get set_winsock_errno, FD_TO_SOCKET etc. */ +#include "w32sock.h" + +#undef connect + +int +rpl_connect (int fd, const struct sockaddr *sockaddr, socklen_t len) +{ + SOCKET sock = FD_TO_SOCKET (fd); + + if (sock == INVALID_SOCKET) + { + errno = EBADF; + return -1; + } + else + { + int r = connect (sock, sockaddr, len); + if (r < 0) + { + /* EINPROGRESS is not returned by WinSock 2.0; for backwards + compatibility, connect(2) uses EWOULDBLOCK. */ + if (WSAGetLastError () == WSAEWOULDBLOCK) + WSASetLastError (WSAEINPROGRESS); + + set_winsock_errno (); + } + + return r; + } +} diff --git a/gl/tests/ftruncate.c b/gl/tests/ftruncate.c new file mode 100644 index 0000000000..ae1e85890c --- /dev/null +++ b/gl/tests/ftruncate.c @@ -0,0 +1,43 @@ +/* ftruncate emulations for native Windows. + This file is in the public domain. */ + +#include <config.h> + +/* Specification. */ +#include <unistd.h> + +#if HAVE_CHSIZE + +# include <errno.h> +# include <io.h> + +# if HAVE_MSVC_INVALID_PARAMETER_HANDLER +# include "msvc-inval.h" +static inline int +chsize_nothrow (int fd, long length) +{ + int result; + + TRY_MSVC_INVAL + { + result = chsize (fd, length); + } + CATCH_MSVC_INVAL + { + result = -1; + errno = EBADF; + } + DONE_MSVC_INVAL; + + return result; +} +# define chsize chsize_nothrow +# endif + +int +ftruncate (int fd, off_t length) +{ + return chsize (fd, length); +} + +#endif diff --git a/gl/tests/glthread/lock.c b/gl/tests/glthread/lock.c new file mode 100644 index 0000000000..64dbd3f518 --- /dev/null +++ b/gl/tests/glthread/lock.c @@ -0,0 +1,1058 @@ +/* Locking in multithreaded situations. + Copyright (C) 2005-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, 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, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Bruno Haible <bruno@clisp.org>, 2005. + Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-solaris.h, + gthr-win32.h. */ + +#include <config.h> + +#include "glthread/lock.h" + +/* ========================================================================= */ + +#if USE_POSIX_THREADS + +/* -------------------------- gl_lock_t datatype -------------------------- */ + +/* ------------------------- gl_rwlock_t datatype ------------------------- */ + +# if HAVE_PTHREAD_RWLOCK + +# if !defined PTHREAD_RWLOCK_INITIALIZER + +int +glthread_rwlock_init_multithreaded (gl_rwlock_t *lock) +{ + int err; + + err = pthread_rwlock_init (&lock->rwlock, NULL); + if (err != 0) + return err; + lock->initialized = 1; + return 0; +} + +int +glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock) +{ + if (!lock->initialized) + { + int err; + + err = pthread_mutex_lock (&lock->guard); + if (err != 0) + return err; + if (!lock->initialized) + { + err = glthread_rwlock_init_multithreaded (lock); + if (err != 0) + { + pthread_mutex_unlock (&lock->guard); + return err; + } + } + err = pthread_mutex_unlock (&lock->guard); + if (err != 0) + return err; + } + return pthread_rwlock_rdlock (&lock->rwlock); +} + +int +glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock) +{ + if (!lock->initialized) + { + int err; + + err = pthread_mutex_lock (&lock->guard); + if (err != 0) + return err; + if (!lock->initialized) + { + err = glthread_rwlock_init_multithreaded (lock); + if (err != 0) + { + pthread_mutex_unlock (&lock->guard); + return err; + } + } + err = pthread_mutex_unlock (&lock->guard); + if (err != 0) + return err; + } + return pthread_rwlock_wrlock (&lock->rwlock); +} + +int +glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock) +{ + if (!lock->initialized) + return EINVAL; + return pthread_rwlock_unlock (&lock->rwlock); +} + +int +glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock) +{ + int err; + + if (!lock->initialized) + return EINVAL; + err = pthread_rwlock_destroy (&lock->rwlock); + if (err != 0) + return err; + lock->initialized = 0; + return 0; +} + +# endif + +# else + +int +glthread_rwlock_init_multithreaded (gl_rwlock_t *lock) +{ + int err; + + err = pthread_mutex_init (&lock->lock, NULL); + if (err != 0) + return err; + err = pthread_cond_init (&lock->waiting_readers, NULL); + if (err != 0) + return err; + err = pthread_cond_init (&lock->waiting_writers, NULL); + if (err != 0) + return err; + lock->waiting_writers_count = 0; + lock->runcount = 0; + return 0; +} + +int +glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock) +{ + int err; + + err = pthread_mutex_lock (&lock->lock); + if (err != 0) + return err; + /* Test whether only readers are currently running, and whether the runcount + field will not overflow. */ + /* POSIX says: "It is implementation-defined whether the calling thread + acquires the lock when a writer does not hold the lock and there are + writers blocked on the lock." Let's say, no: give the writers a higher + priority. */ + while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0)) + { + /* This thread has to wait for a while. Enqueue it among the + waiting_readers. */ + err = pthread_cond_wait (&lock->waiting_readers, &lock->lock); + if (err != 0) + { + pthread_mutex_unlock (&lock->lock); + return err; + } + } + lock->runcount++; + return pthread_mutex_unlock (&lock->lock); +} + +int +glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock) +{ + int err; + + err = pthread_mutex_lock (&lock->lock); + if (err != 0) + return err; + /* Test whether no readers or writers are currently running. */ + while (!(lock->runcount == 0)) + { + /* This thread has to wait for a while. Enqueue it among the + waiting_writers. */ + lock->waiting_writers_count++; + err = pthread_cond_wait (&lock->waiting_writers, &lock->lock); + if (err != 0) + { + lock->waiting_writers_count--; + pthread_mutex_unlock (&lock->lock); + return err; + } + lock->waiting_writers_count--; + } + lock->runcount--; /* runcount becomes -1 */ + return pthread_mutex_unlock (&lock->lock); +} + +int +glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock) +{ + int err; + + err = pthread_mutex_lock (&lock->lock); + if (err != 0) + return err; + if (lock->runcount < 0) + { + /* Drop a writer lock. */ + if (!(lock->runcount == -1)) + { + pthread_mutex_unlock (&lock->lock); + return EINVAL; + } + lock->runcount = 0; + } + else + { + /* Drop a reader lock. */ + if (!(lock->runcount > 0)) + { + pthread_mutex_unlock (&lock->lock); + return EINVAL; + } + lock->runcount--; + } + if (lock->runcount == 0) + { + /* POSIX recommends that "write locks shall take precedence over read + locks", to avoid "writer starvation". */ + if (lock->waiting_writers_count > 0) + { + /* Wake up one of the waiting writers. */ + err = pthread_cond_signal (&lock->waiting_writers); + if (err != 0) + { + pthread_mutex_unlock (&lock->lock); + return err; + } + } + else + { + /* Wake up all waiting readers. */ + err = pthread_cond_broadcast (&lock->waiting_readers); + if (err != 0) + { + pthread_mutex_unlock (&lock->lock); + return err; + } + } + } + return pthread_mutex_unlock (&lock->lock); +} + +int +glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock) +{ + int err; + + err = pthread_mutex_destroy (&lock->lock); + if (err != 0) + return err; + err = pthread_cond_destroy (&lock->waiting_readers); + if (err != 0) + return err; + err = pthread_cond_destroy (&lock->waiting_writers); + if (err != 0) + return err; + return 0; +} + +# endif + +/* --------------------- gl_recursive_lock_t datatype --------------------- */ + +# if HAVE_PTHREAD_MUTEX_RECURSIVE + +# if defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER || defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP + +int +glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock) +{ + pthread_mutexattr_t attributes; + int err; + + err = pthread_mutexattr_init (&attributes); + if (err != 0) + return err; + err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE); + if (err != 0) + { + pthread_mutexattr_destroy (&attributes); + return err; + } + err = pthread_mutex_init (lock, &attributes); + if (err != 0) + { + pthread_mutexattr_destroy (&attributes); + return err; + } + err = pthread_mutexattr_destroy (&attributes); + if (err != 0) + return err; + return 0; +} + +# else + +int +glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock) +{ + pthread_mutexattr_t attributes; + int err; + + err = pthread_mutexattr_init (&attributes); + if (err != 0) + return err; + err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE); + if (err != 0) + { + pthread_mutexattr_destroy (&attributes); + return err; + } + err = pthread_mutex_init (&lock->recmutex, &attributes); + if (err != 0) + { + pthread_mutexattr_destroy (&attributes); + return err; + } + err = pthread_mutexattr_destroy (&attributes); + if (err != 0) + return err; + lock->initialized = 1; + return 0; +} + +int +glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock) +{ + if (!lock->initialized) + { + int err; + + err = pthread_mutex_lock (&lock->guard); + if (err != 0) + return err; + if (!lock->initialized) + { + err = glthread_recursive_lock_init_multithreaded (lock); + if (err != 0) + { + pthread_mutex_unlock (&lock->guard); + return err; + } + } + err = pthread_mutex_unlock (&lock->guard); + if (err != 0) + return err; + } + return pthread_mutex_lock (&lock->recmutex); +} + +int +glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock) +{ + if (!lock->initialized) + return EINVAL; + return pthread_mutex_unlock (&lock->recmutex); +} + +int +glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock) +{ + int err; + + if (!lock->initialized) + return EINVAL; + err = pthread_mutex_destroy (&lock->recmutex); + if (err != 0) + return err; + lock->initialized = 0; + return 0; +} + +# endif + +# else + +int +glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock) +{ + int err; + + err = pthread_mutex_init (&lock->mutex, NULL); + if (err != 0) + return err; + lock->owner = (pthread_t) 0; + lock->depth = 0; + return 0; +} + +int +glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock) +{ + pthread_t self = pthread_self (); + if (lock->owner != self) + { + int err; + + err = pthread_mutex_lock (&lock->mutex); + if (err != 0) + return err; + lock->owner = self; + } + if (++(lock->depth) == 0) /* wraparound? */ + { + lock->depth--; + return EAGAIN; + } + return 0; +} + +int +glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock) +{ + if (lock->owner != pthread_self ()) + return EPERM; + if (lock->depth == 0) + return EINVAL; + if (--(lock->depth) == 0) + { + lock->owner = (pthread_t) 0; + return pthread_mutex_unlock (&lock->mutex); + } + else + return 0; +} + +int +glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock) +{ + if (lock->owner != (pthread_t) 0) + return EBUSY; + return pthread_mutex_destroy (&lock->mutex); +} + +# endif + +/* -------------------------- gl_once_t datatype -------------------------- */ + +static const pthread_once_t fresh_once = PTHREAD_ONCE_INIT; + +int +glthread_once_singlethreaded (pthread_once_t *once_control) +{ + /* We don't know whether pthread_once_t is an integer type, a floating-point + type, a pointer type, or a structure type. */ + char *firstbyte = (char *)once_control; + if (*firstbyte == *(const char *)&fresh_once) + { + /* First time use of once_control. Invert the first byte. */ + *firstbyte = ~ *(const char *)&fresh_once; + return 1; + } + else + return 0; +} + +#endif + +/* ========================================================================= */ + +#if USE_PTH_THREADS + +/* Use the GNU Pth threads library. */ + +/* -------------------------- gl_lock_t datatype -------------------------- */ + +/* ------------------------- gl_rwlock_t datatype ------------------------- */ + +/* --------------------- gl_recursive_lock_t datatype --------------------- */ + +/* -------------------------- gl_once_t datatype -------------------------- */ + +static void +glthread_once_call (void *arg) +{ + void (**gl_once_temp_addr) (void) = (void (**) (void)) arg; + void (*initfunction) (void) = *gl_once_temp_addr; + initfunction (); +} + +int +glthread_once_multithreaded (pth_once_t *once_control, void (*initfunction) (void)) +{ + void (*temp) (void) = initfunction; + return (!pth_once (once_control, glthread_once_call, &temp) ? errno : 0); +} + +int +glthread_once_singlethreaded (pth_once_t *once_control) +{ + /* We know that pth_once_t is an integer type. */ + if (*once_control == PTH_ONCE_INIT) + { + /* First time use of once_control. Invert the marker. */ + *once_control = ~ PTH_ONCE_INIT; + return 1; + } + else + return 0; +} + +#endif + +/* ========================================================================= */ + +#if USE_SOLARIS_THREADS + +/* Use the old Solaris threads library. */ + +/* -------------------------- gl_lock_t datatype -------------------------- */ + +/* ------------------------- gl_rwlock_t datatype ------------------------- */ + +/* --------------------- gl_recursive_lock_t datatype --------------------- */ + +int +glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock) +{ + int err; + + err = mutex_init (&lock->mutex, USYNC_THREAD, NULL); + if (err != 0) + return err; + lock->owner = (thread_t) 0; + lock->depth = 0; + return 0; +} + +int +glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock) +{ + thread_t self = thr_self (); + if (lock->owner != self) + { + int err; + + err = mutex_lock (&lock->mutex); + if (err != 0) + return err; + lock->owner = self; + } + if (++(lock->depth) == 0) /* wraparound? */ + { + lock->depth--; + return EAGAIN; + } + return 0; +} + +int +glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock) +{ + if (lock->owner != thr_self ()) + return EPERM; + if (lock->depth == 0) + return EINVAL; + if (--(lock->depth) == 0) + { + lock->owner = (thread_t) 0; + return mutex_unlock (&lock->mutex); + } + else + return 0; +} + +int +glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock) +{ + if (lock->owner != (thread_t) 0) + return EBUSY; + return mutex_destroy (&lock->mutex); +} + +/* -------------------------- gl_once_t datatype -------------------------- */ + +int +glthread_once_multithreaded (gl_once_t *once_control, void (*initfunction) (void)) +{ + if (!once_control->inited) + { + int err; + + /* Use the mutex to guarantee that if another thread is already calling + the initfunction, this thread waits until it's finished. */ + err = mutex_lock (&once_control->mutex); + if (err != 0) + return err; + if (!once_control->inited) + { + once_control->inited = 1; + initfunction (); + } + return mutex_unlock (&once_control->mutex); + } + else + return 0; +} + +int +glthread_once_singlethreaded (gl_once_t *once_control) +{ + /* We know that gl_once_t contains an integer type. */ + if (!once_control->inited) + { + /* First time use of once_control. Invert the marker. */ + once_control->inited = ~ 0; + return 1; + } + else + return 0; +} + +#endif + +/* ========================================================================= */ + +#if USE_WIN32_THREADS + +/* -------------------------- gl_lock_t datatype -------------------------- */ + +void +glthread_lock_init_func (gl_lock_t *lock) +{ + InitializeCriticalSection (&lock->lock); + lock->guard.done = 1; +} + +int +glthread_lock_lock_func (gl_lock_t *lock) +{ + if (!lock->guard.done) + { + if (InterlockedIncrement (&lock->guard.started) == 0) + /* This thread is the first one to need this lock. Initialize it. */ + glthread_lock_init (lock); + else + /* Yield the CPU while waiting for another thread to finish + initializing this lock. */ + while (!lock->guard.done) + Sleep (0); + } + EnterCriticalSection (&lock->lock); + return 0; +} + +int +glthread_lock_unlock_func (gl_lock_t *lock) +{ + if (!lock->guard.done) + return EINVAL; + LeaveCriticalSection (&lock->lock); + return 0; +} + +int +glthread_lock_destroy_func (gl_lock_t *lock) +{ + if (!lock->guard.done) + return EINVAL; + DeleteCriticalSection (&lock->lock); + lock->guard.done = 0; + return 0; +} + +/* ------------------------- gl_rwlock_t datatype ------------------------- */ + +/* In this file, the waitqueues are implemented as circular arrays. */ +#define gl_waitqueue_t gl_carray_waitqueue_t + +static inline void +gl_waitqueue_init (gl_waitqueue_t *wq) +{ + wq->array = NULL; + wq->count = 0; + wq->alloc = 0; + wq->offset = 0; +} + +/* Enqueues the current thread, represented by an event, in a wait queue. + Returns INVALID_HANDLE_VALUE if an allocation failure occurs. */ +static HANDLE +gl_waitqueue_add (gl_waitqueue_t *wq) +{ + HANDLE event; + unsigned int index; + + if (wq->count == wq->alloc) + { + unsigned int new_alloc = 2 * wq->alloc + 1; + HANDLE *new_array = + (HANDLE *) realloc (wq->array, new_alloc * sizeof (HANDLE)); + if (new_array == NULL) + /* No more memory. */ + return INVALID_HANDLE_VALUE; + /* Now is a good opportunity to rotate the array so that its contents + starts at offset 0. */ + if (wq->offset > 0) + { + unsigned int old_count = wq->count; + unsigned int old_alloc = wq->alloc; + unsigned int old_offset = wq->offset; + unsigned int i; + if (old_offset + old_count > old_alloc) + { + unsigned int limit = old_offset + old_count - old_alloc; + for (i = 0; i < limit; i++) + new_array[old_alloc + i] = new_array[i]; + } + for (i = 0; i < old_count; i++) + new_array[i] = new_array[old_offset + i]; + wq->offset = 0; + } + wq->array = new_array; + wq->alloc = new_alloc; + } + /* Whether the created event is a manual-reset one or an auto-reset one, + does not matter, since we will wait on it only once. */ + event = CreateEvent (NULL, TRUE, FALSE, NULL); + if (event == INVALID_HANDLE_VALUE) + /* No way to allocate an event. */ + return INVALID_HANDLE_VALUE; + index = wq->offset + wq->count; + if (index >= wq->alloc) + index -= wq->alloc; + wq->array[index] = event; + wq->count++; + return event; +} + +/* Notifies the first thread from a wait queue and dequeues it. */ +static inline void +gl_waitqueue_notify_first (gl_waitqueue_t *wq) +{ + SetEvent (wq->array[wq->offset + 0]); + wq->offset++; + wq->count--; + if (wq->count == 0 || wq->offset == wq->alloc) + wq->offset = 0; +} + +/* Notifies all threads from a wait queue and dequeues them all. */ +static inline void +gl_waitqueue_notify_all (gl_waitqueue_t *wq) +{ + unsigned int i; + + for (i = 0; i < wq->count; i++) + { + unsigned int index = wq->offset + i; + if (index >= wq->alloc) + index -= wq->alloc; + SetEvent (wq->array[index]); + } + wq->count = 0; + wq->offset = 0; +} + +void +glthread_rwlock_init_func (gl_rwlock_t *lock) +{ + InitializeCriticalSection (&lock->lock); + gl_waitqueue_init (&lock->waiting_readers); + gl_waitqueue_init (&lock->waiting_writers); + lock->runcount = 0; + lock->guard.done = 1; +} + +int +glthread_rwlock_rdlock_func (gl_rwlock_t *lock) +{ + if (!lock->guard.done) + { + if (InterlockedIncrement (&lock->guard.started) == 0) + /* This thread is the first one to need this lock. Initialize it. */ + glthread_rwlock_init (lock); + else + /* Yield the CPU while waiting for another thread to finish + initializing this lock. */ + while (!lock->guard.done) + Sleep (0); + } + EnterCriticalSection (&lock->lock); + /* Test whether only readers are currently running, and whether the runcount + field will not overflow. */ + if (!(lock->runcount + 1 > 0)) + { + /* This thread has to wait for a while. Enqueue it among the + waiting_readers. */ + HANDLE event = gl_waitqueue_add (&lock->waiting_readers); + if (event != INVALID_HANDLE_VALUE) + { + DWORD result; + LeaveCriticalSection (&lock->lock); + /* Wait until another thread signals this event. */ + result = WaitForSingleObject (event, INFINITE); + if (result == WAIT_FAILED || result == WAIT_TIMEOUT) + abort (); + CloseHandle (event); + /* The thread which signalled the event already did the bookkeeping: + removed us from the waiting_readers, incremented lock->runcount. */ + if (!(lock->runcount > 0)) + abort (); + return 0; + } + else + { + /* Allocation failure. Weird. */ + do + { + LeaveCriticalSection (&lock->lock); + Sleep (1); + EnterCriticalSection (&lock->lock); + } + while (!(lock->runcount + 1 > 0)); + } + } + lock->runcount++; + LeaveCriticalSection (&lock->lock); + return 0; +} + +int +glthread_rwlock_wrlock_func (gl_rwlock_t *lock) +{ + if (!lock->guard.done) + { + if (InterlockedIncrement (&lock->guard.started) == 0) + /* This thread is the first one to need this lock. Initialize it. */ + glthread_rwlock_init (lock); + else + /* Yield the CPU while waiting for another thread to finish + initializing this lock. */ + while (!lock->guard.done) + Sleep (0); + } + EnterCriticalSection (&lock->lock); + /* Test whether no readers or writers are currently running. */ + if (!(lock->runcount == 0)) + { + /* This thread has to wait for a while. Enqueue it among the + waiting_writers. */ + HANDLE event = gl_waitqueue_add (&lock->waiting_writers); + if (event != INVALID_HANDLE_VALUE) + { + DWORD result; + LeaveCriticalSection (&lock->lock); + /* Wait until another thread signals this event. */ + result = WaitForSingleObject (event, INFINITE); + if (result == WAIT_FAILED || result == WAIT_TIMEOUT) + abort (); + CloseHandle (event); + /* The thread which signalled the event already did the bookkeeping: + removed us from the waiting_writers, set lock->runcount = -1. */ + if (!(lock->runcount == -1)) + abort (); + return 0; + } + else + { + /* Allocation failure. Weird. */ + do + { + LeaveCriticalSection (&lock->lock); + Sleep (1); + EnterCriticalSection (&lock->lock); + } + while (!(lock->runcount == 0)); + } + } + lock->runcount--; /* runcount becomes -1 */ + LeaveCriticalSection (&lock->lock); + return 0; +} + +int +glthread_rwlock_unlock_func (gl_rwlock_t *lock) +{ + if (!lock->guard.done) + return EINVAL; + EnterCriticalSection (&lock->lock); + if (lock->runcount < 0) + { + /* Drop a writer lock. */ + if (!(lock->runcount == -1)) + abort (); + lock->runcount = 0; + } + else + { + /* Drop a reader lock. */ + if (!(lock->runcount > 0)) + { + LeaveCriticalSection (&lock->lock); + return EPERM; + } + lock->runcount--; + } + if (lock->runcount == 0) + { + /* POSIX recommends that "write locks shall take precedence over read + locks", to avoid "writer starvation". */ + if (lock->waiting_writers.count > 0) + { + /* Wake up one of the waiting writers. */ + lock->runcount--; + gl_waitqueue_notify_first (&lock->waiting_writers); + } + else + { + /* Wake up all waiting readers. */ + lock->runcount += lock->waiting_readers.count; + gl_waitqueue_notify_all (&lock->waiting_readers); + } + } + LeaveCriticalSection (&lock->lock); + return 0; +} + +int +glthread_rwlock_destroy_func (gl_rwlock_t *lock) +{ + if (!lock->guard.done) + return EINVAL; + if (lock->runcount != 0) + return EBUSY; + DeleteCriticalSection (&lock->lock); + if (lock->waiting_readers.array != NULL) + free (lock->waiting_readers.array); + if (lock->waiting_writers.array != NULL) + free (lock->waiting_writers.array); + lock->guard.done = 0; + return 0; +} + +/* --------------------- gl_recursive_lock_t datatype --------------------- */ + +void +glthread_recursive_lock_init_func (gl_recursive_lock_t *lock) +{ + lock->owner = 0; + lock->depth = 0; + InitializeCriticalSection (&lock->lock); + lock->guard.done = 1; +} + +int +glthread_recursive_lock_lock_func (gl_recursive_lock_t *lock) +{ + if (!lock->guard.done) + { + if (InterlockedIncrement (&lock->guard.started) == 0) + /* This thread is the first one to need this lock. Initialize it. */ + glthread_recursive_lock_init (lock); + else + /* Yield the CPU while waiting for another thread to finish + initializing this lock. */ + while (!lock->guard.done) + Sleep (0); + } + { + DWORD self = GetCurrentThreadId (); + if (lock->owner != self) + { + EnterCriticalSection (&lock->lock); + lock->owner = self; + } + if (++(lock->depth) == 0) /* wraparound? */ + { + lock->depth--; + return EAGAIN; + } + } + return 0; +} + +int +glthread_recursive_lock_unlock_func (gl_recursive_lock_t *lock) +{ + if (lock->owner != GetCurrentThreadId ()) + return EPERM; + if (lock->depth == 0) + return EINVAL; + if (--(lock->depth) == 0) + { + lock->owner = 0; + LeaveCriticalSection (&lock->lock); + } + return 0; +} + +int +glthread_recursive_lock_destroy_func (gl_recursive_lock_t *lock) +{ + if (lock->owner != 0) + return EBUSY; + DeleteCriticalSection (&lock->lock); + lock->guard.done = 0; + return 0; +} + +/* -------------------------- gl_once_t datatype -------------------------- */ + +void +glthread_once_func (gl_once_t *once_control, void (*initfunction) (void)) +{ + if (once_control->inited <= 0) + { + if (InterlockedIncrement (&once_control->started) == 0) + { + /* This thread is the first one to come to this once_control. */ + InitializeCriticalSection (&once_control->lock); + EnterCriticalSection (&once_control->lock); + once_control->inited = 0; + initfunction (); + once_control->inited = 1; + LeaveCriticalSection (&once_control->lock); + } + else + { + /* Undo last operation. */ + InterlockedDecrement (&once_control->started); + /* Some other thread has already started the initialization. + Yield the CPU while waiting for the other thread to finish + initializing and taking the lock. */ + while (once_control->inited < 0) + Sleep (0); + if (once_control->inited <= 0) + { + /* Take the lock. This blocks until the other thread has + finished calling the initfunction. */ + EnterCriticalSection (&once_control->lock); + LeaveCriticalSection (&once_control->lock); + if (!(once_control->inited > 0)) + abort (); + } + } + } +} + +#endif + +/* ========================================================================= */ diff --git a/gl/tests/glthread/lock.h b/gl/tests/glthread/lock.h new file mode 100644 index 0000000000..028d881d4c --- /dev/null +++ b/gl/tests/glthread/lock.h @@ -0,0 +1,928 @@ +/* Locking in multithreaded situations. + Copyright (C) 2005-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, 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, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Bruno Haible <bruno@clisp.org>, 2005. + Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-solaris.h, + gthr-win32.h. */ + +/* This file contains locking primitives for use with a given thread library. + It does not contain primitives for creating threads or for other + synchronization primitives. + + Normal (non-recursive) locks: + Type: gl_lock_t + Declaration: gl_lock_define(extern, name) + Initializer: gl_lock_define_initialized(, name) + Initialization: gl_lock_init (name); + Taking the lock: gl_lock_lock (name); + Releasing the lock: gl_lock_unlock (name); + De-initialization: gl_lock_destroy (name); + Equivalent functions with control of error handling: + Initialization: err = glthread_lock_init (&name); + Taking the lock: err = glthread_lock_lock (&name); + Releasing the lock: err = glthread_lock_unlock (&name); + De-initialization: err = glthread_lock_destroy (&name); + + Read-Write (non-recursive) locks: + Type: gl_rwlock_t + Declaration: gl_rwlock_define(extern, name) + Initializer: gl_rwlock_define_initialized(, name) + Initialization: gl_rwlock_init (name); + Taking the lock: gl_rwlock_rdlock (name); + gl_rwlock_wrlock (name); + Releasing the lock: gl_rwlock_unlock (name); + De-initialization: gl_rwlock_destroy (name); + Equivalent functions with control of error handling: + Initialization: err = glthread_rwlock_init (&name); + Taking the lock: err = glthread_rwlock_rdlock (&name); + err = glthread_rwlock_wrlock (&name); + Releasing the lock: err = glthread_rwlock_unlock (&name); + De-initialization: err = glthread_rwlock_destroy (&name); + + Recursive locks: + Type: gl_recursive_lock_t + Declaration: gl_recursive_lock_define(extern, name) + Initializer: gl_recursive_lock_define_initialized(, name) + Initialization: gl_recursive_lock_init (name); + Taking the lock: gl_recursive_lock_lock (name); + Releasing the lock: gl_recursive_lock_unlock (name); + De-initialization: gl_recursive_lock_destroy (name); + Equivalent functions with control of error handling: + Initialization: err = glthread_recursive_lock_init (&name); + Taking the lock: err = glthread_recursive_lock_lock (&name); + Releasing the lock: err = glthread_recursive_lock_unlock (&name); + De-initialization: err = glthread_recursive_lock_destroy (&name); + + Once-only execution: + Type: gl_once_t + Initializer: gl_once_define(extern, name) + Execution: gl_once (name, initfunction); + Equivalent functions with control of error handling: + Execution: err = glthread_once (&name, initfunction); +*/ + + +#ifndef _LOCK_H +#define _LOCK_H + +#include <errno.h> +#include <stdlib.h> + +/* ========================================================================= */ + +#if USE_POSIX_THREADS + +/* Use the POSIX threads library. */ + +# include <pthread.h> + +# ifdef __cplusplus +extern "C" { +# endif + +# if PTHREAD_IN_USE_DETECTION_HARD + +/* The pthread_in_use() detection needs to be done at runtime. */ +# define pthread_in_use() \ + glthread_in_use () +extern int glthread_in_use (void); + +# endif + +# if USE_POSIX_THREADS_WEAK + +/* Use weak references to the POSIX threads library. */ + +/* Weak references avoid dragging in external libraries if the other parts + of the program don't use them. Here we use them, because we don't want + every program that uses libintl to depend on libpthread. This assumes + that libpthread would not be loaded after libintl; i.e. if libintl is + loaded first, by an executable that does not depend on libpthread, and + then a module is dynamically loaded that depends on libpthread, libintl + will not be multithread-safe. */ + +/* The way to test at runtime whether libpthread is present is to test + whether a function pointer's value, such as &pthread_mutex_init, is + non-NULL. However, some versions of GCC have a bug through which, in + PIC mode, &foo != NULL always evaluates to true if there is a direct + call to foo(...) in the same function. To avoid this, we test the + address of a function in libpthread that we don't use. */ + +# pragma weak pthread_mutex_init +# pragma weak pthread_mutex_lock +# pragma weak pthread_mutex_unlock +# pragma weak pthread_mutex_destroy +# pragma weak pthread_rwlock_init +# pragma weak pthread_rwlock_rdlock +# pragma weak pthread_rwlock_wrlock +# pragma weak pthread_rwlock_unlock +# pragma weak pthread_rwlock_destroy +# pragma weak pthread_once +# pragma weak pthread_cond_init +# pragma weak pthread_cond_wait +# pragma weak pthread_cond_signal +# pragma weak pthread_cond_broadcast +# pragma weak pthread_cond_destroy +# pragma weak pthread_mutexattr_init +# pragma weak pthread_mutexattr_settype +# pragma weak pthread_mutexattr_destroy +# ifndef pthread_self +# pragma weak pthread_self +# endif + +# if !PTHREAD_IN_USE_DETECTION_HARD +# pragma weak pthread_cancel +# define pthread_in_use() (pthread_cancel != NULL) +# endif + +# else + +# if !PTHREAD_IN_USE_DETECTION_HARD +# define pthread_in_use() 1 +# endif + +# endif + +/* -------------------------- gl_lock_t datatype -------------------------- */ + +typedef pthread_mutex_t gl_lock_t; +# define gl_lock_define(STORAGECLASS, NAME) \ + STORAGECLASS pthread_mutex_t NAME; +# define gl_lock_define_initialized(STORAGECLASS, NAME) \ + STORAGECLASS pthread_mutex_t NAME = gl_lock_initializer; +# define gl_lock_initializer \ + PTHREAD_MUTEX_INITIALIZER +# define glthread_lock_init(LOCK) \ + (pthread_in_use () ? pthread_mutex_init (LOCK, NULL) : 0) +# define glthread_lock_lock(LOCK) \ + (pthread_in_use () ? pthread_mutex_lock (LOCK) : 0) +# define glthread_lock_unlock(LOCK) \ + (pthread_in_use () ? pthread_mutex_unlock (LOCK) : 0) +# define glthread_lock_destroy(LOCK) \ + (pthread_in_use () ? pthread_mutex_destroy (LOCK) : 0) + +/* ------------------------- gl_rwlock_t datatype ------------------------- */ + +# if HAVE_PTHREAD_RWLOCK + +# ifdef PTHREAD_RWLOCK_INITIALIZER + +typedef pthread_rwlock_t gl_rwlock_t; +# define gl_rwlock_define(STORAGECLASS, NAME) \ + STORAGECLASS pthread_rwlock_t NAME; +# define gl_rwlock_define_initialized(STORAGECLASS, NAME) \ + STORAGECLASS pthread_rwlock_t NAME = gl_rwlock_initializer; +# define gl_rwlock_initializer \ + PTHREAD_RWLOCK_INITIALIZER +# define glthread_rwlock_init(LOCK) \ + (pthread_in_use () ? pthread_rwlock_init (LOCK, NULL) : 0) +# define glthread_rwlock_rdlock(LOCK) \ + (pthread_in_use () ? pthread_rwlock_rdlock (LOCK) : 0) +# define glthread_rwlock_wrlock(LOCK) \ + (pthread_in_use () ? pthread_rwlock_wrlock (LOCK) : 0) +# define glthread_rwlock_unlock(LOCK) \ + (pthread_in_use () ? pthread_rwlock_unlock (LOCK) : 0) +# define glthread_rwlock_destroy(LOCK) \ + (pthread_in_use () ? pthread_rwlock_destroy (LOCK) : 0) + +# else + +typedef struct + { + int initialized; + pthread_mutex_t guard; /* protects the initialization */ + pthread_rwlock_t rwlock; /* read-write lock */ + } + gl_rwlock_t; +# define gl_rwlock_define(STORAGECLASS, NAME) \ + STORAGECLASS gl_rwlock_t NAME; +# define gl_rwlock_define_initialized(STORAGECLASS, NAME) \ + STORAGECLASS gl_rwlock_t NAME = gl_rwlock_initializer; +# define gl_rwlock_initializer \ + { 0, PTHREAD_MUTEX_INITIALIZER } +# define glthread_rwlock_init(LOCK) \ + (pthread_in_use () ? glthread_rwlock_init_multithreaded (LOCK) : 0) +# define glthread_rwlock_rdlock(LOCK) \ + (pthread_in_use () ? glthread_rwlock_rdlock_multithreaded (LOCK) : 0) +# define glthread_rwlock_wrlock(LOCK) \ + (pthread_in_use () ? glthread_rwlock_wrlock_multithreaded (LOCK) : 0) +# define glthread_rwlock_unlock(LOCK) \ + (pthread_in_use () ? glthread_rwlock_unlock_multithreaded (LOCK) : 0) +# define glthread_rwlock_destroy(LOCK) \ + (pthread_in_use () ? glthread_rwlock_destroy_multithreaded (LOCK) : 0) +extern int glthread_rwlock_init_multithreaded (gl_rwlock_t *lock); +extern int glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock); +extern int glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock); +extern int glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock); +extern int glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock); + +# endif + +# else + +typedef struct + { + pthread_mutex_t lock; /* protects the remaining fields */ + pthread_cond_t waiting_readers; /* waiting readers */ + pthread_cond_t waiting_writers; /* waiting writers */ + unsigned int waiting_writers_count; /* number of waiting writers */ + int runcount; /* number of readers running, or -1 when a writer runs */ + } + gl_rwlock_t; +# define gl_rwlock_define(STORAGECLASS, NAME) \ + STORAGECLASS gl_rwlock_t NAME; +# define gl_rwlock_define_initialized(STORAGECLASS, NAME) \ + STORAGECLASS gl_rwlock_t NAME = gl_rwlock_initializer; +# define gl_rwlock_initializer \ + { PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, PTHREAD_COND_INITIALIZER, 0, 0 } +# define glthread_rwlock_init(LOCK) \ + (pthread_in_use () ? glthread_rwlock_init_multithreaded (LOCK) : 0) +# define glthread_rwlock_rdlock(LOCK) \ + (pthread_in_use () ? glthread_rwlock_rdlock_multithreaded (LOCK) : 0) +# define glthread_rwlock_wrlock(LOCK) \ + (pthread_in_use () ? glthread_rwlock_wrlock_multithreaded (LOCK) : 0) +# define glthread_rwlock_unlock(LOCK) \ + (pthread_in_use () ? glthread_rwlock_unlock_multithreaded (LOCK) : 0) +# define glthread_rwlock_destroy(LOCK) \ + (pthread_in_use () ? glthread_rwlock_destroy_multithreaded (LOCK) : 0) +extern int glthread_rwlock_init_multithreaded (gl_rwlock_t *lock); +extern int glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock); +extern int glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock); +extern int glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock); +extern int glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock); + +# endif + +/* --------------------- gl_recursive_lock_t datatype --------------------- */ + +# if HAVE_PTHREAD_MUTEX_RECURSIVE + +# if defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER || defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP + +typedef pthread_mutex_t gl_recursive_lock_t; +# define gl_recursive_lock_define(STORAGECLASS, NAME) \ + STORAGECLASS pthread_mutex_t NAME; +# define gl_recursive_lock_define_initialized(STORAGECLASS, NAME) \ + STORAGECLASS pthread_mutex_t NAME = gl_recursive_lock_initializer; +# ifdef PTHREAD_RECURSIVE_MUTEX_INITIALIZER +# define gl_recursive_lock_initializer \ + PTHREAD_RECURSIVE_MUTEX_INITIALIZER +# else +# define gl_recursive_lock_initializer \ + PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP +# endif +# define glthread_recursive_lock_init(LOCK) \ + (pthread_in_use () ? glthread_recursive_lock_init_multithreaded (LOCK) : 0) +# define glthread_recursive_lock_lock(LOCK) \ + (pthread_in_use () ? pthread_mutex_lock (LOCK) : 0) +# define glthread_recursive_lock_unlock(LOCK) \ + (pthread_in_use () ? pthread_mutex_unlock (LOCK) : 0) +# define glthread_recursive_lock_destroy(LOCK) \ + (pthread_in_use () ? pthread_mutex_destroy (LOCK) : 0) +extern int glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock); + +# else + +typedef struct + { + pthread_mutex_t recmutex; /* recursive mutex */ + pthread_mutex_t guard; /* protects the initialization */ + int initialized; + } + gl_recursive_lock_t; +# define gl_recursive_lock_define(STORAGECLASS, NAME) \ + STORAGECLASS gl_recursive_lock_t NAME; +# define gl_recursive_lock_define_initialized(STORAGECLASS, NAME) \ + STORAGECLASS gl_recursive_lock_t NAME = gl_recursive_lock_initializer; +# define gl_recursive_lock_initializer \ + { PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, 0 } +# define glthread_recursive_lock_init(LOCK) \ + (pthread_in_use () ? glthread_recursive_lock_init_multithreaded (LOCK) : 0) +# define glthread_recursive_lock_lock(LOCK) \ + (pthread_in_use () ? glthread_recursive_lock_lock_multithreaded (LOCK) : 0) +# define glthread_recursive_lock_unlock(LOCK) \ + (pthread_in_use () ? glthread_recursive_lock_unlock_multithreaded (LOCK) : 0) +# define glthread_recursive_lock_destroy(LOCK) \ + (pthread_in_use () ? glthread_recursive_lock_destroy_multithreaded (LOCK) : 0) +extern int glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock); +extern int glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock); +extern int glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock); +extern int glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock); + +# endif + +# else + +/* Old versions of POSIX threads on Solaris did not have recursive locks. + We have to implement them ourselves. */ + +typedef struct + { + pthread_mutex_t mutex; + pthread_t owner; + unsigned long depth; + } + gl_recursive_lock_t; +# define gl_recursive_lock_define(STORAGECLASS, NAME) \ + STORAGECLASS gl_recursive_lock_t NAME; +# define gl_recursive_lock_define_initialized(STORAGECLASS, NAME) \ + STORAGECLASS gl_recursive_lock_t NAME = gl_recursive_lock_initializer; +# define gl_recursive_lock_initializer \ + { PTHREAD_MUTEX_INITIALIZER, (pthread_t) 0, 0 } +# define glthread_recursive_lock_init(LOCK) \ + (pthread_in_use () ? glthread_recursive_lock_init_multithreaded (LOCK) : 0) +# define glthread_recursive_lock_lock(LOCK) \ + (pthread_in_use () ? glthread_recursive_lock_lock_multithreaded (LOCK) : 0) +# define glthread_recursive_lock_unlock(LOCK) \ + (pthread_in_use () ? glthread_recursive_lock_unlock_multithreaded (LOCK) : 0) +# define glthread_recursive_lock_destroy(LOCK) \ + (pthread_in_use () ? glthread_recursive_lock_destroy_multithreaded (LOCK) : 0) +extern int glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock); +extern int glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock); +extern int glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock); +extern int glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock); + +# endif + +/* -------------------------- gl_once_t datatype -------------------------- */ + +typedef pthread_once_t gl_once_t; +# define gl_once_define(STORAGECLASS, NAME) \ + STORAGECLASS pthread_once_t NAME = PTHREAD_ONCE_INIT; +# define glthread_once(ONCE_CONTROL, INITFUNCTION) \ + (pthread_in_use () \ + ? pthread_once (ONCE_CONTROL, INITFUNCTION) \ + : (glthread_once_singlethreaded (ONCE_CONTROL) ? (INITFUNCTION (), 0) : 0)) +extern int glthread_once_singlethreaded (pthread_once_t *once_control); + +# ifdef __cplusplus +} +# endif + +#endif + +/* ========================================================================= */ + +#if USE_PTH_THREADS + +/* Use the GNU Pth threads library. */ + +# include <pth.h> + +# ifdef __cplusplus +extern "C" { +# endif + +# if USE_PTH_THREADS_WEAK + +/* Use weak references to the GNU Pth threads library. */ + +# pragma weak pth_mutex_init +# pragma weak pth_mutex_acquire +# pragma weak pth_mutex_release +# pragma weak pth_rwlock_init +# pragma weak pth_rwlock_acquire +# pragma weak pth_rwlock_release +# pragma weak pth_once + +# pragma weak pth_cancel +# define pth_in_use() (pth_cancel != NULL) + +# else + +# define pth_in_use() 1 + +# endif + +/* -------------------------- gl_lock_t datatype -------------------------- */ + +typedef pth_mutex_t gl_lock_t; +# define gl_lock_define(STORAGECLASS, NAME) \ + STORAGECLASS pth_mutex_t NAME; +# define gl_lock_define_initialized(STORAGECLASS, NAME) \ + STORAGECLASS pth_mutex_t NAME = gl_lock_initializer; +# define gl_lock_initializer \ + PTH_MUTEX_INIT +# define glthread_lock_init(LOCK) \ + (pth_in_use () && !pth_mutex_init (LOCK) ? errno : 0) +# define glthread_lock_lock(LOCK) \ + (pth_in_use () && !pth_mutex_acquire (LOCK, 0, NULL) ? errno : 0) +# define glthread_lock_unlock(LOCK) \ + (pth_in_use () && !pth_mutex_release (LOCK) ? errno : 0) +# define glthread_lock_destroy(LOCK) \ + ((void)(LOCK), 0) + +/* ------------------------- gl_rwlock_t datatype ------------------------- */ + +typedef pth_rwlock_t gl_rwlock_t; +# define gl_rwlock_define(STORAGECLASS, NAME) \ + STORAGECLASS pth_rwlock_t NAME; +# define gl_rwlock_define_initialized(STORAGECLASS, NAME) \ + STORAGECLASS pth_rwlock_t NAME = gl_rwlock_initializer; +# define gl_rwlock_initializer \ + PTH_RWLOCK_INIT +# define glthread_rwlock_init(LOCK) \ + (pth_in_use () && !pth_rwlock_init (LOCK) ? errno : 0) +# define glthread_rwlock_rdlock(LOCK) \ + (pth_in_use () && !pth_rwlock_acquire (LOCK, PTH_RWLOCK_RD, 0, NULL) ? errno : 0) +# define glthread_rwlock_wrlock(LOCK) \ + (pth_in_use () && !pth_rwlock_acquire (LOCK, PTH_RWLOCK_RW, 0, NULL) ? errno : 0) +# define glthread_rwlock_unlock(LOCK) \ + (pth_in_use () && !pth_rwlock_release (LOCK) ? errno : 0) +# define glthread_rwlock_destroy(LOCK) \ + ((void)(LOCK), 0) + +/* --------------------- gl_recursive_lock_t datatype --------------------- */ + +/* In Pth, mutexes are recursive by default. */ +typedef pth_mutex_t gl_recursive_lock_t; +# define gl_recursive_lock_define(STORAGECLASS, NAME) \ + STORAGECLASS pth_mutex_t NAME; +# define gl_recursive_lock_define_initialized(STORAGECLASS, NAME) \ + STORAGECLASS pth_mutex_t NAME = gl_recursive_lock_initializer; +# define gl_recursive_lock_initializer \ + PTH_MUTEX_INIT +# define glthread_recursive_lock_init(LOCK) \ + (pth_in_use () && !pth_mutex_init (LOCK) ? errno : 0) +# define glthread_recursive_lock_lock(LOCK) \ + (pth_in_use () && !pth_mutex_acquire (LOCK, 0, NULL) ? errno : 0) +# define glthread_recursive_lock_unlock(LOCK) \ + (pth_in_use () && !pth_mutex_release (LOCK) ? errno : 0) +# define glthread_recursive_lock_destroy(LOCK) \ + ((void)(LOCK), 0) + +/* -------------------------- gl_once_t datatype -------------------------- */ + +typedef pth_once_t gl_once_t; +# define gl_once_define(STORAGECLASS, NAME) \ + STORAGECLASS pth_once_t NAME = PTH_ONCE_INIT; +# define glthread_once(ONCE_CONTROL, INITFUNCTION) \ + (pth_in_use () \ + ? glthread_once_multithreaded (ONCE_CONTROL, INITFUNCTION) \ + : (glthread_once_singlethreaded (ONCE_CONTROL) ? (INITFUNCTION (), 0) : 0)) +extern int glthread_once_multithreaded (pth_once_t *once_control, void (*initfunction) (void)); +extern int glthread_once_singlethreaded (pth_once_t *once_control); + +# ifdef __cplusplus +} +# endif + +#endif + +/* ========================================================================= */ + +#if USE_SOLARIS_THREADS + +/* Use the old Solaris threads library. */ + +# include <thread.h> +# include <synch.h> + +# ifdef __cplusplus +extern "C" { +# endif + +# if USE_SOLARIS_THREADS_WEAK + +/* Use weak references to the old Solaris threads library. */ + +# pragma weak mutex_init +# pragma weak mutex_lock +# pragma weak mutex_unlock +# pragma weak mutex_destroy +# pragma weak rwlock_init +# pragma weak rw_rdlock +# pragma weak rw_wrlock +# pragma weak rw_unlock +# pragma weak rwlock_destroy +# pragma weak thr_self + +# pragma weak thr_suspend +# define thread_in_use() (thr_suspend != NULL) + +# else + +# define thread_in_use() 1 + +# endif + +/* -------------------------- gl_lock_t datatype -------------------------- */ + +typedef mutex_t gl_lock_t; +# define gl_lock_define(STORAGECLASS, NAME) \ + STORAGECLASS mutex_t NAME; +# define gl_lock_define_initialized(STORAGECLASS, NAME) \ + STORAGECLASS mutex_t NAME = gl_lock_initializer; +# define gl_lock_initializer \ + DEFAULTMUTEX +# define glthread_lock_init(LOCK) \ + (thread_in_use () ? mutex_init (LOCK, USYNC_THREAD, NULL) : 0) +# define glthread_lock_lock(LOCK) \ + (thread_in_use () ? mutex_lock (LOCK) : 0) +# define glthread_lock_unlock(LOCK) \ + (thread_in_use () ? mutex_unlock (LOCK) : 0) +# define glthread_lock_destroy(LOCK) \ + (thread_in_use () ? mutex_destroy (LOCK) : 0) + +/* ------------------------- gl_rwlock_t datatype ------------------------- */ + +typedef rwlock_t gl_rwlock_t; +# define gl_rwlock_define(STORAGECLASS, NAME) \ + STORAGECLASS rwlock_t NAME; +# define gl_rwlock_define_initialized(STORAGECLASS, NAME) \ + STORAGECLASS rwlock_t NAME = gl_rwlock_initializer; +# define gl_rwlock_initializer \ + DEFAULTRWLOCK +# define glthread_rwlock_init(LOCK) \ + (thread_in_use () ? rwlock_init (LOCK, USYNC_THREAD, NULL) : 0) +# define glthread_rwlock_rdlock(LOCK) \ + (thread_in_use () ? rw_rdlock (LOCK) : 0) +# define glthread_rwlock_wrlock(LOCK) \ + (thread_in_use () ? rw_wrlock (LOCK) : 0) +# define glthread_rwlock_unlock(LOCK) \ + (thread_in_use () ? rw_unlock (LOCK) : 0) +# define glthread_rwlock_destroy(LOCK) \ + (thread_in_use () ? rwlock_destroy (LOCK) : 0) + +/* --------------------- gl_recursive_lock_t datatype --------------------- */ + +/* Old Solaris threads did not have recursive locks. + We have to implement them ourselves. */ + +typedef struct + { + mutex_t mutex; + thread_t owner; + unsigned long depth; + } + gl_recursive_lock_t; +# define gl_recursive_lock_define(STORAGECLASS, NAME) \ + STORAGECLASS gl_recursive_lock_t NAME; +# define gl_recursive_lock_define_initialized(STORAGECLASS, NAME) \ + STORAGECLASS gl_recursive_lock_t NAME = gl_recursive_lock_initializer; +# define gl_recursive_lock_initializer \ + { DEFAULTMUTEX, (thread_t) 0, 0 } +# define glthread_recursive_lock_init(LOCK) \ + (thread_in_use () ? glthread_recursive_lock_init_multithreaded (LOCK) : 0) +# define glthread_recursive_lock_lock(LOCK) \ + (thread_in_use () ? glthread_recursive_lock_lock_multithreaded (LOCK) : 0) +# define glthread_recursive_lock_unlock(LOCK) \ + (thread_in_use () ? glthread_recursive_lock_unlock_multithreaded (LOCK) : 0) +# define glthread_recursive_lock_destroy(LOCK) \ + (thread_in_use () ? glthread_recursive_lock_destroy_multithreaded (LOCK) : 0) +extern int glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock); +extern int glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock); +extern int glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock); +extern int glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock); + +/* -------------------------- gl_once_t datatype -------------------------- */ + +typedef struct + { + volatile int inited; + mutex_t mutex; + } + gl_once_t; +# define gl_once_define(STORAGECLASS, NAME) \ + STORAGECLASS gl_once_t NAME = { 0, DEFAULTMUTEX }; +# define glthread_once(ONCE_CONTROL, INITFUNCTION) \ + (thread_in_use () \ + ? glthread_once_multithreaded (ONCE_CONTROL, INITFUNCTION) \ + : (glthread_once_singlethreaded (ONCE_CONTROL) ? (INITFUNCTION (), 0) : 0)) +extern int glthread_once_multithreaded (gl_once_t *once_control, void (*initfunction) (void)); +extern int glthread_once_singlethreaded (gl_once_t *once_control); + +# ifdef __cplusplus +} +# endif + +#endif + +/* ========================================================================= */ + +#if USE_WIN32_THREADS + +# define WIN32_LEAN_AND_MEAN /* avoid including junk */ +# include <windows.h> + +# ifdef __cplusplus +extern "C" { +# endif + +/* We can use CRITICAL_SECTION directly, rather than the Win32 Event, Mutex, + Semaphore types, because + - we need only to synchronize inside a single process (address space), + not inter-process locking, + - we don't need to support trylock operations. (TryEnterCriticalSection + does not work on Windows 95/98/ME. Packages that need trylock usually + define their own mutex type.) */ + +/* There is no way to statically initialize a CRITICAL_SECTION. It needs + to be done lazily, once only. For this we need spinlocks. */ + +typedef struct { volatile int done; volatile long started; } gl_spinlock_t; + +/* -------------------------- gl_lock_t datatype -------------------------- */ + +typedef struct + { + gl_spinlock_t guard; /* protects the initialization */ + CRITICAL_SECTION lock; + } + gl_lock_t; +# define gl_lock_define(STORAGECLASS, NAME) \ + STORAGECLASS gl_lock_t NAME; +# define gl_lock_define_initialized(STORAGECLASS, NAME) \ + STORAGECLASS gl_lock_t NAME = gl_lock_initializer; +# define gl_lock_initializer \ + { { 0, -1 } } +# define glthread_lock_init(LOCK) \ + (glthread_lock_init_func (LOCK), 0) +# define glthread_lock_lock(LOCK) \ + glthread_lock_lock_func (LOCK) +# define glthread_lock_unlock(LOCK) \ + glthread_lock_unlock_func (LOCK) +# define glthread_lock_destroy(LOCK) \ + glthread_lock_destroy_func (LOCK) +extern void glthread_lock_init_func (gl_lock_t *lock); +extern int glthread_lock_lock_func (gl_lock_t *lock); +extern int glthread_lock_unlock_func (gl_lock_t *lock); +extern int glthread_lock_destroy_func (gl_lock_t *lock); + +/* ------------------------- gl_rwlock_t datatype ------------------------- */ + +/* It is impossible to implement read-write locks using plain locks, without + introducing an extra thread dedicated to managing read-write locks. + Therefore here we need to use the low-level Event type. */ + +typedef struct + { + HANDLE *array; /* array of waiting threads, each represented by an event */ + unsigned int count; /* number of waiting threads */ + unsigned int alloc; /* length of allocated array */ + unsigned int offset; /* index of first waiting thread in array */ + } + gl_carray_waitqueue_t; +typedef struct + { + gl_spinlock_t guard; /* protects the initialization */ + CRITICAL_SECTION lock; /* protects the remaining fields */ + gl_carray_waitqueue_t waiting_readers; /* waiting readers */ + gl_carray_waitqueue_t waiting_writers; /* waiting writers */ + int runcount; /* number of readers running, or -1 when a writer runs */ + } + gl_rwlock_t; +# define gl_rwlock_define(STORAGECLASS, NAME) \ + STORAGECLASS gl_rwlock_t NAME; +# define gl_rwlock_define_initialized(STORAGECLASS, NAME) \ + STORAGECLASS gl_rwlock_t NAME = gl_rwlock_initializer; +# define gl_rwlock_initializer \ + { { 0, -1 } } +# define glthread_rwlock_init(LOCK) \ + (glthread_rwlock_init_func (LOCK), 0) +# define glthread_rwlock_rdlock(LOCK) \ + glthread_rwlock_rdlock_func (LOCK) +# define glthread_rwlock_wrlock(LOCK) \ + glthread_rwlock_wrlock_func (LOCK) +# define glthread_rwlock_unlock(LOCK) \ + glthread_rwlock_unlock_func (LOCK) +# define glthread_rwlock_destroy(LOCK) \ + glthread_rwlock_destroy_func (LOCK) +extern void glthread_rwlock_init_func (gl_rwlock_t *lock); +extern int glthread_rwlock_rdlock_func (gl_rwlock_t *lock); +extern int glthread_rwlock_wrlock_func (gl_rwlock_t *lock); +extern int glthread_rwlock_unlock_func (gl_rwlock_t *lock); +extern int glthread_rwlock_destroy_func (gl_rwlock_t *lock); + +/* --------------------- gl_recursive_lock_t datatype --------------------- */ + +/* The Win32 documentation says that CRITICAL_SECTION already implements a + recursive lock. But we need not rely on it: It's easy to implement a + recursive lock without this assumption. */ + +typedef struct + { + gl_spinlock_t guard; /* protects the initialization */ + DWORD owner; + unsigned long depth; + CRITICAL_SECTION lock; + } + gl_recursive_lock_t; +# define gl_recursive_lock_define(STORAGECLASS, NAME) \ + STORAGECLASS gl_recursive_lock_t NAME; +# define gl_recursive_lock_define_initialized(STORAGECLASS, NAME) \ + STORAGECLASS gl_recursive_lock_t NAME = gl_recursive_lock_initializer; +# define gl_recursive_lock_initializer \ + { { 0, -1 }, 0, 0 } +# define glthread_recursive_lock_init(LOCK) \ + (glthread_recursive_lock_init_func (LOCK), 0) +# define glthread_recursive_lock_lock(LOCK) \ + glthread_recursive_lock_lock_func (LOCK) +# define glthread_recursive_lock_unlock(LOCK) \ + glthread_recursive_lock_unlock_func (LOCK) +# define glthread_recursive_lock_destroy(LOCK) \ + glthread_recursive_lock_destroy_func (LOCK) +extern void glthread_recursive_lock_init_func (gl_recursive_lock_t *lock); +extern int glthread_recursive_lock_lock_func (gl_recursive_lock_t *lock); +extern int glthread_recursive_lock_unlock_func (gl_recursive_lock_t *lock); +extern int glthread_recursive_lock_destroy_func (gl_recursive_lock_t *lock); + +/* -------------------------- gl_once_t datatype -------------------------- */ + +typedef struct + { + volatile int inited; + volatile long started; + CRITICAL_SECTION lock; + } + gl_once_t; +# define gl_once_define(STORAGECLASS, NAME) \ + STORAGECLASS gl_once_t NAME = { -1, -1 }; +# define glthread_once(ONCE_CONTROL, INITFUNCTION) \ + (glthread_once_func (ONCE_CONTROL, INITFUNCTION), 0) +extern void glthread_once_func (gl_once_t *once_control, void (*initfunction) (void)); + +# ifdef __cplusplus +} +# endif + +#endif + +/* ========================================================================= */ + +#if !(USE_POSIX_THREADS || USE_PTH_THREADS || USE_SOLARIS_THREADS || USE_WIN32_THREADS) + +/* Provide dummy implementation if threads are not supported. */ + +/* -------------------------- gl_lock_t datatype -------------------------- */ + +typedef int gl_lock_t; +# define gl_lock_define(STORAGECLASS, NAME) +# define gl_lock_define_initialized(STORAGECLASS, NAME) +# define glthread_lock_init(NAME) 0 +# define glthread_lock_lock(NAME) 0 +# define glthread_lock_unlock(NAME) 0 +# define glthread_lock_destroy(NAME) 0 + +/* ------------------------- gl_rwlock_t datatype ------------------------- */ + +typedef int gl_rwlock_t; +# define gl_rwlock_define(STORAGECLASS, NAME) +# define gl_rwlock_define_initialized(STORAGECLASS, NAME) +# define glthread_rwlock_init(NAME) 0 +# define glthread_rwlock_rdlock(NAME) 0 +# define glthread_rwlock_wrlock(NAME) 0 +# define glthread_rwlock_unlock(NAME) 0 +# define glthread_rwlock_destroy(NAME) 0 + +/* --------------------- gl_recursive_lock_t datatype --------------------- */ + +typedef int gl_recursive_lock_t; +# define gl_recursive_lock_define(STORAGECLASS, NAME) +# define gl_recursive_lock_define_initialized(STORAGECLASS, NAME) +# define glthread_recursive_lock_init(NAME) 0 +# define glthread_recursive_lock_lock(NAME) 0 +# define glthread_recursive_lock_unlock(NAME) 0 +# define glthread_recursive_lock_destroy(NAME) 0 + +/* -------------------------- gl_once_t datatype -------------------------- */ + +typedef int gl_once_t; +# define gl_once_define(STORAGECLASS, NAME) \ + STORAGECLASS gl_once_t NAME = 0; +# define glthread_once(ONCE_CONTROL, INITFUNCTION) \ + (*(ONCE_CONTROL) == 0 ? (*(ONCE_CONTROL) = ~ 0, INITFUNCTION (), 0) : 0) + +#endif + +/* ========================================================================= */ + +/* Macros with built-in error handling. */ + +/* -------------------------- gl_lock_t datatype -------------------------- */ + +#define gl_lock_init(NAME) \ + do \ + { \ + if (glthread_lock_init (&NAME)) \ + abort (); \ + } \ + while (0) +#define gl_lock_lock(NAME) \ + do \ + { \ + if (glthread_lock_lock (&NAME)) \ + abort (); \ + } \ + while (0) +#define gl_lock_unlock(NAME) \ + do \ + { \ + if (glthread_lock_unlock (&NAME)) \ + abort (); \ + } \ + while (0) +#define gl_lock_destroy(NAME) \ + do \ + { \ + if (glthread_lock_destroy (&NAME)) \ + abort (); \ + } \ + while (0) + +/* ------------------------- gl_rwlock_t datatype ------------------------- */ + +#define gl_rwlock_init(NAME) \ + do \ + { \ + if (glthread_rwlock_init (&NAME)) \ + abort (); \ + } \ + while (0) +#define gl_rwlock_rdlock(NAME) \ + do \ + { \ + if (glthread_rwlock_rdlock (&NAME)) \ + abort (); \ + } \ + while (0) +#define gl_rwlock_wrlock(NAME) \ + do \ + { \ + if (glthread_rwlock_wrlock (&NAME)) \ + abort (); \ + } \ + while (0) +#define gl_rwlock_unlock(NAME) \ + do \ + { \ + if (glthread_rwlock_unlock (&NAME)) \ + abort (); \ + } \ + while (0) +#define gl_rwlock_destroy(NAME) \ + do \ + { \ + if (glthread_rwlock_destroy (&NAME)) \ + abort (); \ + } \ + while (0) + +/* --------------------- gl_recursive_lock_t datatype --------------------- */ + +#define gl_recursive_lock_init(NAME) \ + do \ + { \ + if (glthread_recursive_lock_init (&NAME)) \ + abort (); \ + } \ + while (0) +#define gl_recursive_lock_lock(NAME) \ + do \ + { \ + if (glthread_recursive_lock_lock (&NAME)) \ + abort (); \ + } \ + while (0) +#define gl_recursive_lock_unlock(NAME) \ + do \ + { \ + if (glthread_recursive_lock_unlock (&NAME)) \ + abort (); \ + } \ + while (0) +#define gl_recursive_lock_destroy(NAME) \ + do \ + { \ + if (glthread_recursive_lock_destroy (&NAME)) \ + abort (); \ + } \ + while (0) + +/* -------------------------- gl_once_t datatype -------------------------- */ + +#define gl_once(NAME, INITFUNCTION) \ + do \ + { \ + if (glthread_once (&NAME, INITFUNCTION)) \ + abort (); \ + } \ + while (0) + +/* ========================================================================= */ + +#endif /* _LOCK_H */ diff --git a/gl/tests/glthread/thread.c b/gl/tests/glthread/thread.c new file mode 100644 index 0000000000..83b8c57caf --- /dev/null +++ b/gl/tests/glthread/thread.c @@ -0,0 +1,232 @@ +/* Creating and controlling threads. + Copyright (C) 2005-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, 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, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Bruno Haible <bruno@clisp.org>, 2005. + Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-solaris.h, + gthr-win32.h. */ + +#include <config.h> + +/* Specification. */ +#include "glthread/thread.h" + +#include <stdlib.h> +#include "glthread/lock.h" + +/* ========================================================================= */ + +#if USE_POSIX_THREADS + +#include <pthread.h> + +#ifdef PTW32_VERSION + +const gl_thread_t gl_null_thread /* = { .p = NULL } */; + +#endif + +#endif + +/* ========================================================================= */ + +#if USE_WIN32_THREADS + +#include <process.h> + +/* -------------------------- gl_thread_t datatype -------------------------- */ + +/* The Thread-Local Storage (TLS) key that allows to access each thread's + 'struct gl_thread_struct *' pointer. */ +static DWORD self_key = (DWORD)-1; + +/* Initializes self_key. This function must only be called once. */ +static void +do_init_self_key (void) +{ + self_key = TlsAlloc (); + /* If this fails, we're hosed. */ + if (self_key == (DWORD)-1) + abort (); +} + +/* Initializes self_key. */ +static void +init_self_key (void) +{ + gl_once_define(static, once) + gl_once (once, do_init_self_key); +} + +/* This structure contains information about a thread. + It is stored in TLS under key self_key. */ +struct gl_thread_struct +{ + /* Fields for managing the handle. */ + HANDLE volatile handle; + CRITICAL_SECTION handle_lock; + /* Fields for managing the exit value. */ + void * volatile result; + /* Fields for managing the thread start. */ + void * (*func) (void *); + void *arg; +}; + +/* Return a real HANDLE object for the current thread. */ +static inline HANDLE +get_current_thread_handle (void) +{ + HANDLE this_handle; + + /* GetCurrentThread() returns a pseudo-handle, i.e. only a symbolic + identifier, not a real handle. */ + if (!DuplicateHandle (GetCurrentProcess (), GetCurrentThread (), + GetCurrentProcess (), &this_handle, + 0, FALSE, DUPLICATE_SAME_ACCESS)) + abort (); + return this_handle; +} + +gl_thread_t +gl_thread_self_func (void) +{ + gl_thread_t thread; + + if (self_key == (DWORD)-1) + init_self_key (); + thread = TlsGetValue (self_key); + if (thread == NULL) + { + /* This happens only in threads that have not been created through + glthread_create(), such as the main thread. */ + for (;;) + { + thread = + (struct gl_thread_struct *) + malloc (sizeof (struct gl_thread_struct)); + if (thread != NULL) + break; + /* Memory allocation failed. There is not much we can do. Have to + busy-loop, waiting for the availability of memory. */ + Sleep (1); + } + + thread->handle = get_current_thread_handle (); + InitializeCriticalSection (&thread->handle_lock); + thread->result = NULL; /* just to be deterministic */ + TlsSetValue (self_key, thread); + } + return thread; +} + +/* The main function of a freshly creating thread. It's a wrapper around + the FUNC and ARG arguments passed to glthread_create_func. */ +static unsigned int WINAPI +wrapper_func (void *varg) +{ + struct gl_thread_struct *thread = (struct gl_thread_struct *)varg; + + EnterCriticalSection (&thread->handle_lock); + /* Create a new handle for the thread only if the parent thread did not yet + fill in the handle. */ + if (thread->handle == NULL) + thread->handle = get_current_thread_handle (); + LeaveCriticalSection (&thread->handle_lock); + + if (self_key == (DWORD)-1) + init_self_key (); + TlsSetValue (self_key, thread); + + /* Run the thread. Store the exit value if the thread was not terminated + otherwise. */ + thread->result = thread->func (thread->arg); + return 0; +} + +int +glthread_create_func (gl_thread_t *threadp, void * (*func) (void *), void *arg) +{ + struct gl_thread_struct *thread = + (struct gl_thread_struct *) malloc (sizeof (struct gl_thread_struct)); + if (thread == NULL) + return ENOMEM; + thread->handle = NULL; + InitializeCriticalSection (&thread->handle_lock); + thread->result = NULL; /* just to be deterministic */ + thread->func = func; + thread->arg = arg; + + { + unsigned int thread_id; + HANDLE thread_handle; + + thread_handle = (HANDLE) + _beginthreadex (NULL, 100000, wrapper_func, thread, 0, &thread_id); + /* calls CreateThread with the same arguments */ + if (thread_handle == NULL) + { + DeleteCriticalSection (&thread->handle_lock); + free (thread); + return EAGAIN; + } + + EnterCriticalSection (&thread->handle_lock); + if (thread->handle == NULL) + thread->handle = thread_handle; + else + /* thread->handle was already set by the thread itself. */ + CloseHandle (thread_handle); + LeaveCriticalSection (&thread->handle_lock); + + *threadp = thread; + return 0; + } +} + +int +glthread_join_func (gl_thread_t thread, void **retvalp) +{ + if (thread == NULL) + return EINVAL; + + if (thread == gl_thread_self ()) + return EDEADLK; + + if (WaitForSingleObject (thread->handle, INFINITE) == WAIT_FAILED) + return EINVAL; + + if (retvalp != NULL) + *retvalp = thread->result; + + DeleteCriticalSection (&thread->handle_lock); + CloseHandle (thread->handle); + free (thread); + + return 0; +} + +int +gl_thread_exit_func (void *retval) +{ + gl_thread_t thread = gl_thread_self (); + thread->result = retval; + _endthreadex (0); /* calls ExitThread (0) */ + abort (); +} + +#endif + +/* ========================================================================= */ diff --git a/gl/tests/glthread/thread.h b/gl/tests/glthread/thread.h new file mode 100644 index 0000000000..064d72f415 --- /dev/null +++ b/gl/tests/glthread/thread.h @@ -0,0 +1,401 @@ +/* Creating and controlling threads. + Copyright (C) 2005-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, 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, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Bruno Haible <bruno@clisp.org>, 2005. + Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-solaris.h, + gthr-win32.h. */ + +/* This file contains primitives for creating and controlling threads. + + Thread data type: gl_thread_t. + + Creating a thread: + thread = gl_thread_create (func, arg); + Or with control of error handling: + err = glthread_create (&thread, func, arg); + extern int glthread_create (gl_thread_t *result, + void *(*func) (void *), void *arg); + + Querying and changing the signal mask of a thread (not supported on all + platforms): + gl_thread_sigmask (how, newmask, oldmask); + Or with control of error handling: + err = glthread_sigmask (how, newmask, oldmask); + extern int glthread_sigmask (int how, const sigset_t *newmask, sigset_t *oldmask); + + Waiting for termination of another thread: + gl_thread_join (thread, &return_value); + Or with control of error handling: + err = glthread_join (thread, &return_value); + extern int glthread_join (gl_thread_t thread, void **return_value_ptr); + + Getting a reference to the current thread: + current = gl_thread_self (); + extern gl_thread_t gl_thread_self (void); + + Getting a reference to the current thread as a pointer, for debugging: + ptr = gl_thread_self_pointer (); + extern void * gl_thread_self_pointer (void); + + Terminating the current thread: + gl_thread_exit (return_value); + extern _Noreturn void gl_thread_exit (void *return_value); + + Requesting custom code to be executed at fork() time(not supported on all + platforms): + gl_thread_atfork (prepare_func, parent_func, child_func); + Or with control of error handling: + err = glthread_atfork (prepare_func, parent_func, child_func); + extern int glthread_atfork (void (*prepare_func) (void), + void (*parent_func) (void), + void (*child_func) (void)); + Note that even on platforms where this is supported, use of fork() and + threads together is problematic, see + <http://lists.gnu.org/archive/html/bug-gnulib/2008-08/msg00062.html> + */ + + +#ifndef _GLTHREAD_THREAD_H +#define _GLTHREAD_THREAD_H + +#include <errno.h> +#include <stdlib.h> + +/* ========================================================================= */ + +#if USE_POSIX_THREADS + +/* Use the POSIX threads library. */ + +# include <pthread.h> + +# ifdef __cplusplus +extern "C" { +# endif + +# if PTHREAD_IN_USE_DETECTION_HARD + +/* The pthread_in_use() detection needs to be done at runtime. */ +# define pthread_in_use() \ + glthread_in_use () +extern int glthread_in_use (void); + +# endif + +# if USE_POSIX_THREADS_WEAK + +/* Use weak references to the POSIX threads library. */ + +/* Weak references avoid dragging in external libraries if the other parts + of the program don't use them. Here we use them, because we don't want + every program that uses libintl to depend on libpthread. This assumes + that libpthread would not be loaded after libintl; i.e. if libintl is + loaded first, by an executable that does not depend on libpthread, and + then a module is dynamically loaded that depends on libpthread, libintl + will not be multithread-safe. */ + +/* The way to test at runtime whether libpthread is present is to test + whether a function pointer's value, such as &pthread_mutex_init, is + non-NULL. However, some versions of GCC have a bug through which, in + PIC mode, &foo != NULL always evaluates to true if there is a direct + call to foo(...) in the same function. To avoid this, we test the + address of a function in libpthread that we don't use. */ + +# pragma weak pthread_create +# pragma weak pthread_sigmask +# pragma weak pthread_join +# ifndef pthread_self +# pragma weak pthread_self +# endif +# pragma weak pthread_exit +# if HAVE_PTHREAD_ATFORK +# pragma weak pthread_atfork +# endif + +# if !PTHREAD_IN_USE_DETECTION_HARD +# pragma weak pthread_cancel +# define pthread_in_use() (pthread_cancel != NULL) +# endif + +# else + +# if !PTHREAD_IN_USE_DETECTION_HARD +# define pthread_in_use() 1 +# endif + +# endif + +/* -------------------------- gl_thread_t datatype -------------------------- */ + +/* This choice of gl_thread_t assumes that + pthread_equal (a, b) is equivalent to ((a) == (b)). + This is the case on all platforms in use in 2008. */ +typedef pthread_t gl_thread_t; +# define glthread_create(THREADP, FUNC, ARG) \ + (pthread_in_use () ? pthread_create (THREADP, NULL, FUNC, ARG) : ENOSYS) +# define glthread_sigmask(HOW, SET, OSET) \ + (pthread_in_use () ? pthread_sigmask (HOW, SET, OSET) : 0) +# define glthread_join(THREAD, RETVALP) \ + (pthread_in_use () ? pthread_join (THREAD, RETVALP) : 0) +# ifdef PTW32_VERSION + /* In pthreads-win32, pthread_t is a struct with a pointer field 'p' and + other fields. */ +# define gl_thread_self() \ + (pthread_in_use () ? pthread_self () : gl_null_thread) +# define gl_thread_self_pointer() \ + (pthread_in_use () ? pthread_self ().p : NULL) +extern const gl_thread_t gl_null_thread; +# else +# define gl_thread_self() \ + (pthread_in_use () ? pthread_self () : (pthread_t) NULL) +# define gl_thread_self_pointer() \ + (pthread_in_use () ? (void *) pthread_self () : NULL) +# endif +# define gl_thread_exit(RETVAL) \ + (pthread_in_use () ? pthread_exit (RETVAL) : 0) + +# if HAVE_PTHREAD_ATFORK +# define glthread_atfork(PREPARE_FUNC, PARENT_FUNC, CHILD_FUNC) \ + (pthread_in_use () ? pthread_atfork (PREPARE_FUNC, PARENT_FUNC, CHILD_FUNC) : 0) +# else +# define glthread_atfork(PREPARE_FUNC, PARENT_FUNC, CHILD_FUNC) 0 +# endif + +# ifdef __cplusplus +} +# endif + +#endif + +/* ========================================================================= */ + +#if USE_PTH_THREADS + +/* Use the GNU Pth threads library. */ + +# include <pth.h> + +# ifdef __cplusplus +extern "C" { +# endif + +# if USE_PTH_THREADS_WEAK + +/* Use weak references to the GNU Pth threads library. */ + +# pragma weak pth_spawn +# pragma weak pth_sigmask +# pragma weak pth_join +# pragma weak pth_self +# pragma weak pth_exit + +# pragma weak pth_cancel +# define pth_in_use() (pth_cancel != NULL) + +# else + +# define pth_in_use() 1 + +# endif +/* -------------------------- gl_thread_t datatype -------------------------- */ + +typedef pth_t gl_thread_t; +# define glthread_create(THREADP, FUNC, ARG) \ + (pth_in_use () ? ((*(THREADP) = pth_spawn (NULL, FUNC, ARG)) ? 0 : errno) : 0) +# define glthread_sigmask(HOW, SET, OSET) \ + (pth_in_use () && !pth_sigmask (HOW, SET, OSET) ? errno : 0) +# define glthread_join(THREAD, RETVALP) \ + (pth_in_use () && !pth_join (THREAD, RETVALP) ? errno : 0) +# define gl_thread_self() \ + (pth_in_use () ? (void *) pth_self () : NULL) +# define gl_thread_self_pointer() \ + gl_thread_self () +# define gl_thread_exit(RETVAL) \ + (pth_in_use () ? pth_exit (RETVAL) : 0) +# define glthread_atfork(PREPARE_FUNC, PARENT_FUNC, CHILD_FUNC) 0 + +# ifdef __cplusplus +} +# endif + +#endif + +/* ========================================================================= */ + +#if USE_SOLARIS_THREADS + +/* Use the old Solaris threads library. */ + +# include <thread.h> +# include <synch.h> + +# ifdef __cplusplus +extern "C" { +# endif + +# if USE_SOLARIS_THREADS_WEAK + +/* Use weak references to the old Solaris threads library. */ + +# pragma weak thr_create +# pragma weak thr_join +# pragma weak thr_self +# pragma weak thr_exit + +# pragma weak thr_suspend +# define thread_in_use() (thr_suspend != NULL) + +# else + +# define thread_in_use() 1 + +# endif + +/* -------------------------- gl_thread_t datatype -------------------------- */ + +typedef thread_t gl_thread_t; +# define glthread_create(THREADP, FUNC, ARG) \ + (thread_in_use () ? thr_create (NULL, 0, FUNC, ARG, 0, THREADP) : 0) +# define glthread_sigmask(HOW, SET, OSET) \ + (thread_in_use () ? sigprocmask (HOW, SET, OSET) : 0) +# define glthread_join(THREAD, RETVALP) \ + (thread_in_use () ? thr_join (THREAD, NULL, RETVALP) : 0) +# define gl_thread_self() \ + (thread_in_use () ? (void *) thr_self () : NULL) +# define gl_thread_self_pointer() \ + gl_thread_self () +# define gl_thread_exit(RETVAL) \ + (thread_in_use () ? thr_exit (RETVAL) : 0) +# define glthread_atfork(PREPARE_FUNC, PARENT_FUNC, CHILD_FUNC) 0 + +# ifdef __cplusplus +} +# endif + +#endif + +/* ========================================================================= */ + +#if USE_WIN32_THREADS + +# define WIN32_LEAN_AND_MEAN /* avoid including junk */ +# include <windows.h> + +# ifdef __cplusplus +extern "C" { +# endif + +/* -------------------------- gl_thread_t datatype -------------------------- */ + +/* The gl_thread_t is a pointer to a structure in memory. + Why not the thread handle? If it were the thread handle, it would be hard + to implement gl_thread_self() (since GetCurrentThread () returns a pseudo- + handle, DuplicateHandle (GetCurrentThread ()) returns a handle that must be + closed afterwards, and there is no function for quickly retrieving a thread + handle from its id). + Why not the thread id? I tried it. It did not work: Sometimes ids appeared + that did not belong to running threads, and glthread_join failed with ESRCH. + */ +typedef struct gl_thread_struct *gl_thread_t; +# define glthread_create(THREADP, FUNC, ARG) \ + glthread_create_func (THREADP, FUNC, ARG) +# define glthread_sigmask(HOW, SET, OSET) \ + /* unsupported */ 0 +# define glthread_join(THREAD, RETVALP) \ + glthread_join_func (THREAD, RETVALP) +# define gl_thread_self() \ + gl_thread_self_func () +# define gl_thread_self_pointer() \ + gl_thread_self () +# define gl_thread_exit(RETVAL) \ + gl_thread_exit_func (RETVAL) +# define glthread_atfork(PREPARE_FUNC, PARENT_FUNC, CHILD_FUNC) 0 +extern int glthread_create_func (gl_thread_t *threadp, void * (*func) (void *), void *arg); +extern int glthread_join_func (gl_thread_t thread, void **retvalp); +extern gl_thread_t gl_thread_self_func (void); +extern int gl_thread_exit_func (void *retval); + +# ifdef __cplusplus +} +# endif + +#endif + +/* ========================================================================= */ + +#if !(USE_POSIX_THREADS || USE_PTH_THREADS || USE_SOLARIS_THREADS || USE_WIN32_THREADS) + +/* Provide dummy implementation if threads are not supported. */ + +typedef int gl_thread_t; +# define glthread_create(THREADP, FUNC, ARG) ENOSYS +# define glthread_sigmask(HOW, SET, OSET) 0 +# define glthread_join(THREAD, RETVALP) 0 +# define gl_thread_self() 0 +# define gl_thread_self_pointer() \ + ((void *) gl_thread_self ()) +# define gl_thread_exit(RETVAL) 0 +# define glthread_atfork(PREPARE_FUNC, PARENT_FUNC, CHILD_FUNC) 0 + +#endif + +/* ========================================================================= */ + +/* Macros with built-in error handling. */ + +#ifdef __cplusplus +extern "C" { +#endif + +static inline gl_thread_t +gl_thread_create (void *(*func) (void *arg), void *arg) +{ + gl_thread_t thread; + int ret; + + ret = glthread_create (&thread, func, arg); + if (ret != 0) + abort (); + return thread; +} +#define gl_thread_sigmask(HOW, SET, OSET) \ + do \ + { \ + if (glthread_sigmask (HOW, SET, OSET)) \ + abort (); \ + } \ + while (0) +#define gl_thread_join(THREAD, RETVAL) \ + do \ + { \ + if (glthread_join (THREAD, RETVAL)) \ + abort (); \ + } \ + while (0) +#define gl_thread_atfork(PREPARE, PARENT, CHILD) \ + do \ + { \ + if (glthread_atfork (PREPARE, PARENT, CHILD)) \ + abort (); \ + } \ + while (0) + +#ifdef __cplusplus +} +#endif + +#endif /* _GLTHREAD_THREAD_H */ diff --git a/gl/tests/glthread/threadlib.c b/gl/tests/glthread/threadlib.c new file mode 100644 index 0000000000..646defa21b --- /dev/null +++ b/gl/tests/glthread/threadlib.c @@ -0,0 +1,74 @@ +/* Multithreading primitives. + Copyright (C) 2005-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, 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, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Bruno Haible <bruno@clisp.org>, 2005. */ + +#include <config.h> + +/* ========================================================================= */ + +#if USE_POSIX_THREADS + +/* Use the POSIX threads library. */ + +# include <pthread.h> +# include <stdlib.h> + +# if PTHREAD_IN_USE_DETECTION_HARD + +/* The function to be executed by a dummy thread. */ +static void * +dummy_thread_func (void *arg) +{ + return arg; +} + +int +glthread_in_use (void) +{ + static int tested; + static int result; /* 1: linked with -lpthread, 0: only with libc */ + + if (!tested) + { + pthread_t thread; + + if (pthread_create (&thread, NULL, dummy_thread_func, NULL) != 0) + /* Thread creation failed. */ + result = 0; + else + { + /* Thread creation works. */ + void *retval; + if (pthread_join (thread, &retval) != 0) + abort (); + result = 1; + } + tested = 1; + } + return result; +} + +# endif + +#endif + +/* ========================================================================= */ + +/* This declaration is solely to ensure that after preprocessing + this file is never empty. */ +typedef int dummy; diff --git a/gl/tests/glthread/yield.h b/gl/tests/glthread/yield.h new file mode 100644 index 0000000000..4fa2d8b1b7 --- /dev/null +++ b/gl/tests/glthread/yield.h @@ -0,0 +1,122 @@ +/* Yielding the processor to other threads and processes. + Copyright (C) 2005-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, 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, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* This file contains a primitive for yielding the processor to other threads. + extern void gl_thread_yield (void); + */ + +#ifndef _GLTHREAD_YIELD_H +#define _GLTHREAD_YIELD_H + +#include <errno.h> + +/* ========================================================================= */ + +#if USE_POSIX_THREADS + +/* Use the POSIX threads library. */ + +# include <sched.h> + +# ifdef __cplusplus +extern "C" { +# endif + +# define gl_thread_yield() \ + sched_yield () + +# ifdef __cplusplus +} +# endif + +#endif + +/* ========================================================================= */ + +#if USE_PTH_THREADS + +/* Use the GNU Pth threads library. */ + +# include <pth.h> + +# ifdef __cplusplus +extern "C" { +# endif + +# define gl_thread_yield() \ + pth_yield (NULL) + +# ifdef __cplusplus +} +# endif + +#endif + +/* ========================================================================= */ + +#if USE_SOLARIS_THREADS + +/* Use the old Solaris threads library. */ + +# include <thread.h> + +# ifdef __cplusplus +extern "C" { +# endif + +# define gl_thread_yield() \ + thr_yield () + +# ifdef __cplusplus +} +# endif + +#endif + +/* ========================================================================= */ + +#if USE_WIN32_THREADS + +# define WIN32_LEAN_AND_MEAN /* avoid including junk */ +# include <windows.h> + +# ifdef __cplusplus +extern "C" { +# endif + +# define gl_thread_yield() \ + Sleep (0) + +# ifdef __cplusplus +} +# endif + +#endif + +/* ========================================================================= */ + +#if !(USE_POSIX_THREADS || USE_PTH_THREADS || USE_SOLARIS_THREADS || USE_WIN32_THREADS) + +/* Provide dummy implementation if threads are not supported. */ + +# define gl_thread_yield() 0 + +#endif + +/* ========================================================================= */ + +#endif /* _GLTHREAD_YIELD_H */ diff --git a/gl/tests/inet_pton.c b/gl/tests/inet_pton.c deleted file mode 100644 index cb1a872e9b..0000000000 --- a/gl/tests/inet_pton.c +++ /dev/null @@ -1,268 +0,0 @@ -/* inet_pton.c -- convert IPv4 and IPv6 addresses from text to binary form - - Copyright (C) 2006, 2008-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/>. */ - -/* - * Copyright (c) 1996,1999 by Internet Software Consortium. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS - * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE - * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL - * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR - * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS - * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS - * SOFTWARE. - */ - -#include <config.h> - -/* Specification. */ -#include <arpa/inet.h> - -#if HAVE_DECL_INET_PTON - -# undef inet_pton - -int -rpl_inet_pton (int af, const char *restrict src, void *restrict dst) -{ - return inet_pton (af, src, dst); -} - -#else - -# include <c-ctype.h> -# include <string.h> -# include <errno.h> - -# define NS_INADDRSZ 4 -# define NS_IN6ADDRSZ 16 -# define NS_INT16SZ 2 - -/* - * WARNING: Don't even consider trying to compile this on a system where - * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. - */ - -static int inet_pton4 (const char *src, unsigned char *dst); -# if HAVE_IPV6 -static int inet_pton6 (const char *src, unsigned char *dst); -# endif - -/* int - * inet_pton(af, src, dst) - * convert from presentation format (which usually means ASCII printable) - * to network format (which is usually some kind of binary format). - * return: - * 1 if the address was valid for the specified address family - * 0 if the address wasn't valid (`dst' is untouched in this case) - * -1 if some other error occurred (`dst' is untouched in this case, too) - * author: - * Paul Vixie, 1996. - */ -int -inet_pton (int af, const char *restrict src, void *restrict dst) -{ - switch (af) - { - case AF_INET: - return (inet_pton4 (src, dst)); - -# if HAVE_IPV6 - case AF_INET6: - return (inet_pton6 (src, dst)); -# endif - - default: - errno = EAFNOSUPPORT; - return (-1); - } - /* NOTREACHED */ -} - -/* int - * inet_pton4(src, dst) - * like inet_aton() but without all the hexadecimal, octal (with the - * exception of 0) and shorthand. - * return: - * 1 if `src' is a valid dotted quad, else 0. - * notice: - * does not touch `dst' unless it's returning 1. - * author: - * Paul Vixie, 1996. - */ -static int -inet_pton4 (const char *restrict src, unsigned char *restrict dst) -{ - int saw_digit, octets, ch; - unsigned char tmp[NS_INADDRSZ], *tp; - - saw_digit = 0; - octets = 0; - *(tp = tmp) = 0; - while ((ch = *src++) != '\0') - { - - if (ch >= '0' && ch <= '9') - { - unsigned new = *tp * 10 + (ch - '0'); - - if (saw_digit && *tp == 0) - return (0); - if (new > 255) - return (0); - *tp = new; - if (!saw_digit) - { - if (++octets > 4) - return (0); - saw_digit = 1; - } - } - else if (ch == '.' && saw_digit) - { - if (octets == 4) - return (0); - *++tp = 0; - saw_digit = 0; - } - else - return (0); - } - if (octets < 4) - return (0); - memcpy (dst, tmp, NS_INADDRSZ); - return (1); -} - -# if HAVE_IPV6 - -/* int - * inet_pton6(src, dst) - * convert presentation level address to network order binary form. - * return: - * 1 if `src' is a valid [RFC1884 2.2] address, else 0. - * notice: - * (1) does not touch `dst' unless it's returning 1. - * (2) :: in a full address is silently ignored. - * credit: - * inspired by Mark Andrews. - * author: - * Paul Vixie, 1996. - */ -static int -inet_pton6 (const char *restrict src, unsigned char *restrict dst) -{ - static const char xdigits[] = "0123456789abcdef"; - unsigned char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; - const char *curtok; - int ch, saw_xdigit; - unsigned val; - - tp = memset (tmp, '\0', NS_IN6ADDRSZ); - endp = tp + NS_IN6ADDRSZ; - colonp = NULL; - /* Leading :: requires some special handling. */ - if (*src == ':') - if (*++src != ':') - return (0); - curtok = src; - saw_xdigit = 0; - val = 0; - while ((ch = c_tolower (*src++)) != '\0') - { - const char *pch; - - pch = strchr (xdigits, ch); - if (pch != NULL) - { - val <<= 4; - val |= (pch - xdigits); - if (val > 0xffff) - return (0); - saw_xdigit = 1; - continue; - } - if (ch == ':') - { - curtok = src; - if (!saw_xdigit) - { - if (colonp) - return (0); - colonp = tp; - continue; - } - else if (*src == '\0') - { - return (0); - } - if (tp + NS_INT16SZ > endp) - return (0); - *tp++ = (u_char) (val >> 8) & 0xff; - *tp++ = (u_char) val & 0xff; - saw_xdigit = 0; - val = 0; - continue; - } - if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && - inet_pton4 (curtok, tp) > 0) - { - tp += NS_INADDRSZ; - saw_xdigit = 0; - break; /* '\0' was seen by inet_pton4(). */ - } - return (0); - } - if (saw_xdigit) - { - if (tp + NS_INT16SZ > endp) - return (0); - *tp++ = (u_char) (val >> 8) & 0xff; - *tp++ = (u_char) val & 0xff; - } - if (colonp != NULL) - { - /* - * Since some memmove()'s erroneously fail to handle - * overlapping regions, we'll do the shift by hand. - */ - const int n = tp - colonp; - int i; - - if (tp == endp) - return (0); - for (i = 1; i <= n; i++) - { - endp[-i] = colonp[n - i]; - colonp[n - i] = 0; - } - tp = endp; - } - if (tp != endp) - return (0); - memcpy (dst, tmp, NS_IN6ADDRSZ); - return (1); -} - -# endif - -#endif diff --git a/gl/tests/ioctl.c b/gl/tests/ioctl.c new file mode 100644 index 0000000000..c6ba989ee5 --- /dev/null +++ b/gl/tests/ioctl.c @@ -0,0 +1,79 @@ +/* ioctl.c --- wrappers for Windows ioctl function + + Copyright (C) 2008-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 Paolo Bonzini */ + +#include <config.h> + +#include <sys/ioctl.h> + +#include <stdarg.h> + +#if HAVE_IOCTL + +/* Provide a wrapper with the POSIX prototype. */ +# undef ioctl +int +rpl_ioctl (int fd, int request, ... /* {void *,char *} arg */) +{ + void *buf; + va_list args; + + va_start (args, request); + buf = va_arg (args, void *); + va_end (args); + + /* Cast 'request' so that when the system's ioctl function takes a 64-bit + request argument, the value gets zero-extended, not sign-extended. */ + return ioctl (fd, (unsigned int) request, buf); +} + +#else /* mingw */ + +# include <errno.h> + +# include "fd-hook.h" + +static int +primary_ioctl (int fd, int request, void *arg) +{ + /* 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. */ + + errno = ENOSYS; + return -1; +} + +int +ioctl (int fd, int request, ... /* {void *,char *} arg */) +{ + void *arg; + va_list args; + + va_start (args, request); + arg = va_arg (args, void *); + va_end (args); + +# if WINDOWS_SOCKETS + return execute_all_ioctl_hooks (primary_ioctl, fd, request, arg); +# else + return primary_ioctl (fd, request, arg); +# endif +} + +#endif diff --git a/gl/tests/perror.c b/gl/tests/perror.c new file mode 100644 index 0000000000..af4b56cd40 --- /dev/null +++ b/gl/tests/perror.c @@ -0,0 +1,49 @@ +/* Print a message describing error code. + Copyright (C) 2008-2011 Free Software Foundation, Inc. + Written by Bruno Haible and Simon Josefsson. + + 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 <stdio.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "strerror-override.h" + +/* Use the system functions, not the gnulib overrides in this file. */ +#undef fprintf + +void +perror (const char *string) +{ + char stackbuf[STACKBUF_LEN]; + int ret; + + /* Our implementation guarantees that this will be a non-empty + string, even if it returns EINVAL; and stackbuf should be sized + large enough to avoid ERANGE. */ + ret = strerror_r (errno, stackbuf, sizeof stackbuf); + if (ret == ERANGE) + abort (); + + if (string != NULL && *string != '\0') + fprintf (stderr, "%s: %s\n", string, stackbuf); + else + fprintf (stderr, "%s\n", stackbuf); +} diff --git a/gl/tests/pipe.c b/gl/tests/pipe.c new file mode 100644 index 0000000000..425dd70ef4 --- /dev/null +++ b/gl/tests/pipe.c @@ -0,0 +1,51 @@ +/* Create a pipe. + Copyright (C) 2009-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, 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, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include <config.h> + +/* Specification. */ +#include <unistd.h> + +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ +/* Native Woe32 API. */ + +/* Get _pipe(). */ +# include <io.h> + +/* Get _O_BINARY. */ +# include <fcntl.h> + +int +pipe (int fd[2]) +{ + /* Mingw changes fd to {-1,-1} on failure, but this violates + http://austingroupbugs.net/view.php?id=467 */ + int tmp[2]; + int result = _pipe (tmp, 4096, _O_BINARY); + if (!result) + { + fd[0] = tmp[0]; + fd[1] = tmp[1]; + } + return result; +} + +#else + +# error "This platform lacks a pipe function, and Gnulib doesn't provide a replacement. This is a bug in Gnulib." + +#endif diff --git a/gl/tests/strerror_r.c b/gl/tests/strerror_r.c new file mode 100644 index 0000000000..e6cf99b254 --- /dev/null +++ b/gl/tests/strerror_r.c @@ -0,0 +1,327 @@ +/* strerror_r.c --- POSIX compatible system error routine + + Copyright (C) 2010-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 Bruno Haible <bruno@clisp.org>, 2010. */ + +#include <config.h> + +/* Enable declaration of sys_nerr and sys_errlist in <errno.h> on NetBSD. */ +#define _NETBSD_SOURCE 1 + +/* Specification. */ +#include <string.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + +#include "strerror-override.h" + +#if (__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__) && HAVE___XPG_STRERROR_R /* glibc >= 2.3.4, cygwin >= 1.7.9 */ + +# define USE_XPG_STRERROR_R 1 + +#elif HAVE_DECL_STRERROR_R && !(__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__) + +/* The system's strerror_r function is OK, except that its third argument + is 'int', not 'size_t', or its return type is wrong. */ + +# include <limits.h> + +# define USE_SYSTEM_STRERROR_R 1 + +#else /* (__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__ ? !HAVE___XPG_STRERROR_R : !HAVE_DECL_STRERROR_R) */ + +/* Use the system's strerror(). Exclude glibc and cygwin because the + system strerror_r has the wrong return type, and cygwin 1.7.9 + strerror_r clobbers strerror. */ +# undef strerror + +# define USE_SYSTEM_STRERROR 1 + +# if defined __NetBSD__ || defined __hpux || ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __sgi || (defined __sun && !defined _LP64) || defined __CYGWIN__ + +/* No locking needed. */ + +/* Get catgets internationalization functions. */ +# if HAVE_CATGETS +# include <nl_types.h> +# endif + +/* Get sys_nerr, sys_errlist on HP-UX (otherwise only declared in C++ mode). + Get sys_nerr, sys_errlist on IRIX (otherwise only declared with _SGIAPI). */ +# if defined __hpux || defined __sgi +extern int sys_nerr; +extern char *sys_errlist[]; +# endif + +/* Get sys_nerr on Solaris. */ +# if defined __sun && !defined _LP64 +extern int sys_nerr; +# endif + +# else + +# include "glthread/lock.h" + +/* This lock protects the buffer returned by strerror(). We assume that + no other uses of strerror() exist in the program. */ +gl_lock_define_initialized(static, strerror_lock) + +# endif + +#endif + +/* On MSVC, there is no snprintf() function, just a _snprintf(). + It is of lower quality, but sufficient for the simple use here. + We only have to make sure to NUL terminate the result (_snprintf + does not NUL terminate, like strncpy). */ +#if !HAVE_SNPRINTF +static int +local_snprintf (char *buf, size_t buflen, const char *format, ...) +{ + va_list args; + int result; + + va_start (args, format); + result = _vsnprintf (buf, buflen, format, args); + va_end (args); + if (buflen > 0 && (result < 0 || result >= buflen)) + buf[buflen - 1] = '\0'; + return result; +} +# define snprintf local_snprintf +#endif + +/* Copy as much of MSG into BUF as possible, without corrupting errno. + Return 0 if MSG fit in BUFLEN, otherwise return ERANGE. */ +static int +safe_copy (char *buf, size_t buflen, const char *msg) +{ + size_t len = strlen (msg); + int ret; + + if (len < buflen) + { + /* Although POSIX allows memcpy() to corrupt errno, we don't + know of any implementation where this is a real problem. */ + memcpy (buf, msg, len + 1); + ret = 0; + } + else + { + memcpy (buf, msg, buflen - 1); + buf[buflen - 1] = '\0'; + ret = ERANGE; + } + return ret; +} + + +int +strerror_r (int errnum, char *buf, size_t buflen) +#undef strerror_r +{ + /* Filter this out now, so that rest of this replacement knows that + there is room for a non-empty message and trailing NUL. */ + if (buflen <= 1) + { + if (buflen) + *buf = '\0'; + return ERANGE; + } + *buf = '\0'; + + /* Check for gnulib overrides. */ + { + char const *msg = strerror_override (errnum); + + if (msg) + return safe_copy (buf, buflen, msg); + } + + { + int ret; + int saved_errno = errno; + +#if USE_XPG_STRERROR_R + + { + extern int __xpg_strerror_r (int errnum, char *buf, size_t buflen); + + ret = __xpg_strerror_r (errnum, buf, buflen); + if (ret < 0) + ret = errno; + if (!*buf) + { + /* glibc 2.13 would not touch buf on err, so we have to fall + back to GNU strerror_r which always returns a thread-safe + untruncated string to (partially) copy into our buf. */ + safe_copy (buf, buflen, strerror_r (errnum, buf, buflen)); + } + } + +#elif USE_SYSTEM_STRERROR_R + + if (buflen > INT_MAX) + buflen = INT_MAX; + +# ifdef __hpux + /* On HP-UX 11.31, strerror_r always fails when buflen < 80; it + also fails to change buf on EINVAL. */ + { + char stackbuf[80]; + + if (buflen < sizeof stackbuf) + { + ret = strerror_r (errnum, stackbuf, sizeof stackbuf); + if (ret == 0) + ret = safe_copy (buf, buflen, stackbuf); + } + else + ret = strerror_r (errnum, buf, buflen); + } +# else + ret = strerror_r (errnum, buf, buflen); + + /* Some old implementations may return (-1, EINVAL) instead of EINVAL. */ + if (ret < 0) + ret = errno; +# endif + +# ifdef _AIX + /* AIX returns 0 rather than ERANGE when truncating strings; try + again until we are sure we got the entire string. */ + if (!ret && strlen (buf) == buflen - 1) + { + char stackbuf[STACKBUF_LEN]; + size_t len; + strerror_r (errnum, stackbuf, sizeof stackbuf); + len = strlen (stackbuf); + /* STACKBUF_LEN should have been large enough. */ + if (len + 1 == sizeof stackbuf) + abort (); + if (buflen <= len) + ret = ERANGE; + } +# else + /* Solaris 10 does not populate buf on ERANGE. OpenBSD 4.7 + truncates early on ERANGE rather than return a partial integer. + We prefer the maximal string. We set buf[0] earlier, and we + know of no implementation that modifies buf to be an + unterminated string, so this strlen should be portable in + practice (rather than pulling in a safer strnlen). */ + if (ret == ERANGE && strlen (buf) < buflen - 1) + { + char stackbuf[STACKBUF_LEN]; + + /* STACKBUF_LEN should have been large enough. */ + if (strerror_r (errnum, stackbuf, sizeof stackbuf) == ERANGE) + abort (); + safe_copy (buf, buflen, stackbuf); + } +# endif + +#else /* USE_SYSTEM_STRERROR */ + + /* Try to do what strerror (errnum) does, but without clobbering the + buffer used by strerror(). */ + +# if defined __NetBSD__ || defined __hpux || ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __CYGWIN__ /* NetBSD, HP-UX, native Win32, Cygwin */ + + /* NetBSD: sys_nerr, sys_errlist are declared through _NETBSD_SOURCE + and <errno.h> above. + HP-UX: sys_nerr, sys_errlist are declared explicitly above. + native Win32: sys_nerr, sys_errlist are declared in <stdlib.h>. + Cygwin: sys_nerr, sys_errlist are declared in <errno.h>. */ + if (errnum >= 0 && errnum < sys_nerr) + { +# if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux) +# if defined __NetBSD__ + nl_catd catd = catopen ("libc", NL_CAT_LOCALE); + const char *errmsg = + (catd != (nl_catd)-1 + ? catgets (catd, 1, errnum, sys_errlist[errnum]) + : sys_errlist[errnum]); +# endif +# if defined __hpux + nl_catd catd = catopen ("perror", NL_CAT_LOCALE); + const char *errmsg = + (catd != (nl_catd)-1 + ? catgets (catd, 1, 1 + errnum, sys_errlist[errnum]) + : sys_errlist[errnum]); +# endif +# else + const char *errmsg = sys_errlist[errnum]; +# endif + if (errmsg == NULL || *errmsg == '\0') + ret = EINVAL; + else + ret = safe_copy (buf, buflen, errmsg); +# if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux) + if (catd != (nl_catd)-1) + catclose (catd); +# endif + } + else + ret = EINVAL; + +# elif defined __sgi || (defined __sun && !defined _LP64) /* IRIX, Solaris <= 9 32-bit */ + + /* For a valid error number, the system's strerror() function returns + a pointer to a not copied string, not to a buffer. */ + if (errnum >= 0 && errnum < sys_nerr) + { + char *errmsg = strerror (errnum); + + if (errmsg == NULL || *errmsg == '\0') + ret = EINVAL; + else + ret = safe_copy (buf, buflen, errmsg); + } + else + ret = EINVAL; + +# else + + gl_lock_lock (strerror_lock); + + { + char *errmsg = strerror (errnum); + + /* For invalid error numbers, strerror() on + - IRIX 6.5 returns NULL, + - HP-UX 11 returns an empty string. */ + if (errmsg == NULL || *errmsg == '\0') + ret = EINVAL; + else + ret = safe_copy (buf, buflen, errmsg); + } + + gl_lock_unlock (strerror_lock); + +# endif + +#endif + + if (ret == EINVAL && !*buf) + snprintf (buf, buflen, "Unknown error %d", errnum); + + errno = saved_errno; + return ret; + } +} diff --git a/gl/tests/sys_ioctl.in.h b/gl/tests/sys_ioctl.in.h new file mode 100644 index 0000000000..dc8aedabe0 --- /dev/null +++ b/gl/tests/sys_ioctl.in.h @@ -0,0 +1,79 @@ +/* Substitute for and wrapper around <sys/ioctl.h>. + Copyright (C) 2008-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, 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, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _@GUARD_PREFIX@_SYS_IOCTL_H + +#if __GNUC__ >= 3 +@PRAGMA_SYSTEM_HEADER@ +#endif +@PRAGMA_COLUMNS@ + +/* The include_next requires a split double-inclusion guard. */ +#if @HAVE_SYS_IOCTL_H@ +# @INCLUDE_NEXT@ @NEXT_SYS_IOCTL_H@ +#endif + +#ifndef _@GUARD_PREFIX@_SYS_IOCTL_H +#define _@GUARD_PREFIX@_SYS_IOCTL_H + +/* AIX 5.1 and Solaris 10 declare ioctl() in <unistd.h> and in <stropts.h>, + but not in <sys/ioctl.h>. + But avoid namespace pollution on glibc systems. */ +#ifndef __GLIBC__ +# include <unistd.h> +#endif + +/* The definitions of _GL_FUNCDECL_RPL etc. are copied here. */ + +/* The definition of _GL_WARN_ON_USE is copied here. */ + + +/* Declare overridden functions. */ + +#if @GNULIB_IOCTL@ +# if @REPLACE_IOCTL@ +# if !(defined __cplusplus && defined GNULIB_NAMESPACE) +# undef ioctl +# define ioctl rpl_ioctl +# endif +_GL_FUNCDECL_RPL (ioctl, int, + (int fd, int request, ... /* {void *,char *} arg */)); +_GL_CXXALIAS_RPL (ioctl, int, + (int fd, int request, ... /* {void *,char *} arg */)); +# else +# if @SYS_IOCTL_H_HAVE_WINSOCK2_H@ || 1 +_GL_FUNCDECL_SYS (ioctl, int, + (int fd, int request, ... /* {void *,char *} arg */)); +# endif +_GL_CXXALIAS_SYS (ioctl, int, + (int fd, int request, ... /* {void *,char *} arg */)); +# endif +_GL_CXXALIASWARN (ioctl); +#elif @SYS_IOCTL_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS@ +# undef ioctl +# define ioctl ioctl_used_without_requesting_gnulib_module_ioctl +#elif defined GNULIB_POSIXCHECK +# undef ioctl +# if HAVE_RAW_DECL_IOCTL +_GL_WARN_ON_USE (ioctl, "ioctl does not portably work on sockets - " + "use gnulib module ioctl for portability"); +# endif +#endif + + +#endif /* _@GUARD_PREFIX@_SYS_IOCTL_H */ +#endif /* _@GUARD_PREFIX@_SYS_IOCTL_H */ diff --git a/gl/tests/test-connect.c b/gl/tests/test-connect.c new file mode 100644 index 0000000000..84f00b5425 --- /dev/null +++ b/gl/tests/test-connect.c @@ -0,0 +1,58 @@ +/* Test connecting a client socket. + 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> + +#include <sys/socket.h> + +#include "signature.h" +SIGNATURE_CHECK (connect, int, (int, const struct sockaddr *, socklen_t)); + +#include <errno.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "sockets.h" +#include "macros.h" + +int +main (void) +{ + gl_sockets_startup (SOCKETS_1_1); + + /* Test behaviour for invalid file descriptors. */ + { + struct sockaddr_in addr; + + addr.sin_family = AF_INET; + inet_pton (AF_INET, "127.0.0.1", &addr.sin_addr); + addr.sin_port = htons (80); + { + errno = 0; + ASSERT (connect (-1, (const struct sockaddr *) &addr, sizeof (addr)) + == -1); + ASSERT (errno == EBADF); + } + { + errno = 0; + ASSERT (connect (99, (const struct sockaddr *) &addr, sizeof (addr)) + == -1); + ASSERT (errno == EBADF); + } + } + + return 0; +} diff --git a/gl/tests/test-ftruncate.c b/gl/tests/test-ftruncate.c new file mode 100644 index 0000000000..c5da4eb24d --- /dev/null +++ b/gl/tests/test-ftruncate.c @@ -0,0 +1,59 @@ +/* Test truncating a file. + 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> + +#include <unistd.h> + +#include "signature.h" +SIGNATURE_CHECK (ftruncate, int, (int, off_t)); + +#include <errno.h> +#include <fcntl.h> + +#include "macros.h" + +int +main (int argc, char *argv[]) +{ + const char *filename = argv[1]; + + /* Test behaviour for invalid file descriptors. */ + { + errno = 0; + ASSERT (ftruncate (-1, 0) == -1); + ASSERT (errno == EBADF); + } + { + errno = 0; + ASSERT (ftruncate (99, 0) == -1); + ASSERT (errno == EBADF); + } + + /* Test behaviour for read-only file descriptors. */ + { + int fd = open (filename, O_RDONLY); + ASSERT (fd >= 0); + errno = 0; + ASSERT (ftruncate (fd, 0) == -1); + ASSERT (errno == EBADF || errno == EINVAL + || errno == EACCES /* seen on mingw */ + ); + close (fd); + } + + return 0; +} diff --git a/gl/tests/test-ftruncate.sh b/gl/tests/test-ftruncate.sh new file mode 100755 index 0000000000..d7394d2bfe --- /dev/null +++ b/gl/tests/test-ftruncate.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +exec ./test-ftruncate${EXEEXT} "$srcdir/test-ftruncate.sh" diff --git a/gl/tests/test-ioctl.c b/gl/tests/test-ioctl.c new file mode 100644 index 0000000000..130ee427c4 --- /dev/null +++ b/gl/tests/test-ioctl.c @@ -0,0 +1,49 @@ +/* Test of ioctl() function. + 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 <sys/ioctl.h> + +#include "signature.h" +SIGNATURE_CHECK (ioctl, int, (int, int, ...)); + +#include <errno.h> + +#include "macros.h" + +int +main (void) +{ +#ifdef FIONREAD + /* Test behaviour for invalid file descriptors. */ + { + int value; + errno = 0; + ASSERT (ioctl (-1, FIONREAD, &value) == -1); + ASSERT (errno == EBADF); + } + { + int value; + errno = 0; + ASSERT (ioctl (99, FIONREAD, &value) == -1); + ASSERT (errno == EBADF); + } +#endif + + return 0; +} diff --git a/gl/tests/test-lock.c b/gl/tests/test-lock.c new file mode 100644 index 0000000000..3f8846bc22 --- /dev/null +++ b/gl/tests/test-lock.c @@ -0,0 +1,601 @@ +/* Test of locking in multithreaded situations. + Copyright (C) 2005, 2008-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 Bruno Haible <bruno@clisp.org>, 2005. */ + +#include <config.h> + +#if USE_POSIX_THREADS || USE_SOLARIS_THREADS || USE_PTH_THREADS || USE_WIN32_THREADS + +#if USE_POSIX_THREADS +# define TEST_POSIX_THREADS 1 +#endif +#if USE_SOLARIS_THREADS +# define TEST_SOLARIS_THREADS 1 +#endif +#if USE_PTH_THREADS +# define TEST_PTH_THREADS 1 +#endif +#if USE_WIN32_THREADS +# define TEST_WIN32_THREADS 1 +#endif + +/* Whether to enable locking. + Uncomment this to get a test program without locking, to verify that + it crashes. */ +#define ENABLE_LOCKING 1 + +/* Which tests to perform. + Uncomment some of these, to verify that all tests crash if no locking + is enabled. */ +#define DO_TEST_LOCK 1 +#define DO_TEST_RWLOCK 1 +#define DO_TEST_RECURSIVE_LOCK 1 +#define DO_TEST_ONCE 1 + +/* Whether to help the scheduler through explicit yield(). + Uncomment this to see if the operating system has a fair scheduler. */ +#define EXPLICIT_YIELD 1 + +/* Whether to print debugging messages. */ +#define ENABLE_DEBUGGING 0 + +/* Number of simultaneous threads. */ +#define THREAD_COUNT 10 + +/* Number of operations performed in each thread. + This is quite high, because with a smaller count, say 5000, we often get + an "OK" result even without ENABLE_LOCKING (on Linux/x86). */ +#define REPEAT_COUNT 50000 + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#if !ENABLE_LOCKING +# undef USE_POSIX_THREADS +# undef USE_SOLARIS_THREADS +# undef USE_PTH_THREADS +# undef USE_WIN32_THREADS +#endif +#include "glthread/lock.h" + +#if !ENABLE_LOCKING +# if TEST_POSIX_THREADS +# define USE_POSIX_THREADS 1 +# endif +# if TEST_SOLARIS_THREADS +# define USE_SOLARIS_THREADS 1 +# endif +# if TEST_PTH_THREADS +# define USE_PTH_THREADS 1 +# endif +# if TEST_WIN32_THREADS +# define USE_WIN32_THREADS 1 +# endif +#endif + +#include "glthread/thread.h" +#include "glthread/yield.h" + +#if ENABLE_DEBUGGING +# define dbgprintf printf +#else +# define dbgprintf if (0) printf +#endif + +#if EXPLICIT_YIELD +# define yield() gl_thread_yield () +#else +# define yield() +#endif + +#define ACCOUNT_COUNT 4 + +static int account[ACCOUNT_COUNT]; + +static int +random_account (void) +{ + return ((unsigned int) rand () >> 3) % ACCOUNT_COUNT; +} + +static void +check_accounts (void) +{ + int i, sum; + + sum = 0; + for (i = 0; i < ACCOUNT_COUNT; i++) + sum += account[i]; + if (sum != ACCOUNT_COUNT * 1000) + abort (); +} + + +/* ------------------- Test normal (non-recursive) locks ------------------- */ + +/* Test normal locks by having several bank accounts and several threads + which shuffle around money between the accounts and another thread + checking that all the money is still there. */ + +gl_lock_define_initialized(static, my_lock) + +static void * +lock_mutator_thread (void *arg) +{ + int repeat; + + for (repeat = REPEAT_COUNT; repeat > 0; repeat--) + { + int i1, i2, value; + + dbgprintf ("Mutator %p before lock\n", gl_thread_self_pointer ()); + gl_lock_lock (my_lock); + dbgprintf ("Mutator %p after lock\n", gl_thread_self_pointer ()); + + i1 = random_account (); + i2 = random_account (); + value = ((unsigned int) rand () >> 3) % 10; + account[i1] += value; + account[i2] -= value; + + dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ()); + gl_lock_unlock (my_lock); + dbgprintf ("Mutator %p after unlock\n", gl_thread_self_pointer ()); + + dbgprintf ("Mutator %p before check lock\n", gl_thread_self_pointer ()); + gl_lock_lock (my_lock); + check_accounts (); + gl_lock_unlock (my_lock); + dbgprintf ("Mutator %p after check unlock\n", gl_thread_self_pointer ()); + + yield (); + } + + dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ()); + return NULL; +} + +static volatile int lock_checker_done; + +static void * +lock_checker_thread (void *arg) +{ + while (!lock_checker_done) + { + dbgprintf ("Checker %p before check lock\n", gl_thread_self_pointer ()); + gl_lock_lock (my_lock); + check_accounts (); + gl_lock_unlock (my_lock); + dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ()); + + yield (); + } + + dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ()); + return NULL; +} + +static void +test_lock (void) +{ + int i; + gl_thread_t checkerthread; + gl_thread_t threads[THREAD_COUNT]; + + /* Initialization. */ + for (i = 0; i < ACCOUNT_COUNT; i++) + account[i] = 1000; + lock_checker_done = 0; + + /* Spawn the threads. */ + checkerthread = gl_thread_create (lock_checker_thread, NULL); + for (i = 0; i < THREAD_COUNT; i++) + threads[i] = gl_thread_create (lock_mutator_thread, NULL); + + /* Wait for the threads to terminate. */ + for (i = 0; i < THREAD_COUNT; i++) + gl_thread_join (threads[i], NULL); + lock_checker_done = 1; + gl_thread_join (checkerthread, NULL); + check_accounts (); +} + + +/* ----------------- Test read-write (non-recursive) locks ----------------- */ + +/* Test read-write locks by having several bank accounts and several threads + which shuffle around money between the accounts and several other threads + that check that all the money is still there. */ + +gl_rwlock_define_initialized(static, my_rwlock) + +static void * +rwlock_mutator_thread (void *arg) +{ + int repeat; + + for (repeat = REPEAT_COUNT; repeat > 0; repeat--) + { + int i1, i2, value; + + dbgprintf ("Mutator %p before wrlock\n", gl_thread_self_pointer ()); + gl_rwlock_wrlock (my_rwlock); + dbgprintf ("Mutator %p after wrlock\n", gl_thread_self_pointer ()); + + i1 = random_account (); + i2 = random_account (); + value = ((unsigned int) rand () >> 3) % 10; + account[i1] += value; + account[i2] -= value; + + dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ()); + gl_rwlock_unlock (my_rwlock); + dbgprintf ("Mutator %p after unlock\n", gl_thread_self_pointer ()); + + yield (); + } + + dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ()); + return NULL; +} + +static volatile int rwlock_checker_done; + +static void * +rwlock_checker_thread (void *arg) +{ + while (!rwlock_checker_done) + { + dbgprintf ("Checker %p before check rdlock\n", gl_thread_self_pointer ()); + gl_rwlock_rdlock (my_rwlock); + check_accounts (); + gl_rwlock_unlock (my_rwlock); + dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ()); + + yield (); + } + + dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ()); + return NULL; +} + +static void +test_rwlock (void) +{ + int i; + gl_thread_t checkerthreads[THREAD_COUNT]; + gl_thread_t threads[THREAD_COUNT]; + + /* Initialization. */ + for (i = 0; i < ACCOUNT_COUNT; i++) + account[i] = 1000; + rwlock_checker_done = 0; + + /* Spawn the threads. */ + for (i = 0; i < THREAD_COUNT; i++) + checkerthreads[i] = gl_thread_create (rwlock_checker_thread, NULL); + for (i = 0; i < THREAD_COUNT; i++) + threads[i] = gl_thread_create (rwlock_mutator_thread, NULL); + + /* Wait for the threads to terminate. */ + for (i = 0; i < THREAD_COUNT; i++) + gl_thread_join (threads[i], NULL); + rwlock_checker_done = 1; + for (i = 0; i < THREAD_COUNT; i++) + gl_thread_join (checkerthreads[i], NULL); + check_accounts (); +} + + +/* -------------------------- Test recursive locks -------------------------- */ + +/* Test recursive locks by having several bank accounts and several threads + which shuffle around money between the accounts (recursively) and another + thread checking that all the money is still there. */ + +gl_recursive_lock_define_initialized(static, my_reclock) + +static void +recshuffle (void) +{ + int i1, i2, value; + + dbgprintf ("Mutator %p before lock\n", gl_thread_self_pointer ()); + gl_recursive_lock_lock (my_reclock); + dbgprintf ("Mutator %p after lock\n", gl_thread_self_pointer ()); + + i1 = random_account (); + i2 = random_account (); + value = ((unsigned int) rand () >> 3) % 10; + account[i1] += value; + account[i2] -= value; + + /* Recursive with probability 0.5. */ + if (((unsigned int) rand () >> 3) % 2) + recshuffle (); + + dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ()); + gl_recursive_lock_unlock (my_reclock); + dbgprintf ("Mutator %p after unlock\n", gl_thread_self_pointer ()); +} + +static void * +reclock_mutator_thread (void *arg) +{ + int repeat; + + for (repeat = REPEAT_COUNT; repeat > 0; repeat--) + { + recshuffle (); + + dbgprintf ("Mutator %p before check lock\n", gl_thread_self_pointer ()); + gl_recursive_lock_lock (my_reclock); + check_accounts (); + gl_recursive_lock_unlock (my_reclock); + dbgprintf ("Mutator %p after check unlock\n", gl_thread_self_pointer ()); + + yield (); + } + + dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ()); + return NULL; +} + +static volatile int reclock_checker_done; + +static void * +reclock_checker_thread (void *arg) +{ + while (!reclock_checker_done) + { + dbgprintf ("Checker %p before check lock\n", gl_thread_self_pointer ()); + gl_recursive_lock_lock (my_reclock); + check_accounts (); + gl_recursive_lock_unlock (my_reclock); + dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ()); + + yield (); + } + + dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ()); + return NULL; +} + +static void +test_recursive_lock (void) +{ + int i; + gl_thread_t checkerthread; + gl_thread_t threads[THREAD_COUNT]; + + /* Initialization. */ + for (i = 0; i < ACCOUNT_COUNT; i++) + account[i] = 1000; + reclock_checker_done = 0; + + /* Spawn the threads. */ + checkerthread = gl_thread_create (reclock_checker_thread, NULL); + for (i = 0; i < THREAD_COUNT; i++) + threads[i] = gl_thread_create (reclock_mutator_thread, NULL); + + /* Wait for the threads to terminate. */ + for (i = 0; i < THREAD_COUNT; i++) + gl_thread_join (threads[i], NULL); + reclock_checker_done = 1; + gl_thread_join (checkerthread, NULL); + check_accounts (); +} + + +/* ------------------------ Test once-only execution ------------------------ */ + +/* Test once-only execution by having several threads attempt to grab a + once-only task simultaneously (triggered by releasing a read-write lock). */ + +gl_once_define(static, fresh_once) +static int ready[THREAD_COUNT]; +static gl_lock_t ready_lock[THREAD_COUNT]; +#if ENABLE_LOCKING +static gl_rwlock_t fire_signal[REPEAT_COUNT]; +#else +static volatile int fire_signal_state; +#endif +static gl_once_t once_control; +static int performed; +gl_lock_define_initialized(static, performed_lock) + +static void +once_execute (void) +{ + gl_lock_lock (performed_lock); + performed++; + gl_lock_unlock (performed_lock); +} + +static void * +once_contender_thread (void *arg) +{ + int id = (int) (long) arg; + int repeat; + + for (repeat = 0; repeat <= REPEAT_COUNT; repeat++) + { + /* Tell the main thread that we're ready. */ + gl_lock_lock (ready_lock[id]); + ready[id] = 1; + gl_lock_unlock (ready_lock[id]); + + if (repeat == REPEAT_COUNT) + break; + + dbgprintf ("Contender %p waiting for signal for round %d\n", + gl_thread_self_pointer (), repeat); +#if ENABLE_LOCKING + /* Wait for the signal to go. */ + gl_rwlock_rdlock (fire_signal[repeat]); + /* And don't hinder the others (if the scheduler is unfair). */ + gl_rwlock_unlock (fire_signal[repeat]); +#else + /* Wait for the signal to go. */ + while (fire_signal_state <= repeat) + yield (); +#endif + dbgprintf ("Contender %p got the signal for round %d\n", + gl_thread_self_pointer (), repeat); + + /* Contend for execution. */ + gl_once (once_control, once_execute); + } + + return NULL; +} + +static void +test_once (void) +{ + int i, repeat; + gl_thread_t threads[THREAD_COUNT]; + + /* Initialize all variables. */ + for (i = 0; i < THREAD_COUNT; i++) + { + ready[i] = 0; + gl_lock_init (ready_lock[i]); + } +#if ENABLE_LOCKING + for (i = 0; i < REPEAT_COUNT; i++) + gl_rwlock_init (fire_signal[i]); +#else + fire_signal_state = 0; +#endif + + /* Block all fire_signals. */ + for (i = REPEAT_COUNT-1; i >= 0; i--) + gl_rwlock_wrlock (fire_signal[i]); + + /* Spawn the threads. */ + for (i = 0; i < THREAD_COUNT; i++) + threads[i] = gl_thread_create (once_contender_thread, (void *) (long) i); + + for (repeat = 0; repeat <= REPEAT_COUNT; repeat++) + { + /* Wait until every thread is ready. */ + dbgprintf ("Main thread before synchonizing for round %d\n", repeat); + for (;;) + { + int ready_count = 0; + for (i = 0; i < THREAD_COUNT; i++) + { + gl_lock_lock (ready_lock[i]); + ready_count += ready[i]; + gl_lock_unlock (ready_lock[i]); + } + if (ready_count == THREAD_COUNT) + break; + yield (); + } + dbgprintf ("Main thread after synchonizing for round %d\n", repeat); + + if (repeat > 0) + { + /* Check that exactly one thread executed the once_execute() + function. */ + if (performed != 1) + abort (); + } + + if (repeat == REPEAT_COUNT) + break; + + /* Preparation for the next round: Initialize once_control. */ + memcpy (&once_control, &fresh_once, sizeof (gl_once_t)); + + /* Preparation for the next round: Reset the performed counter. */ + performed = 0; + + /* Preparation for the next round: Reset the ready flags. */ + for (i = 0; i < THREAD_COUNT; i++) + { + gl_lock_lock (ready_lock[i]); + ready[i] = 0; + gl_lock_unlock (ready_lock[i]); + } + + /* Signal all threads simultaneously. */ + dbgprintf ("Main thread giving signal for round %d\n", repeat); +#if ENABLE_LOCKING + gl_rwlock_unlock (fire_signal[repeat]); +#else + fire_signal_state = repeat + 1; +#endif + } + + /* Wait for the threads to terminate. */ + for (i = 0; i < THREAD_COUNT; i++) + gl_thread_join (threads[i], NULL); +} + + +/* -------------------------------------------------------------------------- */ + +int +main () +{ +#if TEST_PTH_THREADS + if (!pth_init ()) + abort (); +#endif + +#if DO_TEST_LOCK + printf ("Starting test_lock ..."); fflush (stdout); + test_lock (); + printf (" OK\n"); fflush (stdout); +#endif +#if DO_TEST_RWLOCK + printf ("Starting test_rwlock ..."); fflush (stdout); + test_rwlock (); + printf (" OK\n"); fflush (stdout); +#endif +#if DO_TEST_RECURSIVE_LOCK + printf ("Starting test_recursive_lock ..."); fflush (stdout); + test_recursive_lock (); + printf (" OK\n"); fflush (stdout); +#endif +#if DO_TEST_ONCE + printf ("Starting test_once ..."); fflush (stdout); + test_once (); + printf (" OK\n"); fflush (stdout); +#endif + + return 0; +} + +#else + +/* No multithreading available. */ + +#include <stdio.h> + +int +main () +{ + fputs ("Skipping test: multithreading not enabled\n", stderr); + return 77; +} + +#endif diff --git a/gl/tests/test-perror.c b/gl/tests/test-perror.c new file mode 100644 index 0000000000..d3751d8976 --- /dev/null +++ b/gl/tests/test-perror.c @@ -0,0 +1,37 @@ +/* Test of perror() function. + Copyright (C) 2008-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, 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, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include <config.h> + +#include <stdio.h> + +#include "signature.h" +SIGNATURE_CHECK (perror, void, (char const *)); + +#include <errno.h> + +int +main (int argc, char **argv) +{ + const char *prefix = (argc > 1 ? argv[1] : NULL); + + errno = EACCES; perror (prefix); + errno = ETIMEDOUT; perror (prefix); + errno = EOVERFLOW; perror (prefix); + + return 0; +} diff --git a/gl/tests/test-perror.sh b/gl/tests/test-perror.sh new file mode 100755 index 0000000000..7274d3223f --- /dev/null +++ b/gl/tests/test-perror.sh @@ -0,0 +1,24 @@ +#!/bin/sh +: ${srcdir=.} +. "$srcdir/init.sh"; path_prepend_ . + +# Test NULL prefix. Result should not contain a number. +test-perror 2>&1 >/dev/null | LC_ALL=C tr -d '\r' > t-perror.tmp +grep '[0-9]' t-perror.tmp > /dev/null \ + && fail_ "result should not contain a number" + +# Test empty prefix. Result should be the same. +test-perror '' 2>&1 >/dev/null | LC_ALL=C tr -d '\r' > t-perror1.tmp +diff t-perror.tmp t-perror1.tmp \ + || fail_ "empty prefix should behave like NULL argument" + +# Test non-empty prefix. +test-perror foo 2>&1 >/dev/null | LC_ALL=C tr -d '\r' > t-perror3.tmp +sed -e 's/^/foo: /' < t-perror.tmp > t-perror2.tmp +diff t-perror2.tmp t-perror3.tmp || fail_ "prefix applied incorrectly" + +# Test exit status. +test-perror >out 2>/dev/null || fail_ "unexpected exit status" +test -s out && fail_ "unexpected output" + +Exit 0 diff --git a/gl/tests/test-perror2.c b/gl/tests/test-perror2.c new file mode 100644 index 0000000000..e230f92320 --- /dev/null +++ b/gl/tests/test-perror2.c @@ -0,0 +1,137 @@ +/* Test of perror() function. + 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, 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, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include <config.h> + +#include <stdio.h> + +#include <errno.h> +#include <string.h> +#include <unistd.h> + +/* This test intentionally parses stderr. So, we arrange to have fd 10 + (outside the range of interesting fd's during the test) set up to + duplicate the original stderr. */ +#define BACKUP_STDERR_FILENO 10 +#define ASSERT_STREAM myerr +#include "macros.h" + +static FILE *myerr; + +#define BASE "test-perror2" + +int +main (void) +{ + /* We change fd 2 later, so save it in fd 10. */ + if (dup2 (STDERR_FILENO, BACKUP_STDERR_FILENO) != BACKUP_STDERR_FILENO + || (myerr = fdopen (BACKUP_STDERR_FILENO, "w")) == NULL) + return 2; + + ASSERT (freopen (BASE ".tmp", "w+", stderr) == stderr); + + /* Test that perror does not clobber strerror buffer. */ + { + const char *msg1; + const char *msg2; + const char *msg3; + const char *msg4; + char *str1; + char *str2; + char *str3; + char *str4; + + msg1 = strerror (ENOENT); + ASSERT (msg1); + str1 = strdup (msg1); + ASSERT (str1); + + msg2 = strerror (ERANGE); + ASSERT (msg2); + str2 = strdup (msg2); + ASSERT (str2); + + msg3 = strerror (-4); + ASSERT (msg3); + str3 = strdup (msg3); + ASSERT (str3); + + msg4 = strerror (1729576); + ASSERT (msg4); + str4 = strdup (msg4); + ASSERT (str4); + + errno = EACCES; + perror (""); + errno = -5; + perror (""); + ASSERT (!ferror (stderr)); + ASSERT (msg1 == msg2 || msg1 == msg4 || STREQ (msg1, str1)); + ASSERT (msg2 == msg4 || STREQ (msg2, str2)); + ASSERT (msg3 == msg4 || STREQ (msg3, str3)); + ASSERT (STREQ (msg4, str4)); + + free (str1); + free (str2); + free (str3); + free (str4); + } + + /* Test that perror uses the same message as strerror. */ + { + int errs[] = { EACCES, 0, -3, }; + int i; + for (i = 0; i < SIZEOF (errs); i++) + { + char buf[256]; + char *err = strerror (errs[i]); + + ASSERT (err); + ASSERT (strlen (err) < sizeof buf); + rewind (stderr); + ASSERT (ftruncate (fileno (stderr), 0) == 0); + errno = errs[i]; + perror (NULL); + ASSERT (!ferror (stderr)); + rewind (stderr); + ASSERT (fgets (buf, sizeof buf, stderr) == buf); + ASSERT (strstr (buf, err)); + } + } + + /* Test that perror reports write failure. */ + { + ASSERT (freopen (BASE ".tmp", "r", stderr) == stderr); + ASSERT (setvbuf (stderr, NULL, _IONBF, BUFSIZ) == 0); + errno = -1; + ASSERT (!ferror (stderr)); + perror (NULL); +#if 0 + /* Commented out until cygwin behaves: + http://sourceware.org/ml/newlib/2011/msg00228.html */ + ASSERT (errno > 0); + /* Commented out until glibc behaves: + http://sourceware.org/bugzilla/show_bug.cgi?id=12792 */ + ASSERT (ferror (stderr)); +#endif + } + + ASSERT (fclose (stderr) == 0); + ASSERT (remove (BASE ".tmp") == 0); + + return 0; +} diff --git a/gl/tests/test-pipe.c b/gl/tests/test-pipe.c new file mode 100644 index 0000000000..90f41f9107 --- /dev/null +++ b/gl/tests/test-pipe.c @@ -0,0 +1,105 @@ +/* Test of pipe. + Copyright (C) 2009-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, 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, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include <config.h> + +#include <unistd.h> + +#include "signature.h" +SIGNATURE_CHECK (pipe, int, (int[2])); + +#include <fcntl.h> +#include <stdbool.h> + +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ +/* Get declarations of the Win32 API functions. */ +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +/* Get _get_osfhandle. */ +# include "msvc-nothrow.h" +#endif + +#include "binary-io.h" +#include "macros.h" + +/* Return true if FD is open. */ +static bool +is_open (int fd) +{ +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ + /* On Win32, the initial state of unassigned standard file + descriptors is that they are open but point to an + INVALID_HANDLE_VALUE, and there is no fcntl. */ + return (HANDLE) _get_osfhandle (fd) != INVALID_HANDLE_VALUE; +#else +# ifndef F_GETFL +# error Please port fcntl to your platform +# endif + return 0 <= fcntl (fd, F_GETFL); +#endif +} + +/* Return true if FD is not inherited to child processes. */ +static bool +is_cloexec (int fd) +{ +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ + HANDLE h = (HANDLE) _get_osfhandle (fd); + DWORD flags; + ASSERT (GetHandleInformation (h, &flags)); + return (flags & HANDLE_FLAG_INHERIT) == 0; +#else + int flags; + ASSERT ((flags = fcntl (fd, F_GETFD)) >= 0); + return (flags & FD_CLOEXEC) != 0; +#endif +} + +/* Return true if FD is in non-blocking mode. */ +static bool +is_nonblocking (int fd) +{ +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ + /* We don't use the non-blocking mode for sockets here. */ + return 0; +#else + int flags; + ASSERT ((flags = fcntl (fd, F_GETFL)) >= 0); + return (flags & O_NONBLOCK) != 0; +#endif +} + +int +main () +{ + int fd[2]; + + fd[0] = -1; + fd[1] = -1; + ASSERT (pipe (fd) >= 0); + ASSERT (fd[0] >= 0); + ASSERT (fd[1] >= 0); + ASSERT (fd[0] != fd[1]); + ASSERT (is_open (fd[0])); + ASSERT (is_open (fd[1])); + ASSERT (!is_cloexec (fd[0])); + ASSERT (!is_cloexec (fd[1])); + ASSERT (!is_nonblocking (fd[0])); + ASSERT (!is_nonblocking (fd[1])); + + return 0; +} diff --git a/gl/tests/test-select-fd.c b/gl/tests/test-select-fd.c new file mode 100644 index 0000000000..de2f3e9c46 --- /dev/null +++ b/gl/tests/test-select-fd.c @@ -0,0 +1,72 @@ +/* Test of select() substitute, reading or writing from a given file descriptor. + Copyright (C) 2008-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 Bruno Haible <bruno@clisp.org>, 2008. */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <sys/select.h> + +int +main (int argc, char *argv[]) +{ + if (argc == 4) + { + char mode = argv[1][0]; + + if (mode == 'r' || mode == 'w') + { + int fd = atoi (argv[2]); + + if (fd >= 0) + { + const char *result_file_name = argv[3]; + FILE *result_file = fopen (result_file_name, "wb"); + + if (result_file != NULL) + { + fd_set fds; + struct timeval timeout; + int ret; + + FD_ZERO (&fds); + FD_SET (fd, &fds); + timeout.tv_sec = 0; + timeout.tv_usec = 10000; + ret = (mode == 'r' + ? select (fd + 1, &fds, NULL, NULL, &timeout) + : select (fd + 1, NULL, &fds, NULL, &timeout)); + if (ret < 0) + { + perror ("select failed"); + exit (1); + } + if ((ret == 0) != ! FD_ISSET (fd, &fds)) + { + fprintf (stderr, "incorrect return value\n"); + exit (1); + } + fprintf (result_file, "%d\n", ret); + exit (0); + } + } + } + } + fprintf (stderr, "Usage: test-select-fd mode fd result-file-name\n"); + exit (1); +} diff --git a/gl/tests/test-select-in.sh b/gl/tests/test-select-in.sh new file mode 100755 index 0000000000..2a8b742268 --- /dev/null +++ b/gl/tests/test-select-in.sh @@ -0,0 +1,39 @@ +#!/bin/sh +# Test select() on file descriptors opened for reading. + +# This test is known to fail on Solaris 2.6 and older, due to its handling +# of /dev/null. + +tmpfiles="" +trap 'rm -fr $tmpfiles' 1 2 3 15 + +tmpfiles="$tmpfiles t-select-in.tmp" + +# Regular files. + +rm -f t-select-in.tmp +./test-select-fd${EXEEXT} r 0 t-select-in.tmp < ./test-select-fd${EXEEXT} +test `cat t-select-in.tmp` = "1" || exit 1 + +# Pipes. + +rm -f t-select-in.tmp +{ sleep 1; echo abc; } | \ + { ./test-select-fd${EXEEXT} r 0 t-select-in.tmp; cat > /dev/null; } +test `cat t-select-in.tmp` = "0" || exit 1 + +rm -f t-select-in.tmp +echo abc | { sleep 1; ./test-select-fd${EXEEXT} r 0 t-select-in.tmp; } +test `cat t-select-in.tmp` = "1" || exit 1 + +# Special files. +# This part of the test is known to fail on Solaris 2.6 and older. + +# Doesn't work under mingw -- simon@josefsson.org +#rm -f t-select-in.tmp +#./test-select-fd${EXEEXT} r 0 t-select-in.tmp < /dev/null +#test `cat t-select-in.tmp` = "1" || exit 1 + +rm -fr $tmpfiles + +exit 0 diff --git a/gl/tests/test-select-out.sh b/gl/tests/test-select-out.sh new file mode 100755 index 0000000000..c5fd8619f6 --- /dev/null +++ b/gl/tests/test-select-out.sh @@ -0,0 +1,35 @@ +#!/bin/sh +# Test select() on file descriptors opened for writing. + +tmpfiles="" +trap 'rm -fr $tmpfiles' 1 2 3 15 + +tmpfiles="$tmpfiles t-select-out.out t-select-out.tmp" + +# Regular files. + +rm -f t-select-out.tmp +./test-select-fd${EXEEXT} w 1 t-select-out.tmp > t-select-out.out +test `cat t-select-out.tmp` = "1" || exit 1 + +# Pipes. + +if false; then # This test fails on some platforms. + rm -f t-select-out.tmp + ( { echo abc; ./test-select-fd${EXEEXT} w 1 t-select-out.tmp; } | { sleep 1; cat; } ) > /dev/null + test `cat t-select-out.tmp` = "0" || exit 1 +fi + +rm -f t-select-out.tmp +( { sleep 1; echo abc; ./test-select-fd${EXEEXT} w 1 t-select-out.tmp; } | cat) > /dev/null +test `cat t-select-out.tmp` = "1" || exit 1 + +# Special files. + +rm -f t-select-out.tmp +./test-select-fd${EXEEXT} w 1 t-select-out.tmp > /dev/null +test `cat t-select-out.tmp` = "1" || exit 1 + +rm -fr $tmpfiles + +exit 0 diff --git a/gl/tests/test-select-stdin.c b/gl/tests/test-select-stdin.c new file mode 100644 index 0000000000..5a6f81a86a --- /dev/null +++ b/gl/tests/test-select-stdin.c @@ -0,0 +1,83 @@ +/* Test of select() substitute, reading from stdin. + Copyright (C) 2008-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 Bruno Haible <bruno@clisp.org>, 2008. */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <sys/select.h> +#include <sys/time.h> +#include <unistd.h> + +#include "macros.h" + +int +main (void) +{ + printf ("Applying select() from standard input. Press Ctrl-C to abort.\n"); + for (;;) + { + struct timeval before; + struct timeval after; + unsigned long spent_usec; + fd_set readfds; + struct timeval timeout; + int ret; + + gettimeofday (&before, NULL); + + FD_ZERO (&readfds); + FD_SET (0, &readfds); + timeout.tv_sec = 0; + timeout.tv_usec = 500000; + ret = select (1, &readfds, NULL, NULL, &timeout); + + gettimeofday (&after, NULL); + spent_usec = (after.tv_sec - before.tv_sec) * 1000000 + + after.tv_usec - before.tv_usec; + + if (ret < 0) + { + perror ("select failed"); + exit (1); + } + if ((ret == 0) != ! FD_ISSET (0, &readfds)) + { + fprintf (stderr, "incorrect return value\n"); + exit (1); + } + if (ret == 0) + { + if (spent_usec < 250000) + { + fprintf (stderr, "returned too early\n"); + exit (1); + } + /* Timeout */ + printf ("."); + ASSERT (fflush (stdout) == 0); + } + else + { + char c; + + printf ("Input available! Trying to read 1 byte...\n"); + ASSERT (read (0, &c, 1) == 1); + } + } +} diff --git a/gl/tests/test-select.c b/gl/tests/test-select.c new file mode 100644 index 0000000000..9c6fb9dbc7 --- /dev/null +++ b/gl/tests/test-select.c @@ -0,0 +1,34 @@ +/* Test of select() substitute. + Copyright (C) 2008-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 Paolo Bonzini, 2008. */ + +#include <config.h> + +#include <sys/select.h> + +#include "signature.h" + +SIGNATURE_CHECK (select, int, (int, fd_set *, fd_set *, fd_set *, + struct timeval *)); + +#include "test-select.h" + +int +main (void) +{ + return test_function (select); +} diff --git a/gl/tests/test-select.h b/gl/tests/test-select.h new file mode 100644 index 0000000000..4612dfc719 --- /dev/null +++ b/gl/tests/test-select.h @@ -0,0 +1,436 @@ +/* Test of select() substitute. + Copyright (C) 2008-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 Paolo Bonzini, 2008. */ + +#include <stdio.h> +#include <string.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdbool.h> +#include <sys/ioctl.h> +#include <errno.h> + +#include "macros.h" + +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ +# define WIN32_NATIVE +#endif + +#ifdef HAVE_SYS_WAIT_H +# include <sys/wait.h> +#endif + +#ifndef SO_REUSEPORT +# define SO_REUSEPORT SO_REUSEADDR +#endif + +#define TEST_PORT 12345 + + +typedef int (*select_fn) (int, fd_set *, fd_set *, fd_set *, struct timeval *); + + +/* Minimal testing infrastructure. */ + +static int failures; + +static void +failed (const char *reason) +{ + if (++failures > 1) + printf (" "); + printf ("failed (%s)\n", reason); +} + +static int +test (void (*fn) (select_fn), select_fn my_select, const char *msg) +{ + failures = 0; + printf ("%s... ", msg); + fflush (stdout); + fn (my_select); + + if (!failures) + printf ("passed\n"); + + return failures; +} + + +/* Funny socket code. */ + +static int +open_server_socket (void) +{ + int s, x; + struct sockaddr_in ia; + + s = socket (AF_INET, SOCK_STREAM, 0); + + memset (&ia, 0, sizeof (ia)); + ia.sin_family = AF_INET; + inet_pton (AF_INET, "127.0.0.1", &ia.sin_addr); + ia.sin_port = htons (TEST_PORT); + if (bind (s, (struct sockaddr *) &ia, sizeof (ia)) < 0) + { + perror ("bind"); + exit (77); + } + + x = 1; + setsockopt (s, SOL_SOCKET, SO_REUSEPORT, &x, sizeof (x)); + + if (listen (s, 1) < 0) + { + perror ("listen"); + exit (77); + } + + return s; +} + +static int +connect_to_socket (bool blocking) +{ + int s; + struct sockaddr_in ia; + + s = socket (AF_INET, SOCK_STREAM, 0); + + memset (&ia, 0, sizeof (ia)); + ia.sin_family = AF_INET; + inet_pton (AF_INET, "127.0.0.1", &ia.sin_addr); + ia.sin_port = htons (TEST_PORT); + + if (!blocking) + { +#ifdef WIN32_NATIVE + unsigned long iMode = 1; + ioctl (s, FIONBIO, (char *) &iMode); + +#elif defined F_GETFL + int oldflags = fcntl (s, F_GETFL, NULL); + + if (!(oldflags & O_NONBLOCK)) + fcntl (s, F_SETFL, oldflags | O_NONBLOCK); +#endif + } + + if (connect (s, (struct sockaddr *) &ia, sizeof (ia)) < 0 + && (blocking || errno != EINPROGRESS)) + { + perror ("connect"); + exit (77); + } + + return s; +} + + +/* A slightly more convenient interface to select(2). + Waits until a specific event occurs on a file descriptor FD. + EV is a bit mask of events to look for: + SEL_IN - input can be polled without blocking, + SEL_OUT - output can be provided without blocking, + SEL_EXC - an exception occurred, + A maximum wait time is specified by TIMEOUT. + *TIMEOUT = { 0, 0 } means to return immediately, + TIMEOUT = NULL means to wait indefinitely. */ + +enum { SEL_IN = 1, SEL_OUT = 2, SEL_EXC = 4 }; + +static int +do_select (int fd, int ev, struct timeval *timeout, select_fn my_select) +{ + fd_set rfds, wfds, xfds; + int r, rev; + + FD_ZERO (&rfds); + FD_ZERO (&wfds); + FD_ZERO (&xfds); + if (ev & SEL_IN) + FD_SET (fd, &rfds); + if (ev & SEL_OUT) + FD_SET (fd, &wfds); + if (ev & SEL_EXC) + FD_SET (fd, &xfds); + r = my_select (fd + 1, &rfds, &wfds, &xfds, timeout); + if (r < 0) + return r; + + rev = 0; + if (FD_ISSET (fd, &rfds)) + rev |= SEL_IN; + if (FD_ISSET (fd, &wfds)) + rev |= SEL_OUT; + if (FD_ISSET (fd, &xfds)) + rev |= SEL_EXC; + if (rev && r == 0) + failed ("select returned 0"); + if (rev & ~ev) + failed ("select returned unrequested events"); + + return rev; +} + +static int +do_select_nowait (int fd, int ev, select_fn my_select) +{ + struct timeval tv0; + tv0.tv_sec = 0; + tv0.tv_usec = 0; + return do_select (fd, ev, &tv0, my_select); +} + +static int +do_select_wait (int fd, int ev, select_fn my_select) +{ + return do_select (fd, ev, NULL, my_select); +} + + +/* Test select(2) for TTYs. */ + +#ifdef INTERACTIVE +static void +test_tty (select_fn my_select) +{ + if (do_select_nowait (0, SEL_IN, my_select) != 0) + failed ("can read"); + if (do_select_nowait (0, SEL_OUT, my_select) == 0) + failed ("cannot write"); + + if (do_select_wait (0, SEL_IN, my_select) == 0) + failed ("return with infinite timeout"); + + getchar (); + if (do_select_nowait (0, SEL_IN, my_select) != 0) + failed ("can read after getc"); +} +#endif + + +/* Test select(2) on invalid file descriptors. */ + +static int +do_select_bad_fd (int fd, int ev, struct timeval *timeout, select_fn my_select) +{ + fd_set rfds, wfds, xfds; + + FD_ZERO (&rfds); + FD_ZERO (&wfds); + FD_ZERO (&xfds); + if (ev & SEL_IN) + FD_SET (fd, &rfds); + if (ev & SEL_OUT) + FD_SET (fd, &wfds); + if (ev & SEL_EXC) + FD_SET (fd, &xfds); + return my_select (fd + 1, &rfds, &wfds, &xfds, timeout); + /* In this case, when fd is invalid, on some platforms, the bit for fd + is left alone in the fd_set, whereas on other platforms it is cleared. + So, don't check the bit for fd here. */ +} + +static int +do_select_bad_fd_nowait (int fd, int ev, select_fn my_select) +{ + struct timeval tv0; + tv0.tv_sec = 0; + tv0.tv_usec = 0; + return do_select_bad_fd (fd, ev, &tv0, my_select); +} + +static void +test_bad_fd (select_fn my_select) +{ + /* This tests fails on OSF/1 and native Windows, even with fd = 16. */ +#if !(defined __osf__ || defined WIN32_NATIVE) + int fd; + + /* On Linux, MacOS X, *BSD, values of fd like 99 or 399 are discarded + by the kernel early and therefore do *not* lead to EBADF, as required + by POSIX. */ +# if defined __linux__ || (defined __APPLE__ && defined __MACH__) || defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__ + fd = 16; +# else + fd = 99; +# endif + + if (do_select_bad_fd_nowait (fd, SEL_IN, my_select) == 0 || errno != EBADF) + failed ("invalid fd among rfds"); + if (do_select_bad_fd_nowait (fd, SEL_OUT, my_select) == 0 || errno != EBADF) + failed ("invalid fd among wfds"); + if (do_select_bad_fd_nowait (fd, SEL_EXC, my_select) == 0 || errno != EBADF) + failed ("invalid fd among xfds"); +#endif +} + + +/* Test select(2) for unconnected nonblocking sockets. */ + +static void +test_connect_first (select_fn my_select) +{ + int s = open_server_socket (); + struct sockaddr_in ia; + socklen_t addrlen; + + int c1, c2; + + if (do_select_nowait (s, SEL_IN | SEL_EXC, my_select) != 0) + failed ("can read, socket not connected"); + + c1 = connect_to_socket (false); + + if (do_select_wait (s, SEL_IN | SEL_EXC, my_select) != SEL_IN) + failed ("expecting readability on passive socket"); + if (do_select_nowait (s, SEL_IN | SEL_EXC, my_select) != SEL_IN) + failed ("expecting readability on passive socket"); + + addrlen = sizeof (ia); + c2 = accept (s, (struct sockaddr *) &ia, &addrlen); + ASSERT (close (s) == 0); + ASSERT (close (c1) == 0); + ASSERT (close (c2) == 0); +} + + +/* Test select(2) for unconnected blocking sockets. */ + +static void +test_accept_first (select_fn my_select) +{ +#ifndef WIN32_NATIVE + int s = open_server_socket (); + struct sockaddr_in ia; + socklen_t addrlen; + char buf[3]; + int c, pid; + + pid = fork (); + if (pid < 0) + return; + + if (pid == 0) + { + addrlen = sizeof (ia); + c = accept (s, (struct sockaddr *) &ia, &addrlen); + ASSERT (close (s) == 0); + ASSERT (write (c, "foo", 3) == 3); + ASSERT (read (c, buf, 3) == 3); + shutdown (c, SHUT_RD); + ASSERT (close (c) == 0); + exit (0); + } + else + { + ASSERT (close (s) == 0); + c = connect_to_socket (true); + if (do_select_nowait (c, SEL_OUT, my_select) != SEL_OUT) + failed ("cannot write after blocking connect"); + ASSERT (write (c, "foo", 3) == 3); + wait (&pid); + if (do_select_wait (c, SEL_IN, my_select) != SEL_IN) + failed ("cannot read data left in the socket by closed process"); + ASSERT (read (c, buf, 3) == 3); + ASSERT (write (c, "foo", 3) == 3); + (void) close (c); /* may fail with errno = ECONNRESET */ + } +#endif +} + + +/* Common code for pipes and connected sockets. */ + +static void +test_pair (int rd, int wd, select_fn my_select) +{ + char buf[3]; + if (do_select_wait (wd, SEL_IN | SEL_OUT | SEL_EXC, my_select) != SEL_OUT) + failed ("expecting writability before writing"); + if (do_select_nowait (wd, SEL_IN | SEL_OUT | SEL_EXC, my_select) != SEL_OUT) + failed ("expecting writability before writing"); + + ASSERT (write (wd, "foo", 3) == 3); + if (do_select_wait (rd, SEL_IN, my_select) != SEL_IN) + failed ("expecting readability after writing"); + if (do_select_nowait (rd, SEL_IN, my_select) != SEL_IN) + failed ("expecting readability after writing"); + + ASSERT (read (rd, buf, 3) == 3); +} + + +/* Test select(2) on connected sockets. */ + +static void +test_socket_pair (select_fn my_select) +{ + struct sockaddr_in ia; + + socklen_t addrlen = sizeof (ia); + int s = open_server_socket (); + int c1 = connect_to_socket (false); + int c2 = accept (s, (struct sockaddr *) &ia, &addrlen); + + ASSERT (close (s) == 0); + + test_pair (c1, c2, my_select); + ASSERT (close (c1) == 0); + ASSERT (write (c2, "foo", 3) == 3); + (void) close (c2); /* may fail with errno = ECONNRESET */ +} + + +/* Test select(2) on pipes. */ + +static void +test_pipe (select_fn my_select) +{ + int fd[2]; + + ASSERT (pipe (fd) == 0); + test_pair (fd[0], fd[1], my_select); + ASSERT (close (fd[0]) == 0); + ASSERT (close (fd[1]) == 0); +} + + +/* Do them all. */ + +static int +test_function (select_fn my_select) +{ + int result = 0; + +#ifdef INTERACTIVE + printf ("Please press Enter\n"); + test (test_tty, "TTY", my_select); +#endif + + result += test (test_bad_fd, my_select, "Invalid fd test"); + result += test (test_connect_first, my_select, "Unconnected socket test"); + result += test (test_socket_pair, my_select, "Connected sockets test"); + result += test (test_accept_first, my_select, "General socket test with fork"); + result += test (test_pipe, my_select, "Pipe test"); + + return result; +} diff --git a/gl/tests/test-signal-h.c b/gl/tests/test-signal-h.c new file mode 100644 index 0000000000..7103e1ff19 --- /dev/null +++ b/gl/tests/test-signal-h.c @@ -0,0 +1,129 @@ +/* Test of <signal.h> substitute. + Copyright (C) 2009-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>, 2009. */ + +#include <config.h> + +#include <signal.h> + +/* Check for required types. */ +struct +{ + size_t a; + uid_t b; + volatile sig_atomic_t c; + sigset_t d; + pid_t e; +#if 0 + /* Not guaranteed by gnulib. */ + pthread_t f; + struct timespec g; +#endif +} s; + +/* Check that NSIG is defined. */ +int nsig = NSIG; + +int +main (void) +{ + switch (0) + { + /* The following are guaranteed by C. */ + case 0: + case SIGABRT: + case SIGFPE: + case SIGILL: + case SIGINT: + case SIGSEGV: + case SIGTERM: + /* The following is guaranteed by gnulib. */ +#if GNULIB_SIGPIPE || defined SIGPIPE + case SIGPIPE: +#endif + /* Ensure no conflict with other standardized names. */ +#ifdef SIGALRM + case SIGALRM: +#endif + /* On Haiku, SIGBUS is mistakenly equal to SIGSEGV. */ +#if defined SIGBUS && SIGBUS != SIGSEGV + case SIGBUS: +#endif +#ifdef SIGCHLD + case SIGCHLD: +#endif +#ifdef SIGCONT + case SIGCONT: +#endif +#ifdef SIGHUP + case SIGHUP: +#endif +#ifdef SIGKILL + case SIGKILL: +#endif +#ifdef SIGQUIT + case SIGQUIT: +#endif +#ifdef SIGSTOP + case SIGSTOP: +#endif +#ifdef SIGTSTP + case SIGTSTP: +#endif +#ifdef SIGTTIN + case SIGTTIN: +#endif +#ifdef SIGTTOU + case SIGTTOU: +#endif +#ifdef SIGUSR1 + case SIGUSR1: +#endif +#ifdef SIGUSR2 + case SIGUSR2: +#endif +#ifdef SIGSYS + case SIGSYS: +#endif +#ifdef SIGTRAP + case SIGTRAP: +#endif +#ifdef SIGURG + case SIGURG: +#endif +#ifdef SIGVTALRM + case SIGVTALRM: +#endif +#ifdef SIGXCPU + case SIGXCPU: +#endif +#ifdef SIGXFSZ + case SIGXFSZ: +#endif + /* SIGRTMIN and SIGRTMAX need not be compile-time constants. */ +#if 0 +# ifdef SIGRTMIN + case SIGRTMIN: +# endif +# ifdef SIGRTMAX + case SIGRTMAX: +# endif +#endif + ; + } + return s.a + s.b + s.c + s.e; +} diff --git a/gl/tests/test-strerror_r.c b/gl/tests/test-strerror_r.c new file mode 100644 index 0000000000..956c458d7b --- /dev/null +++ b/gl/tests/test-strerror_r.c @@ -0,0 +1,182 @@ +/* Test of strerror_r() function. + Copyright (C) 2007-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, 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, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include <config.h> + +#include <string.h> + +#include "signature.h" +SIGNATURE_CHECK (strerror_r, int, (int, char *, size_t)); + +#include <errno.h> + +#include "macros.h" + +int +main (void) +{ + char buf[100]; + int ret; + + /* Test results with valid errnum and enough room. */ + + errno = 0; + buf[0] = '\0'; + ASSERT (strerror_r (EACCES, buf, sizeof buf) == 0); + ASSERT (buf[0] != '\0'); + ASSERT (errno == 0); + ASSERT (strlen (buf) < sizeof buf); + + errno = 0; + buf[0] = '\0'; + ASSERT (strerror_r (ETIMEDOUT, buf, sizeof buf) == 0); + ASSERT (buf[0] != '\0'); + ASSERT (errno == 0); + ASSERT (strlen (buf) < sizeof buf); + + errno = 0; + buf[0] = '\0'; + ASSERT (strerror_r (EOVERFLOW, buf, sizeof buf) == 0); + ASSERT (buf[0] != '\0'); + ASSERT (errno == 0); + ASSERT (strlen (buf) < sizeof buf); + + /* POSIX requires strerror (0) to succeed. Reject use of "Unknown + error", but allow "Success", "No error", or even Solaris' "Error + 0" which are distinct patterns from true out-of-range strings. + http://austingroupbugs.net/view.php?id=382 */ + errno = 0; + buf[0] = '\0'; + ret = strerror_r (0, buf, sizeof buf); + ASSERT (ret == 0); + ASSERT (buf[0]); + ASSERT (errno == 0); + ASSERT (strstr (buf, "nknown") == NULL); + ASSERT (strstr (buf, "ndefined") == NULL); + + /* Test results with out-of-range errnum and enough room. POSIX + allows an empty string on success, and allows an unchanged buf on + error, but these are not useful, so we guarantee contents. */ + errno = 0; + buf[0] = '^'; + ret = strerror_r (-3, buf, sizeof buf); + ASSERT (ret == 0 || ret == EINVAL); + ASSERT (buf[0] != '^'); + ASSERT (*buf); + ASSERT (errno == 0); + ASSERT (strlen (buf) < sizeof buf); + + /* Test results with a too small buffer. POSIX requires an error; + only ERANGE for 0 and valid errors, and a choice of ERANGE or + EINVAL for out-of-range values. On error, POSIX permits buf to + be empty, unchanged, or unterminated, but these are not useful, + so we guarantee NUL-terminated truncated contents for all but + size 0. http://austingroupbugs.net/view.php?id=398. Also ensure + that no out-of-bounds writes occur. */ + { + int errs[] = { EACCES, 0, -3, }; + int j; + + buf[sizeof buf - 1] = '\0'; + for (j = 0; j < SIZEOF (errs); j++) + { + int err = errs[j]; + char buf2[sizeof buf] = ""; + size_t len; + size_t i; + + strerror_r (err, buf2, sizeof buf2); + len = strlen (buf2); + ASSERT (len < sizeof buf); + + for (i = 0; i <= len; i++) + { + memset (buf, '^', sizeof buf - 1); + errno = 0; + ret = strerror_r (err, buf, i); + ASSERT (errno == 0); + if (err < 0) + ASSERT (ret == ERANGE || ret == EINVAL); + else + ASSERT (ret == ERANGE); + if (i) + { + ASSERT (strncmp (buf, buf2, i - 1) == 0); + ASSERT (buf[i - 1] == '\0'); + } + ASSERT (strspn (buf + i, "^") == sizeof buf - 1 - i); + } + + strcpy (buf, "BADFACE"); + errno = 0; + ret = strerror_r (err, buf, len + 1); + ASSERT (ret != ERANGE); + ASSERT (errno == 0); + ASSERT (strcmp (buf, buf2) == 0); + } + } + +#if GNULIB_STRERROR + /* Test that strerror_r does not clobber strerror buffer. On some + platforms, this test can only succeed if gnulib also replaces + strerror. */ + { + const char *msg1; + const char *msg2; + const char *msg3; + const char *msg4; + char *str1; + char *str2; + char *str3; + char *str4; + + msg1 = strerror (ENOENT); + ASSERT (msg1); + str1 = strdup (msg1); + ASSERT (str1); + + msg2 = strerror (ERANGE); + ASSERT (msg2); + str2 = strdup (msg2); + ASSERT (str2); + + msg3 = strerror (-4); + ASSERT (msg3); + str3 = strdup (msg3); + ASSERT (str3); + + msg4 = strerror (1729576); + ASSERT (msg4); + str4 = strdup (msg4); + ASSERT (str4); + + strerror_r (EACCES, buf, sizeof buf); + strerror_r (-5, buf, sizeof buf); + ASSERT (msg1 == msg2 || msg1 == msg4 || STREQ (msg1, str1)); + ASSERT (msg2 == msg4 || STREQ (msg2, str2)); + ASSERT (msg3 == msg4 || STREQ (msg3, str3)); + ASSERT (STREQ (msg4, str4)); + + free (str1); + free (str2); + free (str3); + free (str4); + } +#endif + + return 0; +} diff --git a/gl/tests/test-sys_ioctl.c b/gl/tests/test-sys_ioctl.c new file mode 100644 index 0000000000..dd01b4aadd --- /dev/null +++ b/gl/tests/test-sys_ioctl.c @@ -0,0 +1,27 @@ +/* Test of <sys/ioctl.h> substitute. + Copyright (C) 2009-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>, 2009. */ + +#include <config.h> + +#include <sys/ioctl.h> + +int +main (void) +{ + return 0; +} diff --git a/gl/tests/test-sys_select.c b/gl/tests/test-sys_select.c new file mode 100644 index 0000000000..76596dba30 --- /dev/null +++ b/gl/tests/test-sys_select.c @@ -0,0 +1,55 @@ +/* Test of <sys/select.h> substitute. + Copyright (C) 2007-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 Bruno Haible <bruno@clisp.org>, 2007. */ + +#include <config.h> + +#include <sys/select.h> + +#include "signature.h" + +/* The following may be macros without underlying functions, so only + check signature if they are not macros. */ +#ifndef FD_CLR +SIGNATURE_CHECK (FD_CLR, void, (int, fd_set *)); +#endif +#ifndef FD_ISSET +SIGNATURE_CHECK (FD_ISSET, void, (int, fd_set *)); +#endif +#ifndef FD_SET +SIGNATURE_CHECK (FD_SET, int, (int, fd_set *)); +#endif +#ifndef FD_ZERO +SIGNATURE_CHECK (FD_ZERO, void, (fd_set *)); +#endif + +/* Check that the 'struct timeval' type is defined. */ +struct timeval t1; + +/* Check that sigset_t is defined. */ +sigset_t t2; + +int +main (void) +{ + /* Check that FD_ZERO can be used. This should not yield a warning + such as "warning: implicit declaration of function 'memset'". */ + fd_set fds; + FD_ZERO (&fds); + + return 0; +} diff --git a/gl/tests/test-thread_create.c b/gl/tests/test-thread_create.c new file mode 100644 index 0000000000..1348c8c3c6 --- /dev/null +++ b/gl/tests/test-thread_create.c @@ -0,0 +1,78 @@ +/* Test of gl_thread_create () macro. + 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 Bruno Haible <bruno@clisp.org>, 2011. */ + +#include <config.h> + +#include "glthread/thread.h" + +#include <stdio.h> +#include <string.h> + +#include "macros.h" + +static gl_thread_t main_thread_before; +static gl_thread_t main_thread_after; +static gl_thread_t worker_thread; + +static int dummy; +static volatile int work_done; + +static void * +worker_thread_func (void *arg) +{ + work_done = 1; + return &dummy; +} + +int +main () +{ + main_thread_before = gl_thread_self (); + + if (glthread_create (&worker_thread, worker_thread_func, NULL) == 0) + { + void *ret; + + /* Check that gl_thread_self () has the same value before than after the + first call to gl_thread_create (). */ + main_thread_after = gl_thread_self (); + ASSERT (memcmp (&main_thread_before, &main_thread_after, + sizeof (gl_thread_t)) + == 0); + + gl_thread_join (worker_thread, &ret); + + /* Check the return value of the thread. */ + ASSERT (ret == &dummy); + + /* Check that worker_thread_func () has finished executing. */ + ASSERT (work_done); + + return 0; + } + else + { +#if USE_POSIX_THREADS || USE_SOLARIS_THREADS || USE_PTH_THREADS || USE_WIN32_THREADS + fputs ("glthread_create failed\n", stderr); + return 1; +#else + fputs ("Skipping test: multithreading not enabled\n", stderr); + return 77; +#endif + } +} diff --git a/gl/tests/test-thread_self.c b/gl/tests/test-thread_self.c new file mode 100644 index 0000000000..816ee09680 --- /dev/null +++ b/gl/tests/test-thread_self.c @@ -0,0 +1,34 @@ +/* Test of gl_thread_self () macro. + 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 Bruno Haible <bruno@clisp.org>, 2011. */ + +#include <config.h> + +#include "glthread/thread.h" + +gl_thread_t main_thread; + +int +main () +{ + /* Check that gl_thread_self () can be used with just $(LIBTHREAD), not + $(LIBMULTITHREAD), i.e. in libraries that are multithread-safe but don't + create threads themselves. */ + main_thread = gl_thread_self (); + + return 0; +} diff --git a/gl/tests/w32sock.h b/gl/tests/w32sock.h new file mode 100644 index 0000000000..0b8334e4ec --- /dev/null +++ b/gl/tests/w32sock.h @@ -0,0 +1,136 @@ +/* w32sock.h --- internal auxilliary functions for Windows socket functions + + Copyright (C) 2008-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 Paolo Bonzini */ + +#include <errno.h> + +/* Get O_RDWR and O_BINARY. */ +#include <fcntl.h> + +/* Get _open_osfhandle(). */ +#include <io.h> + +/* Get _get_osfhandle(). */ +#include "msvc-nothrow.h" + +#define FD_TO_SOCKET(fd) ((SOCKET) _get_osfhandle ((fd))) +#define SOCKET_TO_FD(fh) (_open_osfhandle ((long) (fh), O_RDWR | O_BINARY)) + +static inline void +set_winsock_errno (void) +{ + int err = WSAGetLastError (); + + /* Map some WSAE* errors to the runtime library's error codes. */ + switch (err) + { + case WSA_INVALID_HANDLE: + errno = EBADF; + break; + case WSA_NOT_ENOUGH_MEMORY: + errno = ENOMEM; + break; + case WSA_INVALID_PARAMETER: + errno = EINVAL; + break; + case WSAENAMETOOLONG: + errno = ENAMETOOLONG; + break; + case WSAENOTEMPTY: + errno = ENOTEMPTY; + break; + case WSAEWOULDBLOCK: + errno = EWOULDBLOCK; + break; + case WSAEINPROGRESS: + errno = EINPROGRESS; + break; + case WSAEALREADY: + errno = EALREADY; + break; + case WSAENOTSOCK: + errno = ENOTSOCK; + break; + case WSAEDESTADDRREQ: + errno = EDESTADDRREQ; + break; + case WSAEMSGSIZE: + errno = EMSGSIZE; + break; + case WSAEPROTOTYPE: + errno = EPROTOTYPE; + break; + case WSAENOPROTOOPT: + errno = ENOPROTOOPT; + break; + case WSAEPROTONOSUPPORT: + errno = EPROTONOSUPPORT; + break; + case WSAEOPNOTSUPP: + errno = EOPNOTSUPP; + break; + case WSAEAFNOSUPPORT: + errno = EAFNOSUPPORT; + break; + case WSAEADDRINUSE: + errno = EADDRINUSE; + break; + case WSAEADDRNOTAVAIL: + errno = EADDRNOTAVAIL; + break; + case WSAENETDOWN: + errno = ENETDOWN; + break; + case WSAENETUNREACH: + errno = ENETUNREACH; + break; + case WSAENETRESET: + errno = ENETRESET; + break; + case WSAECONNABORTED: + errno = ECONNABORTED; + break; + case WSAECONNRESET: + errno = ECONNRESET; + break; + case WSAENOBUFS: + errno = ENOBUFS; + break; + case WSAEISCONN: + errno = EISCONN; + break; + case WSAENOTCONN: + errno = ENOTCONN; + break; + case WSAETIMEDOUT: + errno = ETIMEDOUT; + break; + case WSAECONNREFUSED: + errno = ECONNREFUSED; + break; + case WSAELOOP: + errno = ELOOP; + break; + case WSAEHOSTUNREACH: + errno = EHOSTUNREACH; + break; + default: + errno = (err > 10000 && err < 10025) ? err - 10000 : err; + break; + } +} |