diff --git a/configure.ac b/configure.ac index d9c9160..0bd8e04 100644 --- a/configure.ac +++ b/configure.ac @@ -1,376 +1,377 @@ # configure.ac -*- Autoconf -*- # Copyright (C) 2011, 2012 g10 Code GmbH # This file is part of NPTH. # # NPTH is free software; you can redistribute it and/or modify it # under the terms of either # # - the GNU Lesser General Public License as published by the Free # Software Foundation; either version 3 of the License, or (at # your option) any later version. # # or # # - the GNU General Public License as published by the Free # Software Foundation; either version 2 of the License, or (at # your option) any later version. # # or both in parallel, as here. # # NPTH 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 . # Process this file with autoconf to produce a configure script. AC_PREREQ([2.67]) min_automake_version="1.14" # To build a release you need to create a tag with the version number # (git tag -s npth-n.m) and run "./autogen.sh --force". Please bump # the version number immediately after the release and do another # commit and push so that the git magic is able to work. # See below for the LT versions. m4_define(my_version, [1.4]) # Below is m4 magic to extract and compute the git revision number, # the decimalized short revision number, a beta version string and a # flag indicating a development version (my_isgit). The latter works # by requiring the final tag in the repository. m4_define([git_revision], m4_esyscmd([git rev-parse --short HEAD | tr -d '\n\r'])) m4_define([git_revision_dec], m4_esyscmd_s([echo $((0x$(echo ]git_revision[|head -c 4)))])) m4_define([git_betastring], m4_esyscmd_s([git describe --match 'npth-[0-9].*[0-9]' --long|\ awk -F- '$3!=0{print"-beta"$3}'])) m4_define([my_isgit],m4_if(git_betastring,[],[no],[yes])) m4_define([my_full_version],[my_version[]git_betastring]) AC_INIT([npth],[my_full_version],[gnupg-devel@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/changed: AGE=0) # LIBNPTH_LT_CURRENT=0 LIBNPTH_LT_AGE=0 LIBNPTH_LT_REVISION=6 # If the API is changed in an incompatible way: increment the next counter. NPTH_CONFIG_API_VERSION=1 ############################################## PACKAGE=$PACKAGE_NAME VERSION=$PACKAGE_VERSION AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_SRCDIR([src/npth.c]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([serial-tests dist-bzip2 no-dist-gzip]) AM_MAINTAINER_MODE AC_CANONICAL_HOST AM_SILENT_RULES # Enable GNU extensions on systems that have them. AC_GNU_SOURCE AH_VERBATIM([_REENTRANT], [#ifndef _REENTRANT # define _REENTRANT 1 #endif]) # Checks for programs. AC_PROG_CC AC_SUBST(LIBNPTH_LT_CURRENT) AC_SUBST(LIBNPTH_LT_AGE) AC_SUBST(LIBNPTH_LT_REVISION) AC_SUBST(PACKAGE) AC_SUBST(VERSION) AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of this package]) AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version of this package]) run_tests="yes" AC_ARG_ENABLE(tests, AC_HELP_STRING([--disable-tests], [disable tests]), run_tests=$enableval) AM_CONDITIONAL(RUN_TESTS, test "$run_tests" = "yes") # Don't default to build static libs. LT_PREREQ([2.2.6]) LT_INIT([win32-dll disable-static]) LT_LANG([Windows Resource]) # For now we hardcode the use of version scripts. It would be better # to write a test for this or even implement this within libtool. have_ld_version_script=no case "${host}" in *-*-linux*) have_ld_version_script=yes ;; *-*-gnu*) have_ld_version_script=yes ;; *-apple-darwin*) - AC_DEFINE(_XOPEN_SOURCE, 500, Activate POSIX interface on MacOS X) + AC_SEARCH_LIBS([dispatch_semaphore_create],[dispatch], + [AC_DEFINE([HAVE_LIB_DISPATCH],1,[Defined if we have libdispatch])]) ;; *-*-aix*) have_fork_unsafe_semaphore=yes ;; esac AM_CONDITIONAL(HAVE_LD_VERSION_SCRIPT, test "$have_ld_version_script" = "yes") if test "$have_fork_unsafe_semaphore" = yes; then AC_DEFINE(HAVE_FORK_UNSAFE_SEMAPHORE, 1, [Defined if we have fork-unsafe semaphore]) fi # Set some default values config_libs="-lnpth" have_w32_system=no have_w32ce_system=no have_w64_system=no # Define OS macros case "${host}" in x86_64-*mingw32*) have_w64_system=yes ;; *-mingw32ce*) have_w32ce_system=yes ;; esac case "${host}" in *-mingw32ce*|*-mingw32*) have_w32_system=yes ;; *) ;; esac if test "$have_w32_system" = yes; then AC_DEFINE(HAVE_W32_SYSTEM,1, [Defined if we run on any kind of W32 API based system]) fi AM_CONDITIONAL(HAVE_W32_SYSTEM, test "$have_w32_system" = yes) if test "$have_w64_system" = yes; then AC_DEFINE(HAVE_W64_SYSTEM,1, [Defined if we run on a 64 bit W32 API based system]) fi AM_CONDITIONAL(HAVE_W64_SYSTEM, test "$have_w64_system" = yes) # # Generate values for the DLL version info # if test "$have_w32_system" = yes; then BUILD_ISODATE=`date --iso-8601` changequote(,)dnl BUILD_FILEVERSION=`echo "$VERSION" | sed 's/\([0-9.]*\).*/\1./;s/\./,/g'` changequote([,])dnl BUILD_FILEVERSION="${BUILD_FILEVERSION}git_revision_dec" fi AC_SUBST(BUILD_ISODATE) 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" = "yes"; then BUILD_TIMESTAMP=`date -u +%Y-%m-%dT%H:%M+0000 2>/dev/null || date` else BUILD_TIMESTAMP="$enableval" fi], [BUILD_TIMESTAMP=""]) AC_SUBST(BUILD_TIMESTAMP) AC_DEFINE_UNQUOTED(BUILD_TIMESTAMP, "$BUILD_TIMESTAMP", [The time this package was configured for a build]) # # Checks for header files. # # fixme: For what do we need the sys/socket test? AC_CHECK_HEADERS([sys/socket.h sys/select.h unistd.h sys/time.h time.h \ signal.h]) INSERT_SYS_SELECT_H= if test x"$ac_cv_header_sys_select_h" = xyes; then INSERT_SYS_SELECT_H="include " fi AC_SUBST(INSERT_SYS_SELECT_H) if test x"$ac_cv_header_sys_time_h" = xyes; then INSERT_SYS_TIME_H="include " fi AC_SUBST(INSERT_SYS_TIME_H) if test x"$ac_cv_header_time_h" = xyes; then INSERT_TIME_H="include " fi AC_SUBST(INSERT_TIME_H) if test x"$ac_cv_header_signal_h" = xyes; then INSERT_SIGNAL_H="include " fi AC_SUBST(INSERT_SIGNAL_H) # Some systems lack socklen_t - provide a replacement. gl_TYPE_SOCKLEN_T case "${host}" in *-*-mingw32*) # socklen_t may or may not be defined depending on what headers # are included. To be safe we use int as this is the actual type. INSERT_SOCKLEN_T="define _npth_socklen_t int" ;; *) if test ".$gl_cv_socklen_t_equiv" = "."; then INSERT_SOCKLEN_T="define _npth_socklen_t socklen_t" else INSERT_SOCKLEN_T="define _npth_socklen_t ${gl_cv_socklen_t_equiv}" fi esac AC_SUBST(INSERT_SOCKLEN_T) # # Checks for typedefs, structures, and compiler characteristics. # AC_TYPE_PID_T AC_TYPE_SIZE_T AC_TYPE_SSIZE_T # # Checks for libraries and functions. # # We test for pthread_detach because glibc 2.22 includes # pthread_create but not pthread_detach. if test "$have_w32_system" = no; then AC_SEARCH_LIBS([pthread_detach],[pthread]) case "x$ac_cv_search_pthread_detach" in xno) have_pthread=no ;; xnone\ required) have_pthread=yes ;; *) have_pthread=yes config_libs="$config_libs $ac_cv_search_pthread_detach" ;; esac if test "$have_pthread" != no; then AC_DEFINE(HAVE_PTHREAD,1,[Define if we have pthread.]) AC_CHECK_FUNCS([pthread_tryjoin_np pthread_setname_np pthread_getname_np]) AC_CHECK_FUNCS([pthread_mutex_timedlock]) AC_CHECK_FUNCS([pthread_rwlock_rdlock pthread_rwlock_wrlock]) AC_CHECK_FUNCS([pthread_rwlock_timedrdlock pthread_rwlock_timedwrlock]) AC_CHECK_FUNCS([pthread_rwlock_tryrdlock pthread_rwlock_trywrlock]) AC_CHECK_FUNCS([pthread_atfork]) fi fi AC_CHECK_FUNCS([select pselect gettimeofday]) npth_LIBSOCKET config_libs="$config_libs $LIBSOCKET" # Save and restore LIBS so e.g., -lrt, isn't added to it. Otherwise, *all* # programs in the package would end up linked with that potentially-shared # library, inducing unnecessary run-time overhead. LIB_CLOCK_GETTIME= AC_SUBST([LIB_CLOCK_GETTIME]) gl_saved_libs=$LIBS AC_SEARCH_LIBS([clock_gettime], [rt posix4], [if test "$ac_cv_search_clock_gettime" != "none required"; then LIB_CLOCK_GETTIME=$ac_cv_search_clock_gettime config_libs="$config_libs $LIB_CLOCK_GETTIME" fi AC_DEFINE([HAVE_CLOCK_GETTIME],1, [Define to 1 if you have the `clock_gettime' function.]) ]) LIBS=$gl_saved_libs # # Set NETLIBS # if test "$have_w32ce_system" = yes; then NETLIBS="-lws2 $NETLIBS" elif test "$have_w32_system" = yes; then NETLIBS="-lws2_32 $NETLIBS" fi AC_SUBST(NETLIBS) # # Substitutions to set generated files in a Emacs buffer to read-only. # AC_SUBST(emacs_local_vars_begin, [['Local][ ][Variables:']]) AC_SUBST(emacs_local_vars_read_only, ['buffer-read-only: t']) AC_SUBST(emacs_local_vars_end, ['End:']) # # Substitution used for npth-config # NPTH_CONFIG_LIBS="$config_libs" NPTH_CONFIG_CFLAGS="" NPTH_CONFIG_HOST="$host" AC_SUBST(NPTH_CONFIG_API_VERSION) AC_SUBST(NPTH_CONFIG_LIBS) AC_SUBST(NPTH_CONFIG_CFLAGS) AC_SUBST(NPTH_CONFIG_HOST) # # Last check. # die=no if test "$have_w32_system" = no; then if test "$have_pthread" = "no"; then die=yes AC_MSG_NOTICE([[ *** *** You need Pthread to build this program. *** Normally, this library comes with your system. On Windows, you can use: *** http://sourceware.org/pthreads-win32/ ***]]) fi fi if test "$die" = "yes"; then AC_MSG_ERROR([[ *** *** Required libraries not found. Please consult the above messages *** and install them before running configure again. ***]]) fi # # Write output # AC_CONFIG_FILES([Makefile src/npth.h src/Makefile w32/Makefile tests/Makefile]) AC_CONFIG_FILES(npth-config, chmod +x npth-config) AC_OUTPUT echo " $PACKAGE_NAME-$PACKAGE_VERSION prepared for make Revision: git_revision (git_revision_dec) Platform: $host " diff --git a/src/npth.c b/src/npth.c index bfcf0e9..d914a04 100644 --- a/src/npth.c +++ b/src/npth.c @@ -1,771 +1,773 @@ /* npth.c - a lightweight implementation of pth over pthread. Copyright (C) 2011 g10 Code GmbH This file is part of NPTH. NPTH is free software; you can redistribute it and/or modify it under the terms of either - the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. or - the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. or both in parallel, as here. NPTH is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copies of the GNU General Public License and the GNU Lesser General Public License along with this program; if not, see . */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include -#include +#ifdef HAVE_LIB_DISPATCH +# include +typedef dispatch_semaphore_t sem_t; + +/* This glue code is for macOS which does not have full implementation + of POSIX semaphore. On macOS, using semaphore in Grand Central + Dispatch library is better than using the partial implementation of + POSIX semaphore where sem_init doesn't work well. + */ + +static int +sem_init (sem_t *sem, int is_shared, unsigned int value) +{ + (void)is_shared; + if ((*sem = dispatch_semaphore_create (value)) == NULL) + return -1; + else + return 0; +} + +static int +sem_post (sem_t *sem) +{ + dispatch_semaphore_signal (*sem); + return 0; +} + +static int +sem_wait (sem_t *sem) +{ + dispatch_semaphore_wait (*sem, DISPATCH_TIME_FOREVER); + return 0; +} +#else +# include +#endif #ifdef HAVE_UNISTD_H # include #endif #ifndef HAVE_PSELECT # include #endif #include "npth.h" /* The global lock that excludes all threads but one. This is a semaphore, because these can be safely used in a library even if the application or other libraries call fork(), including from a signal handler. sem_post is async-signal-safe. (The reason a semaphore is safe and a mutex is not safe is that a mutex has an owner, while a semaphore does not.) We init sceptre to a static buffer for use by sem_init; in case sem_open is used instead SCEPTRE will changed to the value returned by sem_open. */ static sem_t sceptre_buffer; static sem_t *sceptre = &sceptre_buffer; /* Configure defines HAVE_FORK_UNSAFE_SEMAPHORE if child process can't access non-shared unnamed semaphore which is created by its parent. We use unnamed semaphore (if available) for the global lock. The specific semaphore is only valid for those threads in a process, and it is no use by other processes. Thus, PSHARED argument for sem_init is naturally 0. However, there are daemon-like applications which use fork after npth's initialization by npth_init. In this case, a child process uses the semaphore which was created by its parent process, while parent does nothing with the semaphore. In some system (e.g. AIX), access by child process to non-shared unnamed semaphore is prohibited. For such a system, HAVE_FORK_UNSAFE_SEMAPHORE should be defined, so that unnamed semaphore will be created with the option PSHARED=1. The purpose of the setting of PSHARED=1 is only for allowing the access of the lock by child process. For NPTH, it does not mean any other interactions between processes. */ #ifdef HAVE_FORK_UNSAFE_SEMAPHORE #define NPTH_SEMAPHORE_PSHARED 1 #else #define NPTH_SEMAPHORE_PSHARED 0 #endif /* The main thread is the active thread at the time pth_init was called. As of now it is only useful for debugging. The volatile make sure the compiler does not eliminate this set but not used variable. */ static volatile pthread_t main_thread; /* This flag is set as soon as npth_init has been called or if any * thread has been created. It will never be cleared again. The only * purpose is to make npth_protect and npth_unprotect more robust in * that they can be shortcut when npth_init has not yet been called. * This is important for libraries which want to support nPth by using * those two functions but may have be initialized before pPth. */ static int initialized_or_any_threads; /* Systems that don't have pthread_mutex_timedlock get a busy wait implementation that probes the lock every BUSY_WAIT_INTERVAL milliseconds. */ #define BUSY_WAIT_INTERVAL 200 typedef int (*trylock_func_t) (void *); static int busy_wait_for (trylock_func_t trylock, void *lock, const struct timespec *abstime) { int err; /* This is not great, but better than nothing. Only works for locks which are mostly uncontested. Provides absolutely no fairness at all. Creates many wake-ups. */ while (1) { struct timespec ts; err = npth_clock_gettime (&ts); if (err < 0) { /* Just for safety make sure we return some error. */ err = errno ? errno : EINVAL; break; } if (! npth_timercmp (abstime, &ts, <)) { err = ETIMEDOUT; break; } err = (*trylock) (lock); if (err != EBUSY) break; /* Try again after waiting a bit. We could calculate the maximum wait time from ts and abstime, but we don't bother, as our granularity is pretty fine. */ usleep (BUSY_WAIT_INTERVAL * 1000); } return err; } static void enter_npth (void) { int res; res = sem_post (sceptre); assert (res == 0); } static void leave_npth (void) { int res; int save_errno = errno; do { res = sem_wait (sceptre); } while (res < 0 && errno == EINTR); assert (!res); errno = save_errno; } #define ENTER() enter_npth () #define LEAVE() leave_npth () -static int -try_sem_open (sem_t **r_sem) -{ - sem_t *sem; - char name [256]; - int counter = 0; - - do - { - snprintf (name, sizeof name - 1, "/npth-sceptre-%lu-%u", - (unsigned long)getpid (), counter); - name[sizeof name -1] = 0; - counter++; - - sem = sem_open (name, (O_CREAT | O_EXCL), (S_IRUSR | S_IWUSR), 1); - if (sem != SEM_FAILED) - { - *r_sem = sem; - return 0; - } - fprintf (stderr, " semOpen(%s): %s\n", name, strerror (errno)); - } - while (errno == EEXIST); - - return -1; -} - - int npth_init (void) { int res; main_thread = pthread_self(); /* Track that we have been initialized. */ initialized_or_any_threads |= 1; /* Better reset ERRNO so that we know that it has been set by sem_init. */ errno = 0; /* The semaphore is binary. */ res = sem_init (sceptre, NPTH_SEMAPHORE_PSHARED, 1); + /* There are some versions of operating systems which have sem_init + symbol defined but the call actually returns ENOSYS at runtime. + We know this problem for older versions of AIX (<= 4.3.3) and + macOS. For macOS, we use semaphore in Grand Central Dispatch + library, so ENOSYS doesn't happen. We only support AIX >= 5.2, + where sem_init is supported. + */ if (res < 0) { - /* Mac OSX and some AIX versions have sem_init but return - ENOSYS. This is allowed according to some POSIX versions but - the informative section is quite fuzzy about it. We resort - to sem_open in this case. */ - if (errno == ENOSYS) - { - if (try_sem_open (&sceptre)) - return errno; - } - else - { - /* POSIX.1-2001 defines the semaphore interface but does not - specify the return value for success. Thus we better - bail out on error only on a POSIX.1-2008 system. */ + /* POSIX.1-2001 defines the semaphore interface but does not + specify the return value for success. Thus we better + bail out on error only on a POSIX.1-2008 system. */ #if _POSIX_C_SOURCE >= 200809L - return errno; + return errno; #endif - } } LEAVE(); return 0; } int npth_getname_np (npth_t target_thread, char *buf, size_t buflen) { #ifdef HAVE_PTHREAD_GETNAME_NP return pthread_getname_np (target_thread, buf, buflen); #else (void)target_thread; (void)buf; (void)buflen; return ENOSYS; #endif } int npth_setname_np (npth_t target_thread, const char *name) { #ifdef HAVE_PTHREAD_SETNAME_NP #ifdef __NetBSD__ return pthread_setname_np (target_thread, "%s", (void*) name); #else #ifdef __APPLE__ if (target_thread == npth_self ()) return pthread_setname_np (name); else return ENOTSUP; #else return pthread_setname_np (target_thread, name); #endif #endif #else (void)target_thread; (void)name; return ENOSYS; #endif } struct startup_s { void *(*start_routine) (void *); void *arg; }; static void * thread_start (void *startup_arg) { struct startup_s *startup = startup_arg; void *(*start_routine) (void *); void *arg; void *result; start_routine = startup->start_routine; arg = startup->arg; free (startup); LEAVE(); result = (*start_routine) (arg); /* Note: instead of returning here, we might end up in npth_exit() instead. */ ENTER(); return result; } int npth_create (npth_t *thread, const npth_attr_t *attr, void *(*start_routine) (void *), void *arg) { int err; struct startup_s *startup; startup = malloc (sizeof (*startup)); if (!startup) return errno; initialized_or_any_threads |= 2; startup->start_routine = start_routine; startup->arg = arg; err = pthread_create (thread, attr, thread_start, startup); if (err) { free (startup); return err; } /* Memory is released in thread_start. */ return 0; } int npth_join (npth_t thread, void **retval) { int err; #ifdef HAVE_PTHREAD_TRYJOIN_NP /* No need to allow competing threads to enter when we can get the lock immediately. pthread_tryjoin_np is a GNU extension. */ err = pthread_tryjoin_np (thread, retval); if (err != EBUSY) return err; #endif /*HAVE_PTHREAD_TRYJOIN_NP*/ ENTER(); err = pthread_join (thread, retval); LEAVE(); return err; } void npth_exit (void *retval) { ENTER(); pthread_exit (retval); /* Never reached. But just in case pthread_exit does return... */ LEAVE(); } int npth_mutex_lock (npth_mutex_t *mutex) { int err; /* No need to allow competing threads to enter when we can get the lock immediately. */ err = pthread_mutex_trylock (mutex); if (err != EBUSY) return err; ENTER(); err = pthread_mutex_lock (mutex); LEAVE(); return err; } int npth_mutex_timedlock (npth_mutex_t *mutex, const struct timespec *abstime) { int err; /* No need to allow competing threads to enter when we can get the lock immediately. */ err = pthread_mutex_trylock (mutex); if (err != EBUSY) return err; ENTER(); #if HAVE_PTHREAD_MUTEX_TIMEDLOCK err = pthread_mutex_timedlock (mutex, abstime); #else err = busy_wait_for ((trylock_func_t) pthread_mutex_trylock, mutex, abstime); #endif LEAVE(); return err; } #ifndef _NPTH_NO_RWLOCK int npth_rwlock_rdlock (npth_rwlock_t *rwlock) { int err; #ifdef HAVE_PTHREAD_RWLOCK_TRYRDLOCK /* No need to allow competing threads to enter when we can get the lock immediately. */ err = pthread_rwlock_tryrdlock (rwlock); if (err != EBUSY) return err; #endif ENTER(); err = pthread_rwlock_rdlock (rwlock); LEAVE(); return err; } int npth_rwlock_timedrdlock (npth_rwlock_t *rwlock, const struct timespec *abstime) { int err; #ifdef HAVE_PTHREAD_RWLOCK_TRYRDLOCK /* No need to allow competing threads to enter when we can get the lock immediately. */ err = pthread_rwlock_tryrdlock (rwlock); if (err != EBUSY) return err; #endif ENTER(); #if HAVE_PTHREAD_RWLOCK_TIMEDRDLOCK err = pthread_rwlock_timedrdlock (rwlock, abstime); #else err = busy_wait_for ((trylock_func_t) pthread_rwlock_tryrdlock, rwlock, abstime); #endif LEAVE(); return err; } int npth_rwlock_wrlock (npth_rwlock_t *rwlock) { int err; #ifdef HAVE_PTHREAD_RWLOCK_TRYWRLOCK /* No need to allow competing threads to enter when we can get the lock immediately. */ err = pthread_rwlock_trywrlock (rwlock); if (err != EBUSY) return err; #endif ENTER(); err = pthread_rwlock_wrlock (rwlock); LEAVE(); return err; } int npth_rwlock_timedwrlock (npth_rwlock_t *rwlock, const struct timespec *abstime) { int err; #ifdef HAVE_PTHREAD_RWLOCK_TRYWRLOCK /* No need to allow competing threads to enter when we can get the lock immediately. */ err = pthread_rwlock_trywrlock (rwlock); if (err != EBUSY) return err; #endif ENTER(); #if HAVE_PTHREAD_RWLOCK_TIMEDWRLOCK err = pthread_rwlock_timedwrlock (rwlock, abstime); #elif HAVE_PTHREAD_RWLOCK_TRYRDLOCK err = busy_wait_for ((trylock_func_t) pthread_rwlock_trywrlock, rwlock, abstime); #else err = ENOSYS; #endif LEAVE(); return err; } #endif int npth_cond_wait (npth_cond_t *cond, npth_mutex_t *mutex) { int err; ENTER(); err = pthread_cond_wait (cond, mutex); LEAVE(); return err; } int npth_cond_timedwait (npth_cond_t *cond, npth_mutex_t *mutex, const struct timespec *abstime) { int err; ENTER(); err = pthread_cond_timedwait (cond, mutex, abstime); LEAVE(); return err; } /* Standard POSIX Replacement API */ int npth_usleep(unsigned int usec) { int res; ENTER(); res = usleep(usec); LEAVE(); return res; } unsigned int npth_sleep(unsigned int sec) { unsigned res; ENTER(); res = sleep(sec); LEAVE(); return res; } int npth_system(const char *cmd) { int res; ENTER(); res = system(cmd); LEAVE(); return res; } pid_t npth_waitpid(pid_t pid, int *status, int options) { pid_t res; ENTER(); res = waitpid(pid,status, options); LEAVE(); return res; } int npth_connect(int s, const struct sockaddr *addr, socklen_t addrlen) { int res; ENTER(); res = connect(s, addr, addrlen); LEAVE(); return res; } int npth_accept(int s, struct sockaddr *addr, socklen_t *addrlen) { int res; ENTER(); res = accept(s, addr, addrlen); LEAVE(); return res; } int npth_select(int nfd, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *timeout) { int res; ENTER(); res = select(nfd, rfds, wfds, efds, timeout); LEAVE(); return res; } int npth_pselect(int nfd, fd_set *rfds, fd_set *wfds, fd_set *efds, const struct timespec *timeout, const sigset_t *sigmask) { int res; ENTER(); #ifdef HAVE_PSELECT res = pselect (nfd, rfds, wfds, efds, timeout, sigmask); #else /*!HAVE_PSELECT*/ { /* A better emulation of pselect would be to create a pipe, wait in the select for one end and have a signal handler write to the other end. However, this is non-trivial to implement and thus we only print a compile time warning. */ # ifdef __GNUC__ # warning Using a non race free pselect emulation. # endif struct timeval t, *tp; tp = NULL; if (!timeout) ; else if (timeout->tv_nsec >= 0 && timeout->tv_nsec < 1000000000) { t.tv_sec = timeout->tv_sec; t.tv_usec = (timeout->tv_nsec + 999) / 1000; tp = &t; } else { errno = EINVAL; res = -1; goto leave; } if (sigmask) { int save_errno; sigset_t savemask; pthread_sigmask (SIG_SETMASK, sigmask, &savemask); res = select (nfd, rfds, wfds, efds, tp); save_errno = errno; pthread_sigmask (SIG_SETMASK, &savemask, NULL); errno = save_errno; } else res = select (nfd, rfds, wfds, efds, tp); leave: ; } #endif /*!HAVE_PSELECT*/ LEAVE(); return res; } ssize_t npth_read(int fd, void *buf, size_t nbytes) { ssize_t res; ENTER(); res = read(fd, buf, nbytes); LEAVE(); return res; } ssize_t npth_write(int fd, const void *buf, size_t nbytes) { ssize_t res; ENTER(); res = write(fd, buf, nbytes); LEAVE(); return res; } int npth_recvmsg (int fd, struct msghdr *msg, int flags) { int res; ENTER(); res = recvmsg (fd, msg, flags); LEAVE(); return res; } int npth_sendmsg (int fd, const struct msghdr *msg, int flags) { int res; ENTER(); res = sendmsg (fd, msg, flags); LEAVE(); return res; } void npth_unprotect (void) { /* If we are not initialized we may not access the semaphore and * thus we shortcut it. Note that in this case the unprotect/protect * is not needed. For failsafe reasons if an nPth thread has ever * been created but nPth has accidentally not initialized we do not * shortcut so that a stack backtrace (due to the access of the * uninitialized semaphore) is more expressive. */ if (initialized_or_any_threads) ENTER(); } void npth_protect (void) { /* See npth_unprotect for commentary. */ if (initialized_or_any_threads) LEAVE(); } int npth_clock_gettime (struct timespec *ts) { #if defined(CLOCK_REALTIME) && HAVE_CLOCK_GETTIME return clock_gettime (CLOCK_REALTIME, ts); #elif HAVE_GETTIMEOFDAY { struct timeval tv; if (gettimeofday (&tv, NULL)) return -1; ts->tv_sec = tv.tv_sec; ts->tv_nsec = tv.tv_usec * 1000; return 0; } #else /* FIXME: fall back on time() with seconds resolution. */ # error clock_gettime not available - please provide a fallback. #endif } diff --git a/tests/t-fork.c b/tests/t-fork.c index 2f0b181..7ec542c 100644 --- a/tests/t-fork.c +++ b/tests/t-fork.c @@ -1,49 +1,59 @@ /* t-fork.c * Copyright 2016 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 "t-support.h" +/* This is a test if nPth can allow daemon-like applications + initializing earlier. + + For daemon-like applications, ideally, it is expected to call + npth_init after fork. This condition is not satisfied sometimes. + + Failure of this test means nPth implementation doesn't allow + npth_init after fork. In such a case, application should be + modified. + */ int main (int argc, const char *argv[]) { int rc; pid_t pid; if (argc >= 2 && !strcmp (argv[1], "--verbose")) opt_verbose = 1; rc = npth_init (); fail_if_err (rc); pid = fork (); if (pid == (pid_t)-1) fail_msg ("fork failed"); else if (pid) { int status; info_msg ("forked"); wait (&status); fail_if_err (status); } else { info_msg ("child exit"); npth_usleep (1000); /* Let NPTH enter, sleep, and leave. */ } return 0; }