diff --git a/configure.ac b/configure.ac index b6ca5fb..2f49bd1 100644 --- a/configure.ac +++ b/configure.ac @@ -1,548 +1,554 @@ # configure.ac for libgpg-error # Copyright (C) 2003, 2004, 2006, 2010, 2013, 2014, 2015 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 General Public License # along with this program; if not, see . # (Process this file with autoconf to produce a configure script.) # The following lines are used by ./autogen.sh. AC_PREREQ(2.59) min_automake_version="1.14" # To build a release you need to create a tag with the version number # (git tag -s libgpg-error-n.m) and run "./autogen.sh --force". # Please bump the version number immediately after the release, do # another commit, and a push so that the git magic is able to work. # See below for the LT versions. m4_define([mym4_version_major], [1]) m4_define([mym4_version_minor], [20]) # Below is m4 magic to extract and compute the revision number, the # decimalized short revision number, a beta version string, and a flag # indicating a development version (mym4_isgit). Note that the m4 # processing is done by autoconf and not during the configure run. m4_define([mym4_version], [mym4_version_major.mym4_version_minor]) m4_define([mym4_revision], m4_esyscmd([git rev-parse --short HEAD | tr -d '\n\r'])) m4_define([mym4_revision_dec], m4_esyscmd_s([echo $((0x$(echo ]mym4_revision[|head -c 4)))])) m4_define([mym4_betastring], m4_esyscmd_s([git describe --match 'libgpg-error-[0-9].*[0-9]' --long|\ awk -F- '$4!=0{print"-beta"$4}'])) m4_define([mym4_isgit],m4_if(mym4_betastring,[],[no],[yes])) m4_define([mym4_full_version],[mym4_version[]mym4_betastring]) AC_INIT([libgpg-error],[mym4_full_version],[http://bugs.gnupg.org]) # LT Version numbers, remember to change them just *before* a release. # (Code changed: REVISION++) # (Interfaces added/removed/changed: CURRENT++, REVISION=0) # (Interfaces added: AGE++) # (Interfaces removed: AGE=0) # Note that added error codes don't constitute an interface change. LIBGPG_ERROR_LT_CURRENT=15 LIBGPG_ERROR_LT_AGE=15 LIBGPG_ERROR_LT_REVISION=0 ################################################ AC_SUBST(LIBGPG_ERROR_LT_CURRENT) AC_SUBST(LIBGPG_ERROR_LT_AGE) AC_SUBST(LIBGPG_ERROR_LT_REVISION) VERSION_NUMBER=m4_esyscmd(printf "0x%02x%02x00" mym4_version_major \ mym4_version_minor) AC_SUBST(VERSION_NUMBER) AC_CONFIG_AUX_DIR([build-aux]) AM_INIT_AUTOMAKE([serial-tests dist-bzip2]) AM_MAINTAINER_MODE AC_CONFIG_SRCDIR([src/err-sources.h.in]) AC_CONFIG_HEADER([config.h]) AC_CONFIG_MACRO_DIR([m4]) AM_SILENT_RULES # We need to know about the host architecture to avoid spurious # warnings. AC_CANONICAL_HOST AB_INIT # Checks for programs. AC_PROG_CC AM_PROG_CC_C_O AC_PROG_CPP AC_PROG_AWK AC_CHECK_TOOL(AR, ar, :) AC_GNU_SOURCE # Set some internal variables depending on the platform for later use. have_w32_system=no have_w64_system=no have_w32ce_system=no case "${host}" in x86_64-*mingw32*) have_w32_system=yes have_w64_system=yes ;; *-mingw32ce*) have_w32_system=yes have_w32ce_system=yes ;; *-mingw32*) have_w32_system=yes ;; *) ;; esac if test "$have_w32_system" != yes; then gl_THREADLIB_EARLY fi # We build libgpg-error with large file support so that we have a 64 # bit off_t. Our external interface uses the gpgrt_off_t which is # anyway specified as 64 bit. Thus the same libgpg-error can be used # by software which is not build with large file support. AC_SYS_LARGEFILE LT_PREREQ([2.2.6]) LT_INIT([win32-dll disable-static]) LT_LANG([Windows Resource]) # We need to compile and run a program on the build machine. dnl The AC_PROG_CC_FOR_BUILD macro in the AC archive is broken for dnl autoconf 2.57. dnl AC_PROG_CC_FOR_BUILD AC_MSG_CHECKING(for cc for build) if test "$cross_compiling" = "yes"; then CC_FOR_BUILD="${CC_FOR_BUILD-cc}" else CC_FOR_BUILD="${CC_FOR_BUILD-$CC}" fi AC_MSG_RESULT($CC_FOR_BUILD) AC_ARG_VAR(CC_FOR_BUILD,[build system C compiler]) AH_BOTTOM([ /* Force using of NLS for W32 even if no libintl has been found. This is okay because we have our own gettext implementation for W32. */ #if defined(HAVE_W32_SYSTEM) && !defined(ENABLE_NLS) #define ENABLE_NLS 1 #endif /* Connect the generic estream-printf.c to our framework. */ #define _ESTREAM_PRINTF_REALLOC _gpgrt_realloc #define _ESTREAM_PRINTF_EXTRA_INCLUDE "gpgrt-int.h" /* For building we need to define these macro. */ #define GPG_ERR_ENABLE_GETTEXT_MACROS 1 #define GPG_ERR_ENABLE_ERRNO_MACROS 1 #define GPGRT_ENABLE_ES_MACROS 1 ]) # Note, that autogen.sh greps for the next line. AM_GNU_GETTEXT_VERSION([0.19.3]) AM_GNU_GETTEXT([external]) # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([stdlib.h locale.h stdint.h]) AC_FUNC_STRERROR_R case "${host_os}" in solaris*) # All versions of Solaris from 2.4 have a thread-safe strerror(). # Since Solaris 10, in addition strerror_r() exists. ;; *) AC_CHECK_FUNC([strerror_r], [], AC_MSG_WARN([[Without strerror_r, gpg_strerror_r might not be thread-safe]])) ;; esac AC_CHECK_FUNCS([flockfile vasprintf]) # # Checks for typedefs, structures, and compiler characteristics. # AC_C_CONST AC_CHECK_SIZEOF(int) AC_CHECK_SIZEOF(long) AC_CHECK_SIZEOF(long long) # Find a 64 bit integer type to be used instead of off_t. We prefer # the standard integer types over int64_t and finally try long long. if test "$ac_cv_sizeof_int" = "8"; then replacement_for_off_t="int" elif test "$ac_cv_sizeof_long" = "8"; then replacement_for_off_t="long" elif test "$ac_cv_header_stdint_h" = yes; then replacement_for_off_t="int64_t" elif test "$ac_cv_sizeof_long_long" = "8"; then replacement_for_off_t="long long" else AC_MSG_ERROR([[ *** *** No 64 bit signed integer type found. Can't build this library. ***]]) fi AC_DEFINE_UNQUOTED(REPLACEMENT_FOR_OFF_T, "$replacement_for_off_t", [Used by mkheader to insert the replacement type.]) # # Setup gcc specific options # AC_MSG_NOTICE([checking for cc features]) if test "$GCC" = yes; then # Check whether gcc does not emit a diagnostic for unknown -Wno-* # options. This is the case for gcc >= 4.6 AC_MSG_CHECKING([if gcc ignores unknown -Wno-* options]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6 ) #kickerror #endif]],[])],[_gcc_silent_wno=yes],[_gcc_silent_wno=no]) AC_MSG_RESULT($_gcc_silent_wno) # Note that it is okay to use CFLAGS here because these are just # warning options and the user should have a chance of overriding # them. if test "$USE_MAINTAINER_MODE" = "yes"; then CFLAGS="$CFLAGS -O3 -Wall -Wcast-align -Wshadow -Wstrict-prototypes" CFLAGS="$CFLAGS -Wformat -Wno-format-y2k -Wformat-security" if test x"$_gcc_silent_wno" = xyes ; then _gcc_wopt=yes else AC_MSG_CHECKING([if gcc supports -Wno-missing-field-initializers]) _gcc_cflags_save=$CFLAGS CFLAGS="-Wno-missing-field-initializers" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], [_gcc_wopt=yes],[_gcc_wopt=no]) AC_MSG_RESULT($_gcc_wopt) CFLAGS=$_gcc_cflags_save; fi if test x"$_gcc_wopt" = xyes ; then CFLAGS="$CFLAGS -W -Wno-sign-compare -Wno-missing-field-initializers" fi AC_MSG_CHECKING([if gcc supports -Wdeclaration-after-statement]) _gcc_cflags_save=$CFLAGS CFLAGS="-Wdeclaration-after-statement" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_wopt=yes,_gcc_wopt=no) AC_MSG_RESULT($_gcc_wopt) CFLAGS=$_gcc_cflags_save; if test x"$_gcc_wopt" = xyes ; then CFLAGS="$CFLAGS -Wdeclaration-after-statement" fi else CFLAGS="$CFLAGS -Wall" fi AC_MSG_CHECKING([if gcc supports -Wpointer-arith]) _gcc_cflags_save=$CFLAGS CFLAGS="-Wpointer-arith" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_psign=yes,_gcc_psign=no) AC_MSG_RESULT($_gcc_psign) CFLAGS=$_gcc_cflags_save; if test x"$_gcc_psign" = xyes ; then CFLAGS="$CFLAGS -Wpointer-arith" fi # The undocumented option -Wno-psabi suppresses the annoying # "the ABI of passing union with long double has changed in GCC 4.4" # which is emitted in estream-printf.c but entirely irrelvant # because that union is local to the file. if test x"$_gcc_silent_wno" = xyes ; then CFLAGS="$CFLAGS -Wno-psabi" fi fi # # Check whether the compiler supports the GCC style aligned attribute # AC_CACHE_CHECK([whether the GCC style aligned attribute is supported], [gcry_cv_gcc_attribute_aligned], [gcry_cv_gcc_attribute_aligned=no AC_COMPILE_IFELSE([AC_LANG_SOURCE( [[struct { int a; } foo __attribute__ ((aligned (16)));]])], [gcry_cv_gcc_attribute_aligned=yes])]) if test "$gcry_cv_gcc_attribute_aligned" = "yes" ; then AC_DEFINE(HAVE_GCC_ATTRIBUTE_ALIGNED,1, [Defined if a GCC style "__attribute__ ((aligned (n))" is supported]) fi # # Check for ELF visibility support. # AC_CACHE_CHECK(whether the visibility attribute is supported, gcry_cv_visibility_attribute, [gcry_cv_visibility_attribute=no AC_LANG_CONFTEST([AC_LANG_SOURCE( [[int foo __attribute__ ((visibility ("hidden"))) = 1; int bar __attribute__ ((visibility ("protected"))) = 1; ]])]) if ${CC-cc} -Werror -S conftest.c -o conftest.s \ 1>&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ; then if grep '\.hidden.*foo' conftest.s >/dev/null 2>&1 ; then if grep '\.protected.*bar' conftest.s >/dev/null 2>&1; then gcry_cv_visibility_attribute=yes fi fi fi ]) if test "$gcry_cv_visibility_attribute" = "yes"; then AC_CACHE_CHECK(for broken visibility attribute, gcry_cv_broken_visibility_attribute, [gcry_cv_broken_visibility_attribute=yes AC_LANG_CONFTEST([AC_LANG_SOURCE( [[int foo (int x); int bar (int x) __asm__ ("foo") __attribute__ ((visibility ("hidden"))); int bar (int x) { return x; } ]])]) if ${CC-cc} -Werror -S conftest.c -o conftest.s \ 1>&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ; then if grep '\.hidden@<:@ _@:>@foo' conftest.s >/dev/null 2>&1; then gcry_cv_broken_visibility_attribute=no fi fi ]) fi if test "$gcry_cv_visibility_attribute" = "yes"; then AC_CACHE_CHECK(for broken alias attribute, gcry_cv_broken_alias_attribute, [gcry_cv_broken_alias_attribute=yes AC_LANG_CONFTEST([AC_LANG_SOURCE( [[extern int foo (int x) __asm ("xyzzy"); int bar (int x) { return x; } extern __typeof (bar) foo __attribute ((weak, alias ("bar"))); extern int dfoo; extern __typeof (dfoo) dfoo __asm ("abccb"); int dfoo = 1; ]])]) if ${CC-cc} -Werror -S conftest.c -o conftest.s \ 1>&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ; then if grep 'xyzzy' conftest.s >/dev/null 2>&1 && \ grep 'abccb' conftest.s >/dev/null 2>&1; then gcry_cv_broken_alias_attribute=no fi fi ]) fi if test "$gcry_cv_visibility_attribute" = "yes"; then AC_CACHE_CHECK(if gcc supports -fvisibility=hidden, gcry_cv_gcc_has_f_visibility, [gcry_cv_gcc_has_f_visibility=no _gcc_cflags_save=$CFLAGS CFLAGS="-fvisibility=hidden" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], gcry_cv_gcc_has_f_visibility=yes) CFLAGS=$_gcc_cflags_save; ]) fi if test "$gcry_cv_visibility_attribute" = "yes" \ && test "$gcry_cv_broken_visibility_attribute" != "yes" \ && test "$gcry_cv_broken_alias_attribute" != "yes" \ && test "$gcry_cv_gcc_has_f_visibility" = "yes" then AC_DEFINE(GPGRT_USE_VISIBILITY, 1, [Define to use the GNU C visibility attribute.]) CFLAGS="$CFLAGS -fvisibility=hidden" fi # # Check whether ld supports a version script. # (Actually not a check but a list of systems which are known to support it.) # have_ld_version_script=no case "${host}" in *-*-linux*) have_ld_version_script=yes ;; *-*-gnu*) have_ld_version_script=yes ;; esac AC_ARG_ENABLE([ld-version-script], AC_HELP_STRING([--enable-ld-version-script], [enable/disable use of linker version script. (default is system dependent)]), [have_ld_version_script=$enableval], [ : ] ) AM_CONDITIONAL(HAVE_LD_VERSION_SCRIPT, test "$have_ld_version_script" = "yes") # # Check for thread library. # # Windows has always thread support; thus we don't bother to test for # it as it may lead to false results when cross building. if test "$have_w32_system" = yes; then AC_DEFINE([USE_WINDOWS_THREADS], [1]) LIBTHREAD= LTLIBTHREAD= LIBMULTITHREAD= LTLIBMULTITHREAD= THREADLIB_CPPFLAGS="" AC_SUBST([LIBTHREAD]) AC_SUBST([LTLIBTHREAD]) AC_SUBST([LIBMULTITHREAD]) AC_SUBST([LTLIBMULTITHREAD]) else gl_LOCK if test "$gl_threads_api" = posix; then AC_CHECK_SIZEOF(pthread_mutex_t,,[AC_INCLUDES_DEFAULT #include ]) fi fi # # Prepare building of estream # estream_INIT # # 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 if test "x$LIBMULTITHREAD" != x; then GPG_ERROR_CONFIG_MT_LIBS="${LIBMULTITHREAD}" else GPG_ERROR_CONFIG_MT_LIBS="" fi GPG_ERROR_CONFIG_CFLAGS="" if test "x$THREADLIB_CPPFLAGS" != x; then GPG_ERROR_CONFIG_MT_CFLAGS="${THREADLIB_CPPFLAGS}" else GPG_ERROR_CONFIG_MT_CFLAGS="" fi GPG_ERROR_CONFIG_ISUBDIRAFTER="" GPG_ERROR_CONFIG_HOST="$host" AC_SUBST(GPG_ERROR_CONFIG_LIBS) AC_SUBST(GPG_ERROR_CONFIG_CFLAGS) AC_SUBST(GPG_ERROR_CONFIG_MT_LIBS) AC_SUBST(GPG_ERROR_CONFIG_MT_CFLAGS) AC_SUBST(GPG_ERROR_CONFIG_ISUBDIRAFTER) AC_SUBST(GPG_ERROR_CONFIG_HOST) # # Special defines for certain platforms # force_use_syscfg=no if test "$have_w32_system" = yes; then AC_DEFINE(HAVE_W32_SYSTEM,1,[Defined if we run on a W32 API based system]) if test "$have_w64_system" = yes; then AC_DEFINE(HAVE_W64_SYSTEM,1,[Defined if we run on 64 bit W32 API system]) fi if test "$have_w32ce_system" = yes; then AC_DEFINE(HAVE_W32CE_SYSTEM,1,[Defined if we run on WindowsCE]) GPG_ERROR_CONFIG_ISUBDIRAFTER="gpg-extra" fi force_use_syscfg=yes fi if test x$cross_compiling = xyes; then force_use_syscfg=yes fi AM_CONDITIONAL(HAVE_W32_SYSTEM, test "$have_w32_system" = yes) AM_CONDITIONAL(HAVE_W64_SYSTEM, test "$have_w64_system" = yes) AM_CONDITIONAL(HAVE_W32CE_SYSTEM, test "$have_w32ce_system" = yes) AM_CONDITIONAL(CROSS_COMPILING, test x$cross_compiling = xyes) AM_CONDITIONAL(FORCE_USE_SYSCFG, test x$force_use_syscfg = xyes) AC_DEFINE_UNQUOTED(HOST_TRIPLET_STRING, "$host", [The host triplet]) # # Provide information about the build. # BUILD_REVISION="mym4_revision" AC_SUBST(BUILD_REVISION) AC_DEFINE_UNQUOTED(BUILD_REVISION, "$BUILD_REVISION", [GIT commit id revision used to build this package]) changequote(,)dnl BUILD_VERSION=`echo "$PACKAGE_VERSION"|sed 's/\([0-9.]*\).*/\1./'` changequote([,])dnl BUILD_VERSION="${BUILD_VERSION}0.mym4_revision_dec" BUILD_FILEVERSION=`echo "${BUILD_VERSION}" | tr . ,` AC_SUBST(BUILD_VERSION) AC_SUBST(BUILD_FILEVERSION) AC_ARG_ENABLE([build-timestamp], AC_HELP_STRING([--enable-build-timestamp], [set an explicit build timestamp for reproducibility. (default is the current time in ISO-8601 format)]), [if test "$enableval" = "no"; then BUILD_TIMESTAMP="" else BUILD_TIMESTAMP="$enableval" fi], [BUILD_TIMESTAMP=`date -u +%Y-%m-%dT%H:%M+0000 2>/dev/null || date`]) AC_SUBST(BUILD_TIMESTAMP) AC_DEFINE_UNQUOTED(BUILD_TIMESTAMP, "$BUILD_TIMESTAMP", [The time this package was configured for a build]) AC_ARG_ENABLE(languages, AC_HELP_STRING([--disable-languages], [do not build support for other languages than C])) AM_CONDITIONAL([LANGUAGES_SOME], [test "x$enable_languages" != xno]) build_doc=yes AC_ARG_ENABLE([doc], AC_HELP_STRING([--disable-doc], [do not build the documentation]), build_doc=$enableval, build_doc=yes) AM_CONDITIONAL([BUILD_DOC], [test "x$build_doc" != xno]) # # Substitution # AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([doc/Makefile po/Makefile.in m4/Makefile]) AC_CONFIG_FILES([src/Makefile tests/Makefile]) AC_CONFIG_FILES([lang/Makefile lang/cl/Makefile lang/cl/gpg-error.asd]) AC_CONFIG_FILES([src/versioninfo.rc src/gpg-error.w32-manifest]) AC_CONFIG_FILES([src/gpg-error-config], [chmod +x src/gpg-error-config]) AC_OUTPUT +tmp= +if test "$have_w32_system" != yes; then + if test x"$gl_use_threads" = xno; then + tmp=" NO-THREADS" + fi +fi echo " $PACKAGE_NAME-$PACKAGE_VERSION prepared for make Revision: mym4_revision (mym4_revision_dec) - Platform: $host + Platform: $host$tmp " if test "$gcry_cv_gcc_attribute_aligned" != "yes" ; then cat <. */ #if HAVE_CONFIG_H #include #endif #ifdef HAVE_W32_SYSTEM # error This module may not be build for Windows. #endif #include #include #include #include #include #include "posix-lock-obj.h" #define PGM "gen-posix-lock-obj" /* Check that configure did its job. */ +#ifdef USE_POSIX_THREADS #if SIZEOF_PTHREAD_MUTEX_T < 4 # error sizeof pthread_mutex_t is not known. #endif +#endif /* Special requirements for certain platforms. */ #if defined(__hppa__) && defined(__linux__) # define USE_16BYTE_ALIGNMENT 1 #else # define USE_16BYTE_ALIGNMENT 0 #endif #if USE_16BYTE_ALIGNMENT && !HAVE_GCC_ATTRIBUTE_ALIGNED # error compiler is not able to enforce a 16 byte alignment #endif - +#ifdef USE_POSIX_THREADS static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; - +#endif int main (void) { +#ifdef USE_POSIX_THREADS unsigned char *p; int i; +#endif struct { - pthread_mutex_t mtx; long vers; +#ifdef USE_POSIX_THREADS + pthread_mutex_t mtx; +#endif } dummyobj; + +#ifdef USE_POSIX_THREADS if (sizeof mtx != SIZEOF_PTHREAD_MUTEX_T) { fprintf (stderr, PGM ": pthread_mutex_t mismatch\n"); exit (1); } +#endif /*USE_POSIX_THREADS*/ 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 ("## lock-obj-pub.%s.h\n" + printf ("## lock-obj-pub.%s.h%s\n" "## File created by " PGM " - DO NOT EDIT\n" "## To be included by mkheader into gpg-error.h\n" - "\n" - "typedef struct\n" + "\n", + HOST_TRIPLET_STRING, +#ifdef USE_POSIX_THREADS + "" +#else + " - NO LOCK SUPPORT" +#endif + ); + +#ifdef USE_POSIX_THREADS + + /* To force a probably suitable alignment of the structure we use a + union and include a long and a pointer to a long. */ + printf ("typedef struct\n" "{\n" " long _vers;\n" " union {\n" " volatile char _priv[%d];\n" "%s" " long _x_align;\n" " long *_xp_align;\n" " } u;\n" "} gpgrt_lock_t;\n" "\n" "#define GPGRT_LOCK_INITIALIZER {%d,{{", - HOST_TRIPLET_STRING, SIZEOF_PTHREAD_MUTEX_T, -#if USE_16BYTE_ALIGNMENT +# if USE_16BYTE_ALIGNMENT " int _x16_align __attribute__ ((aligned (16)));\n", -#else +# else "", -#endif +# endif LOCK_ABI_VERSION); p = (unsigned char *)&mtx; for (i=0; i < sizeof mtx; i++) { if (i && !(i % 8)) printf (" \\\n%*s", 36, ""); printf ("%u", p[i]); if (i < sizeof mtx - 1) putchar (','); } - fputs ("}}}\n" - "##\n" + fputs ("}}}\n", stdout); + +#else /*!USE_POSIX_THREADS*/ + + printf ("/* Dummy object - no locking available. */\n" + "typedef struct\n" + "{\n" + " long _vers;\n" + "} gpgrt_lock_t;\n" + "\n" + "#define GPGRT_LOCK_INITIALIZER {%d}\n", + LOCK_ABI_VERSION); + +#endif /*!USE_POSIX_THREADS*/ + + fputs ("##\n" "## Loc" "al Variables:\n" "## mode: c\n" "## buffer-read-only: t\n" "## End:\n" "##\n", stdout); if (ferror (stdout)) { fprintf (stderr, PGM ": error writing to stdout: %s\n", strerror (errno)); return 1; } return 0; } diff --git a/src/mkheader.c b/src/mkheader.c index 380c7e3..b8fd783 100644 --- a/src/mkheader.c +++ b/src/mkheader.c @@ -1,639 +1,644 @@ /* mkheader.c - Create a header file for libgpg-error * Copyright (C) 2010 Free Software Foundation, Inc. * 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. */ #include #include #include #include #include #define PGM "mkheader" #define LINESIZE 1024 static const char *host_os; static char *host_triplet; static char *srcdir; static const char *hdr_version; static const char *hdr_version_number; /* Values take from the supplied config.h. */ static int have_stdint_h; static int have_w32_system; static int have_w64_system; static char *replacement_for_off_type; +static int use_posix_threads; /* Various state flags. */ static int stdint_h_included; /* The usual free wrapper. */ static void xfree (void *a) { if (a) free (a); } static char * xstrdup (const char *string) { char *p; p = malloc (strlen (string)+1); if (!p) { fputs (PGM ": out of core\n", stderr); exit (1); } strcpy (p, string); return p; } /* Return a malloced string with TRIPLET. If TRIPLET has an alias return that instead. In general build-aux/config.sub should do the aliasing but some returned triplets are anyway identical and thus we use this function to map it to the canonical form. */ static char * canon_host_triplet (const char *triplet) { struct { const char *name; const char *alias; } tbl[] = { {"i486-pc-linux-gnu", "i686-pc-linux-gnu" }, {"i586-pc-linux-gnu" }, { NULL } }; int i; const char *lastalias = NULL; for (i=0; tbl[i].name; i++) { if (tbl[i].alias) lastalias = tbl[i].alias; if (!strcmp (tbl[i].name, triplet)) { if (!lastalias) break; /* Ooops: first entry has no alias. */ return xstrdup (lastalias); } } return xstrdup (triplet); } /* Parse the supplied config.h file and extract required info. Returns 0 on success. */ static int parse_config_h (const char *fname) { FILE *fp; char line[LINESIZE]; int lnr = 0; char *p1; fp = fopen (fname, "r"); if (!fp) { fprintf (stderr, "%s:%d: can't open file: %s", fname, lnr, strerror (errno)); return 1; } while (fgets (line, LINESIZE, fp)) { size_t n = strlen (line); lnr++; if (!n || line[n-1] != '\n') { fprintf (stderr, "%s:%d: trailing linefeed missing, line too long or " "embedded nul character\n", fname, lnr); break; } line[--n] = 0; if (strncmp (line, "#define ", 8)) continue; /* We are only interested in define lines. */ p1 = strtok (line + 8, " \t"); if (!*p1) continue; /* oops */ if (!strcmp (p1, "HAVE_STDINT_H")) have_stdint_h = 1; else if (!strcmp (p1, "HAVE_W32_SYSTEM")) have_w32_system = 1; else if (!strcmp (p1, "HAVE_W64_SYSTEM")) have_w64_system = 1; else if (!strcmp (p1, "REPLACEMENT_FOR_OFF_T")) { p1 = strtok (NULL, "\""); if (!*p1) continue; /* oops */ xfree (replacement_for_off_type); replacement_for_off_type = xstrdup (p1); } + else if (!strcmp (p1, "USE_POSIX_THREADS")) + use_posix_threads = 1; } if (ferror (fp)) { fprintf (stderr, "%s:%d: error reading file: %s\n", fname, lnr, strerror (errno)); return 1; } fclose (fp); return 0; } /* Write LINE to stdout. The function is allowed to modify LINE. */ static void write_str (char *line) { if (fputs (line, stdout) == EOF) { fprintf (stderr, PGM ": error writing to stdout: %s\n", strerror (errno)); exit (1); } } static void write_line (char *line) { if (puts (line) == EOF) { fprintf (stderr, PGM ": error writing to stdout: %s\n", strerror (errno)); exit (1); } } /* Write SOURCE or CODES line to stdout. The function is allowed to modify LINE. Trailing white space is already removed. Passing NULL resets the internal state. */ static void write_sources_or_codes (char *line) { static int in_intro; char *p1, *p2; if (!line) { in_intro = 1; return; } if (!*line) return; if (in_intro) { if (!strchr ("0123456789", *line)) return; in_intro = 0; } p1 = strtok (line, " \t"); p2 = p1? strtok (NULL, " \t") : NULL; if (p1 && p2 && strchr ("0123456789", *p1) && *p2) { write_str (" "); write_str (p2); write_str (" = "); write_str (p1); write_str (",\n"); } } /* Write system errnos to stdout. The function is allowed to modify LINE. Trailing white space is already removed. Passing NULL resets the internal state. */ static void write_errnos_in (char *line) { static int state; char *p1, *p2; if (!line) { state = 0; return; } if (!*line) return; if (!state && strchr ("0123456789", *line)) state = 1; else if (state == 1 && !strchr ("0123456789", *line)) state = 2; if (state != 1) return; p1 = strtok (line, " \t"); p2 = p1? strtok (NULL, " \t") : NULL; if (p1 && p2 && strchr ("0123456789", *p1) && *p2) { write_str (" GPG_ERR_"); write_str (p2); write_str (" = GPG_ERR_SYSTEM_ERROR | "); write_str (p1); write_str (",\n"); } } /* Create the full file name for NAME and return a newly allocated string with it. If name contains a '&' and REPL is not NULL replace '&' with REPL. */ static char * mk_include_name (const char *name, const char *repl) { FILE *fp; char *incfname, *p; const char *s; incfname = malloc (strlen (srcdir) + strlen (name) + (repl?strlen (repl):0) + 1); if (!incfname) { fputs (PGM ": out of core\n", stderr); exit (1); } if (*name == '.' && name[1] == '/') *incfname = 0; else strcpy (incfname, srcdir); p = incfname + strlen (incfname); for (s=name; *s; s++) { if (*s == '&' && repl) { while (*repl) *p++ = *repl++; repl = NULL; /* Replace only once. */ } else *p++ = *s; } *p = 0; return incfname; } /* Include the file NAME from the source directory. The included file 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. 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*)) { FILE *fp; char *incfname; int inclnr; char line[LINESIZE]; int repl_flag; repl_flag = !!strchr (name, '&'); incfname = mk_include_name (name, repl_flag? host_triplet : NULL); fp = fopen (incfname, "r"); if (!fp && repl_flag) { /* Try again using the OS string. */ free (incfname); incfname = mk_include_name (name, host_os); fp = fopen (incfname, "r"); } if (!fp) { fprintf (stderr, "%s:%d: error including `%s': %s\n", fname, lnr, incfname, strerror (errno)); exit (1); } if (repl_flag) fprintf (stderr,"%s:%d: note: including '%s'\n", fname, lnr, incfname); inclnr = 0; while (fgets (line, LINESIZE, fp)) { size_t n = strlen (line); inclnr++; if (!n || line[n-1] != '\n') { fprintf (stderr, "%s:%d: trailing linefeed missing, line too long or " "embedded nul character\n", incfname, inclnr); fprintf (stderr,"%s:%d: note: file '%s' included from here\n", fname, lnr, incfname); exit (1); } line[--n] = 0; while (line[n] == ' ' || line[n] == '\t' || line[n] == '\r') { line[n] = 0; if (!n) break; n--; } if (line[0] == '#' && line[1] == '#') { if (!strncmp (line+2, "EOF##", 5)) break; /* Forced EOF. */ } else outf (line); } if (ferror (fp)) { fprintf (stderr, "%s:%d: error reading `%s': %s\n", fname, lnr, incfname, strerror (errno)); exit (1); } fclose (fp); free (incfname); } /* Try to include the file NAME. Returns true if it does not exist. */ static int try_include_file (const char *fname, int lnr, const char *name, void (*outf)(char*)) { int rc; char *incfname; int repl_flag; repl_flag = !!strchr (name, '&'); incfname = mk_include_name (name, repl_flag? host_triplet : NULL); rc = access (incfname, R_OK); if (rc && repl_flag) { free (incfname); incfname = mk_include_name (name, host_os); rc = access (incfname, R_OK); } if (!rc) include_file (fname, lnr, name, outf); free (incfname); return rc; } static int write_special (const char *fname, int lnr, const char *tag) { if (!strcmp (tag, "version")) { putchar ('\"'); fputs (hdr_version, stdout); putchar ('\"'); } else if (!strcmp (tag, "version-number")) { fputs (hdr_version_number, stdout); } else if (!strcmp (tag, "define:gpgrt_off_t")) { if (!replacement_for_off_type) { fprintf (stderr, "%s:%d: replacement for off_t not defined\n", fname, lnr); exit (1); } else { if (!strcmp (replacement_for_off_type, "int64_t") && !stdint_h_included && have_stdint_h) { fputs ("#include \n\n", stdout); stdint_h_included = 1; } printf ("typedef %s gpgrt_off_t;\n", replacement_for_off_type); } } else if (!strcmp (tag, "define:gpgrt_ssize_t")) { if (have_w64_system) { if (!stdint_h_included && have_stdint_h) { fputs ("# include \n", stdout); stdint_h_included = 1; } fputs ("typedef int64_t gpgrt_ssize_t;\n", stdout); } else if (have_w32_system) { fputs ("typedef long gpgrt_ssize_t;\n", stdout); } else { fputs ("#include \n" "typedef ssize_t gpgrt_ssize_t;\n", stdout); } } else if (!strcmp (tag, "api_ssize_t")) { if (have_w32_system) fputs ("gpgrt_ssize_t", stdout); else fputs ("ssize_t", stdout); } else if (!strcmp (tag, "include:err-sources")) { write_sources_or_codes (NULL); include_file (fname, lnr, "err-sources.h.in", write_sources_or_codes); } else if (!strcmp (tag, "include:err-codes")) { write_sources_or_codes (NULL); include_file (fname, lnr, "err-codes.h.in", write_sources_or_codes); } else if (!strcmp (tag, "include:errnos")) { include_file (fname, lnr, "errnos.in", write_errnos_in); } else if (!strcmp (tag, "include:os-add")) { if (!strcmp (host_os, "mingw32")) { include_file (fname, lnr, "w32-add.h", write_line); } else if (!strcmp (host_os, "mingw32ce")) { include_file (fname, lnr, "w32-add.h", write_line); include_file (fname, lnr, "w32ce-add.h", write_line); } } else if (!strcmp (tag, "include:lock-obj")) { if (try_include_file (fname, lnr, "./lock-obj-pub.native.h", write_line)) include_file (fname, lnr, "syscfg/lock-obj-pub.&.h", write_line); } else return 0; /* Unknown tag. */ return 1; /* Tag processed. */ } int main (int argc, char **argv) { FILE *fp; char line[LINESIZE]; int lnr = 0; const char *fname, *s; char *p1, *p2; const char *config_h; const char *host_triplet_raw; if (argc) { argc--; argv++; } if (argc != 6) { fputs ("usage: " PGM " host_os host_triplet template.h config.h" " version version_number\n", stderr); return 1; } host_os = argv[0]; host_triplet_raw = argv[1]; fname = argv[2]; config_h = argv[3]; hdr_version = argv[4]; hdr_version_number = argv[5]; host_triplet = canon_host_triplet (host_triplet_raw); srcdir = malloc (strlen (fname) + 2 + 1); if (!srcdir) { fputs (PGM ": out of core\n", stderr); return 1; } strcpy (srcdir, fname); p1 = strrchr (srcdir, '/'); if (p1) p1[1] = 0; else strcpy (srcdir, "./"); if (parse_config_h (config_h)) return 1; fp = fopen (fname, "r"); if (!fp) { fprintf (stderr, "%s:%d: can't open file: %s", fname, lnr, strerror (errno)); return 1; } while (fgets (line, LINESIZE, fp)) { size_t n = strlen (line); lnr++; if (!n || line[n-1] != '\n') { fprintf (stderr, "%s:%d: trailing linefeed missing, line too long or " "embedded nul character\n", fname, lnr); break; } line[--n] = 0; p1 = strchr (line, '@'); p2 = p1? strchr (p1+1, '@') : NULL; if (!p1 || !p2 || p2-p1 == 1) { puts (line); continue; } *p1++ = 0; *p2++ = 0; fputs (line, stdout); if (!strcmp (p1, "configure_input")) { s = strrchr (fname, '/'); printf ("Do not edit. Generated from %s for:\n%*s", s? s+1 : fname, (int)(p1 - line) + 13, ""); if (!strcmp (host_triplet, host_triplet_raw)) printf ("%s", host_triplet); else printf ("%s (%s)", host_triplet, host_triplet_raw); + if (!use_posix_threads && !have_w32_system && !have_w64_system) + fputs (" NO-THREADS", stdout); fputs (p2, stdout); } else if (!write_special (fname, lnr, p1)) { putchar ('@'); fputs (p1, stdout); putchar ('@'); fputs (p2, stdout); } else if (*p2) { fputs (p2, stdout); } putchar ('\n'); } if (ferror (fp)) { fprintf (stderr, "%s:%d: error reading file: %s\n", fname, lnr, strerror (errno)); return 1; } fputs ("/*\n" "Loc" "al Variables:\n" "buffer-read-only: t\n" "End:\n" "*/\n", stdout); if (ferror (stdout)) { fprintf (stderr, PGM ": error writing to stdout: %s\n", strerror (errno)); return 1; } fclose (fp); xfree (host_triplet); return 0; } diff --git a/src/posix-lock-obj.h b/src/posix-lock-obj.h index 7714d3c..872e55a 100644 --- a/src/posix-lock-obj.h +++ b/src/posix-lock-obj.h @@ -1,35 +1,42 @@ /* 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 . */ #ifndef POSIX_LOCK_OBJ_H #define POSIX_LOCK_OBJ_H -#define LOCK_ABI_VERSION 1 +#define LOCK_ABI_NOT_AVAILABLE (-1) +#if USE_POSIX_THREADS +# define LOCK_ABI_VERSION 1 +#else +# define LOCK_ABI_VERSION LOCK_ABI_NOT_AVAILABLE +#endif typedef struct { long vers; +#if USE_POSIX_THREADS union { pthread_mutex_t mtx; long *dummy; } u; +#endif } _gpgrt_lock_t; #endif /*POSIX_LOCK_OBJ_H*/ diff --git a/src/posix-lock.c b/src/posix-lock.c index 89be944..d8f5465 100644 --- a/src/posix-lock.c +++ b/src/posix-lock.c @@ -1,262 +1,262 @@ /* 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 . Parts of the code, in particular use_pthreads_p, are based on code from gettext, written by Bruno Haible , 2005. */ #if HAVE_CONFIG_H #include #endif #ifdef HAVE_W32_SYSTEM # error This module may not be build for Windows. #endif #include #include #include #include #include #if USE_POSIX_THREADS # include #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_trylock # 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) { assert (!"pthread_join"); 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) { assert (!"lock ABI version"); abort (); } if (sizeof (gpgrt_lock_t) < sizeof (_gpgrt_lock_t)) { assert (!"sizeof lock obj"); abort (); } return lock; } gpg_err_code_t _gpgrt_lock_init (gpgrt_lock_t *lockhd) { _gpgrt_lock_t *lock = (_gpgrt_lock_t*)lockhd; int rc; /* If VERS is zero we assume that no static initialization has been done, so we setup our ABI version right here. The caller might have called us to test whether lock support is at all available. */ if (!lock->vers) { if (sizeof (gpgrt_lock_t) < sizeof (_gpgrt_lock_t)) { assert (!"sizeof lock obj"); abort (); } lock->vers = LOCK_ABI_VERSION; } else /* Run the usual check. */ lock = get_lock_object (lockhd); #if USE_POSIX_THREADS if (use_pthread_p()) { rc = pthread_mutex_init (&lock->u.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; + rc = lock->vers == LOCK_ABI_NOT_AVAILABLE? 0 : 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->u.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; + rc = lock->vers == LOCK_ABI_NOT_AVAILABLE? 0 : GPG_ERR_NOT_IMPLEMENTED; #endif /* Unknown thread system. */ return rc; } gpg_err_code_t _gpgrt_lock_trylock (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_trylock (&lock->u.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; + rc = lock->vers == LOCK_ABI_NOT_AVAILABLE? 0 : 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->u.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; + rc = lock->vers == LOCK_ABI_NOT_AVAILABLE? 0 : 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->u.mtx); if (rc) rc = gpg_err_code_from_errno (rc); else { /* Re-init 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; + rc = lock->vers == LOCK_ABI_NOT_AVAILABLE? 0 : GPG_ERR_NOT_IMPLEMENTED; #endif /* Unknown thread system. */ return rc; } diff --git a/tests/t-lock.c b/tests/t-lock.c index fd645eb..831e224 100644 --- a/tests/t-lock.c +++ b/tests/t-lock.c @@ -1,324 +1,331 @@ /* t-lock.c - Check the lock functions - * Copyright (C) 2013 g10 Code GmbH + * Copyright (C) 2013, 2015 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 . */ #if HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #ifdef _WIN32 # include # include #else # include #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]); } +#if defined(_WIN32) || defined(USE_POSIX_THREADS) /* 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 = 0; (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; #ifdef _WIN32 srand (time(NULL)*getpid()); /* Windows needs it per thread. */ #endif 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; } +#endif /*_WIN32||USE_POSIX_THREADS*/ 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*/ +# ifdef USE_POSIX_THREADS 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"); - +# else /*!USE_POSIX_THREADS*/ + verbose++; + show ("no thread support - skipping test\n", PGM); + verbose--; +# endif /*!USE_POSIX_THREADS*/ #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; }