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;
}