Initial support for w32.
authorMarcus Brinkmann <marcus.brinkmann@ruhr-uni-bochum.de>
Fri, 13 Jan 2012 00:37:07 +0000 (01:37 +0100)
committerMarcus Brinkmann <marcus.brinkmann@ruhr-uni-bochum.de>
Fri, 13 Jan 2012 00:37:07 +0000 (01:37 +0100)
* Makefile.am [HAVE_W32_SYSTEM]: Build from w32 directory.
* configure.ac: Don't check for pthread on windows.  Add winsock
library on windows.  Add w32 subdirectory to config files.
* w32/npth.h, w32/npth.c, w32/Makefile.am, w32/npth.def,
w32/npth-config.in, w32/npth.m4: New files.
* tests/Makefile.am: Allow building on w32.

Makefile.am
configure.ac
tests/Makefile.am
w32/Makefile.am [new file with mode: 0644]
w32/npth-config.in [new file with mode: 0644]
w32/npth.c [new file with mode: 0644]
w32/npth.def [new file with mode: 0644]
w32/npth.h [new file with mode: 0644]
w32/npth.m4 [new file with mode: 0644]

index 4fbd669..a22c0f9 100644 (file)
@@ -29,7 +29,13 @@ else
 tests =
 endif
 
-SUBDIRS = src ${tests}
+if HAVE_W32_SYSTEM
+npthdir = w32
+else
+npthdir = src
+endif
+
+SUBDIRS = ${npthdir} ${tests}
 
 # Fix the version of the spec file and create a file named VERSION
 # to be used for patch's Prereq: feature.
index 5ef1ca0..3081cd9 100644 (file)
@@ -150,12 +150,26 @@ AM_CONDITIONAL(HAVE_W64_SYSTEM, test "$have_w64_system" = yes)
 
 # Checks for libraries.
 
-have_pthread=no
-AC_CHECK_LIB(pthread,pthread_create,have_pthread=yes)
-if test "$have_pthread" = yes; then
-  AC_DEFINE(HAVE_PTHREAD, ,[Define if we have pthread.])
+if test "$have_w32_system" = no; then
+  have_pthread=no
+  AC_CHECK_LIB(pthread,pthread_create,have_pthread=yes)
+  if test "$have_pthread" = yes; then
+    AC_DEFINE(HAVE_PTHREAD, ,[Define if we have pthread.])
+  fi
 fi
 
+NETLIBS=
+if test "$have_w32_system" = yes; then
+   if test "$have_w32ce_system" = yes; then
+     NETLIBS="-lws2 $NETLIBS"
+   else
+     # FIXME: Check why we need to use ws2_32 and document that.
+     NETLIBS="-lws2_32 $NETLIBS"
+   fi
+fi
+AC_SUBST(NETLIBS)
+
+
 # Checks for header files.
 AC_CHECK_HEADERS([sys/socket.h])
 
@@ -193,14 +207,16 @@ AM_CONDITIONAL(RUN_TESTS, test "$run_tests" = "yes")
 
 # Last check.
 die=no
-if test "$have_pthread" = "no"; then
-   die=yes
-   AC_MSG_NOTICE([[
+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
@@ -214,6 +230,7 @@ fi
 # Checks for library functions.
 AC_CHECK_FUNCS([select])
 
-AC_CONFIG_FILES([Makefile src/Makefile tests/Makefile])
+AC_CONFIG_FILES([Makefile src/Makefile w32/Makefile tests/Makefile])
 AC_CONFIG_FILES(src/npth-config, chmod +x src/npth-config)
+AC_CONFIG_FILES(w32/npth-config, chmod +x w32/npth-config)
 AC_OUTPUT
index 56768d9..0268e07 100644 (file)
 
 TESTS = t-mutex
 
+if HAVE_W32_SYSTEM
+AM_CPPFLAGS = -I$(top_srcdir)/w32
+AM_LDFLAGS =
+LDADD = ../w32/libnpth.la
+else
 AM_CPPFLAGS = -pthread -I$(top_srcdir)/src
 AM_LDFLAGS = -pthread
 LDADD = ../src/libnpth.la
+endif
 
 noinst_HEADERS = t-support.h
 
diff --git a/w32/Makefile.am b/w32/Makefile.am
new file mode 100644 (file)
index 0000000..4442f2d
--- /dev/null
@@ -0,0 +1,83 @@
+# Makefile.am - src/ Makefile for npth.
+# 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 Lesser General
+# Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+## Process this file with automake to produce Makefile.in
+
+EXTRA_DIST = npth-config.in npth.m4 npth.def
+bin_SCRIPTS = npth-config
+m4datadir = $(datadir)/aclocal
+m4data_DATA = npth.m4
+nodist_include_HEADERS = npth.h
+
+lib_LTLIBRARIES = libnpth.la
+
+libnpth_la_SOURCES = npth.h npth.c
+
+# AM_CPPFLAGS =
+# AM_CFLAGS =
+
+if HAVE_W32_SYSTEM
+RCCOMPILE = $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES)
+LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE)
+
+SUFFIXES = .rc .lo
+
+.rc.lo:
+       $(LTRCCOMPILE) -i "$<" -o "$@"
+
+npth_res = versioninfo.lo
+no_undefined = -no-undefined
+export_symbols = -export-symbols $(srcdir)/npth.def
+
+install-def-file:
+       $(INSTALL) $(srcdir)/npth.def $(DESTDIR)$(libdir)/npth.def
+
+uninstall-def-file:
+       -rm $(DESTDIR)$(libdir)/npth.def
+
+gpgme_deps = $(gpgme_res) npth.def
+
+else
+npth_res =
+no_undefined =
+export_symbols =
+install-def-file:
+uninstall-def-file:
+
+npth_deps =
+endif
+
+libnpth_la_LDFLAGS = $(no_undefined) $(export_symbols) \
+       -version-info \
+       @LIBNPTH_LT_CURRENT@:@LIBNPTH_LT_REVISION@:@LIBNPTH_LT_AGE@
+libnpth_la_DEPENDENCIES = $(srcdir)/libnpth.vers $(npth_deps)
+libnpth_la_LIBADD = @NETLIBS@
+
+install-data-local: install-def-file
+
+uninstall-local: uninstall-def-file
diff --git a/w32/npth-config.in b/w32/npth-config.in
new file mode 100644 (file)
index 0000000..374bff2
--- /dev/null
@@ -0,0 +1,140 @@
+#!/bin/sh
+# Copyright (C) 1999, 2002, 2003 Free Software Foundation, Inc.
+#
+# 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.
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+
+# Make sure that no weird locale setting messes up our sed regexps etc.
+LC_COLLATE=C
+LC_ALL=C
+LANG=C
+
+# NPTH's own cflags and libs
+cflags="-I@includedir@"
+libs="-L@libdir@"
+
+output=""
+
+usage()
+{
+    cat <<EOF
+Usage: npth-config [OPTIONS]
+Options:
+       [--thread={${thread_modules}}]
+       [--prefix]
+       [--exec-prefix]
+       [--version]
+        [--api-version]
+        [--host]
+       [--libs]
+       [--cflags]
+        [--get-gpg]
+        [--get-gpgsm]
+EOF
+    exit $1
+}
+
+if test $# -eq 0; then
+    usage 1 1>&2
+fi
+
+while test $# -gt 0; do
+    case "$1" in
+       -*=*)
+           optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'`
+           ;;
+       *)
+           optarg=
+           ;;
+    esac
+
+    case $1 in
+        --prefix=*)
+            # For compatibility reasons with old M4 macros, we ignore
+            # setting of prefix.
+            ;;
+        --prefix)
+           output="$output $prefix"
+           ;;
+        --exec-prefix=*)
+            ;;
+        --exec-prefix)
+           output="$output $exec_prefix"
+           ;;
+       --glib)
+           with_glib=yes
+           ;;
+        --version)
+            echo "@VERSION@"
+           exit 0
+           ;;
+        --api-version)
+           echo "@NPTH_CONFIG_API_VERSION@"
+           exit 0
+           ;;
+        --host)
+           echo "@NPTH_CONFIG_HOST@"
+           exit 0
+           ;;
+        --cflags)
+            result=
+            for i in $cflags ; do
+              skip=no
+              case $i in
+                  -I/usr/include|-I/include)
+                      skip=yes
+                      ;;
+                  -I*)
+                      for j in $result ; do
+                          if test x"$j" = x"$i" ; then
+                              skip=yes
+                              break;
+                          fi
+                      done
+                      ;;
+              esac
+              if test $skip = no ; then
+                  result="$result $i"
+              fi
+            done
+            output="$output $result"
+            ;;
+       --libs)
+            result=
+            for i in $libs "-lnpth"; do
+              skip=no
+              case $i in
+                  -L/usr/lib|-L/lib)
+                      skip=yes
+                      ;;
+                  -L*|-l*)
+                      for j in $result ; do
+                          if test x"$j" = x"$i" ; then
+                              skip=yes
+                              break;
+                          fi
+                      done
+                      ;;
+              esac
+              if test $skip = no ; then
+                  result="$result $i"
+              fi
+            done
+            output="$output $result"
+           ;;
+       *)
+            usage 1 1>&2
+           ;;
+    esac
+    shift
+done
+
+echo $output
diff --git a/w32/npth.c b/w32/npth.c
new file mode 100644 (file)
index 0000000..92d2317
--- /dev/null
@@ -0,0 +1,1735 @@
+/* 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 <http://www.gnu.org/licenses/>.  */
+
+/* We implement the join mechanism ourself.  */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <errno.h>
+
+#include "npth.h"
+
+#include <stdio.h>
+#define DEBUG_CALLS 1
+#define _npth_debug(x, ...) printf(__VA_ARGS__)
+
+#ifndef TEST
+#undef  DEBUG_CALLS
+#define DEBUG_CALLS 0
+#undef _npth_debug
+#define _npth_debug(x, ...)
+#endif
+
+/* This seems to be a common standard.  */
+#define THREAD_NAME_MAX 15
+
+/* The global lock that excludes all threads but one.  Note that this
+   implements the single-user-thread policy, but also protects all our
+   global data such as the thread_table.  */
+static CRITICAL_SECTION sceptre;
+
+typedef struct npth_impl_s *npth_impl_t;
+#define MAX_THREADS 1024
+#define INVALID_THREAD_ID 0
+/* Thread ID to thread context table.  We never allocate ID 0.  */
+static npth_impl_t thread_table[MAX_THREADS];
+
+/* The TLS index to store thread ID of the current thread.  Used to
+   make faster lookups of the thread data.  */
+DWORD tls_index;
+
+
+\f
+/* Map a windows error value (GetLastError) to a POSIX error value.  */
+static int
+map_error (int winerr)
+{
+  /* FIXME */
+  return EIO;
+}
+
+static int
+wait_for_single_object (HANDLE obj, DWORD msecs)
+{
+  DWORD res;
+
+  res = WaitForSingleObject(obj, msecs);
+
+  if (res == WAIT_ABANDONED)
+    return EDEADLK;
+  else if (res == WAIT_TIMEOUT)
+    return ETIMEDOUT;
+  else if (res == WAIT_FAILED)
+    return map_error (GetLastError());
+  else if (res != WAIT_OBJECT_0)
+    return EINTR;
+  else
+    return 0;
+}
+
+\f
+int
+npth_clock_gettime(struct timespec *tp)
+{
+  FILETIME ftime;
+  ULARGE_INTEGER systime;
+  unsigned long long usecs;
+
+  GetSystemTimeAsFileTime (&ftime);
+  systime.LowPart = ftime.dwLowDateTime;
+  systime.HighPart = ftime.dwHighDateTime;
+
+  /* systime.QuadPart has the 100-nanosecond intervals since Jan 1, 1601.  */
+  tp->tv_sec = systime.QuadPart / 10000000ULL;
+  tp->tv_nsec = (systime.QuadPart * 100ULL) % 1000000000ULL;
+  return 0;
+}
+
+
+static int
+calculate_timeout (const struct timespec *abstime, DWORD *msecs_r)
+{
+  struct timespec tp;
+  struct timespec tp_delta;
+  DWORD msecs;
+
+  npth_clock_gettime (&tp);
+  /* Make sure there is a positive time delta.  */
+  if (!(npth_timercmp (&tp, abstime, <)))
+    return ETIMEDOUT;
+
+  npth_timersub (abstime, &tp, &tp_delta);
+  /* Make sure to round up to at least one millisecond.  Note that
+     within reasonable timeouts and the above macros, we should always
+     end up with a positive wait time here.  */
+  msecs = (tp_delta.tv_sec * 1000) + (tp_delta.tv_nsec + 999999) / 1000000;
+  if (msecs < 1)
+    {
+      /* Log a critical error here.  */
+      return ETIMEDOUT;
+    }
+
+  *msecs_r = msecs;
+  return 0;
+}
+
+\f
+static void
+enter_npth (const char *function)
+{
+  int res;
+
+  if (DEBUG_CALLS)
+    _npth_debug (DEBUG_CALLS, "enter_npth (%s)\n",
+                function ? function : "unknown");
+  LeaveCriticalSection (&sceptre);
+}
+
+
+static void
+leave_npth (const char *function)
+{
+  EnterCriticalSection (&sceptre);
+
+  if (DEBUG_CALLS)
+    _npth_debug (DEBUG_CALLS, "leave_npth (%s)\n",
+                function ? function : "");
+}
+
+#define ENTER() enter_npth(__FUNCTION__)
+#define LEAVE() leave_npth(__FUNCTION__)
+
+\f
+struct npth_impl_s
+{
+  /* Usually there is one ref owned by the thread as long as it is
+     running, and one ref for everybody else as long as the thread is
+     joinable.  */
+  int refs;
+
+  HANDLE handle;
+
+  /* True if thread is detached.  */
+  int detached;
+
+  /* The start routine and arg.  */
+  void *(*start_routine) (void *);
+  void *start_arg;
+
+  char name[THREAD_NAME_MAX + 1];
+
+  /* Doubly-linked list for the waiter queue in condition
+     variables.  */
+  npth_impl_t next;
+  npth_impl_t *prev_ptr;
+
+  /* The event on which this thread waits when it is queued.  */
+  HANDLE event;
+
+  void *result;
+};
+
+
+static void
+dequeue_thread (npth_impl_t thread)
+{
+  /* Unlink the thread from any condition waiter queue.  */
+  if (thread->next)
+    {
+      thread->next->prev_ptr = thread->prev_ptr;
+      thread->next = NULL;
+    }
+  if (thread->prev_ptr)
+    {
+      *(thread->prev_ptr) = thread->next;
+      thread->prev_ptr = NULL;
+    }
+}
+
+
+/* Enqueue THREAD to come after the thread whose next pointer is
+   prev_ptr.  */
+static void
+enqueue_thread (npth_impl_t thread, npth_impl_t *prev_ptr)
+{
+  if (*prev_ptr)
+    (*prev_ptr)->prev_ptr = &thread->next;
+  thread->prev_ptr = prev_ptr;
+  thread->next = *prev_ptr;
+  *prev_ptr = thread;
+}
+
+
+static int
+find_thread (npth_t thread_id, npth_impl_t *thread)
+{
+  if (thread_id < 1 || thread_id >= MAX_THREADS)
+    return ESRCH;
+
+  if (! thread_table[thread_id])
+    return ESRCH;
+
+  *thread = thread_table[thread_id];
+  return 0;
+}
+
+
+static int
+new_thread (npth_t *thread_id)
+{
+  npth_impl_t thread;
+  int id;
+
+  /* ID 0 is never allocated.  */
+  for (id = 1; id < MAX_THREADS; id++)
+    if (! thread_table[id])
+      break;
+  if (id == MAX_THREADS)
+    return EAGAIN;
+
+  thread = malloc (sizeof (*thread));
+  if (! thread)
+    return errno;
+
+  thread->refs = 1;
+  thread->handle = INVALID_HANDLE_VALUE;
+  thread->detached = 0;
+  thread->start_routine = NULL;
+  thread->start_arg = NULL;
+  thread->next = NULL;
+  thread->prev_ptr = NULL;
+  /* We create the event when it is first needed (not all threads wait
+     on conditions).  */
+  thread->event = INVALID_HANDLE_VALUE;
+  memset (thread->name, '\0', sizeof (thread->name));
+
+  thread_table[id] = thread;
+
+  *thread_id = id;
+  return 0;
+}
+
+
+static void
+free_thread (npth_t thread_id)
+{
+  npth_impl_t thread = thread_table[thread_id];
+
+  if (thread->handle)
+    CloseHandle (thread->handle);
+
+  /* Unlink the thread from any condition waiter queue.  */
+  dequeue_thread (thread);
+
+  free (thread);
+
+  thread_table[thread_id] = NULL;
+}
+
+
+static void
+deref_thread (npth_t thread_id)
+{
+  npth_impl_t thread = thread_table[thread_id];
+
+  thread->refs -= 1;
+  if (thread->refs == 0)
+    free_thread (thread_id);
+}
+
+
+\f
+int
+npth_init (void)
+{
+  int err;
+  npth_t thread_id;
+  BOOL res;
+  HANDLE handle;
+  npth_impl_t thread;
+
+  InitializeCriticalSection (&sceptre);
+
+  /* Fake a thread table item for the main thread.  */
+  tls_index = TlsAlloc();
+  if (tls_index == TLS_OUT_OF_INDEXES)
+    return map_error (GetLastError());
+
+  err = new_thread(&thread_id);
+  if (err)
+    return err;
+
+  /* GetCurrentThread() is not usable by other threads, so it needs to
+     be duplicated.  */
+  res = DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
+                       GetCurrentProcess(), &handle,
+                       0, FALSE, DUPLICATE_SAME_ACCESS);
+  if (!res)
+    {
+      free_thread (thread_id);
+      return map_error(GetLastError());
+    }
+
+  thread = thread_table[thread_id];
+  thread->handle = handle;
+
+  if (! TlsSetValue(tls_index, (LPVOID) thread_id))
+    return map_error (GetLastError());
+
+  LEAVE();
+  return 0;
+}
+
+\f
+struct npth_attr_s
+{
+  int detachstate;
+};
+
+
+int
+npth_attr_init (npth_attr_t *attr_r)
+{
+  npth_attr_t attr;
+
+  attr = malloc (sizeof *attr);
+  if (!attr)
+    return errno;
+
+  attr->detachstate = NPTH_CREATE_JOINABLE;
+  *attr_r = attr;
+  return 0;
+}
+
+
+int
+npth_attr_destroy (npth_attr_t *attr)
+{
+  free (*attr);
+  return 0;
+}
+
+
+int
+npth_attr_getdetachstate (npth_attr_t *attr,
+                         int *detachstate)
+{
+  *detachstate = (*attr)->detachstate;
+  return 0;
+}
+
+
+int
+npth_attr_setdetachstate (npth_attr_t *attr, int detachstate)
+{
+  if (detachstate != NPTH_CREATE_JOINABLE
+      && detachstate != NPTH_CREATE_DETACHED)
+    return EINVAL;
+
+  (*attr)->detachstate = detachstate;
+  return 0;
+}
+
+
+int
+npth_getname_np (npth_t target_thread, char *buf, size_t buflen)
+{
+  npth_impl_t thread;
+  int err;
+
+  if (buflen < THREAD_NAME_MAX + 1)
+    return ERANGE;
+
+  err = find_thread (target_thread, &thread);
+  if (err)
+    return err;
+
+  strcpy (buf, thread->name);
+  return 0;
+}
+
+
+int
+npth_setname_np (npth_t target_thread, const char *name)
+{
+  npth_impl_t thread;
+  int err;
+
+  if (strlen(name) > THREAD_NAME_MAX)
+    return ERANGE;
+
+  err = find_thread (target_thread, &thread);
+  if (err)
+    return err;
+
+  strcpy (thread->name, name);
+  return 0;
+}
+
+
+static DWORD
+thread_start (void *arg)
+{
+  npth_t thread_id = (npth_t) arg;
+  npth_impl_t thread;
+  void *result;
+
+  if (! TlsSetValue(tls_index, (LPVOID) thread_id))
+    /* FIXME: There is not much we can do here.  */
+    ;
+
+  LEAVE();
+  /* We must be protected here, because we access the global
+     thread_table.  */
+
+  thread = thread_table[thread_id];
+  result = thread->start_routine (thread->start_arg);
+  /* We might not return here if the thread calls npth_exit().  */
+
+  thread->result = result;
+
+  /* Any joiner will be signaled once we terminate.  */
+  deref_thread (thread_id);
+
+  ENTER();
+
+  /* We can not return result, as that is a void*, not a DWORD.  */
+  return 0;
+}
+
+
+int
+npth_create (npth_t *newthread, const npth_attr_t *user_attr,
+            void *(*start_routine) (void *), void *start_arg)
+{
+  int err = 0;
+  npth_t thread_id = INVALID_THREAD_ID;
+  npth_attr_t attr;
+  int attr_allocated;
+  npth_impl_t thread;
+  HANDLE handle;
+
+  /* We must stay protected here, because we access the global
+     thread_table.  Also, creating a new thread is not a blocking
+     operation.  */
+  if (user_attr)
+    {
+      attr = *user_attr;
+      attr_allocated = 0;
+    }
+  else
+    {
+      err = npth_attr_init (&attr);
+      if (err)
+       return err;
+      attr_allocated = 1;
+    }
+
+  err = new_thread (&thread_id);
+  if (err)
+    goto err_out;
+
+  thread = thread_table[thread_id];
+  if (attr->detachstate == NPTH_CREATE_DETACHED)
+    thread->detached = 1;
+  else
+    thread->refs += 1;
+
+  thread->start_routine = start_routine;
+  thread->start_arg = start_arg;
+
+  handle = CreateThread (NULL, 0,
+                        (LPTHREAD_START_ROUTINE)thread_start,
+                        (void *) thread_id, CREATE_SUSPENDED,
+                        NULL);
+  if (handle == NULL)
+    {
+      err = map_error (GetLastError());
+      goto err_out;
+    }
+
+  thread->handle = handle;
+  *newthread = thread_id;
+
+  ResumeThread (thread->handle);
+
+  return 0;
+
+ err_out:
+  if (thread_id)
+    free_thread (thread_id);
+  if (attr_allocated)
+    npth_attr_destroy (&attr);
+
+  return err;
+}
+
+
+npth_t
+npth_self (void)
+{
+  LPVOID thread_id;
+
+  thread_id = TlsGetValue (tls_index);
+  if (thread_id == 0 && GetLastError() != ERROR_SUCCESS)
+    /* FIXME: Log the error.  */
+    ;
+  return (npth_t) thread_id;
+}
+
+
+int
+npth_yield (void)
+{
+  ENTER();
+  Sleep (0);
+  LEAVE();
+  return 0;
+}
+
+
+/* Not part of the public interface at the moment, thus static.  */
+static int
+npth_tryjoin_np (npth_t thread_id, void **thread_return)
+{
+  int err;
+  npth_impl_t thread;
+  int res;
+
+  err = find_thread (thread_id, &thread);
+  if (err)
+    return err;
+
+  if (thread->detached)
+    return EINVAL;
+
+  /* No need to allow competing threads to enter when we can get the
+     lock immediately.  */
+  err = wait_for_single_object (thread->handle, 0);
+  if (err == ETIMEDOUT)
+    err = EBUSY;
+
+  if (err)
+    return err;
+
+  if (thread_return)
+    *thread_return = thread->result;
+  deref_thread (thread_id);
+
+  return 0;
+}
+
+
+int
+npth_join (npth_t thread_id, void **thread_return)
+{
+  int err;
+  npth_impl_t thread;
+  int res;
+
+  /* No need to allow competing threads to enter when we can get the
+     lock immediately.  */
+  err = npth_tryjoin_np (thread_id, thread_return);
+  if (err != EBUSY)
+    return err;
+
+  err = find_thread (thread_id, &thread);
+  if (err)
+    return err;
+
+  if (thread->detached)
+    return EINVAL;
+
+  ENTER();
+  err = wait_for_single_object (thread->handle, INFINITE);
+  LEAVE();
+
+  if (err)
+    return err;
+
+  if (thread_return)
+    *thread_return = thread->result;
+  deref_thread (thread_id);
+
+  return 0;
+}
+
+
+int
+npth_detach (npth_t thread_id)
+{
+  int err;
+  npth_impl_t thread;
+
+  err = find_thread (thread_id, &thread);
+  if (err)
+    return err;
+
+  if (thread->detached)
+    return EINVAL;
+
+  /* The detached flag indicates to other thread that the outside
+     reference in the global thread table has been consumed.  */
+  thread->detached = 1;
+  deref_thread (thread_id);
+
+  return 0;
+}
+
+
+void
+npth_exit (void *retval)
+{
+  int err;
+  npth_t thread_id;
+  npth_impl_t thread;
+
+  thread_id = npth_self();
+  err = find_thread (thread_id, &thread);
+  if (err)
+    /* FIXME: log this?  */
+    return;
+
+  thread->result = retval;
+  /* Any joiner will be signaled once we terminate.  */
+  deref_thread (thread_id);
+
+  ENTER();
+
+  /* We can not use retval here, as that is a void*, not a DWORD.  */
+  ExitThread(0);
+
+  /* Never reached.  But just in case ExitThread does return... */
+  LEAVE();
+}
+
+
+\f
+int
+npth_key_create (npth_key_t *key,
+                void (*destr_function) (void *))
+{
+  DWORD idx;
+
+  if (destr_function)
+    return EOPNOTSUPP;
+
+  idx = TlsAlloc ();
+  if (idx == TLS_OUT_OF_INDEXES)
+    return map_error (GetLastError());
+
+  *key = idx;
+  return 0;
+}
+
+
+int
+npth_key_delete (npth_key_t key)
+{
+  BOOL res;
+
+  res = TlsFree (key);
+  if (res == 0)
+    return map_error (GetLastError());
+  return 0;
+}
+
+
+void *
+npth_getspecific (npth_key_t key)
+{
+  /* Pthread doesn't support error reporting beyond returning NULL for
+     an invalid key, which is also what TlsGetValue returns in that
+     case.  */
+  return TlsGetValue (key);
+}
+
+
+int
+npth_setspecific (npth_key_t key, const void *pointer)
+{
+  BOOL res;
+
+  res = TlsSetValue (key, (void *) pointer);
+  if (res == 0)
+    return map_error (GetLastError());
+
+  return 0;
+}
+
+\f
+struct npth_mutexattr_s
+{
+  int kind;
+};
+
+
+int
+npth_mutexattr_init (npth_mutexattr_t *attr_r)
+{
+  npth_mutexattr_t attr;
+
+  attr = malloc (sizeof *attr);
+  if (!attr)
+    return errno;
+
+  attr->kind = NPTH_MUTEX_DEFAULT;
+  *attr_r = attr;
+  return 0;
+}
+
+
+int
+npth_mutexattr_destroy (npth_mutexattr_t *attr)
+{
+  free (*attr);
+  *attr = NULL;
+  return 0;
+}
+
+
+int
+npth_mutexattr_gettype (const npth_mutexattr_t *attr,
+                       int *kind)
+{
+  *kind = (*attr)->kind;
+  return 0;
+}
+
+
+int
+npth_mutexattr_settype (npth_mutexattr_t *attr, int kind)
+{
+  if (kind != NPTH_MUTEX_NORMAL && kind != NPTH_MUTEX_RECURSIVE
+      && kind != NPTH_MUTEX_ERRORCHECK)
+    return EINVAL;
+
+  (*attr)->kind = kind;
+  return 0;
+}
+
+\f
+struct npth_mutex_s
+{
+  /* We have to use a mutex, not a CRITICAL_SECTION, because the
+     latter can not be used with timed waits.  */
+  HANDLE mutex;
+};
+
+
+int
+npth_mutex_init (npth_mutex_t *mutex_r, const npth_mutexattr_t *mutex_attr)
+{
+  npth_mutex_t mutex;
+
+  /* We can not check *mutex_r here, as it may contain random data.  */
+  mutex = malloc (sizeof (*mutex));
+  if (!mutex)
+    return errno;
+
+  /* We ignore MUTEX_ATTR.  */
+  mutex->mutex = CreateMutex (NULL, FALSE, NULL);
+  if (!mutex->mutex)
+    {
+      int err = map_error (GetLastError());
+      free (mutex);
+      return err;
+    }
+
+  *mutex_r = mutex;
+  return 0;
+}
+
+
+int
+npth_mutex_destroy (npth_mutex_t *mutex)
+{
+  BOOL res;
+
+  if (*mutex == 0 || *mutex == NPTH_MUTEX_INITIALIZER
+      || *mutex == NPTH_RECURSIVE_MUTEX_INITIALIZER_NP)
+    return EINVAL;
+
+  res = CloseHandle ((*mutex)->mutex);
+  if (res == 0)
+    return map_error (GetLastError());
+
+  free (*mutex);
+  *mutex = NULL;
+
+  return 0;
+}
+
+
+/* Must be called with global lock held.  */
+static int
+mutex_init_check (npth_mutex_t *mutex)
+{
+  int err;
+  npth_mutexattr_t attr;
+  int kind;
+
+  if (*mutex == 0)
+    return EINVAL;
+
+  if ((*mutex) == NPTH_MUTEX_INITIALIZER)
+    kind = NPTH_MUTEX_NORMAL;
+  else if ((*mutex) == NPTH_RECURSIVE_MUTEX_INITIALIZER_NP)
+    kind = NPTH_MUTEX_RECURSIVE;
+  else if ((*mutex) == NPTH_ERRORCHECK_MUTEX_INITIALIZER_NP)
+    kind = NPTH_MUTEX_ERRORCHECK;
+  else
+    /* Already initialized.  */
+    return 0;
+
+  /* Make sure we don't try again in case of error.  */
+  *mutex = 0;
+
+  err = npth_mutexattr_init (&attr);
+  if (err)
+    return err;
+
+  err = npth_mutexattr_settype (&attr, kind);
+  if (err)
+    {
+      npth_mutexattr_destroy (&attr);
+      return err;
+    }
+
+  err = npth_mutex_init (mutex, &attr);
+  npth_mutexattr_destroy (&attr);
+
+  return err;
+}
+
+
+int
+npth_mutex_lock (npth_mutex_t *mutex)
+{
+  int err;
+
+  /* While we are protected, let's check for a static initializer.  */
+  err = mutex_init_check (mutex);
+  if (err)
+    return err;
+
+  /* No need to allow competing threads to enter when we can get the
+     lock immediately.  */
+  err = npth_mutex_trylock (mutex);
+  if (err != EBUSY)
+    return err;
+
+  ENTER();
+  err = wait_for_single_object ((*mutex)->mutex, INFINITE);
+  LEAVE();
+
+  if (err)
+    return err;
+
+  return 0;
+}
+
+
+int
+npth_mutex_trylock (npth_mutex_t *mutex)
+{
+  int err;
+  DWORD res;
+
+  /* While we are protected, let's check for a static initializer.  */
+  err = mutex_init_check (mutex);
+  if (err)
+    return err;
+
+  /* We do not leave the global lock for a quick try.  */
+  err = wait_for_single_object ((*mutex)->mutex, 0);
+  if (err == ETIMEDOUT)
+    err = EBUSY;
+
+  if (err)
+    return err;
+
+  return 0;
+}
+
+
+int
+npth_mutex_timedlock (npth_mutex_t *mutex, const struct timespec *abstime)
+{
+  int err;
+  DWORD msecs;
+
+  /* While we are protected, let's check for a static initializer.  */
+  err = mutex_init_check (mutex);
+  if (err)
+    return err;
+
+  /* No need to allow competing threads to enter when we can get the
+     lock immediately.  */
+  err = npth_mutex_trylock (mutex);
+  if (err != EBUSY)
+    return err;
+
+  err = calculate_timeout (abstime, &msecs);
+  if (err)
+    return err;
+
+  ENTER();
+  err = wait_for_single_object ((*mutex)->mutex, msecs);
+  LEAVE();
+
+  if (err)
+    return err;
+
+  return 0;
+}
+
+
+int
+npth_mutex_unlock (npth_mutex_t *mutex)
+{
+  BOOL res;
+
+  if (*mutex == 0 || *mutex == NPTH_MUTEX_INITIALIZER
+      || *mutex == NPTH_RECURSIVE_MUTEX_INITIALIZER_NP)
+    return EINVAL;
+
+  res = ReleaseMutex ((*mutex)->mutex);
+  if (res == 0)
+    return map_error (GetLastError());
+
+  return 0;
+}
+
+\f
+struct npth_cond_s
+{
+  /* All conditions are protected by the global lock, so this is
+     simple.  */
+
+  /* The waiter queue.  */
+  npth_impl_t waiter;
+};
+
+
+int
+npth_cond_init (npth_cond_t *cond_r,
+               const npth_condattr_t *cond_attr)
+{
+  npth_cond_t cond;
+
+  if (cond_attr != NULL)
+    return EINVAL;
+
+  /* We can not check *cond_r here, as it may contain random data.  */
+  cond = malloc (sizeof (*cond));
+  if (!cond)
+    return errno;
+
+  cond->waiter = NULL;
+
+  *cond_r = cond;
+  return 0;
+}
+
+
+int
+npth_cond_destroy (npth_cond_t *cond)
+{
+  if (*cond == 0)
+    return EINVAL;
+
+  if ((*cond)->waiter)
+    return EBUSY;
+
+  free (*cond);
+  *cond = NULL;
+
+  return 0;
+}
+
+
+/* Must be called with global lock held.  */
+static int
+cond_init_check (npth_cond_t *cond)
+{
+  int err;
+
+  if (*cond == 0 || *cond == NPTH_COND_INITIALIZER)
+    return EINVAL;
+
+  if (*cond != NPTH_COND_INITIALIZER)
+    /* Already initialized.  */
+    return 0;
+
+  /* Make sure we don't try again in case of error.  */
+  *cond = 0;
+
+  err = npth_cond_init (cond, NULL);
+
+  return err;
+}
+
+
+int
+npth_cond_signal (npth_cond_t *cond)
+{
+  int err;
+  npth_impl_t thread;
+  DWORD res;
+
+  /* While we are protected, let's check for a static initializer.  */
+  err = cond_init_check (cond);
+  if (err)
+    return err;
+
+  if ((*cond)->waiter == INVALID_THREAD_ID)
+    return 0;
+
+  /* Dequeue the first thread and wake it up.  */
+  thread = (*cond)->waiter;
+  dequeue_thread (thread);
+
+  res = SetEvent (thread->event);
+  if (res == 0)
+    /* FIXME: An error here implies a mistake in the npth code.  Log it.  */
+    ;
+
+  /* Force the woken up thread into the mutex lock function (for the
+     mutex associated with the condition, which is why we have to
+     release the global lock here).  This helps to ensure fairness,
+     because otherwise our own thread might release and reacquire the
+     lock first (followed by removing the condition that lead to the
+     wakeup) and starve the woken up thread.  */
+  ENTER ();
+  Sleep (0);
+  LEAVE ();
+
+  return 0;
+}
+
+
+int
+npth_cond_broadcast (npth_cond_t *cond)
+{
+  int err;
+  npth_impl_t thread;
+  DWORD res;
+  int any;
+
+  /* While we are protected, let's check for a static initializer.  */
+  err = cond_init_check (cond);
+  if (err)
+    return err;
+
+  if ((*cond)->waiter == INVALID_THREAD_ID)
+    return 0;
+
+  while ((*cond)->waiter)
+    {
+      /* Dequeue the first thread and wake it up.  */
+      thread = (*cond)->waiter;
+      dequeue_thread (thread);
+
+      res = SetEvent (thread->event);
+      if (res == 0)
+       /* FIXME: An error here implies a mistake in the npth code.  Log it.  */
+       ;
+    }
+
+  /* Force the woken up threads into the mutex lock function (for the
+     mutex associated with the condition, which is why we have to
+     release the global lock here).  This helps to ensure fairness,
+     because otherwise our own thread might release and reacquire the
+     lock first (followed by removing the condition that lead to the
+     wakeup) and starve the woken up threads.  */
+  ENTER ();
+  Sleep (0);
+  LEAVE ();
+
+  return 0;
+}
+
+
+/* As a special exception in W32 NPTH, mutex can be NULL, in which
+   case the global lock doubles as the mutex protecting the condition.
+   This is used internally in the RW implementation as an
+   optimization.  Note that this is safe as long as the caller does
+   not yield to other threads (directly or indirectly) between
+   checking the condition and waiting on it.  */
+int
+npth_cond_wait (npth_cond_t *cond, npth_mutex_t *mutex)
+{
+  int err;
+  int err2;
+  BOOL bres;
+  npth_impl_t thread;
+  npth_impl_t *prev_ptr;
+
+  /* While we are protected, let's check for a static initializer.  */
+  err = cond_init_check (cond);
+  if (err)
+    return err;
+
+  err = find_thread (npth_self(), &thread);
+  if (err)
+    return err;
+
+  /* Ensure there is an event.  */
+  if (thread->event == INVALID_HANDLE_VALUE)
+    {
+      thread->event = CreateEvent (NULL, TRUE, FALSE, NULL);
+      if (thread->event == INVALID_HANDLE_VALUE)
+       return map_error (GetLastError());
+    }
+
+  /* Find end of queue and enqueue the thread.  */
+  prev_ptr = &(*cond)->waiter;
+  while (*prev_ptr)
+    prev_ptr = &(*prev_ptr)->next;
+  enqueue_thread (thread, prev_ptr);
+
+  /* Make sure the event is not signaled before releasing the mutex.  */
+  bres = ResetEvent (thread->event);
+  if (bres == 0)
+    /* Log an error.  */
+    ;
+
+  if (mutex)
+    {
+      err = npth_mutex_unlock (mutex);
+      if (err)
+       {
+         dequeue_thread (thread);
+         return err;
+       }
+    }
+
+  ENTER();
+  err = wait_for_single_object (thread->event, INFINITE);
+  LEAVE();
+
+  /* Make sure the thread is dequeued (in case of error).  */
+  dequeue_thread (thread);
+
+  if (mutex)
+    {
+      err2 = npth_mutex_lock (mutex);
+      if (err2)
+       /* FIXME: Log this at least.  */
+       ;
+    }
+
+  if (err)
+    return err;
+
+  return 0;
+}
+
+
+int
+npth_cond_timedwait (npth_cond_t *cond, npth_mutex_t *mutex,
+                    const struct timespec *abstime)
+{
+  int err;
+  int err2;
+  BOOL bres;
+  npth_impl_t thread;
+  npth_impl_t *prev_ptr;
+  DWORD msecs;
+
+  err = calculate_timeout (abstime, &msecs);
+  if (err)
+    return err;
+
+  /* While we are protected, let's check for a static initializer.  */
+  err = cond_init_check (cond);
+  if (err)
+    return err;
+
+  err = find_thread (npth_self(), &thread);
+  if (err)
+    return err;
+
+  /* Ensure there is an event.  */
+  if (thread->event == INVALID_HANDLE_VALUE)
+    {
+      thread->event = CreateEvent (NULL, TRUE, FALSE, NULL);
+      if (thread->event == INVALID_HANDLE_VALUE)
+       return map_error (GetLastError());
+    }
+
+  /* Make sure the event is not signaled.  */
+  bres = ResetEvent (thread->event);
+  if (bres == 0)
+    /* Log an error.  */
+    ;
+
+  /* Find end of queue and enqueue the thread.  */
+  prev_ptr = &(*cond)->waiter;
+  while (*prev_ptr)
+    prev_ptr = &(*prev_ptr)->next;
+  enqueue_thread (thread, prev_ptr);
+
+  err = npth_mutex_unlock (mutex);
+  if (err)
+    {
+      dequeue_thread (thread);
+      return err;
+    }
+
+  ENTER();
+  err = wait_for_single_object (thread->event, msecs);
+  LEAVE();
+
+  err2 = npth_mutex_lock (mutex);
+  if (err2)
+    /* FIXME: Log this at least.  */
+    ;
+
+  if (err)
+    return err;
+
+  return 0;
+}
+
+\f
+struct npth_rwlockattr_s
+{
+  int kind;
+};
+
+
+int
+npth_rwlockattr_init (npth_rwlockattr_t *attr_r)
+{
+  npth_rwlockattr_t attr;
+
+  attr = malloc (sizeof *attr);
+  if (!attr)
+    return errno;
+
+  attr->kind = NPTH_RWLOCK_DEFAULT_NP;
+  *attr_r = attr;
+  return 0;
+}
+
+
+int
+npth_rwlockattr_destroy (npth_rwlockattr_t *attr)
+{
+  free (*attr);
+  *attr = NULL;
+  return 0;
+}
+
+
+int
+npth_rwlockattr_gettype_np (const npth_rwlockattr_t *attr,
+                           int *kind)
+{
+  *kind = (*attr)->kind;
+  return 0;
+}
+
+
+int
+npth_rwlockattr_settype_np (npth_rwlockattr_t *attr, int kind)
+{
+  if (kind != NPTH_RWLOCK_PREFER_READER_NP
+      && kind != NPTH_RWLOCK_PREFER_WRITER_NP
+      && kind != NPTH_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP)
+    return EINVAL;
+
+  (*attr)->kind = kind;
+  return 0;
+}
+
+
+struct npth_rwlock_s
+{
+  /* Objects are protected by the global lock, so no lock here
+     necessary.  This is even true for the condition (by specifying
+     NULL as the mutex in npth_cond_wait and npth_cond_timedwait).  */
+
+  /* True if we prefer writers over readers.  */
+  int prefer_writer;
+
+  /* Readers who want the lock wait on this condition, which is
+     broadcast when the last writer goes away.  */
+  npth_cond_t reader_wait;
+
+  /* The number of readers waiting on the condition.  */
+  int nr_readers_queued;
+
+  /* The number of current readers.  */
+  int nr_readers;
+
+  /* Writers who want the lock wait on this condition, which is
+     signaled when the current writer or last reader goes away.  */
+  npth_cond_t writer_wait;
+
+  /* The number of queued writers.  */
+  int nr_writers_queued;
+
+  /* The number of current writers.  This is either 1 (then nr_readers
+     is 0) or it is 0.  At unlock time this value tells us if the
+     current lock holder is a writer or a reader.  */
+  int nr_writers;
+};
+
+
+int
+npth_rwlock_init (npth_rwlock_t *rwlock_r,
+                 const npth_rwlockattr_t *user_attr)
+{
+  int err;
+  npth_rwlock_t rwlock;
+  npth_rwlockattr_t attr;
+  int attr_allocated;
+
+  if (user_attr != NULL)
+    {
+      attr = *user_attr;
+      attr_allocated = 0;
+    }
+  else
+    {
+      err = npth_rwlockattr_init (&attr);
+      if (err)
+       return err;
+    }
+
+  /* We can not check *rwlock_r here, as it may contain random data.  */
+  rwlock = malloc (sizeof (*rwlock));
+  if (!rwlock)
+    {
+      err = errno;
+      goto err_out;
+    }
+
+  rwlock->prefer_writer = (attr->kind == NPTH_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
+
+  err = npth_cond_init (&rwlock->reader_wait, NULL);
+  if (err)
+    {
+      free (rwlock);
+      goto err_out;
+    }
+
+  err = npth_cond_init (&rwlock->writer_wait, NULL);
+  if (err)
+    {
+      npth_cond_destroy (&rwlock->reader_wait);
+      free (rwlock);
+      goto err_out;
+    }
+
+  rwlock->nr_readers = 0;
+  rwlock->nr_readers_queued = 0;
+  rwlock->nr_writers = 0;
+  rwlock->nr_writers_queued = 0;
+
+  *rwlock_r = rwlock;
+
+ err_out:
+  if (attr_allocated)
+    npth_rwlockattr_destroy (&attr);
+  return err;
+}
+
+
+/* Must be called with global lock held.  */
+static int
+rwlock_init_check (npth_rwlock_t *rwlock)
+{
+  int err;
+  npth_rwlockattr_t attr;
+  int kind;
+
+  if (*rwlock == 0)
+    return EINVAL;
+
+  if ((*rwlock) == NPTH_RWLOCK_INITIALIZER)
+    kind = NPTH_RWLOCK_PREFER_READER_NP;
+  if ((*rwlock) == NPTH_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP)
+    kind = NPTH_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP;
+  else
+    /* Already initialized.  */
+    return 0;
+
+  /* Make sure we don't try again in case of error.  */
+  *rwlock = 0;
+
+  err = npth_rwlockattr_init (&attr);
+  if (err)
+    return err;
+
+  err = npth_rwlockattr_settype_np (&attr, kind);
+  if (err)
+    {
+      npth_rwlockattr_destroy (&attr);
+      return err;
+    }
+
+  err = npth_rwlock_init (rwlock, &attr);
+  npth_rwlockattr_destroy (&attr);
+
+  return err;
+}
+
+
+int
+npth_rwlock_destroy (npth_rwlock_t *rwlock)
+{
+  int err;
+
+  if (*rwlock == 0 || *rwlock == NPTH_RWLOCK_INITIALIZER)
+    return EINVAL;
+
+  if ((*rwlock)->nr_writers || (*rwlock)->nr_readers || (*rwlock)->nr_writers_queued
+      || (*rwlock)->nr_readers_queued)
+    return EBUSY;
+
+  err = npth_cond_destroy (&(*rwlock)->reader_wait);
+  if (err)
+    /* FIXME: Log this.  */
+    ;
+
+  err = npth_cond_destroy (&(*rwlock)->writer_wait);
+  if (err)
+    /* FIXME: Log this.  */
+    ;
+
+  free (rwlock);
+
+  *rwlock = NULL;
+  return 0;
+}
+
+
+int
+npth_rwlock_tryrdlock (npth_rwlock_t *rwlock)
+{
+  if ((*rwlock)->nr_writers)
+    return EBUSY;
+
+  if ((*rwlock)->nr_writers_queued && (*rwlock)->prefer_writer)
+    return EBUSY;
+
+  (*rwlock)->nr_readers++;
+  return 0;
+}
+
+
+int
+npth_rwlock_rdlock (npth_rwlock_t *rwlock)
+{
+  int err;
+
+  while (1)
+    {
+      /* Quick check.  */
+      err = npth_rwlock_tryrdlock (rwlock);
+      if (err != EBUSY)
+       return err;
+
+      (*rwlock)->nr_readers_queued++;
+      err = npth_cond_wait (&(*rwlock)->reader_wait, NULL);
+      (*rwlock)->nr_readers_queued--;
+      if (err)
+       return err;
+    }
+}
+
+int
+npth_rwlock_timedrdlock (npth_rwlock_t *rwlock,
+                        const struct timespec *abstime)
+{
+  int err;
+
+  while (1)
+    {
+      /* Quick check.  */
+      err = npth_rwlock_tryrdlock (rwlock);
+      if (err != EBUSY)
+       return err;
+
+      (*rwlock)->nr_readers_queued++;
+      err = npth_cond_timedwait (&(*rwlock)->reader_wait, NULL, abstime);
+      (*rwlock)->nr_readers_queued--;
+      if (err)
+       return err;
+    }
+}
+
+int
+npth_rwlock_trywrlock (npth_rwlock_t *rwlock)
+{
+  if ((*rwlock)->nr_writers)
+    return EBUSY;
+
+  if ((*rwlock)->nr_readers)
+    return EBUSY;
+
+  (*rwlock)->nr_writers = 1;
+  return 0;
+}
+
+
+int
+npth_rwlock_wrlock (npth_rwlock_t *rwlock)
+{
+  int err;
+
+  while (1)
+    {
+      /* Quick check.  */
+      err = npth_rwlock_trywrlock (rwlock);
+      if (err != EBUSY)
+       return err;
+
+      (*rwlock)->nr_writers_queued++;
+      err = npth_cond_wait (&(*rwlock)->writer_wait, NULL);
+      (*rwlock)->nr_writers_queued--;
+      if (err)
+       return err;
+    }
+}
+
+
+int
+npth_rwlock_timedwrlock (npth_rwlock_t *rwlock,
+                        const struct timespec *abstime)
+{
+  int err;
+
+  while (1)
+    {
+      /* Quick check.  */
+      err = npth_rwlock_trywrlock (rwlock);
+      if (err != EBUSY)
+       return err;
+
+      (*rwlock)->nr_writers_queued++;
+      err = npth_cond_timedwait (&(*rwlock)->writer_wait, NULL, abstime);
+      (*rwlock)->nr_writers_queued--;
+      if (err)
+       return err;
+    }
+}
+
+
+int
+npth_rwlock_unlock (npth_rwlock_t *rwlock)
+{
+  int err;
+
+  if ((*rwlock)->nr_writers)
+    /* We are the writer.  */
+    (*rwlock)->nr_writers = 0;
+  else
+    /* We are the reader.  */
+    (*rwlock)->nr_readers--;
+
+  if ((*rwlock)->nr_readers == 0)
+    {
+      if ((*rwlock)->nr_writers_queued)
+       {
+         err = npth_cond_signal (&(*rwlock)->writer_wait);
+         if (err)
+           return err;
+       }
+      else if ((*rwlock)->nr_readers_queued)
+       {
+         err = npth_cond_broadcast (&(*rwlock)->reader_wait);
+         return err;
+       }
+    }
+  return 0;
+}
+
+\f
+/* Standard POSIX Replacement API */
+
+int
+npth_usleep(unsigned int usec)
+{
+  ENTER();
+  Sleep((usec + 999) / 1000);
+  LEAVE();
+  return 0;
+}
+
+
+unsigned int
+npth_sleep(unsigned int sec)
+{
+  ENTER();
+  Sleep (sec * 1000);
+  LEAVE();
+  return 0;
+}
+
+
+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)
+{
+  return EOPNOTSUPP;
+}
+
+
+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;
+}
+
+
+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)
+{
+  return EOPNOTSUPP;
+}
+
+
+int
+npth_sendmsg (int fd, const struct msghdr *msg, int flags)
+{
+  return EOPNOTSUPP;
+}
diff --git a/w32/npth.def b/w32/npth.def
new file mode 100644 (file)
index 0000000..7014c3a
--- /dev/null
@@ -0,0 +1,81 @@
+; npth.def - List of symbols to export.
+; 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 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.
+;
+; 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, write to the Free Software
+; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+
+EXPORTS
+    npth_init                          @1
+    npth_attr_init                     @2
+    npth_attr_destroy                  @3
+    npth_attr_getdetachstate           @4
+    npth_attr_setdetachstate           @5
+    npth_getname_np                    @6
+    npth_setname_np                    @7
+    npth_create                                @8
+    npth_self                          @9
+    npth_yield                         @10
+    npth_join                          @11
+    npth_detach                                @12
+    npth_exit                          @13
+    npth_key_create                    @14
+    npth_key_delete                    @15
+    npth_getspecific                   @16
+    npth_setspecific                   @17
+    npth_mutexattr_init                        @18
+    npth_mutexattr_destroy             @19
+    npth_mutexattr_gettype             @20
+    npth_mutexattr_settype             @21
+    npth_mutex_init                    @22
+    npth_mutex_destroy                 @23
+    npth_mutex_trylock                 @24
+    npth_mutex_lock                    @25
+    npth_mutex_timedlock               @26
+    npth_mutex_unlock                  @27
+    npth_rwlockattr_init               @28
+    npth_rwlockattr_destroy            @29
+    npth_rwlockattr_gettype_np         @30
+    npth_rwlockattr_settype_np         @31
+    npth_rwlock_init                   @32
+    npth_rwlock_destroy                        @33
+    npth_rwlock_tryrdlock              @34
+    npth_rwlock_rdlock                 @35
+    npth_rwlock_timedrdlock            @36
+    npth_rwlock_trywrlock              @37
+    npth_rwlock_wrlock                 @38
+    npth_rwlock_timedwrlock            @39
+    npth_rwlock_unlock                 @40
+    npth_cond_init                     @41
+    npth_cond_broadcast                        @42
+    npth_cond_signal                   @43
+    npth_cond_destroy                  @44
+    npth_cond_wait                     @45
+    npth_cond_timedwait                        @46
+    npth_usleep                                @47
+    npth_sleep                         @48
+    npth_waitpid                       @49
+    npth_system                                @50
+    npth_connect                       @51
+    npth_accept                                @52
+    npth_select                                @53
+    npth_read                          @54
+    npth_write                         @55
+    npth_recvmsg                       @56
+    npth_sendmsg                       @57
+    npth_clock_gettime                 @58
+
+; END
diff --git a/w32/npth.h b/w32/npth.h
new file mode 100644 (file)
index 0000000..786202d
--- /dev/null
@@ -0,0 +1,228 @@
+/* npth.h - 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 <http://www.gnu.org/licenses/>.  */
+
+#ifndef _NPTH_H
+#define _NPTH_H
+
+#include <sys/types.h>
+#include <time.h>
+#include <errno.h>
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <windows.h>
+
+#ifdef __cplusplus
+extern "C" {
+#if 0 /* (Keep Emacsens' auto-indent happy.) */
+}
+#endif
+#endif
+
+struct msghdr;
+
+struct timespec {
+  long tv_sec;                 /* seconds */
+  long tv_nsec;                /* nanoseconds */
+};
+
+/* These are new in MSVC 10.  */
+#ifndef ETIMEDOUT
+#define ETIMEDOUT 138
+#endif
+#ifndef EOPNOTSUPP
+#define EOPNOTSUPP 130
+#endif
+
+int npth_init (void);
+
+typedef struct npth_attr_s *npth_attr_t;
+typedef unsigned long int npth_t;
+typedef struct npth_mutexattr_s *npth_mutexattr_t;
+typedef struct npth_mutex_s *npth_mutex_t;
+typedef struct npth_rwlockattr_s *npth_rwlockattr_t;
+typedef struct npth_rwlock_s *npth_rwlock_t;
+typedef struct npth_condattr_s *npth_condattr_t;
+typedef struct npth_cond_s *npth_cond_t;
+
+int npth_attr_init (npth_attr_t *attr);
+int npth_attr_destroy (npth_attr_t *attr);
+#define NPTH_CREATE_JOINABLE 0
+#define NPTH_CREATE_DETACHED 1
+int npth_attr_getdetachstate(npth_attr_t *attr, int *detachstate);
+int npth_attr_setdetachstate(npth_attr_t *attr, int detachstate);
+int npth_getname_np (npth_t target_thread, char *buf, size_t buflen);
+int npth_setname_np (npth_t target_thread, const char *name);
+
+int npth_create (npth_t *newthread, const npth_attr_t *attr,
+                void *(*start_routine) (void *), void *arg);
+
+npth_t npth_self (void);
+
+int npth_yield (void);
+
+int npth_join (npth_t th, void **thread_return);
+int npth_detach (npth_t th);
+void npth_exit (void *retval);
+
+typedef DWORD npth_key_t;
+int npth_key_create (npth_key_t *key,
+                    void (*destr_function) (void *));
+int npth_key_delete (npth_key_t key);
+void *npth_getspecific (npth_key_t key);
+int npth_setspecific (npth_key_t key, const void *pointer);
+
+int npth_mutexattr_init (npth_mutexattr_t *attr);
+int npth_mutexattr_destroy (npth_mutexattr_t *attr);
+int npth_mutexattr_gettype (const npth_mutexattr_t *attr,
+                           int *kind);
+int npth_mutexattr_settype (npth_mutexattr_t *attr, int kind);
+#define NPTH_MUTEX_NORMAL 0
+#define NPTH_MUTEX_RECURSIVE 1
+#define NPTH_MUTEX_ERRORCHECK 2
+#define NPTH_MUTEX_DEFAULT NPTH_MUTEX_NORMAL
+
+#define NPTH_MUTEX_INITIALIZER ((npth_mutex_t) -1)
+#define NPTH_RECURSIVE_MUTEX_INITIALIZER_NP ((npth_mutex_t) -2)
+#define NPTH_ERRORCHECK_MUTEX_INITIALIZER_NP ((npth_mutex_t) -3)
+int npth_mutex_init (npth_mutex_t *mutex, const npth_mutexattr_t *mutexattr);
+int npth_mutex_destroy (npth_mutex_t *mutex);
+int npth_mutex_trylock(npth_mutex_t *mutex);
+int npth_mutex_lock(npth_mutex_t *mutex);
+int npth_mutex_timedlock(npth_mutex_t *mutex, const struct timespec *abstime);
+int npth_mutex_unlock(npth_mutex_t *mutex);
+
+int npth_rwlockattr_init (npth_rwlockattr_t *attr);
+int npth_rwlockattr_destroy (npth_rwlockattr_t *attr);
+int npth_rwlockattr_gettype_np (const npth_rwlockattr_t *attr,
+                               int *kind);
+int npth_rwlockattr_settype_np (npth_rwlockattr_t *attr, int kind);
+#define NPTH_RWLOCK_PREFER_READER_NP 0
+#define NPTH_RWLOCK_PREFER_WRITER_NP 1
+#define NPTH_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP 2
+#define NPTH_RWLOCK_DEFAULT_NP NPTH_RWLOCK_PREFER_READER_NP
+#define NPTH_RWLOCK_INITIALIZER ((npth_rwlock_t) -1)
+#define NPTH_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP ((npth_rwlock_t) -2)
+
+/* For now, we don't support any rwlock attributes.  */
+int npth_rwlock_init (npth_rwlock_t *rwlock,
+                     const npth_rwlockattr_t *attr);
+int npth_rwlock_destroy (npth_rwlock_t *rwlock);
+int npth_rwlock_tryrdlock (npth_rwlock_t *rwlock);
+int npth_rwlock_rdlock (npth_rwlock_t *rwlock);
+int npth_rwlock_timedrdlock (npth_rwlock_t *rwlock,
+                            const struct timespec *abstime);
+int npth_rwlock_trywrlock (npth_rwlock_t *rwlock);
+
+int npth_rwlock_wrlock (npth_rwlock_t *rwlock);
+int npth_rwlock_timedwrlock (npth_rwlock_t *rwlock,
+                            const struct timespec *abstime);
+int npth_rwlock_unlock (npth_rwlock_t *rwlock);
+
+#define NPTH_COND_INITIALIZER ((npth_cond_t) -1)
+/* For now, we don't support any cond attributes.  */
+int npth_cond_init (npth_cond_t *cond,
+                   const npth_condattr_t *cond_attr);
+int npth_cond_broadcast (npth_cond_t *cond);
+int npth_cond_signal (npth_cond_t *cond);
+int npth_cond_destroy (npth_cond_t *cond);
+int npth_cond_wait (npth_cond_t *cond, npth_mutex_t *mutex);
+int npth_cond_timedwait (npth_cond_t *cond, npth_mutex_t *mutex,
+                        const struct timespec *abstime);
+
+int npth_usleep(unsigned int usec);
+unsigned int npth_sleep(unsigned int sec);
+
+pid_t npth_waitpid(pid_t pid, int *status, int options);
+int npth_system(const char *cmd);
+
+#if 0
+/* We do not support this on windows.  */
+int npth_sigmask(int how, const sigset_t *set, sigset_t *oldset);
+int npth_sigwait(const sigset_t *set, int *sig);
+#endif
+
+int npth_connect(int s, const struct sockaddr *addr, socklen_t addrlen);
+int npth_accept(int s, struct sockaddr *addr, socklen_t *addrlen);
+int npth_select(int nfd, fd_set *rfds, fd_set *wfds, fd_set *efds,
+               struct timeval *timeout);
+#if 0
+/* We do not support this on windows.  */
+int npth_pselect(int nfd, fd_set *rfds, fd_set *wfds, fd_set *efds,
+                const struct timespec *timeout, const sigset_t *sigmask);
+#endif
+ssize_t npth_read(int fd, void *buf, size_t nbytes);
+ssize_t npth_write(int fd, const void *buf, size_t nbytes);
+int npth_recvmsg (int fd, struct msghdr *msg, int flags);
+int npth_sendmsg (int fd, const struct msghdr *msg, int flags);
+
+int npth_clock_gettime(struct timespec *tp);
+
+/* CMP may be ==, < or >.  Do not use <= or >=.  */
+#define npth_timercmp(t1, t2, cmp)                                     \
+  (((t1)->tv_sec == (t2)->tv_sec) ?                                    \
+   ((t1)->tv_nsec cmp (t2)->tv_nsec) :                                 \
+   ((t1)->tv_sec cmp (t2)->tv_sec))
+#define npth_timeradd(t1, t2, result)                                  \
+  do {                                                                 \
+    (result)->tv_sec = (t1)->tv_sec + (t2)->tv_sec;                    \
+    (result)->tv_nsec = (t1)->tv_nsec + (t2)->tv_nsec;                 \
+    if ((result)->tv_nsec >= 1000000000)                               \
+      {                                                                        \
+       ++(result)->tv_sec;                                             \
+       (result)->tv_nsec -= 1000000000;                                \
+      }                                                                        \
+  } while (0)
+#define npth_timersub(t1, t2, result)                                  \
+  do {                                                                 \
+    (result)->tv_sec = (t1)->tv_sec - (t2)->tv_sec;                    \
+    (result)->tv_nsec = (t1)->tv_nsec - (t2)->tv_nsec;                 \
+    if ((result)->tv_nsec < 0) {                                       \
+      --(result)->tv_sec;                                              \
+      (result)->tv_nsec += 1000000000;                                 \
+    }                                                                  \
+  } while (0)
+
+\f
+#if 0
+/* We do not support this on windows.  */
+void npth_sigev_init (void);
+void npth_sigev_add (int signum);
+void npth_sigev_fini (void);
+sigset_t *npth_sigev_sigmask (void);
+int npth_sigev_get_pending (int *r_signum);
+#endif
+
+#if 0 /* (Keep Emacsens' auto-indent happy.) */
+{
+#endif
+#ifdef __cplusplus
+}
+#endif
+#endif /*_NPTH_H*/
diff --git a/w32/npth.m4 b/w32/npth.m4
new file mode 100644 (file)
index 0000000..8058ba4
--- /dev/null
@@ -0,0 +1,98 @@
+# npth.m4 - autoconf macro to detect NPTH.
+# Copyright (C) 2002, 2003, 2004, 2011 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.
+
+AC_DEFUN([_AM_PATH_NPTH_CONFIG],
+[ AC_ARG_WITH(npth-prefix,
+            AC_HELP_STRING([--with-npth-prefix=PFX],
+                           [prefix where NPTH is installed (optional)]),
+     npth_config_prefix="$withval", npth_config_prefix="")
+  if test "x$npth_config_prefix" != x ; then
+      NPTH_CONFIG="$npth_config_prefix/bin/npth-config"
+  fi
+  AC_PATH_PROG(NPTH_CONFIG, npth-config, no)
+
+  if test "$NPTH_CONFIG" != "no" ; then
+    npth_version=`$NPTH_CONFIG --version`
+  fi
+  npth_version_major=`echo $npth_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'`
+  npth_version_minor=`echo $npth_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'`
+  npth_version_micro=`echo $npth_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\3/'`
+])
+
+dnl AM_PATH_NPTH([MINIMUM-VERSION,
+dnl               [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]])
+dnl Test for libnpth and define NPTH_CFLAGS and NPTH_LIBS.
+dnl
+AC_DEFUN([AM_PATH_NPTH],
+[ AC_REQUIRE([_AM_PATH_NPTH_CONFIG])dnl
+  tmp=ifelse([$1], ,1:0.4.2,$1)
+  if echo "$tmp" | grep ':' >/dev/null 2>/dev/null ; then
+     req_npth_api=`echo "$tmp"     | sed 's/\(.*\):\(.*\)/\1/'`
+     min_npth_version=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\2/'`
+  else
+     req_npth_api=0
+     min_npth_version="$tmp"
+  fi
+
+  AC_MSG_CHECKING(for NPTH - version >= $min_npth_version)
+  ok=no
+  if test "$NPTH_CONFIG" != "no" ; then
+    req_major=`echo $min_npth_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'`
+    req_minor=`echo $min_npth_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'`
+    req_micro=`echo $min_npth_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'`
+    if test "$npth_version_major" -gt "$req_major"; then
+        ok=yes
+    else
+        if test "$npth_version_major" -eq "$req_major"; then
+            if test "$npth_version_minor" -gt "$req_minor"; then
+               ok=yes
+            else
+               if test "$npth_version_minor" -eq "$req_minor"; then
+                   if test "$npth_version_micro" -ge "$req_micro"; then
+                     ok=yes
+                   fi
+               fi
+            fi
+        fi
+    fi
+  fi
+  if test $ok = yes; then
+     # If we have a recent NPTH, we should also check that the
+     # API is compatible.
+     if test "$req_npth_api" -gt 0 ; then
+        tmp=`$NPTH_CONFIG --api-version 2>/dev/null || echo 0`
+        if test "$tmp" -gt 0 ; then
+           if test "$req_npth_api" -ne "$tmp" ; then
+             ok=no
+           fi
+        fi
+     fi
+  fi
+  if test $ok = yes; then
+    NPTH_CFLAGS=`$NPTH_CONFIG --cflags`
+    NPTH_LIBS=`$NPTH_CONFIG --libs`
+    AC_MSG_RESULT(yes)
+    ifelse([$2], , :, [$2])
+  else
+    NPTH_CFLAGS=""
+    NPTH_LIBS=""
+    AC_MSG_RESULT(no)
+    ifelse([$3], , :, [$3])
+  fi
+  AC_SUBST(NPTH_CFLAGS)
+  AC_SUBST(NPTH_LIBS)
+])