diff options
author | Werner Koch <[email protected]> | 2014-01-09 18:14:09 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2014-01-16 08:58:22 +0000 |
commit | ff937c39febe63d52c55590d8e3bd3a460f26651 (patch) | |
tree | 5eb58c4075c5e8981c13fca80db99ed8f9679742 | |
parent | Improve maintainability by rewriting the mkheader helper. (diff) | |
download | libgpg-error-ff937c39febe63d52c55590d8e3bd3a460f26651.tar.gz libgpg-error-ff937c39febe63d52c55590d8e3bd3a460f26651.zip |
Add gpgrt_lock_ functions.
* src/gpg-error.h.in (GPGRT_LOCK_DEFINE): New.
(gpgrt_lock_init): New.
(gpgrt_lock_lock): New.
(gpgrt_lock_unlock): New.
(gpgrt_lock_destroy): New.
(gpgrt_yield): New.
* src/gpg-error.def.in: Add new functions.
* m4/lock.m4, m4/threadlib.m4: New. Taken from current gnulib.
* configure.ac: Call gl_LOCK. Check size of pthread_mutex_t. Add
LIBTHREAD to GPG_ERROR_CONFIG_LIBS.
* src/err-codes.h.in (GPG_ERR_INV_LOCK_OBJ): New.
* src/gen-posix-lock-obj.c: New.
* src/gen-w32-lock-obj.c: New.
* src/lock.h, src/thread.h: New.
* src/posix-lock-obj.h, src/w32-lock-obj.h: New.
* src/posix-lock.c, src/w32-lock.c: New.
* src/posix-thread.c, src/w32-thread.c:
* src/w32-lock-obj-pub.in: New.
* src/mkheader.c (include_file): Support build time include files.
(write_special): Add keyword "include:lock-obj".
* src/Makefile.am:
(posix-lock-obj-pub.in): New rule.
(noinst_PROGRAMS): Add gen-*-lock-obj helpers.
* tests/t-common.h: New.
* tests/t-lock.c: New.
* tests/Makefile.am (t_lock_LDADD): Add new test.
--
This patch introduces the gpgrt_ functions which will be extended over
time to provide a library of commonly used code in GnuPG and
Libgcrypt. Having them in a library named libgpg-error is a misnomer
but this way we can achieve a smooth upgrade path.
In contrasts to other GnuPG libraries, the gpgrt_ functions return a
simple gpg_err_code_t and not gpg_error_t. The rationale for this is
that a source of error identifier does not make sense here; it is
better to use the source of error identifier of the caller. This can
easily be achieved in a component by wrapping these function in a
gpg_error macro/inline.
There is no cross-compiling support for Posix platforms; the
gen-posix-lock-obj tool must be run on the target system.
Note that the gen-w32-lock-obj tool is not needed at build time but
was used to figure out ABI definitions for Windows.
Signed-off-by: Werner Koch <[email protected]>
-rw-r--r-- | NEWS | 13 | ||||
-rw-r--r-- | configure.ac | 18 | ||||
-rw-r--r-- | m4/lock.m4 | 301 | ||||
-rw-r--r-- | m4/threadlib.m4 | 341 | ||||
-rw-r--r-- | src/Makefile.am | 44 | ||||
-rw-r--r-- | src/err-codes.h.in | 6 | ||||
-rw-r--r-- | src/gen-posix-lock-obj.c | 95 | ||||
-rw-r--r-- | src/gen-w32-lock-obj.c | 55 | ||||
-rw-r--r-- | src/gpg-error.def.in | 6 | ||||
-rw-r--r-- | src/gpg-error.h.in | 35 | ||||
-rw-r--r-- | src/lock.h | 24 | ||||
-rw-r--r-- | src/mkheader.c | 18 | ||||
-rw-r--r-- | src/posix-lock-obj.h | 32 | ||||
-rw-r--r-- | src/posix-lock.c | 213 | ||||
-rw-r--r-- | src/posix-thread.c | 63 | ||||
-rw-r--r-- | src/thread.h | 24 | ||||
-rw-r--r-- | src/version.c | 4 | ||||
-rw-r--r-- | src/w32-lock-obj-pub.in | 44 | ||||
-rw-r--r-- | src/w32-lock-obj.h | 38 | ||||
-rw-r--r-- | src/w32-lock.c | 119 | ||||
-rw-r--r-- | src/w32-thread.c | 44 | ||||
-rw-r--r-- | tests/Makefile.am | 9 | ||||
-rw-r--r-- | tests/t-common.h | 99 | ||||
-rw-r--r-- | tests/t-lock.c | 318 |
24 files changed, 1665 insertions, 298 deletions
@@ -1,9 +1,20 @@ Noteworthy changes in version 1.13 (unreleased) ----------------------------------------------- - * Interface changes relative to the 1.11 release: + Add a portable mutex API. + + * Interface changes relative to the 1.12 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GPG_ERR_MAC_ALGO NEW. + GPG_ERR_INV_LOCK_OBJ NEW. + gpgrt_lock_t NEW. + GPGRT_LOCK_INITIALIZER NEW. + GPGRT_LOCK_DEFINE NEW. + gpgrt_lock_init NEW. + gpgrt_lock_lock NEW. + gpgrt_lock_unlock NEW. + gpgrt_lock_destroy NEW. + gpgrt_yield NEW. Noteworthy changes in version 1.12 (2013-06-24) diff --git a/configure.ac b/configure.ac index 73de716..3d1e12f 100644 --- a/configure.ac +++ b/configure.ac @@ -85,6 +85,9 @@ AC_PROG_AWK AC_CHECK_TOOL(AR, ar, :) AC_GNU_SOURCE +gl_THREADLIB_EARLY + + LT_PREREQ([2.2.6]) LT_INIT([win32-dll disable-static]) LT_LANG([Windows Resource]) @@ -156,11 +159,26 @@ AC_MSG_WARN([[Without strerror_r, gpg_strerror_r might not be thread-safe]])) ;; esac +AC_CHECK_FUNCS([flockfile]) + + # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST + +# Check for thread library +gl_LOCK +if test "$gl_threads_api" = posix; then + AC_CHECK_SIZEOF(pthread_mutex_t,,[AC_INCLUDES_DEFAULT + #include <pthread.h>]) +fi + + # Substitution used for gpg-error-config GPG_ERROR_CONFIG_LIBS="-lgpg-error" +if test "x$LIBTHREAD" != x; then + GPG_ERROR_CONFIG_LIBS="${GPG_ERROR_CONFIG_LIBS} ${LIBTHREAD}" +fi GPG_ERROR_CONFIG_CFLAGS="" GPG_ERROR_CONFIG_ISUBDIRAFTER="" GPG_ERROR_CONFIG_HOST="$host" @@ -1,289 +1,42 @@ -# lock.m4 serial 2 (gettext-0.15) -dnl Copyright (C) 2005 Free Software Foundation, Inc. +# lock.m4 serial 13 (gettext-0.18.2) +dnl Copyright (C) 2005-2014 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Bruno Haible. -dnl Tests for a multithreading library to be used. -dnl Defines at most one of the macros USE_POSIX_THREADS, USE_SOLARIS_THREADS, -dnl USE_PTH_THREADS, USE_WIN32_THREADS -dnl Sets the variables LIBTHREAD and LTLIBTHREAD to the linker options for use -dnl in a Makefile (LIBTHREAD for use without libtool, LTLIBTHREAD for use with -dnl libtool). -dnl Sets the variables LIBMULTITHREAD and LTLIBMULTITHREAD similarly, for -dnl programs that really need multithread functionality. The difference -dnl between LIBTHREAD and LIBMULTITHREAD is that on platforms supporting weak -dnl symbols, typically LIBTHREAD="" whereas LIBMULTITHREAD="-lpthread". -dnl Adds to CPPFLAGS the flag -D_REENTRANT or -D_THREAD_SAFE if needed for -dnl multithread-safe programs. - AC_DEFUN([gl_LOCK], [ - AC_REQUIRE([gl_LOCK_BODY]) -]) - -dnl The guts of gl_LOCK. Needs to be expanded only once. - -AC_DEFUN([gl_LOCK_BODY], -[ - dnl Ordering constraints: This macro modifies CPPFLAGS in a way that - dnl influences the result of the autoconf tests that test for *_unlocked - dnl declarations, on AIX 5 at least. Therefore it must come early. - AC_BEFORE([$0], [gl_FUNC_GLIBC_UNLOCKED_IO])dnl - AC_BEFORE([$0], [gl_ARGP])dnl - - AC_REQUIRE([AC_CANONICAL_HOST]) - AC_REQUIRE([AC_GNU_SOURCE]) dnl needed for pthread_rwlock_t on glibc systems - dnl Check for multithreading. - AC_ARG_ENABLE(threads, -AC_HELP_STRING([--enable-threads={posix|solaris|pth|win32}], [specify multithreading API]) -AC_HELP_STRING([--disable-threads], [build without multithread safety]), - gl_use_threads=$enableval, gl_use_threads=yes) - gl_threads_api=none - LIBTHREAD= - LTLIBTHREAD= - LIBMULTITHREAD= - LTLIBMULTITHREAD= - if test "$gl_use_threads" != no; then - dnl Check whether the compiler and linker support weak declarations. - AC_MSG_CHECKING([whether imported symbols can be declared weak]) - gl_have_weak=no - AC_TRY_LINK([extern void xyzzy (); -#pragma weak xyzzy], [xyzzy();], [gl_have_weak=yes]) - AC_MSG_RESULT([$gl_have_weak]) - if test "$gl_use_threads" = yes || test "$gl_use_threads" = posix; then - # On OSF/1, the compiler needs the flag -pthread or -D_REENTRANT so that - # it groks <pthread.h>. - gl_save_CPPFLAGS="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS -D_REENTRANT" - AC_CHECK_HEADER(pthread.h, gl_have_pthread_h=yes, gl_have_pthread_h=no) - CPPFLAGS="$gl_save_CPPFLAGS" - if test "$gl_have_pthread_h" = yes; then - # Other possible tests: - # -lpthreads (FSU threads, PCthreads) - # -lgthreads - case "$host_os" in - osf*) - # On OSF/1, the compiler needs the flag -D_REENTRANT so that it - # groks <pthread.h>. cc also understands the flag -pthread, but - # we don't use it because 1. gcc-2.95 doesn't understand -pthread, - # 2. putting a flag into CPPFLAGS that has an effect on the linker - # causes the AC_TRY_LINK test below to succeed unexpectedly, - # leading to wrong values of LIBTHREAD and LTLIBTHREAD. - CPPFLAGS="$CPPFLAGS -D_REENTRANT" - ;; - esac - gl_have_pthread= - # Test whether both pthread_mutex_lock and pthread_mutexattr_init exist - # in libc. IRIX 6.5 has the first one in both libc and libpthread, but - # the second one only in libpthread, and lock.c needs it. - AC_TRY_LINK([#include <pthread.h>], - [pthread_mutex_lock((pthread_mutex_t*)0); - pthread_mutexattr_init((pthread_mutexattr_t*)0);], - [gl_have_pthread=yes]) - # Test for libpthread by looking for pthread_kill. (Not pthread_self, - # since it is defined as a macro on OSF/1.) - if test -n "$gl_have_pthread"; then - # The program links fine without libpthread. But it may actually - # need to link with libpthread in order to create multiple threads. - AC_CHECK_LIB(pthread, pthread_kill, - [LIBMULTITHREAD=-lpthread LTLIBMULTITHREAD=-lpthread - # On Solaris and HP-UX, most pthread functions exist also in libc. - # Therefore pthread_in_use() needs to actually try to create a - # thread: pthread_create from libc will fail, whereas - # pthread_create will actually create a thread. - case "$host_os" in - solaris* | hpux*) - AC_DEFINE([PTHREAD_IN_USE_DETECTION_HARD], 1, - [Define if the pthread_in_use() detection is hard.]) - esac - ]) - else - # Some library is needed. Try libpthread and libc_r. - AC_CHECK_LIB(pthread, pthread_kill, - [gl_have_pthread=yes - LIBTHREAD=-lpthread LTLIBTHREAD=-lpthread - LIBMULTITHREAD=-lpthread LTLIBMULTITHREAD=-lpthread]) - if test -z "$gl_have_pthread"; then - # For FreeBSD 4. - AC_CHECK_LIB(c_r, pthread_kill, - [gl_have_pthread=yes - LIBTHREAD=-lc_r LTLIBTHREAD=-lc_r - LIBMULTITHREAD=-lc_r LTLIBMULTITHREAD=-lc_r]) - fi - fi - if test -n "$gl_have_pthread"; then - gl_threads_api=posix - AC_DEFINE([USE_POSIX_THREADS], 1, - [Define if the POSIX multithreading library can be used.]) - if test -n "$LIBMULTITHREAD" || test -n "$LTLIBMULTITHREAD"; then - if test $gl_have_weak = yes; then - AC_DEFINE([USE_POSIX_THREADS_WEAK], 1, - [Define if references to the POSIX multithreading library should be made weak.]) - LIBTHREAD= - LTLIBTHREAD= - fi - fi - # OSF/1 4.0 and MacOS X 10.1 lack the pthread_rwlock_t type and the - # pthread_rwlock_* functions. - AC_CHECK_TYPE([pthread_rwlock_t], - [AC_DEFINE([HAVE_PTHREAD_RWLOCK], 1, - [Define if the POSIX multithreading library has read/write locks.])], - [], - [#include <pthread.h>]) - # glibc defines PTHREAD_MUTEX_RECURSIVE as enum, not as a macro. - AC_TRY_COMPILE([#include <pthread.h>], - [#if __FreeBSD__ == 4 + AC_REQUIRE([gl_THREADLIB]) + if test "$gl_threads_api" = posix; then + # OSF/1 4.0 and Mac OS X 10.1 lack the pthread_rwlock_t type and the + # pthread_rwlock_* functions. + AC_CHECK_TYPE([pthread_rwlock_t], + [AC_DEFINE([HAVE_PTHREAD_RWLOCK], [1], + [Define if the POSIX multithreading library has read/write locks.])], + [], + [#include <pthread.h>]) + # glibc defines PTHREAD_MUTEX_RECURSIVE as enum, not as a macro. + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM( + [[#include <pthread.h>]], + [[ +#if __FreeBSD__ == 4 error "No, in FreeBSD 4.0 recursive mutexes actually don't work." +#elif (defined __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ \ + && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070) +error "No, in Mac OS X < 10.7 recursive mutexes actually don't work." #else int x = (int)PTHREAD_MUTEX_RECURSIVE; -#endif], - [AC_DEFINE([HAVE_PTHREAD_MUTEX_RECURSIVE], 1, - [Define if the <pthread.h> defines PTHREAD_MUTEX_RECURSIVE.])]) - # Some systems optimize for single-threaded programs by default, and - # need special flags to disable these optimizations. For example, the - # definition of 'errno' in <errno.h>. - case "$host_os" in - aix* | freebsd*) CPPFLAGS="$CPPFLAGS -D_THREAD_SAFE" ;; - solaris*) CPPFLAGS="$CPPFLAGS -D_REENTRANT" ;; - esac - fi - fi - fi - if test -z "$gl_have_pthread"; then - if test "$gl_use_threads" = yes || test "$gl_use_threads" = solaris; then - gl_have_solaristhread= - gl_save_LIBS="$LIBS" - LIBS="$LIBS -lthread" - AC_TRY_LINK([#include <thread.h> -#include <synch.h>], - [thr_self();], - [gl_have_solaristhread=yes]) - LIBS="$gl_save_LIBS" - if test -n "$gl_have_solaristhread"; then - gl_threads_api=solaris - LIBTHREAD=-lthread - LTLIBTHREAD=-lthread - LIBMULTITHREAD="$LIBTHREAD" - LTLIBMULTITHREAD="$LTLIBTHREAD" - AC_DEFINE([USE_SOLARIS_THREADS], 1, - [Define if the old Solaris multithreading library can be used.]) - if test $gl_have_weak = yes; then - AC_DEFINE([USE_SOLARIS_THREADS_WEAK], 1, - [Define if references to the old Solaris multithreading library should be made weak.]) - LIBTHREAD= - LTLIBTHREAD= - fi - fi - fi - fi - if test "$gl_use_threads" = pth; then - gl_save_CPPFLAGS="$CPPFLAGS" - AC_LIB_LINKFLAGS(pth) - gl_have_pth= - gl_save_LIBS="$LIBS" - LIBS="$LIBS -lpth" - AC_TRY_LINK([#include <pth.h>], [pth_self();], gl_have_pth=yes) - LIBS="$gl_save_LIBS" - if test -n "$gl_have_pth"; then - gl_threads_api=pth - LIBTHREAD="$LIBPTH" - LTLIBTHREAD="$LTLIBPTH" - LIBMULTITHREAD="$LIBTHREAD" - LTLIBMULTITHREAD="$LTLIBTHREAD" - AC_DEFINE([USE_PTH_THREADS], 1, - [Define if the GNU Pth multithreading library can be used.]) - if test -n "$LIBMULTITHREAD" || test -n "$LTLIBMULTITHREAD"; then - if test $gl_have_weak = yes; then - AC_DEFINE([USE_PTH_THREADS_WEAK], 1, - [Define if references to the GNU Pth multithreading library should be made weak.]) - LIBTHREAD= - LTLIBTHREAD= - fi - fi - else - CPPFLAGS="$gl_save_CPPFLAGS" - fi - fi - if test -z "$gl_have_pthread"; then - if test "$gl_use_threads" = yes || test "$gl_use_threads" = win32; then - if { case "$host_os" in - mingw*) true;; - *) false;; - esac - }; then - gl_threads_api=win32 - AC_DEFINE([USE_WIN32_THREADS], 1, - [Define if the Win32 multithreading API can be used.]) - fi - fi - fi +return !x; +#endif + ]])], + [AC_DEFINE([HAVE_PTHREAD_MUTEX_RECURSIVE], [1], + [Define if the <pthread.h> defines PTHREAD_MUTEX_RECURSIVE.])]) fi - AC_MSG_CHECKING([for multithread API to use]) - AC_MSG_RESULT([$gl_threads_api]) - AC_SUBST(LIBTHREAD) - AC_SUBST(LTLIBTHREAD) - AC_SUBST(LIBMULTITHREAD) - AC_SUBST(LTLIBMULTITHREAD) gl_PREREQ_LOCK ]) -# Prerequisites of lib/lock.c. -AC_DEFUN([gl_PREREQ_LOCK], [ - AC_REQUIRE([AC_C_INLINE]) -]) - -dnl Survey of platforms: -dnl -dnl Platform Available Compiler Supports test-lock -dnl flavours option weak result -dnl --------------- --------- --------- -------- --------- -dnl Linux 2.4/glibc posix -lpthread Y OK -dnl -dnl GNU Hurd/glibc posix -dnl -dnl FreeBSD 5.3 posix -lc_r Y -dnl posix -lkse ? Y -dnl posix -lpthread ? Y -dnl posix -lthr Y -dnl -dnl FreeBSD 5.2 posix -lc_r Y -dnl posix -lkse Y -dnl posix -lthr Y -dnl -dnl FreeBSD 4.0,4.10 posix -lc_r Y OK -dnl -dnl NetBSD 1.6 -- -dnl -dnl OpenBSD 3.4 posix -lpthread Y OK -dnl -dnl MacOS X 10.[123] posix -lpthread Y OK -dnl -dnl Solaris 7,8,9 posix -lpthread Y Sol 7,8: 0.0; Sol 9: OK -dnl solaris -lthread Y Sol 7,8: 0.0; Sol 9: OK -dnl -dnl HP-UX 11 posix -lpthread N (cc) OK -dnl Y (gcc) -dnl -dnl IRIX 6.5 posix -lpthread Y 0.5 -dnl -dnl AIX 4.3,5.1 posix -lpthread N AIX 4: 0.5; AIX 5: OK -dnl -dnl OSF/1 4.0,5.1 posix -pthread (cc) N OK -dnl -lpthread (gcc) Y -dnl -dnl Cygwin posix -lpthread Y OK -dnl -dnl Any of the above pth -lpth 0.0 -dnl -dnl Mingw win32 N OK -dnl -dnl BeOS 5 -- -dnl -dnl The test-lock result shows what happens if in test-lock.c EXPLICIT_YIELD is -dnl turned off: -dnl OK if all three tests terminate OK, -dnl 0.5 if the first test terminates OK but the second one loops endlessly, -dnl 0.0 if the first test already loops endlessly. +# Prerequisites of lib/glthread/lock.c. +AC_DEFUN([gl_PREREQ_LOCK], [:]) diff --git a/m4/threadlib.m4 b/m4/threadlib.m4 new file mode 100644 index 0000000..a91e819 --- /dev/null +++ b/m4/threadlib.m4 @@ -0,0 +1,341 @@ +# threadlib.m4 serial 10 (gettext-0.18.2) modified by wk 2014-01-15. +dnl Copyright (C) 2005-2014 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +dnl gl_THREADLIB +dnl ------------ +dnl Tests for a multithreading library to be used. +dnl If the configure.ac contains a definition of the gl_THREADLIB_DEFAULT_NO +dnl (it must be placed before the invocation of gl_THREADLIB_EARLY!), then the +dnl default is 'no', otherwise it is system dependent. In both cases, the user +dnl can change the choice through the options --enable-threads=choice or +dnl --disable-threads. +dnl Defines at most one of the macros USE_POSIX_THREADS, USE_SOLARIS_THREADS, +dnl USE_WINDOWS_THREADS +dnl Sets the variables LIBTHREAD and LTLIBTHREAD to the linker options for use +dnl in a Makefile (LIBTHREAD for use without libtool, LTLIBTHREAD for use with +dnl libtool). +dnl Sets the variables LIBMULTITHREAD and LTLIBMULTITHREAD similarly, for +dnl programs that really need multithread functionality. The difference +dnl between LIBTHREAD and LIBMULTITHREAD is that on platforms supporting weak +dnl symbols, typically LIBTHREAD="" whereas LIBMULTITHREAD="-lpthread". +dnl Adds to CPPFLAGS the flag -D_REENTRANT or -D_THREAD_SAFE if needed for +dnl multithread-safe programs. + +AC_DEFUN([gl_THREADLIB_EARLY], +[ + AC_REQUIRE([gl_THREADLIB_EARLY_BODY]) +]) + +dnl The guts of gl_THREADLIB_EARLY. Needs to be expanded only once. + +AC_DEFUN([gl_THREADLIB_EARLY_BODY], +[ + dnl Ordering constraints: This macro modifies CPPFLAGS in a way that + dnl influences the result of the autoconf tests that test for *_unlocked + dnl declarations, on AIX 5 at least. Therefore it must come early. + AC_BEFORE([$0], [gl_FUNC_GLIBC_UNLOCKED_IO])dnl + AC_BEFORE([$0], [gl_ARGP])dnl + + AC_REQUIRE([AC_CANONICAL_HOST]) + dnl _GNU_SOURCE is needed for pthread_rwlock_t on glibc systems. + dnl AC_USE_SYSTEM_EXTENSIONS was introduced in autoconf 2.60 and obsoletes + dnl AC_GNU_SOURCE. + m4_ifdef([AC_USE_SYSTEM_EXTENSIONS], + [AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])], + [AC_REQUIRE([AC_GNU_SOURCE])]) + dnl Check for multithreading. + m4_ifdef([gl_THREADLIB_DEFAULT_NO], + [m4_divert_text([DEFAULTS], [gl_use_threads_default=no])], + [m4_divert_text([DEFAULTS], [gl_use_threads_default=])]) + AC_ARG_ENABLE([threads], +AC_HELP_STRING([--enable-threads={posix|solaris|windows}], [specify multithreading API])m4_ifdef([gl_THREADLIB_DEFAULT_NO], [], [ +AC_HELP_STRING([--disable-threads], [build without multithread safety])]), + [gl_use_threads=$enableval], + [if test -n "$gl_use_threads_default"; then + gl_use_threads="$gl_use_threads_default" + else +changequote(,)dnl + case "$host_os" in + dnl Disable multithreading by default on OSF/1, because it interferes + dnl with fork()/exec(): When msgexec is linked with -lpthread, its + dnl child process gets an endless segmentation fault inside execvp(). + dnl Disable multithreading by default on Cygwin 1.5.x, because it has + dnl bugs that lead to endless loops or crashes. See + dnl <http://cygwin.com/ml/cygwin/2009-08/msg00283.html>. + osf*) gl_use_threads=no ;; + cygwin*) + case `uname -r` in + 1.[0-5].*) gl_use_threads=no ;; + *) gl_use_threads=yes ;; + esac + ;; + *) gl_use_threads=yes ;; + esac +changequote([,])dnl + fi + ]) + if test "$gl_use_threads" = yes || test "$gl_use_threads" = posix; then + # For using <pthread.h>: + case "$host_os" in + osf*) + # On OSF/1, the compiler needs the flag -D_REENTRANT so that it + # groks <pthread.h>. cc also understands the flag -pthread, but + # we don't use it because 1. gcc-2.95 doesn't understand -pthread, + # 2. putting a flag into CPPFLAGS that has an effect on the linker + # causes the AC_LINK_IFELSE test below to succeed unexpectedly, + # leading to wrong values of LIBTHREAD and LTLIBTHREAD. + CPPFLAGS="$CPPFLAGS -D_REENTRANT" + ;; + esac + # Some systems optimize for single-threaded programs by default, and + # need special flags to disable these optimizations. For example, the + # definition of 'errno' in <errno.h>. + case "$host_os" in + aix* | freebsd*) CPPFLAGS="$CPPFLAGS -D_THREAD_SAFE" ;; + solaris*) CPPFLAGS="$CPPFLAGS -D_REENTRANT" ;; + esac + fi +]) + +dnl The guts of gl_THREADLIB. Needs to be expanded only once. + +AC_DEFUN([gl_THREADLIB_BODY], +[ + AC_REQUIRE([gl_THREADLIB_EARLY_BODY]) + gl_threads_api=none + LIBTHREAD= + LTLIBTHREAD= + LIBMULTITHREAD= + LTLIBMULTITHREAD= + if test "$gl_use_threads" != no; then + dnl Check whether the compiler and linker support weak declarations. + AC_CACHE_CHECK([whether imported symbols can be declared weak], + [gl_cv_have_weak], + [gl_cv_have_weak=no + dnl First, test whether the compiler accepts it syntactically. + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[extern void xyzzy (); +#pragma weak xyzzy]], + [[xyzzy();]])], + [gl_cv_have_weak=maybe]) + if test $gl_cv_have_weak = maybe; then + dnl Second, test whether it actually works. On Cygwin 1.7.2, with + dnl gcc 4.3, symbols declared weak always evaluate to the address 0. + AC_RUN_IFELSE( + [AC_LANG_SOURCE([[ +#include <stdio.h> +#pragma weak fputs +int main () +{ + return (fputs == NULL); +}]])], + [gl_cv_have_weak=yes], + [gl_cv_have_weak=no], + [dnl When cross-compiling, assume that only ELF platforms support + dnl weak symbols. + AC_EGREP_CPP([Extensible Linking Format], + [#ifdef __ELF__ + Extensible Linking Format + #endif + ], + [gl_cv_have_weak="guessing yes"], + [gl_cv_have_weak="guessing no"]) + ]) + fi + ]) + if test "$gl_use_threads" = yes || test "$gl_use_threads" = posix; then + # On OSF/1, the compiler needs the flag -pthread or -D_REENTRANT so that + # it groks <pthread.h>. It's added above, in gl_THREADLIB_EARLY_BODY. + AC_CHECK_HEADER([pthread.h], + [gl_have_pthread_h=yes], [gl_have_pthread_h=no]) + if test "$gl_have_pthread_h" = yes; then + # Other possible tests: + # -lpthreads (FSU threads, PCthreads) + # -lgthreads + gl_have_pthread= + # Test whether both pthread_mutex_lock and pthread_mutexattr_init exist + # in libc. IRIX 6.5 has the first one in both libc and libpthread, but + # the second one only in libpthread, and lock.c needs it. + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[#include <pthread.h>]], + [[pthread_mutex_lock((pthread_mutex_t*)0); + pthread_mutexattr_init((pthread_mutexattr_t*)0);]])], + [gl_have_pthread=yes]) + # Test for libpthread by looking for pthread_kill. (Not pthread_self, + # since it is defined as a macro on OSF/1.) + if test -n "$gl_have_pthread"; then + # The program links fine without libpthread. But it may actually + # need to link with libpthread in order to create multiple threads. + AC_CHECK_LIB([pthread], [pthread_kill], + [LIBMULTITHREAD=-lpthread LTLIBMULTITHREAD=-lpthread + # On Solaris and HP-UX, most pthread functions exist also in libc. + # Therefore pthread_in_use() needs to actually try to create a + # thread: pthread_create from libc will fail, whereas + # pthread_create will actually create a thread. + case "$host_os" in + solaris* | hpux*) + AC_DEFINE([PTHREAD_IN_USE_DETECTION_HARD], [1], + [Define if the pthread_in_use() detection is hard.]) + esac + ]) + else + # Some library is needed. Try libpthread and libc_r. + AC_CHECK_LIB([pthread], [pthread_kill], + [gl_have_pthread=yes + LIBTHREAD=-lpthread LTLIBTHREAD=-lpthread + LIBMULTITHREAD=-lpthread LTLIBMULTITHREAD=-lpthread]) + if test -z "$gl_have_pthread"; then + # For FreeBSD 4. + AC_CHECK_LIB([c_r], [pthread_kill], + [gl_have_pthread=yes + LIBTHREAD=-lc_r LTLIBTHREAD=-lc_r + LIBMULTITHREAD=-lc_r LTLIBMULTITHREAD=-lc_r]) + fi + fi + if test -n "$gl_have_pthread"; then + gl_threads_api=posix + AC_DEFINE([USE_POSIX_THREADS], [1], + [Define if the POSIX multithreading library can be used.]) + if test -n "$LIBMULTITHREAD" || test -n "$LTLIBMULTITHREAD"; then + if case "$gl_cv_have_weak" in *yes) true;; *) false;; esac; then + AC_DEFINE([USE_POSIX_THREADS_WEAK], [1], + [Define if references to the POSIX multithreading library should be made weak.]) + LIBTHREAD= + LTLIBTHREAD= + fi + fi + fi + fi + fi + if test -z "$gl_have_pthread"; then + if test "$gl_use_threads" = yes || test "$gl_use_threads" = solaris; then + gl_have_solaristhread= + gl_save_LIBS="$LIBS" + LIBS="$LIBS -lthread" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[ +#include <thread.h> +#include <synch.h> + ]], + [[thr_self();]])], + [gl_have_solaristhread=yes]) + LIBS="$gl_save_LIBS" + if test -n "$gl_have_solaristhread"; then + gl_threads_api=solaris + LIBTHREAD=-lthread + LTLIBTHREAD=-lthread + LIBMULTITHREAD="$LIBTHREAD" + LTLIBMULTITHREAD="$LTLIBTHREAD" + AC_DEFINE([USE_SOLARIS_THREADS], [1], + [Define if the old Solaris multithreading library can be used.]) + if case "$gl_cv_have_weak" in *yes) true;; *) false;; esac; then + AC_DEFINE([USE_SOLARIS_THREADS_WEAK], [1], + [Define if references to the old Solaris multithreading library should be made weak.]) + LIBTHREAD= + LTLIBTHREAD= + fi + fi + fi + fi + if test -z "$gl_have_pthread"; then + case "$gl_use_threads" in + yes | windows | win32) # The 'win32' is for backward compatibility. + if { case "$host_os" in + mingw*) true;; + *) false;; + esac + }; then + gl_threads_api=windows + AC_DEFINE([USE_WINDOWS_THREADS], [1], + [Define if the native Windows multithreading API can be used.]) + fi + ;; + esac + fi + fi + AC_MSG_CHECKING([for multithread API to use]) + AC_MSG_RESULT([$gl_threads_api]) + AC_SUBST([LIBTHREAD]) + AC_SUBST([LTLIBTHREAD]) + AC_SUBST([LIBMULTITHREAD]) + AC_SUBST([LTLIBMULTITHREAD]) +]) + +AC_DEFUN([gl_THREADLIB], +[ + AC_REQUIRE([gl_THREADLIB_EARLY]) + AC_REQUIRE([gl_THREADLIB_BODY]) +]) + + +dnl gl_DISABLE_THREADS +dnl ------------------ +dnl Sets the gl_THREADLIB default so that threads are not used by default. +dnl The user can still override it at installation time, by using the +dnl configure option '--enable-threads'. + +AC_DEFUN([gl_DISABLE_THREADS], [ + m4_divert_text([INIT_PREPARE], [gl_use_threads_default=no]) +]) + + +dnl Survey of platforms: +dnl +dnl Platform Available Compiler Supports test-lock +dnl flavours option weak result +dnl --------------- --------- --------- -------- --------- +dnl Linux 2.4/glibc posix -lpthread Y OK +dnl +dnl GNU Hurd/glibc posix +dnl +dnl FreeBSD 5.3 posix -lc_r Y +dnl posix -lkse ? Y +dnl posix -lpthread ? Y +dnl posix -lthr Y +dnl +dnl FreeBSD 5.2 posix -lc_r Y +dnl posix -lkse Y +dnl posix -lthr Y +dnl +dnl FreeBSD 4.0,4.10 posix -lc_r Y OK +dnl +dnl NetBSD 1.6 -- +dnl +dnl OpenBSD 3.4 posix -lpthread Y OK +dnl +dnl Mac OS X 10.[123] posix -lpthread Y OK +dnl +dnl Solaris 7,8,9 posix -lpthread Y Sol 7,8: 0.0; Sol 9: OK +dnl solaris -lthread Y Sol 7,8: 0.0; Sol 9: OK +dnl +dnl HP-UX 11 posix -lpthread N (cc) OK +dnl Y (gcc) +dnl +dnl IRIX 6.5 posix -lpthread Y 0.5 +dnl +dnl AIX 4.3,5.1 posix -lpthread N AIX 4: 0.5; AIX 5: OK +dnl +dnl OSF/1 4.0,5.1 posix -pthread (cc) N OK +dnl -lpthread (gcc) Y +dnl +dnl Cygwin posix -lpthread Y OK +dnl +dnl Any of the above pth -lpth 0.0 +dnl +dnl Mingw windows N OK +dnl +dnl BeOS 5 -- +dnl +dnl The test-lock result shows what happens if in test-lock.c EXPLICIT_YIELD is +dnl turned off: +dnl OK if all three tests terminate OK, +dnl 0.5 if the first test terminates OK but the second one loops endlessly, +dnl 0.0 if the first test already loops endlessly. diff --git a/src/Makefile.am b/src/Makefile.am index 74d89be..51b2ea9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -30,7 +30,15 @@ extra_cppflags = endif localedir = $(datadir)/locale + bin_PROGRAMS = gpg-error + +if HAVE_W32_SYSTEM +noinst_PROGRAMS = gen-w32-lock-obj +else +noinst_PROGRAMS = gen-posix-lock-obj +endif + lib_LTLIBRARIES = libgpg-error.la include_HEADERS = gpg-error.h bin_SCRIPTS = gpg-error-config @@ -42,7 +50,8 @@ EXTRA_DIST = mkstrtable.awk err-sources.h.in err-codes.h.in \ mkerrcodes.awk mkerrcodes1.awk mkerrcodes2.awk mkerrcodes.c \ mkheader.c gpg-error.h.in mkw32errmap.c w32-add.h w32ce-add.h \ err-sources.h err-codes.h gpg-error-config.in gpg-error.m4 \ - gpg-error.def.in versioninfo.rc.in + gpg-error.def.in versioninfo.rc.in \ + w32-lock-obj-pub.in BUILT_SOURCES = err-sources.h err-codes.h code-to-errno.h code-from-errno.h \ err-sources-sym.h err-codes-sym.h errnos-sym.h gpg-error.h \ @@ -53,11 +62,10 @@ tmp_files = _mkerrcodes.h _gpg-error.def.h mkw32errmap.tab.h mkw32errmap.map.c CLEANFILES = err-sources.h err-codes.h code-to-errno.h code-from-errno.h \ gpg-error.h mkerrcodes mkerrcodes.h gpg-error.def mkw32errmap.tab.h \ mkw32errmap.map.c err-sources-sym.h err-codes-sym.h errnos-sym.h \ - gpg-extra/errno.h mkheader $(tmp_files) + gpg-extra/errno.h mkheader $(tmp_files) posix-lock-obj-pub.in if HAVE_W32_SYSTEM -arch_sources = w32-gettext.c - +arch_sources = w32-gettext.c w32-lock.c w32-lock-obj.h w32-thread.c RCCOMPILE = $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ -DLOCALEDIR=\"$(localedir)\" $(AM_CPPFLAGS) $(CPPFLAGS) LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE) @@ -82,7 +90,7 @@ libgpg_error_la_DEPENDENCIES = $(gpg_error_res) gpg-error.def intllibs = else -arch_sources = +arch_sources = posix-lock.c posix-lock-obj.h posix-thread.c gpg_error_res = no_undefined = export_symbols = @@ -98,17 +106,22 @@ libgpg_error_la_LDFLAGS = -version-info \ @LIBGPG_ERROR_LT_CURRENT@:@LIBGPG_ERROR_LT_REVISION@:@LIBGPG_ERROR_LT_AGE@ \ $(no_undefined) $(export_symbols) -parts_of_gpg_error_h = \ - gpg-error.h.in \ - err-sources.h.in \ - err-codes.h.in \ - errnos.in \ - w32-add.h \ - w32ce-add.h +parts_of_gpg_error_h = \ + gpg-error.h.in \ + err-sources.h.in \ + err-codes.h.in \ + errnos.in \ + w32-add.h \ + w32ce-add.h \ + w32-lock-obj-pub.in + +if ! HAVE_W32_SYSTEM +parts_of_gpg_error_h += posix-lock-obj-pub.in +endif libgpg_error_la_SOURCES = gpg-error.h gettext.h $(arch_sources) \ - init.c init.h version.c \ + init.c init.h version.c lock.h thread.h \ strsource.c strerror.c code-to-errno.c code-from-errno.c # Note that RCCOMPILE needs the same defines as ..._la_CPPFLAGS but @@ -195,9 +208,14 @@ errnos-sym.h: Makefile mkstrtable.awk errnos.in -v prefix=GPG_ERR_ -v namespace=errnos_ \ $(srcdir)/errnos.in >$@ + mkheader: mkheader.c Makefile $(CC_FOR_BUILD) -g -O0 -I. -I$(srcdir) -o $@ $(srcdir)/mkheader.c + +posix-lock-obj-pub.in: Makefile gen-posix-lock-obj posix-lock-obj.h + ./gen-posix-lock-obj >$@ + # We also depend on versioninfo.rc because that is build by # config.status and thus has up-to-date version numbers. gpg-error.h: Makefile mkheader $(parts_of_gpg_error_h) versioninfo.rc diff --git a/src/err-codes.h.in b/src/err-codes.h.in index 4ecc24e..c912bcd 100644 --- a/src/err-codes.h.in +++ b/src/err-codes.h.in @@ -247,7 +247,11 @@ 212 GPG_ERR_SEXP_ODD_HEX_NUMBERS Odd hexadecimal numbers in S-expression 213 GPG_ERR_SEXP_BAD_OCT_CHAR Bad octal character in S-expression -# 214 to 254 are free to be used. 255 and 256 are RFU. +# 214 to 253 are free to be used. + +254 GPG_ERR_INV_LOCK_OBJ Invalid lock object + +# 255 and 256 are RFU. # Error codes pertaining to the Assuan IPC interface 257 GPG_ERR_ASS_GENERAL General IPC error diff --git a/src/gen-posix-lock-obj.c b/src/gen-posix-lock-obj.c new file mode 100644 index 0000000..b641fd0 --- /dev/null +++ b/src/gen-posix-lock-obj.c @@ -0,0 +1,95 @@ +/* gen-posix-lock-obj.c - Build tool to construct the lock object. + Copyright (C) 2014 g10 Code GmbH + + This file is part of libgpg-error. + + libgpg-error is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + libgpg-error 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_W32_SYSTEM +# error This module may not be build for Windows. +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <pthread.h> + +#include "posix-lock-obj.h" + +#define PGM "gen-posix-lock-obj" + +/* Check that configure did its job. */ +#if SIZEOF_PTHREAD_MUTEX_T < 4 +# error sizeof pthread_mutex_t is not known. +#endif + +static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; + + +int +main (void) +{ + unsigned char *p; + int i; + struct { + pthread_mutex_t mtx; + long vers; + } dummyobj; + + if (sizeof mtx != SIZEOF_PTHREAD_MUTEX_T) + { + fprintf (stderr, PGM ": pthread_mutex_t mismatch\n"); + exit (1); + } + + if (sizeof (dummyobj) != sizeof (_gpgrt_lock_t)) + { + fprintf (stderr, PGM ": internal and external lock object mismatch\n"); + exit (1); + } + + /* To force a probably suitable alignment of the structure we use a + union and include a long and a pointer to a long. */ + printf ("## File created by " PGM " - DO NOT EDIT\n" + "## To be included by mkheader into gpg-error.h\n" + "\n" + "typedef union\n" + "{\n" + " struct {\n" + " volatile char _priv[%d];\n" + " long _vers;\n" + " } d;\n" + " long _x_align;\n" + " long *_xp_align;\n" + "} gpgrt_lock_t;\n" + "\n" + "#define GPGRT_LOCK_INITIALIZER {{{", + SIZEOF_PTHREAD_MUTEX_T); + p = (unsigned char *)&mtx; + for (i=0; i < sizeof mtx; i++) + { + if (i && !(i % 8)) + printf (" \\\n%*s", 34, ""); + printf ("%u", p[i]); + if (i < sizeof mtx - 1) + putchar (','); + } + printf ("},%d}}\n", LOCK_ABI_VERSION); + + return 0; +} diff --git a/src/gen-w32-lock-obj.c b/src/gen-w32-lock-obj.c new file mode 100644 index 0000000..9e49d1e --- /dev/null +++ b/src/gen-w32-lock-obj.c @@ -0,0 +1,55 @@ +/* gen-w32-lock-obj.c - Build tool to get the size of the lock object. + Copyright (C) 2014 g10 Code GmbH + + This file is part of libgpg-error. + + libgpg-error is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + libgpg-error 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#ifndef HAVE_W32_SYSTEM +# error This module may only be build for Windows. +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <windows.h> + +#include "w32-lock-obj.h" + + +int +main (void) +{ + _gpgrt_lock_t lk; + unsigned char *p; + int i; + + printf ("sizeof CRITICAL_SECTION = %u\n", (int)sizeof (CRITICAL_SECTION)); + printf ("sizeof _gpgrt_lock_t = %u\n", (int)sizeof lk); + + memset (&lk, 0, sizeof lk); + lk.vers = LOCK_ABI_VERSION; + lk.started = -1; + printf ("#define GPGRT_LOCK_INITIALIZER {"); + p = (unsigned char *)&lk; + for (i=0; i < sizeof lk - 1; i++) + printf ("%u,", p[i]); + printf ("%u}\n", p[sizeof(lk)-1]); + + return 0; +} diff --git a/src/gpg-error.def.in b/src/gpg-error.def.in index 62e0681..2ea482a 100644 --- a/src/gpg-error.def.in +++ b/src/gpg-error.def.in @@ -28,3 +28,9 @@ EXPORTS #endif gpg_err_deinit @18 gpg_error_check_version @19 + + gpgrt_lock_init @20 + gpgrt_lock_lock @21 + gpgrt_lock_unlock @22 + gpgrt_lock_destroy @23 + gpgrt_yield @24 diff --git a/src/gpg-error.h.in b/src/gpg-error.h.in index a2fb044..adb796b 100644 --- a/src/gpg-error.h.in +++ b/src/gpg-error.h.in @@ -66,7 +66,10 @@ extern "C" { GPG_ERR_ENABLE_GETTEXT_MACROS: Define to provide macros to map the internal gettext API to standard names. This has only an effect on - Windows platforms. */ + Windows platforms. + + In addition to the error codes, Libgpg-error also provides a set of + functions used by most GnuPG components. */ /* The error source type gpg_err_source_t. @@ -279,9 +282,35 @@ gpg_error_from_syserror (void) return gpg_error (gpg_err_code_from_syserror ()); } + + +/* Lock functions. */ + +@include:lock-obj@ + +#define GPGRT_LOCK_DEFINE(name) \ + static gpgrt_lock_t name = GPGRT_LOCK_INITIALIZER + + +gpg_err_code_t gpgrt_lock_init (gpgrt_lock_t *lockhd); +gpg_err_code_t gpgrt_lock_lock (gpgrt_lock_t *lockhd); +gpg_err_code_t gpgrt_lock_unlock (gpgrt_lock_t *lockhd); +gpg_err_code_t gpgrt_lock_destroy (gpgrt_lock_t *lockhd); + + + +/* Thread functions. */ + +gpg_err_code_t gpgrt_yield (void); + + + + +/* Estream */ + + + #ifdef __cplusplus } #endif - - #endif /* GPG_ERROR_H */ diff --git a/src/lock.h b/src/lock.h new file mode 100644 index 0000000..b60f2c2 --- /dev/null +++ b/src/lock.h @@ -0,0 +1,24 @@ +/* lock.h - Declarations for *-lock.c + Copyright (C) 2014 g10 Code GmbH + + This file is part of libgpg-error. + + libgpg-error is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + libgpg-error 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LOCK_H +#define LOCK_H + + +#endif /*LOCK_H*/ diff --git a/src/mkheader.c b/src/mkheader.c index eea7914..e7db729 100644 --- a/src/mkheader.c +++ b/src/mkheader.c @@ -132,7 +132,8 @@ write_errnos_in (char *line) is not further expanded. It may have comments indicated by a double hash mark at the begin of a line. OUTF is called for each read line and passed a buffer with the content of line sans line - line endings. */ + line endings. If NAME is prefixed with "./" it is included from + the current directory and not from the source directory. */ static void include_file (const char *fname, int lnr, const char *name, void (*outf)(char*)) { @@ -147,7 +148,11 @@ include_file (const char *fname, int lnr, const char *name, void (*outf)(char*)) fputs (PGM ": out of core\n", stderr); exit (1); } - strcpy (incfname, srcdir); + + if (*name == '.' && name[1] == '/') + *incfname = 0; + else + strcpy (incfname, srcdir); strcat (incfname, name); fp = fopen (incfname, "r"); @@ -242,6 +247,15 @@ write_special (const char *fname, int lnr, const char *tag) include_file (fname, lnr, "w32ce-add.h", write_line); } } + else if (!strcmp (tag, "include:lock-obj")) + { + if (!strcmp (host_os, "mingw32")) + { + include_file (fname, lnr, "w32-lock-obj-pub.in", write_line); + } + else + include_file (fname, lnr, "./posix-lock-obj-pub.in", write_line); + } else return 0; /* Unknown tag. */ diff --git a/src/posix-lock-obj.h b/src/posix-lock-obj.h new file mode 100644 index 0000000..a5a9ee7 --- /dev/null +++ b/src/posix-lock-obj.h @@ -0,0 +1,32 @@ +/* posic-lock-obj.h - Declaration of the POSIX lock object + Copyright (C) 2014 g10 Code GmbH + + This file is part of libgpg-error. + + libgpg-error is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + libgpg-error 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef POSIX_LOCK_OBJ_H +#define POSIX_LOCK_OBJ_H + +#define LOCK_ABI_VERSION 1 + +typedef struct +{ + pthread_mutex_t mtx; + long vers; +} _gpgrt_lock_t; + + +#endif /*POSIX_LOCK_OBJ_H*/ diff --git a/src/posix-lock.c b/src/posix-lock.c new file mode 100644 index 0000000..46e3a84 --- /dev/null +++ b/src/posix-lock.c @@ -0,0 +1,213 @@ +/* posix-lock.c - GPGRT lock functions for POSIX systems + Copyright (C) 2005-2009 Free Software Foundation, Inc. + Copyright (C) 2014 g10 Code GmbH + + This file is part of libgpg-error. + + libgpg-error is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + libgpg-error 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, see <http://www.gnu.org/licenses/>. + + Parts of the code, in particular use_pthreads_p, are based on code + from gettext, written by Bruno Haible <[email protected]>, 2005. + */ + +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_W32_SYSTEM +# error This module may not be build for Windows. +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#if USE_POSIX_THREADS +# include <pthread.h> +#endif + +#include "gpg-error.h" +#include "lock.h" +#include "posix-lock-obj.h" + + +#if USE_POSIX_THREADS +# if USE_POSIX_THREADS_WEAK + /* On ELF systems it is easy to use pthreads using weak + references. Take care not to test the address of a weak + referenced function we actually use; some GCC versions have a + bug were &foo != NULL is always evaluated to true in PIC mode. */ +# pragma weak pthread_cancel +# pragma weak pthread_mutex_init +# pragma weak pthread_mutex_lock +# pragma weak pthread_mutex_unlock +# pragma weak pthread_mutex_destroy +# if ! PTHREAD_IN_USE_DETECTION_HARD +# define use_pthread_p() (!!pthread_cancel) +# endif +# else /*!USE_POSIX_THREADS_WEAK*/ +# if ! PTHREAD_IN_USE_DETECTION_HARD +# define use_pthread_p() (1) +# endif +# endif /*!USE_POSIX_THREADS_WEAK*/ +# if PTHREAD_IN_USE_DETECTION_HARD +/* The function to be executed by a dummy thread. */ +static void * +dummy_thread_func (void *arg) +{ + return arg; +} + +static int +use_pthread_p (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)) + result = 0; /* Thread creation failed. */ + else + { + /* Thread creation works. */ + void *retval; + if (pthread_join (thread, &retval) != 0) + abort (); + result = 1; + } + tested = 1; + } + return result; +} +#endif /*PTHREAD_IN_USE_DETECTION_HARD*/ +#endif /*USE_POSIX_THREADS*/ + + + +static _gpgrt_lock_t * +get_lock_object (gpgrt_lock_t *lockhd) +{ + _gpgrt_lock_t *lock = (_gpgrt_lock_t*)lockhd; + + if (lock->vers != LOCK_ABI_VERSION) + abort (); + if (sizeof (gpgrt_lock_t) < sizeof (_gpgrt_lock_t)) + abort (); + + return lock; +} + + +gpg_err_code_t +gpgrt_lock_init (gpgrt_lock_t *lockhd) +{ + _gpgrt_lock_t *lock = get_lock_object (lockhd); + int rc; + +#if USE_POSIX_THREADS + if (use_pthread_p()) + { + rc = pthread_mutex_init (&lock->mtx, NULL); + if (rc) + rc = gpg_err_code_from_errno (rc); + } + else + rc = 0; /* Threads are not used. */ +#else /* Unknown thread system. */ + rc = GPG_ERR_NOT_IMPLEMENTED; +#endif /* Unknown thread system. */ + + return rc; +} + + +gpg_err_code_t +gpgrt_lock_lock (gpgrt_lock_t *lockhd) +{ + _gpgrt_lock_t *lock = get_lock_object (lockhd); + int rc; + +#if USE_POSIX_THREADS + if (use_pthread_p()) + { + rc = pthread_mutex_lock (&lock->mtx); + if (rc) + rc = gpg_err_code_from_errno (rc); + } + else + rc = 0; /* Threads are not used. */ +#else /* Unknown thread system. */ + rc = GPG_ERR_NOT_IMPLEMENTED; +#endif /* Unknown thread system. */ + + return rc; +} + + +gpg_err_code_t +gpgrt_lock_unlock (gpgrt_lock_t *lockhd) +{ + _gpgrt_lock_t *lock = get_lock_object (lockhd); + int rc; + +#if USE_POSIX_THREADS + if (use_pthread_p()) + { + rc = pthread_mutex_unlock (&lock->mtx); + if (rc) + rc = gpg_err_code_from_errno (rc); + } + else + rc = 0; /* Threads are not used. */ +#else /* Unknown thread system. */ + rc = GPG_ERR_NOT_IMPLEMENTED; +#endif /* Unknown thread system. */ + + return rc; +} + + +/* Note: Use this function only if no other thread holds or waits for + this lock. */ +gpg_err_code_t +gpgrt_lock_destroy (gpgrt_lock_t *lockhd) +{ + _gpgrt_lock_t *lock = get_lock_object (lockhd); + int rc; + +#if USE_POSIX_THREADS + if (use_pthread_p()) + { + rc = pthread_mutex_destroy (&lock->mtx); + if (rc) + rc = gpg_err_code_from_errno (rc); + else + { + /* Re-init the the mutex so that it can be re-used. */ + gpgrt_lock_t tmp = GPGRT_LOCK_INITIALIZER; + memcpy (lockhd, &tmp, sizeof tmp); + } + } + else + rc = 0; /* Threads are not used. */ +#else /* Unknown thread system. */ + rc = GPG_ERR_NOT_IMPLEMENTED; +#endif /* Unknown thread system. */ + + return rc; +} diff --git a/src/posix-thread.c b/src/posix-thread.c new file mode 100644 index 0000000..a739e40 --- /dev/null +++ b/src/posix-thread.c @@ -0,0 +1,63 @@ +/* posix-thread.c - GPGRT thread functions for POSIX systems + Copyright (C) 2014 g10 Code GmbH + + This file is part of libgpg-error. + + libgpg-error is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + libgpg-error 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_W32_SYSTEM +# error This module may not be build for Windows. +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> /* Get posix option macros. */ + +#if USE_POSIX_THREADS +# ifdef _POSIX_PRIORITY_SCHEDULING +# include <sched.h> +# endif +#elif USE_SOLARIS_THREADS +# include <thread.h> +#endif + +#include "gpg-error.h" + +#include "thread.h" + + +gpg_err_code_t +gpgrt_yield (void) +{ +#if USE_POSIX_THREADS +# ifdef _POSIX_PRIORITY_SCHEDULING + sched_yield (); +# else + return GPG_ERR_NOT_SUPPORTED; +# endif +#elif USE_SOLARIS_THREADS + thr_yield (); +#else + return GPG_ERR_NOT_SUPPORTED; +#endif + + return 0; +} diff --git a/src/thread.h b/src/thread.h new file mode 100644 index 0000000..0b2cf04 --- /dev/null +++ b/src/thread.h @@ -0,0 +1,24 @@ +/* thread.h - Declarations for *-thread.c + Copyright (C) 2014 g10 Code GmbH + + This file is part of libgpg-error. + + libgpg-error is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + libgpg-error 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef THREAD_H +#define THREAD_H + + +#endif /*THREAD_H*/ diff --git a/src/version.c b/src/version.c index 36e2d58..5b40537 100644 --- a/src/version.c +++ b/src/version.c @@ -1,5 +1,5 @@ /* version.c - Version checking - * Copyright (C) 2001, 2002, 2012, 2013 g10 Code GmbH + * Copyright (C) 2001, 2002, 2012, 2013, 2014 g10 Code GmbH * * This file is part of libgpg-error. * @@ -39,7 +39,7 @@ cright_blurb (void) static const char blurb[] = "\n\n" "This is Libgpg-error " PACKAGE_VERSION " - An error code library\n" - "Copyright 2003, 2004, 2010, 2013 g10 Code GmbH\n" + "Copyright 2003, 2004, 2010, 2013, 2014 g10 Code GmbH\n" "\n" "(" BUILD_REVISION " " BUILD_TIMESTAMP ")\n" "\n\n"; diff --git a/src/w32-lock-obj-pub.in b/src/w32-lock-obj-pub.in new file mode 100644 index 0000000..2f3f911 --- /dev/null +++ b/src/w32-lock-obj-pub.in @@ -0,0 +1,44 @@ +## w32-lock-obj-pub.in - Include fragment for gpg-error.h -*- c-*- +## Copyright (C) 2014 g10 Code GmbH +## +## This file is free software; as a special exception the author gives +## unlimited permission to copy and/or distribute it, with or without +## modifications, as long as this notice is preserved. +## +## This file is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY, to the extent permitted by law; without even the +## implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +## +## +## This file defines the public version of the lock object for 32 bit +## Windows. The actual used version is in w32-lock-obj.h. This file +## is inserted into gpg-error.h by mkheader.c. The tool +## gen-w32-lock-obj.c has been used to construct it. + +#ifdef _WIN64 + +#pragma pack(push, 8) +typedef struct +{ + volatile char priv[56]; +} gpgrt_lock_t; +#pragma pack(pop) + +#define GPGRT_LOCK_INITIALIZER {1,0,0,0,0,0,0,0,255,255,255,255, \ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \ + 0,0,0,0,0,0,0,0,0,0,0,0} + +#else + +#pragma pack(push, 8) +typedef struct +{ + volatile char priv[36]; +} gpgrt_lock_t; +#pragma pack(pop) + +#define GPGRT_LOCK_INITIALIZER {1,0,0,0,0,0,0,0,255,255,255,255, \ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \ + 0,0,0,0,0,0,0,0} +#endif diff --git a/src/w32-lock-obj.h b/src/w32-lock-obj.h new file mode 100644 index 0000000..ef53546 --- /dev/null +++ b/src/w32-lock-obj.h @@ -0,0 +1,38 @@ +/* w32-lock-obj.h - Declaration of the Windows lock object + Copyright (C) 2014 g10 Code GmbH + + This file is part of libgpg-error. + + libgpg-error is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + libgpg-error 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef W32_LOCK_OBJ_H +#define W32_LOCK_OBJ_H + +#define LOCK_ABI_VERSION 1 + +/* The real definition of our lock object. The public definition is + named gpgrt_lock_t and hides this internal structure. */ +#pragma pack(push, 8) +typedef struct +{ + long vers; + volatile long initdone; + volatile long started; + CRITICAL_SECTION csec; +} _gpgrt_lock_t; +#pragma pack(pop) + + +#endif /*W32_LOCK_OBJ_H*/ diff --git a/src/w32-lock.c b/src/w32-lock.c new file mode 100644 index 0000000..0ad9409 --- /dev/null +++ b/src/w32-lock.c @@ -0,0 +1,119 @@ +/* w32-lock.c - GPGRT lock functions for Windows + Copyright (C) 2014 g10 Code GmbH + + This file is part of libgpg-error. + + libgpg-error is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + libgpg-error 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#ifndef HAVE_W32_SYSTEM +# error This module may only be build for Windows. +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <gpg-error.h> +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +#include "lock.h" +#include "w32-lock-obj.h" + + +static _gpgrt_lock_t * +get_lock_object (gpgrt_lock_t *lockhd) +{ + _gpgrt_lock_t *lock = (_gpgrt_lock_t*)lockhd; + + if (lock->vers != LOCK_ABI_VERSION) + abort (); + + return lock; +} + + +gpg_err_code_t +gpgrt_lock_init (gpgrt_lock_t *lockhd) +{ + _gpgrt_lock_t *lock = get_lock_object (lockhd); + + if (sizeof (gpgrt_lock_t) < sizeof (_gpgrt_lock_t)) + abort (); + InitializeCriticalSection (&lock->csec); + lock->initdone = 1; +} + + +gpg_err_code_t +gpgrt_lock_lock (gpgrt_lock_t *lockhd) +{ + _gpgrt_lock_t *lock = get_lock_object (lockhd); + + if (!lock->initdone) + { + if (!InterlockedIncrement (&lock->started)) + { + /* The new value of started is 0. Because the initial value + if the variable was -1 we known that this thread is the + first who needs this lock. Thus we initialize now. All + other threads won't get 0 back from InterlockedIncrement + and thus fall into the wait loop below. We ignore that + STARTED may in theory overflow if this thread starves for + too long. */ + gpgrt_lock_init (lockhd); + } + else + { + while (!lock->initdone) + Sleep (0); + } + } + + EnterCriticalSection (&lock->csec); + return 0; +} + + +gpg_err_code_t +gpgrt_lock_unlock (gpgrt_lock_t *lockhd) +{ + _gpgrt_lock_t *lock = get_lock_object (lockhd); + + if (!lock->initdone) + return GPG_ERR_INV_LOCK_OBJ; + LeaveCriticalSection (&lock->csec); + return 0; +} + + +/* Note: Use this function only if no other thread holds or waits for + this lock. */ +gpg_err_code_t +gpgrt_lock_destroy (gpgrt_lock_t *lockhd) +{ + _gpgrt_lock_t *lock = get_lock_object (lockhd); + + if (!lock->initdone) + return GPG_ERR_INV_LOCK_OBJ; + DeleteCriticalSection (&lock->csec); + lock->initdone = 0; + lock->started = -1; + return 0; +} diff --git a/src/w32-thread.c b/src/w32-thread.c new file mode 100644 index 0000000..f86be35 --- /dev/null +++ b/src/w32-thread.c @@ -0,0 +1,44 @@ +/* w32-thread.c - GPGRT thread functions for Windows + Copyright (C) 2014 g10 Code GmbH + + This file is part of libgpg-error. + + libgpg-error is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + libgpg-error 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#ifndef HAVE_W32_SYSTEM +# error This module may only be build for Windows. +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <gpg-error.h> +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +#include "thread.h" + + +gpg_err_code_t +gpgrt_yield (void) +{ + Sleep (0); + return 0; +} diff --git a/tests/Makefile.am b/tests/Makefile.am index 1a27f81..277a78b 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -25,11 +25,16 @@ else extra_includes = endif +gpg_error_lib = ../src/libgpg-error.la -TESTS = t-version t-strerror t-syserror +TESTS = t-version t-strerror t-syserror t-lock INCLUDES = -I$(top_builddir)/src $(extra_includes) -LDADD = ../src/libgpg-error.la + +LDADD = $(gpg_error_lib) noinst_PROGRAMS = $(TESTS) +noinst_HEADERS = t-common.h + +t_lock_LDADD = $(gpg_error_lib) $(LIBMULTITHREAD) diff --git a/tests/t-common.h b/tests/t-common.h new file mode 100644 index 0000000..85bcd51 --- /dev/null +++ b/tests/t-common.h @@ -0,0 +1,99 @@ +/* t-common.h - Common code for the tests. + * Copyright (C) 2013 g10 Code GmbH + * + * This file is part of libgpg-error. + * + * libgpg-error is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * libgpg-error 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdarg.h> + +#include "../src/gpg-error.h" + +#ifndef PGM +# error Macro PGM not defined. +#endif + + +static int verbose; +static int debug; +static int errorcount; + + +static void +die (const char *format, ...) +{ + va_list arg_ptr ; + + fflush (stdout); +#ifdef HAVE_FLOCKFILE + flockfile (stderr); +#endif + fprintf (stderr, "%s: ", PGM); + va_start (arg_ptr, format) ; + vfprintf (stderr, format, arg_ptr); + va_end (arg_ptr); + if (*format && format[strlen(format)-1] != '\n') + putc ('\n', stderr); +#ifdef HAVE_FLOCKFILE + funlockfile (stderr); +#endif + exit (1); +} + + +static void +fail (const char *format, ...) +{ + va_list arg_ptr; + + fflush (stdout); +#ifdef HAVE_FLOCKFILE + flockfile (stderr); +#endif + fprintf (stderr, "%s: ", PGM); + va_start (arg_ptr, format); + vfprintf (stderr, format, arg_ptr); + va_end (arg_ptr); + if (*format && format[strlen(format)-1] != '\n') + putc ('\n', stderr); +#ifdef HAVE_FLOCKFILE + funlockfile (stderr); +#endif + errorcount++; + if (errorcount >= 50) + die ("stopped after 50 errors."); +} + + +static void +show (const char *format, ...) +{ + va_list arg_ptr; + + if (!verbose) + return; +#ifdef HAVE_FLOCKFILE + flockfile (stderr); +#endif + fprintf (stderr, "%s: ", PGM); + va_start (arg_ptr, format); + vfprintf (stderr, format, arg_ptr); + if (*format && format[strlen(format)-1] != '\n') + putc ('\n', stderr); + va_end (arg_ptr); +#ifdef HAVE_FLOCKFILE + funlockfile (stderr); +#endif +} diff --git a/tests/t-lock.c b/tests/t-lock.c new file mode 100644 index 0000000..91923fb --- /dev/null +++ b/tests/t-lock.c @@ -0,0 +1,318 @@ +/* t-lock.c - Check the lock functions + * Copyright (C) 2013 g10 Code GmbH + * + * This file is part of libgpg-error. + * + * libgpg-error is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * libgpg-error 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#ifdef _WIN32 +# include <windows.h> +#else +# include <pthread.h> +#endif + +#define PGM "t-lock" + +#include "t-common.h" + +#ifdef _WIN32 +# define THREAD_RET_TYPE DWORD WINAPI +# define THREAD_RET_VALUE 0 +#else +# define THREAD_RET_TYPE void * +# define THREAD_RET_VALUE NULL +#endif + + +/* Our tests works by having a a couple of accountant threads which do + random transactions between accounts and a revision threads which + checks that the balance of all accounts is invariant. The idea for + this check is due to Bruno Haible. */ +#define N_ACCOUNT 8 +#define ACCOUNT_VALUE 42 +static int account[N_ACCOUNT]; +GPGRT_LOCK_DEFINE (accounts_lock); + +/* Number of transactions done by each accountant. */ +#define N_TRANSACTIONS 1000 + +/* Number of accountants to run. */ +#define N_ACCOUNTANTS 5 + +/* Maximum transaction value. A quite low value is used so that we + would get an integer overflow. */ +#define MAX_TRANSACTION_VALUE 50 + +/* Flag to tell the revision thread to finish. */ +static volatile int stop_revision_thread; + + +/* Initialze all accounts. */ +static void +init_accounts (void) +{ + int i; + + for (i=0; i < N_ACCOUNT; i++) + account[i] = ACCOUNT_VALUE; +} + + +/* Check that the sum of all accounts matches the intial sum. */ +static void +check_accounts (void) +{ + int i, sum; + + sum = 0; + for (i = 0; i < N_ACCOUNT; i++) + sum += account[i]; + if (sum != N_ACCOUNT * ACCOUNT_VALUE) + die ("accounts out of balance"); +} + + +static void +print_accounts (void) +{ + int i; + + for (i=0; i < N_ACCOUNT; i++) + printf ("account %d: %6d\n", i, account[i]); +} + + +/* Get a a random integer value in the range 0 to HIGH. */ +static unsigned int +get_rand (int high) +{ + return (unsigned int)(1+(int)((double)(high+1)*rand ()/(RAND_MAX+1.0))) - 1; +} + + +/* Pick a random account. Note that this fucntion is not + thread-safe. */ +static int +pick_account (void) +{ + return get_rand (N_ACCOUNT - 1); +} + + +/* Pick a random value for a transaction. This is not thread-safe. */ +static int +pick_value (void) +{ + return get_rand (MAX_TRANSACTION_VALUE); +} + + +/* This is the revision department. */ +static THREAD_RET_TYPE +revision_thread (void *arg) +{ + gpg_err_code_t rc; + int i; + + (void)arg; + + while (!stop_revision_thread) + { + rc = gpgrt_lock_lock (&accounts_lock); + if (rc) + fail ("gpgrt_lock_lock failed at %d: %s", __LINE__, gpg_strerror (rc)); + + check_accounts (); + rc = gpgrt_lock_unlock (&accounts_lock); + if (rc) + fail ("gpgrt_lock_unlock failed at %d: %s", __LINE__,gpg_strerror (rc)); + if (!(++i%7)) + gpgrt_yield (); + } + return THREAD_RET_VALUE; +} + + +/* This is one of our accountants. */ +static THREAD_RET_TYPE +accountant_thread (void *arg) +{ + gpg_err_code_t rc; + int i; + int acc1, acc2; + int value; + + (void)arg; + + for (i = 0; i < N_TRANSACTIONS; i++) + { + rc = gpgrt_lock_lock (&accounts_lock); + if (rc) + fail ("gpgrt_lock_lock failed at %d: %s", __LINE__, gpg_strerror (rc)); + + acc1 = pick_account (); + acc2 = pick_account (); + value = pick_value (); + account[acc1] += value; + account[acc2] -= value; + + rc = gpgrt_lock_unlock (&accounts_lock); + if (rc) + fail ("gpgrt_lock_unlock failed at %d: %s", __LINE__,gpg_strerror (rc)); + if (i && !(i%8)) + gpgrt_yield (); + } + return THREAD_RET_VALUE; +} + + +static void +run_test (void) +{ +#ifdef _WIN32 + HANDLE rthread; + HANDLE athreads[N_ACCOUNTANTS]; + int i; + int rc; + + stop_revision_thread = 0; + rthread = CreateThread (NULL, 0, revision_thread, NULL, 0, NULL); + if (!rthread) + die ("error creating revision thread: rc=%d", (int)GetLastError ()); + + for (i=0; i < N_ACCOUNTANTS; i++) + { + athreads[i] = CreateThread (NULL, 0, accountant_thread, NULL, 0, NULL); + if (!athreads[i]) + die ("error creating accountant thread %d: rc=%d", + i, (int)GetLastError ()); + } + + for (i=0; i < N_ACCOUNTANTS; i++) + { + rc = WaitForSingleObject (athreads[i], INFINITE); + if (rc == WAIT_OBJECT_0) + show ("accountant thread %d has terminated", i); + else + fail ("waiting for accountant thread %d failed: %d", + i, (int)GetLastError ()); + CloseHandle (athreads[i]); + } + stop_revision_thread = 1; + + rc = WaitForSingleObject (rthread, INFINITE); + if (rc == WAIT_OBJECT_0) + show ("revision thread has terminated"); + else + fail ("waiting for revision thread failed: %d", (int)GetLastError ()); + CloseHandle (rthread); + +#else /*!_WIN32*/ + pthread_t rthread; + pthread_t athreads[N_ACCOUNTANTS]; + int i; + + stop_revision_thread = 0; + pthread_create (&rthread, NULL, revision_thread, NULL); + + for (i=0; i < N_ACCOUNTANTS; i++) + pthread_create (&athreads[i], NULL, accountant_thread, NULL); + + for (i=0; i < N_ACCOUNTANTS; i++) + { + pthread_join (athreads[i], NULL); + show ("accountant thread %d has terminated", i); + } + + stop_revision_thread = 1; + pthread_join (rthread, NULL); + show ("revision thread has terminated"); + +#endif /*!_WIN32*/ + + gpgrt_lock_destroy (&accounts_lock); +} + + +int +main (int argc, char **argv) +{ + int last_argc = -1; + int rc; + + if (argc) + { + argc--; argv++; + } + while (argc && last_argc != argc ) + { + last_argc = argc; + if (!strcmp (*argv, "--help")) + { + puts ( +"usage: ./t-lock [options]\n" +"\n" +"Options:\n" +" --verbose Show what is going on\n" +" --debug Flyswatter\n" +); + exit (0); + } + if (!strcmp (*argv, "--verbose")) + { + verbose = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--debug")) + { + verbose = debug = 1; + argc--; argv++; + } + } + + srand (time(NULL)*getpid()); + + if (!gpg_error_check_version (GPG_ERROR_VERSION)) + { + die ("gpg_error_check_version returned an error"); + errorcount++; + } + + init_accounts (); + check_accounts (); + run_test (); + check_accounts (); + /* Run a second time to check deinit code. */ + run_test (); + check_accounts (); + /* And a third time to test an explicit init. */ + rc = gpgrt_lock_init (&accounts_lock); + if (rc) + fail ("gpgrt_lock_init failed at %d: %s", __LINE__, gpg_strerror (rc)); + run_test (); + check_accounts (); + if (verbose) + print_accounts (); + + return errorcount ? 1 : 0; +} |