about summary refs log tree commit diff
path: root/nptl
diff options
context:
space:
mode:
Diffstat (limited to 'nptl')
-rw-r--r--nptl/ChangeLog56
-rw-r--r--nptl/Makefile59
-rw-r--r--nptl/Versions1
-rw-r--r--nptl/cleanup_routine.c28
-rw-r--r--nptl/sysdeps/i386/tcb-offsets.sym3
-rw-r--r--nptl/sysdeps/pthread/pthread.h137
-rw-r--r--nptl/sysdeps/unix/sysv/linux/i386/i486/sem_timedwait.S78
-rw-r--r--nptl/sysdeps/unix/sysv/linux/i386/i486/sem_wait.S62
-rw-r--r--nptl/sysdeps/unix/sysv/linux/sem_timedwait.c12
-rw-r--r--nptl/sysdeps/unix/sysv/linux/sem_wait.c10
-rw-r--r--nptl/sysdeps/unix/sysv/linux/x86_64/sem_timedwait.S75
-rw-r--r--nptl/sysdeps/unix/sysv/linux/x86_64/sem_wait.S66
-rw-r--r--nptl/sysdeps/x86_64/tcb-offsets.sym3
-rw-r--r--nptl/tst-cancel1.c2
-rw-r--r--nptl/tst-cancel12.c127
-rw-r--r--nptl/tst-cancel13.c129
-rw-r--r--nptl/tst-cancel14.c136
-rw-r--r--nptl/tst-cancel15.c141
-rw-r--r--nptl/tst-cancelx1.c1
-rw-r--r--nptl/tst-cancelx10.c1
-rw-r--r--nptl/tst-cancelx11.c1
-rw-r--r--nptl/tst-cancelx12.c1
-rw-r--r--nptl/tst-cancelx13.c1
-rw-r--r--nptl/tst-cancelx14.c1
-rw-r--r--nptl/tst-cancelx15.c1
-rw-r--r--nptl/tst-cancelx2.c1
-rw-r--r--nptl/tst-cancelx3.c1
-rw-r--r--nptl/tst-cancelx4.c1
-rw-r--r--nptl/tst-cancelx5.c1
-rw-r--r--nptl/tst-cancelx6.c1
-rw-r--r--nptl/tst-cancelx7.c1
-rw-r--r--nptl/tst-cancelx8.c1
-rw-r--r--nptl/tst-cancelx9.c1
-rw-r--r--nptl/tst-cleanup0.c17
-rw-r--r--nptl/tst-cleanup1.c6
-rw-r--r--nptl/tst-cleanupx0.c1
-rw-r--r--nptl/tst-cleanupx0.expect3
-rw-r--r--nptl/tst-cleanupx1.c1
-rw-r--r--nptl/tst-cleanupx2.c1
-rw-r--r--nptl/tst-cleanupx3.c1
40 files changed, 1099 insertions, 71 deletions
diff --git a/nptl/ChangeLog b/nptl/ChangeLog
index 0d0c5682a6..aa18be29a2 100644
--- a/nptl/ChangeLog
+++ b/nptl/ChangeLog
@@ -1,3 +1,59 @@
+2003-06-07  Ulrich Drepper  <drepper@redhat.com>
+
+	* cleanup_routine.c: New file.
+	* Versions (libpthread) [GLIBC_2.3.3]: Add __pthread_cleanup_routine.
+	* sysdeps/pthread/pthread.h: Add support for fully exception-based
+	cleanup handling.
+	* Makefile (libpthread-routines): Add cleanup_routine.
+	Add more CFLAGS variables to compile with exceptions.  Add comments
+	why which file needs unwind tables.
+	(tests) [have-forced-unwind==yes]: Add tst-cancelx* and tst-cleanupx*
+	tests.
+	* tst-cancelx1.c: New file.
+	* tst-cancelx2.c: New file.
+	* tst-cancelx3.c: New file.
+	* tst-cancelx4.c: New file.
+	* tst-cancelx5.c: New file.
+	* tst-cancelx6.c: New file.
+	* tst-cancelx7.c: New file.
+	* tst-cancelx8.c: New file.
+	* tst-cancelx9.c: New file.
+	* tst-cancelx10.c: New file.
+	* tst-cancelx11.c: New file.
+	* tst-cancelx12.c: New file.
+	* tst-cancelx13.c: New file.
+	* tst-cancelx14.c: New file.
+	* tst-cancelx15.c: New file.
+	* tst-cleanupx0.c: New file.
+	* tst-cleanupx0.expect: New file.
+	* tst-cleanupx1.c: New file.
+	* tst-cleanupx2.c: New file.
+	* tst-cleanupx3.c: New file.
+
+	* tst-cleanup0.c: Make standard compliant.
+	* tst-cleanup1.c: Likewise.
+
+	* sysdeps/unix/sysv/linux/sem_timedwait.c: Add cancellation support.
+	* sysdeps/unix/sysv/linux/sem_wait.c: Likewise.
+	* sysdeps/unix/sysv/linux/i386/i486/sem_timedwait.S: Likewise.
+	* sysdeps/unix/sysv/linux/i386/i486/sem_wait.S: Likewise.
+	* sysdeps/unix/sysv/linux/x86_64/sem_timedwait.S: Likewise.
+	* sysdeps/unix/sysv/linux/x86_64/sem_wait.S: Likewise.
+	* sysdeps/i386/tcb-offsets.sym: Add RESULT, CANCELHANDLING, and
+	CLEANUP_JMP_BUF.
+	* sysdeps/x86_64/tcb-offsets.sym: Likewise.
+	* tst-cancel12.c: New file.
+	* tst-cancel13.c: New file.
+	* tst-cancel14.c: New file.
+	* tst-cancel15.c: New file.
+	* Makefile (tests): Add tst-cancel12, tst-cancel13, tst-cancel14,
+	and tst-cancel15.
+
+	* tst-cancel1.c: Add some comments.
+
+	* sysdeps/unix/sysv/linux/x86_64/sem_timedwait.S: Compute relative
+	timeout correctly.
+
 2003-06-06  Ulrich Drepper  <drepper@redhat.com>
 
 	* Makefile (CFLAGS-pthread_cancel.c): Define.
diff --git a/nptl/Makefile b/nptl/Makefile
index 78d05f45e3..74ede7775b 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -116,7 +116,8 @@ libpthread-routines = init events version \
 		      sigaction \
 		      herrno res pt-allocrtsig \
 		      pthread_kill_other_threads \
-		      pthread_getaffinity pthread_setaffinity
+		      pthread_getaffinity pthread_setaffinity \
+		      cleanup_routine
 
 libpthread-shared-only-routines = version pt-allocrtsig
 libpthread-static-only-routines = pthread_atfork
@@ -124,16 +125,41 @@ libpthread-static-only-routines = pthread_atfork
 libpthread-nonshared = pthread_atfork
 
 CFLAGS-pthread_atfork.c = -DNOT_IN_libc
+
+# Since cancellation handling is in large parts handled using exceptions
+# we have to compile some files with exception handling enabled, some
+# even with asynchronous unwind tables.
+
+# init.c contains sigcancel_handler().
 CFLAGS-init.c = -fexceptions -fasynchronous-unwind-tables
+# The unwind code itself,
 CFLAGS-unwind.c = -fexceptions
+
+# The following three functions must be async-cancel safe.
+CFLAGS-pthread_cancel.c = -fexceptions -fasynchronous-unwind-tables
+CFLAGS-pthread_setcancelstate.c = -fexceptions -fasynchronous-unwind-tables
+CFLAGS-pthread_setcanceltype.c = -fexceptions -fasynchronous-unwind-tables
+
+# These are internal functions which similar functionality as setcancelstate
+# and setcanceltype.
+CFLAGS-cancellation.c = -fasynchronous-unwind-tables
+CFLAGS-libc-cancellation.c = -fasynchronous-unwind-tables
+
+# Calling pthread_exit() must cause the registered cancel handlers to
+# be executed.  Therefore exceptions have to be thrown through this
+# function.
+CFLAGS-pthread_exit.c = -fexceptions
+
+# The following are cancellation points.  Some of the functions can
+# block and therefore temporarily enable asynchronous cancellation.
+# Those must be compiled asynchronous unwind tables.
 CFLAGS-pthread_testcancel.c = -fexceptions
 CFLAGS-pthread_join.c = -fexceptions -fasynchronous-unwind-tables
 CFLAGS-pthread_timedjoin.c = -fexceptions -fasynchronous-unwind-tables
 CFLAGS-pthread_cond_wait.c = -fexceptions -fasynchronous-unwind-tables
 CFLAGS-pthread_cond_timedwait.c = -fexceptions -fasynchronous-unwind-tables
-CFLAGS-pthread_cancel.c = -fexceptions -fasynchronous-unwind-tables
-CFLAGS-cancellation.c = -fasynchronous-unwind-tables
-CFLAGS-libc-cancellation.c = -fasynchronous-unwind-tables
+CFLAGS-sem_wait.c = -fexceptions -fasynchronous-unwind-tables
+CFLAGS-sem_timedwait.c = -fexceptions -fasynchronous-unwind-tables
 
 # Don't generate deps for calls with no sources.  See sysdeps/unix/Makefile.
 omit-deps = $(unix-syscalls:%=ptw-%)
@@ -165,7 +191,7 @@ tests = tst-attr1 tst-attr2 \
 	tst-atfork1 \
 	tst-cancel1 tst-cancel2 tst-cancel3 tst-cancel4 tst-cancel5 \
 	tst-cancel6 tst-cancel7 tst-cancel8 tst-cancel9 tst-cancel10 \
-	tst-cancel11 \
+	tst-cancel11 tst-cancel12 tst-cancel13 tst-cancel14 tst-cancel15 \
 	tst-cleanup0 tst-cleanup1 tst-cleanup2 tst-cleanup3 \
 	tst-flock1 tst-flock2 \
 	tst-signal1 tst-signal2 tst-signal3 tst-signal4 tst-signal5 \
@@ -192,6 +218,12 @@ LDFLAGS-pthread.so = -Wl,--enable-new-dtags,-z,nodelete,-z,initfirst
 
 include ../Makeconfig
 
+ifeq ($(have-forced-unwind),yes)
+tests += tst-cancelx2 tst-cancelx3 tst-cancelx4 tst-cancelx5 \
+	 tst-cancelx6 tst-cancelx7 tst-cancelx8 tst-cancelx9 tst-cancelx10 \
+	 tst-cancelx11 tst-cancelx12 tst-cancelx13 tst-cancelx14 tst-cancelx15\
+	 tst-cleanupx0 tst-cleanupx1 tst-cleanupx2 tst-cleanupx3
+endif
 ifeq ($(build-shared),yes)
 tests += tst-atfork2 tst-tls3
 endif
@@ -292,6 +324,23 @@ CFLAGS-pthread_self.os += -fomit-frame-pointer
 
 CFLAGS-tst-unload.c += -DPREFIX=\"$(objpfx)\"
 
+# Run the cancellation and cleanup tests also for the modern, exception-based
+# implementation.  For this we have to pass the -fexceptions parameter.
+CFLAGS-tst-cancelx2.c += -fexceptions
+CFLAGS-tst-cancelx3.c += -fexceptions
+CFLAGS-tst-cancelx4.c += -fexceptions
+CFLAGS-tst-cancelx5.c += -fexceptions
+CFLAGS-tst-cancelx6.c += -fexceptions
+CFLAGS-tst-cancelx7.c += -fexceptions
+CFLAGS-tst-cancelx8.c += -fexceptions
+CFLAGS-tst-cancelx9.c += -fexceptions
+CFLAGS-tst-cancelx10.c += -fexceptions
+CFLAGS-tst-cancelx11.c += -fexceptions
+CFLAGS-tst-cleanupx0.c += -fexceptions -fasynchronous-unwind-tables
+CFLAGS-tst-cleanupx1.c += -fexceptions -fasynchronous-unwind-tables
+CFLAGS-tst-cleanupx2.c += -fexceptions
+CFLAGS-tst-cleanupx3.c += -fexceptions
+
 tst-cancel7-ARGS = --command "$(built-program-cmd)"
 tst-umask1-ARGS = $(objpfx)tst-umask1.temp
 
diff --git a/nptl/Versions b/nptl/Versions
index c602d5e048..a8fa3c85e8 100644
--- a/nptl/Versions
+++ b/nptl/Versions
@@ -216,6 +216,7 @@ libpthread {
     __pthread_register_cancel; __pthread_unregister_cancel;
     __pthread_register_cancel_defer; __pthread_unregister_cancel_restore;
     __pthread_unwind_next;
+    __pthread_cleanup_routine;
 
     # New affinity interfaces.
     pthread_getaffinity_np; pthread_setaffinity_np;
diff --git a/nptl/cleanup_routine.c b/nptl/cleanup_routine.c
new file mode 100644
index 0000000000..cbf2318343
--- /dev/null
+++ b/nptl/cleanup_routine.c
@@ -0,0 +1,28 @@
+/* Copyright (C) 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2003.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <pthread.h>
+
+
+void
+__pthread_cleanup_routine (struct __pthread_cleanup_frame *f)
+{
+  if (f->__do_it)
+    f->__cancel_routine (f->__cancel_arg);
+}
diff --git a/nptl/sysdeps/i386/tcb-offsets.sym b/nptl/sysdeps/i386/tcb-offsets.sym
index 12cb6f00f9..a83c3c9a9b 100644
--- a/nptl/sysdeps/i386/tcb-offsets.sym
+++ b/nptl/sysdeps/i386/tcb-offsets.sym
@@ -1,7 +1,10 @@
 #include <sysdep.h>
 #include <tls.h>
 
+RESULT			offsetof (struct pthread, result)
 TID			offsetof (struct pthread, tid)
+CANCELHANDLING		offsetof (struct pthread, cancelhandling)
+CLEANUP_JMP_BUF		offsetof (struct pthread, cleanup_jmp_buf)
 MULTIPLE_THREADS_OFFSET	offsetof (tcbhead_t, multiple_threads)
 SYSINFO_OFFSET		offsetof (tcbhead_t, sysinfo)
 CLEANUP			offsetof (struct pthread, cleanup)
diff --git a/nptl/sysdeps/pthread/pthread.h b/nptl/sysdeps/pthread/pthread.h
index c0375ae223..1f0a34baa2 100644
--- a/nptl/sysdeps/pthread/pthread.h
+++ b/nptl/sysdeps/pthread/pthread.h
@@ -419,14 +419,132 @@ typedef struct
 #endif
 
 
+/* Structure to hold the cleanup handler information.  */
+struct __pthread_cleanup_frame
+{
+  void (*__cancel_routine) (void *);
+  void *__cancel_arg;
+  int __do_it;
+  int __cancel_type;
+};
+
+#if defined __GNUC__ && defined __EXCEPTIONS
+# ifdef __cplusplus
+/* Class to handle cancellation handler invocation.  */
+class __pthread_cleanup_class
+{
+  void (*__cancel_routine) (void *);
+  void *__cancel_arg;
+  int __do_it;
+  int __cancel_type;
+
+ public:
+  __pthread_cleanup_class (void (*__fct) (void *), void *__arg)
+    : __cancel_routine (__fct), __cancel_arg (__arg), __do_it (1) { }
+  ~__pthread_cleanup_class () { if (__do_it) __cancel_routine (__cancel_arg); }
+  __setdoit (int __newval) { __do_it = __newval; }
+  __defer () { pthread_setcanceltype (PTHREAD_CANCEL_DEFERRED,
+				      &__cancel_type);
+  __restore () const { pthread_setcanceltype (__cancel_type, 0);
+};
+
+/* Install a cleanup handler: ROUTINE will be called with arguments ARG
+   when the thread is canceled or calls pthread_exit.  ROUTINE will also
+   be called with arguments ARG when the matching pthread_cleanup_pop
+   is executed with non-zero EXECUTE argument.
+
+   pthread_cleanup_push and pthread_cleanup_pop are macros and must always
+   be used in matching pairs at the same nesting level of braces.  */
+#  define pthread_cleanup_push(routine, arg) \
+  do {									      \
+    __pthread_cleanup_class __clframe (routine, arg)
+
+/* Remove a cleanup handler installed by the matching pthread_cleanup_push.
+   If EXECUTE is non-zero, the handler function is called. */
+#  define pthread_cleanup_pop(execute) \
+    __clframe.__setdoit (execute);					      \
+  } while (0)
+
+#  ifdef __USE_GNU
+/* Install a cleanup handler as pthread_cleanup_push does, but also
+   saves the current cancellation type and sets it to deferred
+   cancellation.  */
+#   define pthread_cleanup_push_defer_np(routine, arg) \
+  do {									      \
+    __pthread_cleanup_class __clframe (routine, arg);			      \
+    __clframe.__defer ()
+
+/* Remove a cleanup handler as pthread_cleanup_pop does, but also
+   restores the cancellation type that was in effect when the matching
+   pthread_cleanup_push_defer was called.  */
+#   define pthread_cleanup_pop_restore_np(execute) \
+    __clframe.__restore ();						      \
+    __clframe.__setdoit (execute);					      \
+  } while (0)
+#  endif
+# else
+/* Function called to call the cleanup handler.  As an extern inline
+   function the compiler is free to decide inlining the change when
+   needed or fall back on the copy which must exist somewhere
+   else.  */
+extern inline void
+__pthread_cleanup_routine (struct __pthread_cleanup_frame *__frame)
+{
+  if (__frame->__do_it)
+    __frame->__cancel_routine (__frame->__cancel_arg);
+}
+
 /* Install a cleanup handler: ROUTINE will be called with arguments ARG
-   when the thread is cancelled or calls pthread_exit.  ROUTINE will also
+   when the thread is canceled or calls pthread_exit.  ROUTINE will also
    be called with arguments ARG when the matching pthread_cleanup_pop
    is executed with non-zero EXECUTE argument.
 
    pthread_cleanup_push and pthread_cleanup_pop are macros and must always
    be used in matching pairs at the same nesting level of braces.  */
-#define pthread_cleanup_push(routine, arg) \
+#  define pthread_cleanup_push(routine, arg) \
+  do {									      \
+    struct __pthread_cleanup_frame __clframe				      \
+      __attribute__ ((__cleanup__ (__pthread_cleanup_routine)))		      \
+      = { .__cancel_routine = (routine), .__cancel_arg = (arg),	 	      \
+	  .__do_it = 1 };
+
+/* Remove a cleanup handler installed by the matching pthread_cleanup_push.
+   If EXECUTE is non-zero, the handler function is called. */
+#  define pthread_cleanup_pop(execute) \
+    __clframe.__do_it = (execute);					      \
+  } while (0)
+
+#  ifdef __USE_GNU
+/* Install a cleanup handler as pthread_cleanup_push does, but also
+   saves the current cancellation type and sets it to deferred
+   cancellation.  */
+#   define pthread_cleanup_push_defer_np(routine, arg) \
+  do {									      \
+    struct __pthread_cleanup_frame __clframe				      \
+      __attribute__ ((__cleanup__ (__pthread_cleanup_routine)))		      \
+      = { .__cancel_routine = (routine), .__cancel_arg = (arg),		      \
+	  .__do_it = 1 };						      \
+    (void) pthread_setcanceltype (PTHREAD_CANCEL_DEFERRED,		      \
+				  &__clframe.__cancel_type)
+
+/* Remove a cleanup handler as pthread_cleanup_pop does, but also
+   restores the cancellation type that was in effect when the matching
+   pthread_cleanup_push_defer was called.  */
+#   define pthread_cleanup_pop_restore_np(execute) \
+    (void) pthread_setcanceltype (__clframe.__cancel_type, NULL);	      \
+    __clframe.__do_it = (execute);					      \
+  } while (0)
+#  endif
+# endif
+#else
+/* Install a cleanup handler: ROUTINE will be called with arguments ARG
+   when the thread is canceled or calls pthread_exit.  ROUTINE will also
+   be called with arguments ARG when the matching pthread_cleanup_pop
+   is executed with non-zero EXECUTE argument.
+
+   pthread_cleanup_push and pthread_cleanup_pop are macros and must always
+   be used in matching pairs at the same nesting level of braces.  */
+# define pthread_cleanup_push(routine, arg) \
   do {									      \
     __pthread_unwind_buf_t __cancel_buf;				      \
     void (*__cancel_routine) (void *) = (routine);			      \
@@ -447,7 +565,7 @@ extern void __pthread_register_cancel (__pthread_unwind_buf_t *__buf)
 
 /* Remove a cleanup handler installed by the matching pthread_cleanup_push.
    If EXECUTE is non-zero, the handler function is called. */
-#define pthread_cleanup_pop(execute) \
+# define pthread_cleanup_pop(execute) \
     } while (0);							      \
     __pthread_unregister_cancel (&__cancel_buf);			      \
     if (execute)							      \
@@ -456,11 +574,11 @@ extern void __pthread_register_cancel (__pthread_unwind_buf_t *__buf)
 extern void __pthread_unregister_cancel (__pthread_unwind_buf_t *__buf)
   __cleanup_fct_attribute;
 
-#ifdef __USE_GNU
+# ifdef __USE_GNU
 /* Install a cleanup handler as pthread_cleanup_push does, but also
    saves the current cancellation type and sets it to deferred
    cancellation.  */
-# define pthread_cleanup_push_defer(routine, arg) \
+#  define pthread_cleanup_push_defer_np(routine, arg) \
   do {									      \
     __pthread_unwind_buf_t __cancel_buf;				      \
     void (*__cancel_routine) (void *) = (routine);			      \
@@ -482,7 +600,7 @@ extern void __pthread_register_cancel_defer (__pthread_unwind_buf_t *__buf)
 /* Remove a cleanup handler as pthread_cleanup_pop does, but also
    restores the cancellation type that was in effect when the matching
    pthread_cleanup_push_defer was called.  */
-# define pthread_cleanup_pop_cleanup(execute) \
+#  define pthread_cleanup_pop_restore_np(execute) \
     } while (0);							      \
     __pthread_unregister_cancel_restore (&__cancel_buf);		      \
     if (execute)							      \
@@ -490,15 +608,16 @@ extern void __pthread_register_cancel_defer (__pthread_unwind_buf_t *__buf)
   } while (0)
 extern void __pthread_unregister_cancel_restore (__pthread_unwind_buf_t *__buf)
   __cleanup_fct_attribute;
-#endif
+# endif
 
 /* Internal interface to initiate cleanup.  */
 extern void __pthread_unwind_next (__pthread_unwind_buf_t *__buf)
      __cleanup_fct_attribute __attribute ((__noreturn__))
-#ifndef SHARED
+# ifndef SHARED
      __attribute ((__weak__))
-#endif
+# endif
      ;
+#endif
 
 /* Function used in the macros.  */
 struct __jmp_buf_tag;
diff --git a/nptl/sysdeps/unix/sysv/linux/i386/i486/sem_timedwait.S b/nptl/sysdeps/unix/sysv/linux/i386/i486/sem_timedwait.S
index aa3d74593d..9afe85f205 100644
--- a/nptl/sysdeps/unix/sysv/linux/i386/i486/sem_timedwait.S
+++ b/nptl/sysdeps/unix/sysv/linux/i386/i486/sem_timedwait.S
@@ -37,7 +37,14 @@
 	.globl	sem_timedwait
 	.type	sem_timedwait,@function
 	.align	16
+	cfi_startproc
 sem_timedwait:
+	/* First check for cancellation.  */
+	movl	%gs:CANCELHANDLING, %eax
+	andl	$0xfffffff9, %eax
+	cmpl	$8, %eax
+	je	10f
+
 	movl	4(%esp), %ecx
 
 	movl	(%ecx), %eax
@@ -54,19 +61,28 @@ sem_timedwait:
 
 	/* Check whether the timeout value is valid.  */
 1:	pushl	%esi
+	cfi_adjust_cfa_offset(4)
 	pushl	%edi
+	cfi_adjust_cfa_offset(4)
 	pushl	%ebx
-	subl	$8, %esp
+	cfi_adjust_cfa_offset(4)
+	subl	$12, %esp
+	cfi_adjust_cfa_offset(12)
 
-	movl	%esp, %esi
-	movl	28(%esp), %edi
+	movl	32(%esp), %edi
+	cfi_offset(7, -12)		/* %edi */
 
 	/* Check for invalid nanosecond field.  */
 	cmpl	$1000000000, 4(%edi)
-	movl	$EINVAL, %eax
+	movl	$EINVAL, %esi
+	cfi_offset(6, -8)		/* %esi */
 	jae	6f
 
-7:	xorl	%ecx, %ecx
+	cfi_offset(3, -16)		/* %ebx */
+7:	call	__pthread_enable_asynccancel
+	movl	%eax, 8(%esp)
+
+	xorl	%ecx, %ecx
 	movl	%esp, %ebx
 	movl	%ecx, %edx
 	movl	$SYS_gettimeofday, %eax
@@ -84,20 +100,25 @@ sem_timedwait:
 	addl	$1000000000, %edx
 	subl	$1, %ecx
 5:	testl	%ecx, %ecx
-	movl	$ETIMEDOUT, %eax
+	movl	$ETIMEDOUT, %esi
 	js	6f		/* Time is already up.  */
 
 	movl	%ecx, (%esp)	/* Store relative timeout.  */
 	movl	%edx, 4(%esp)
-	movl	24(%esp), %ebx
+	movl	28(%esp), %ebx
 	xorl	%ecx, %ecx
+	movl	%esp, %esi
 	movl	$SYS_futex, %eax
 	xorl	%edx, %edx
 	ENTER_KERNEL
+	movl	%eax, %esi
+
+	movl	8(%esp), %eax
+	call	__pthread_disable_asynccancel
 
-	testl	%eax, %eax
+	testl	%esi, %esi
 	je,pt	9f
-	cmpl	$-EWOULDBLOCK, %eax
+	cmpl	$-EWOULDBLOCK, %esi
 	jne	3f
 
 9:	movl	(%ebx), %eax
@@ -109,14 +130,27 @@ sem_timedwait:
 	cmpxchgl %ecx, (%ebx)
 	jne,pn	8b
 
-	addl	$8, %esp
+	addl	$12, %esp
+	cfi_adjust_cfa_offset(-12)
 	xorl	%eax, %eax
 	popl	%ebx
+	cfi_adjust_cfa_offset(-4)
+	cfi_restore(3)
 	popl	%edi
+	cfi_adjust_cfa_offset(-4)
+	cfi_restore(7)
 	popl	%esi
+	cfi_adjust_cfa_offset(-4)
+	cfi_restore(6)
+	cfi_adjust_cfa_offset(-4)
+	cfi_restore(6)
 	ret
 
-3:	negl	%eax
+	cfi_adjust_cfa_offset(24)
+	cfi_offset(6, -8)		/* %esi */
+	cfi_offset(7, -12)		/* %edi */
+	cfi_offset(3, -16)		/* %ebx */
+3:	negl	%esi
 6:
 #ifdef PIC
 	call	__i686.get_pc_thunk.bx
@@ -128,17 +162,31 @@ sem_timedwait:
 #if USE___THREAD
 	movl	%gs:0, %edx
 	subl	errno@gottpoff(%ebx), %edx
-	movl	%eax, (%edx)
+	movl	%esi, (%edx)
 #else
-	movl	%eax, %edx
 	call	__errno_location@plt
-	movl	%edx, (%eax)
+	movl	%esi, (%eax)
 #endif
 
-	addl	$8, %esp
+	addl	$12, %esp
+	cfi_adjust_cfa_offset(-12)
 	orl	$-1, %eax
 	popl	%ebx
+	cfi_adjust_cfa_offset(-4)
+	cfi_restore(3)
 	popl	%edi
+	cfi_adjust_cfa_offset(-4)
+	cfi_restore(7)
 	popl	%esi
+	cfi_adjust_cfa_offset(-4)
+	cfi_restore(6)
 	ret
+
+10:	/* Canceled.  */
+	movl	$0xffffffff, %gs:RESULT
+	LOCK
+	orl	$0x10, %gs:CANCELHANDLING
+	movl	%gs:CLEANUP_JMP_BUF, %eax
+	jmp	__pthread_unwind
+	cfi_endproc
 	.size	sem_timedwait,.-sem_timedwait
diff --git a/nptl/sysdeps/unix/sysv/linux/i386/i486/sem_wait.S b/nptl/sysdeps/unix/sysv/linux/i386/i486/sem_wait.S
index eb01ca84f7..ba4f54cd64 100644
--- a/nptl/sysdeps/unix/sysv/linux/i386/i486/sem_wait.S
+++ b/nptl/sysdeps/unix/sysv/linux/i386/i486/sem_wait.S
@@ -36,12 +36,25 @@
 	.globl	__new_sem_wait
 	.type	__new_sem_wait,@function
 	.align	16
+	cfi_startproc
 __new_sem_wait:
+	/* First check for cancellation.  */
+	movl	%gs:CANCELHANDLING, %eax
+	andl	$0xfffffff9, %eax
+	cmpl	$8, %eax
+	je	5f
+
 	pushl	%ebx
+	cfi_adjust_cfa_offset(4)
 	pushl	%esi
+	cfi_adjust_cfa_offset(4)
+	subl	$4, %esp
+	cfi_adjust_cfa_offset(4)
 
-	movl	12(%esp), %ebx
+	movl	16(%esp), %ebx
+	cfi_offset(3, -8)		/* %ebx */
 
+	cfi_offset(6, -12)		/* %esi */
 3:	movl	(%ebx), %eax
 2:	testl	%eax, %eax
 	je,pn	1f
@@ -52,21 +65,35 @@ __new_sem_wait:
 	jne,pn	2b
 	xorl	%eax, %eax
 
-	popl	%esi
-	popl	%ebx
+	movl	4(%esp), %esi
+	cfi_restore(6)
+	movl	8(%esp), %ebx
+	cfi_restore(3)
+	addl	$12, %esp
+	cfi_adjust_cfa_offset(-12)
 	ret
 
-1:	xorl	%esi, %esi
+	cfi_adjust_cfa_offset(8)
+	cfi_offset(3, -8)		/* %ebx */
+	cfi_offset(6, -12)		/* %esi */
+1:	call	__pthread_enable_asynccancel
+	movl	%eax, (%esp)
+
+	xorl	%esi, %esi
 	movl	$SYS_futex, %eax
 	movl	%esi, %ecx
 	movl	%esi, %edx
 	ENTER_KERNEL
+	movl	%eax, %esi
+
+	movl	(%esp), %eax
+	call	__pthread_disable_asynccancel
 
-	testl	%eax, %eax
+	testl	%esi, %esi
 	je	3b
-	cmpl	$-EWOULDBLOCK, %eax
+	cmpl	$-EWOULDBLOCK, %esi
 	je	3b
-	negl	%eax
+	negl	%esi
 #ifdef PIC
 	call	__i686.get_pc_thunk.bx
 #else
@@ -77,16 +104,27 @@ __new_sem_wait:
 #if USE___THREAD
 	movl	%gs:0, %edx
 	subl	errno@gottpoff(%ebx), %edx
-	movl	%eax, (%edx)
+	movl	%esi, (%edx)
 #else
-	movl	%eax, %edx
 	call	__errno_location@plt
-	movl	%edx, (%eax)
+	movl	%esi, (%eax)
 #endif
 	orl	$-1, %eax
-	popl	%esi
-	popl	%ebx
+	movl	4(%esp), %esi
+	cfi_restore(6)
+	movl	8(%esp), %ebx
+	cfi_restore(3)
+	addl	$12, %esp
+	cfi_adjust_cfa_offset(-12)
 	ret
+
+5:	/* Canceled.  */
+	movl	$0xffffffff, %gs:RESULT
+	LOCK
+	orl	$0x10, %gs:CANCELHANDLING
+	movl	%gs:CLEANUP_JMP_BUF, %eax
+	jmp	__pthread_unwind
+	cfi_endproc
 	.size	__new_sem_wait,.-__new_sem_wait
 	versioned_symbol(libpthread, __new_sem_wait, sem_wait, GLIBC_2_1)
 #if SHLIB_COMPAT(libpthread, GLIBC_2_0, GLIBC_2_1)
diff --git a/nptl/sysdeps/unix/sysv/linux/sem_timedwait.c b/nptl/sysdeps/unix/sysv/linux/sem_timedwait.c
index 8a65ce2567..ef897c1e93 100644
--- a/nptl/sysdeps/unix/sysv/linux/sem_timedwait.c
+++ b/nptl/sysdeps/unix/sysv/linux/sem_timedwait.c
@@ -24,12 +24,16 @@
 #include <internaltypes.h>
 #include <semaphore.h>
 
+#include <pthreadP.h>
 #include <shlib-compat.h>
 
 
 int
 sem_timedwait (sem_t *sem, const struct timespec *abstime)
 {
+  /* First check for cancellation.  */
+  CANCELLATION_P (THREAD_SELF);
+
   int *futex = (int *) sem;
   int val;
   int err;
@@ -71,7 +75,15 @@ sem_timedwait (sem_t *sem, const struct timespec *abstime)
       /* Do wait.  */
       rt.tv_sec = sec;
       rt.tv_nsec = nsec;
+
+      /* Enable asynchronous cancellation.  Required by the standard.  */
+      int oldtype = __pthread_enable_asynccancel ();
+
       err = lll_futex_timed_wait (futex, 0, &rt);
+
+      /* Disable asynchronous cancellation.  */
+      __pthread_disable_asynccancel (oldtype);
+
       if (err != 0 && err != -EWOULDBLOCK)
 	goto error_return;
 
diff --git a/nptl/sysdeps/unix/sysv/linux/sem_wait.c b/nptl/sysdeps/unix/sysv/linux/sem_wait.c
index 1d39b7c408..36bb158e09 100644
--- a/nptl/sysdeps/unix/sysv/linux/sem_wait.c
+++ b/nptl/sysdeps/unix/sysv/linux/sem_wait.c
@@ -24,12 +24,16 @@
 #include <internaltypes.h>
 #include <semaphore.h>
 
+#include <pthreadP.h>
 #include <shlib-compat.h>
 
 
 int
 __new_sem_wait (sem_t *sem)
 {
+  /* First check for cancellation.  */
+  CANCELLATION_P (THREAD_SELF);
+
   int *futex = (int *) sem;
   int val;
   int err;
@@ -43,7 +47,13 @@ __new_sem_wait (sem_t *sem)
 	    return 0;
 	}
 
+      /* Enable asynchronous cancellation.  Required by the standard.  */
+      int oldtype = __pthread_enable_asynccancel ();
+
       err = lll_futex_wait (futex, 0);
+
+      /* Disable asynchronous cancellation.  */
+      __pthread_disable_asynccancel (oldtype);
     }
   while (err == 0 || err == -EWOULDBLOCK);
 
diff --git a/nptl/sysdeps/unix/sysv/linux/x86_64/sem_timedwait.S b/nptl/sysdeps/unix/sysv/linux/x86_64/sem_timedwait.S
index 29bc1bcf46..7626d7b250 100644
--- a/nptl/sysdeps/unix/sysv/linux/x86_64/sem_timedwait.S
+++ b/nptl/sysdeps/unix/sysv/linux/x86_64/sem_timedwait.S
@@ -38,7 +38,14 @@
 	.globl	sem_timedwait
 	.type	sem_timedwait,@function
 	.align	16
+	cfi_startproc
 sem_timedwait:
+	/* First check for cancellation.  */
+	movl	%fs:CANCELHANDLING, %eax
+	andl	$0xfffffff9, %eax
+	cmpl	$8, %eax
+	je	11f
+
 	movl	(%rdi), %eax
 2:	testl	%eax, %eax
 	je	1f
@@ -53,18 +60,29 @@ sem_timedwait:
 
 	/* Check whether the timeout value is valid.  */
 1:	pushq	%r12
+	cfi_adjust_cfa_offset(8)
 	pushq	%r13
-	subq	$16, %rsp
+	cfi_adjust_cfa_offset(8)
+	pushq	%r14
+	cfi_adjust_cfa_offset(8)
+	subq	$24, %rsp
+	cfi_adjust_cfa_offset(24)
 
 	movq	%rdi, %r12
+	cfi_offset(12, -16)		/* %r12 */
 	movq	%rsi, %r13
+	cfi_offset(13, -24)		/* %r13 */
 
 	/* Check for invalid nanosecond field.  */
 	cmpq	$1000000000, 8(%r13)
-	movl	$EINVAL, %eax
+	movl	$EINVAL, %r14d
+	cfi_offset(14, -24)		/* %r14 */
 	jae	6f
 
-7:	xorq	%rsi, %rsi
+7:	call	__pthread_enable_asynccancel
+	movl	%eax, 16(%rsp)
+
+	xorq	%rsi, %rsi
 	movq	%rsp, %rdi
 	movq	$VSYSCALL_ADDR_vgettimeofday, %rax
 	callq	*%rax
@@ -74,14 +92,14 @@ sem_timedwait:
 	movq	$1000, %rdi
 	mul	%rdi		/* Milli seconds to nano seconds.  */
 	movq	(%r13), %rdi
-	movq	8(%r13), %rdi
+	movq	8(%r13), %rsi
 	subq	(%rsp), %rdi
-	subq	%rax, %rdi
+	subq	%rax, %rsi
 	jns	5f
 	addq	$1000000000, %rsi
 	decq	%rdi
 5:	testq	%rdi, %rdi
-	movl	$ETIMEDOUT, %eax
+	movl	$ETIMEDOUT, %r14d
 	js	6f		/* Time is already up.  */
 
 	movq	%rdi, (%rsp)	/* Store relative timeout.  */
@@ -93,38 +111,65 @@ sem_timedwait:
 	movq	$SYS_futex, %rax
 	xorl	%edx, %edx
 	syscall
+	movq	%rax, %r14
 
-	testq	%rax, %rax
+	movl	16(%rsp), %edi
+	call	__pthread_disable_asynccancel
+
+	testq	%r14, %r14
 	je	9f
-	cmpq	$-EWOULDBLOCK, %rax
+	cmpq	$-EWOULDBLOCK, %r14
 	jne	3f
 
-9:	movl	(%rdi), %eax
+9:	movl	(%r12), %eax
 8:	testl	%eax, %eax
 	je	7b
 
 	leaq	-1(%rax), %rcx
 	LOCK
-	cmpxchgl %ecx, (%rdi)
+	cmpxchgl %ecx, (%r12)
 	jne	8b
 
 	xorl	%eax, %eax
-10:	addq	$16, %rsp
+10:	addq	$24, %rsp
+	cfi_adjust_cfa_offset(-24)
+	popq	%r14
+	cfi_adjust_cfa_offset(-8)
+	cfi_restore(14)
 	popq	%r13
+	cfi_adjust_cfa_offset(-8)
+	cfi_restore(13)
 	popq	%r12
+	cfi_adjust_cfa_offset(-8)
+	cfi_restore(12)
 	retq
 
-3:	negq	%rax
+	cfi_adjust_cfa_offset(48)
+	cfi_offset(12, -16)		/* %r12 */
+	cfi_offset(13, -24)		/* %r13 */
+	cfi_offset(14, -32)		/* %r14 */
+3:	negq	%r14
 6:
 #if USE___THREAD
 	movq	errno@gottpoff(%rip), %rdx
-	movl	%eax, %fs:(%rdx)
+	movl	%r14d, %fs:(%rdx)
 #else
-	movl	%eax, %edx
 	callq	__errno_location@plt
-	movl	%edx, (%rax)
+	movl	%r14d, (%rax)
 #endif
 
 	orl	$-1, %eax
 	jmp	10b
+	cfi_adjust_cfa_offset(-48)
+	cfi_restore(14)
+	cfi_restore(13)
+	cfi_restore(12)
+
+11:	/* Canceled.  */
+	movq	$0xffffffffffffffff, %fs:RESULT
+	LOCK
+	orl	$0x10, %fs:CANCELHANDLING
+	movq	%fs:CLEANUP_JMP_BUF, %rdi
+	jmp	__pthread_unwind
+	cfi_endproc
 	.size	sem_timedwait,.-sem_timedwait
diff --git a/nptl/sysdeps/unix/sysv/linux/x86_64/sem_wait.S b/nptl/sysdeps/unix/sysv/linux/x86_64/sem_wait.S
index 877ee4c4dc..32742309fb 100644
--- a/nptl/sysdeps/unix/sysv/linux/x86_64/sem_wait.S
+++ b/nptl/sysdeps/unix/sysv/linux/x86_64/sem_wait.S
@@ -35,38 +35,86 @@
 	.globl	sem_wait
 	.type	sem_wait,@function
 	.align	16
+	cfi_startproc
 sem_wait:
-3:	movl	(%rdi), %eax
+	/* First check for cancellation.  */
+	movl	%fs:CANCELHANDLING, %eax
+	andl	$0xfffffff9, %eax
+	cmpl	$8, %eax
+	je	4f
+
+	pushq	%r12
+	cfi_adjust_cfa_offset(8)
+	cfi_offset(12, -16)
+	pushq	%r13
+	cfi_adjust_cfa_offset(8)
+	movq	%rdi, %r13
+	cfi_offset(13, -24)
+
+3:	movl	(%r13), %eax
 2:	testl	%eax, %eax
 	je	1f
 
 	leaq	-1(%rax), %rdx
 	LOCK
-	cmpxchgl %edx, (%rdi)
+	cmpxchgl %edx, (%r13)
 	jne	2b
 	xorl	%eax, %eax
 
+	popq	%r13
+	cfi_adjust_cfa_offset(-8)
+	cfi_restore(13)
+	popq	%r12
+	cfi_adjust_cfa_offset(-8)
+	cfi_restore(12)
+
 	retq
 
-1:	xorq	%r10, %r10
+	cfi_adjust_cfa_offset(16)
+	cfi_offset(12, -16)
+	cfi_offset(13, -24)
+1:	call	__pthread_enable_asynccancel
+	movl	%eax, %r8d
+
+	xorq	%r10, %r10
 	movq	$SYS_futex, %rax
+	movq	%r13, %rdi
 	movq	%r10, %rsi
 	movq	%r10, %rdx
 	syscall
+	movq	%rax, %r12
+
+	movl	%r8d, %edi
+	call	__pthread_disable_asynccancel
 
-	testq	%rax, %rax
+	testq	%r12, %r12
 	je	3b
-	cmpq	$-EWOULDBLOCK, %rax
+	cmpq	$-EWOULDBLOCK, %r12
 	je	3b
-	negq	%rax
+	negq	%r12
 #if USE___THREAD
 	movq	errno@gottpoff(%rip), %rdx
-	movl	%eax, %fs:(%rdx)
+	movl	%r12d, %fs:(%rdx)
 #else
-	movl	%eax, %edx
 	callq	__errno_location@plt
-	movl	%edx, (%rax)
+	movl	%r12d, (%rax)
 #endif
 	orl	$-1, %eax
+
+	popq	%r13
+	cfi_adjust_cfa_offset(-8)
+	cfi_restore(13)
+	popq	%r12
+	cfi_adjust_cfa_offset(-8)
+	cfi_restore(12)
+
 	retq
+
+4:	/* Canceled.  */
+	movq	$0xffffffffffffffff, %fs:RESULT
+	LOCK
+	orl	$0x10, %fs:CANCELHANDLING
+	movq	%fs:CLEANUP_JMP_BUF, %rdi
+	jmp	__pthread_unwind
+	cfi_endproc
 	.size	sem_wait,.-sem_wait
diff --git a/nptl/sysdeps/x86_64/tcb-offsets.sym b/nptl/sysdeps/x86_64/tcb-offsets.sym
index e230e3dfb3..dc6e5c4504 100644
--- a/nptl/sysdeps/x86_64/tcb-offsets.sym
+++ b/nptl/sysdeps/x86_64/tcb-offsets.sym
@@ -1,7 +1,10 @@
 #include <sysdep.h>
 #include <tls.h>
 
+RESULT			offsetof (struct pthread, result)
 TID			offsetof (struct pthread, tid)
+CANCELHANDLING		offsetof (struct pthread, cancelhandling)
+CLEANUP_JMP_BUF		offsetof (struct pthread, cleanup_jmp_buf)
 CLEANUP			offsetof (struct pthread, cleanup)
 CLEANUP_PREV		offsetof (struct _pthread_cleanup_buffer, __prev)
 MUTEX_FUTEX		offsetof (pthread_mutex_t, __data.__lock)
diff --git a/nptl/tst-cancel1.c b/nptl/tst-cancel1.c
index 654c8a7a56..690319d8c7 100644
--- a/nptl/tst-cancel1.c
+++ b/nptl/tst-cancel1.c
@@ -63,6 +63,8 @@ tf (void *arg)
       printf ("setcanceltype failed: %s\n", strerror (err));
       exit (1);
     }
+  /* The following code is not standard compliant: the mutex functions
+     must not be called with asynchronous cancellation enabled.  */
 
   err = pthread_mutex_unlock (&m2);
   if (err != 0)
diff --git a/nptl/tst-cancel12.c b/nptl/tst-cancel12.c
new file mode 100644
index 0000000000..6fdf5cf5fe
--- /dev/null
+++ b/nptl/tst-cancel12.c
@@ -0,0 +1,127 @@
+/* Copyright (C) 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2003.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <errno.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+
+static pthread_barrier_t bar;
+static sem_t sem;
+
+
+static void
+cleanup (void *arg)
+{
+  static int ncall;
+
+  if (++ncall != 1)
+    {
+      puts ("second call to cleanup");
+      exit (1);
+    }
+
+  printf ("cleanup call #%d\n", ncall);
+}
+
+
+static void *
+tf (void *arg)
+{
+  pthread_cleanup_push (cleanup, NULL);
+
+  int e = pthread_barrier_wait (&bar);
+  if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD)
+    {
+      puts ("tf: 1st barrier_wait failed");
+      exit (1);
+    }
+
+  /* This call should block and be cancelable.  */
+  sem_wait (&sem);
+
+  pthread_cleanup_pop (0);
+
+  puts ("sem_wait returned");
+
+  return NULL;
+}
+
+
+static int
+do_test (void)
+{
+  pthread_t th;
+
+  if (pthread_barrier_init (&bar, NULL, 2) != 0)
+    {
+      puts ("barrier_init failed");
+      exit (1);
+    }
+
+  if (sem_init (&sem, 0, 1) != 0)
+    {
+      puts ("sem_init failed");
+      exit (1);
+    }
+
+  if (pthread_create (&th, NULL, tf, NULL) != 0)
+    {
+      puts ("create failed");
+      exit (1);
+    }
+
+  /* Check whether cancellation is honored even before sem_wait does
+     anything.  */
+  if (pthread_cancel (th) != 0)
+    {
+      puts ("1st cancel failed");
+      exit (1);
+    }
+
+  int e = pthread_barrier_wait (&bar);
+  if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD)
+    {
+      puts ("1st barrier_wait failed");
+      exit (1);
+    }
+
+  void *r;
+  if (pthread_join (th, &r) != 0)
+    {
+      puts ("join failed");
+      exit (1);
+    }
+
+  if (r != PTHREAD_CANCELED)
+    {
+      puts ("thread not canceled");
+      exit (1);
+    }
+
+  return 0;
+}
+
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/nptl/tst-cancel13.c b/nptl/tst-cancel13.c
new file mode 100644
index 0000000000..66ab455097
--- /dev/null
+++ b/nptl/tst-cancel13.c
@@ -0,0 +1,129 @@
+/* Copyright (C) 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2003.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <errno.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+
+static pthread_barrier_t bar;
+static sem_t sem;
+
+
+static void
+cleanup (void *arg)
+{
+  static int ncall;
+
+  if (++ncall != 1)
+    {
+      puts ("second call to cleanup");
+      exit (1);
+    }
+
+  printf ("cleanup call #%d\n", ncall);
+}
+
+
+static void *
+tf (void *arg)
+{
+  pthread_cleanup_push (cleanup, NULL);
+
+  int e = pthread_barrier_wait (&bar);
+  if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD)
+    {
+      puts ("tf: 1st barrier_wait failed");
+      exit (1);
+    }
+
+  /* This call should block and be cancelable.  */
+  sem_wait (&sem);
+
+  pthread_cleanup_pop (0);
+
+  puts ("sem_wait returned");
+
+  return NULL;
+}
+
+
+static int
+do_test (void)
+{
+  pthread_t th;
+
+  if (pthread_barrier_init (&bar, NULL, 2) != 0)
+    {
+      puts ("barrier_init failed");
+      exit (1);
+    }
+
+  if (sem_init (&sem, 0, 0) != 0)
+    {
+      puts ("sem_init failed");
+      exit (1);
+    }
+
+  if (pthread_create (&th, NULL, tf, NULL) != 0)
+    {
+      puts ("create failed");
+      exit (1);
+    }
+
+  int e = pthread_barrier_wait (&bar);
+  if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD)
+    {
+      puts ("1st barrier_wait failed");
+      exit (1);
+    }
+
+  /* Give the child a chance to go to sleep in sem_wait.  */
+  sleep (1);
+
+  /* Check whether cancellation is honored when waiting in sem_wait.  */
+  if (pthread_cancel (th) != 0)
+    {
+      puts ("1st cancel failed");
+      exit (1);
+    }
+
+  void *r;
+  if (pthread_join (th, &r) != 0)
+    {
+      puts ("join failed");
+      exit (1);
+    }
+
+  if (r != PTHREAD_CANCELED)
+    {
+      puts ("thread not canceled");
+      exit (1);
+    }
+
+  return 0;
+}
+
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/nptl/tst-cancel14.c b/nptl/tst-cancel14.c
new file mode 100644
index 0000000000..bc830ddff1
--- /dev/null
+++ b/nptl/tst-cancel14.c
@@ -0,0 +1,136 @@
+/* Copyright (C) 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2003.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <errno.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+
+static pthread_barrier_t bar;
+static sem_t sem;
+
+
+static void
+cleanup (void *arg)
+{
+  static int ncall;
+
+  if (++ncall != 1)
+    {
+      puts ("second call to cleanup");
+      exit (1);
+    }
+
+  printf ("cleanup call #%d\n", ncall);
+}
+
+
+static void *
+tf (void *arg)
+{
+  pthread_cleanup_push (cleanup, NULL);
+
+  int e = pthread_barrier_wait (&bar);
+  if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD)
+    {
+      puts ("tf: 1st barrier_wait failed");
+      exit (1);
+    }
+
+  struct timeval tv;
+  (void) gettimeofday (&tv, NULL);
+
+  struct timespec ts;
+  TIMEVAL_TO_TIMESPEC (&tv, &ts);
+
+  /* Timeout in 5 seconds.  */
+  ts.tv_sec += 5;
+
+  /* This call should block and be cancelable.  */
+  sem_timedwait (&sem, &ts);
+
+  pthread_cleanup_pop (0);
+
+  puts ("sem_timedwait returned");
+
+  return NULL;
+}
+
+
+static int
+do_test (void)
+{
+  pthread_t th;
+
+  if (pthread_barrier_init (&bar, NULL, 2) != 0)
+    {
+      puts ("barrier_init failed");
+      exit (1);
+    }
+
+  if (sem_init (&sem, 0, 1) != 0)
+    {
+      puts ("sem_init failed");
+      exit (1);
+    }
+
+  if (pthread_create (&th, NULL, tf, NULL) != 0)
+    {
+      puts ("create failed");
+      exit (1);
+    }
+
+  /* Check whether cancellation is honored even before sem_timedwait does
+     anything.  */
+  if (pthread_cancel (th) != 0)
+    {
+      puts ("1st cancel failed");
+      exit (1);
+    }
+
+  int e = pthread_barrier_wait (&bar);
+  if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD)
+    {
+      puts ("1st barrier_wait failed");
+      exit (1);
+    }
+
+  void *r;
+  if (pthread_join (th, &r) != 0)
+    {
+      puts ("join failed");
+      exit (1);
+    }
+
+  if (r != PTHREAD_CANCELED)
+    {
+      puts ("thread not canceled");
+      exit (1);
+    }
+
+  return 0;
+}
+
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/nptl/tst-cancel15.c b/nptl/tst-cancel15.c
new file mode 100644
index 0000000000..88bacafef8
--- /dev/null
+++ b/nptl/tst-cancel15.c
@@ -0,0 +1,141 @@
+/* Copyright (C) 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2003.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <errno.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+
+static pthread_barrier_t bar;
+static sem_t sem;
+
+
+static void
+cleanup (void *arg)
+{
+  static int ncall;
+
+  if (++ncall != 1)
+    {
+      puts ("second call to cleanup");
+      exit (1);
+    }
+
+  printf ("cleanup call #%d\n", ncall);
+}
+
+
+static void *
+tf (void *arg)
+{
+  int e;
+
+  pthread_cleanup_push (cleanup, NULL);
+
+  e = pthread_barrier_wait (&bar);
+  if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD)
+    {
+      puts ("tf: 1st barrier_wait failed");
+      exit (1);
+    }
+
+  struct timeval tv;
+  (void) gettimeofday (&tv, NULL);
+
+  struct timespec ts;
+  TIMEVAL_TO_TIMESPEC (&tv, &ts);
+
+  /* Timeout in 5 seconds.  */
+  ts.tv_sec += 5;
+
+  /* This call should block and be cancelable.  */
+  errno = 0;
+  e = sem_timedwait (&sem, &ts);
+
+  pthread_cleanup_pop (0);
+
+  printf ("sem_timedwait returned, e = %d, errno = %d\n", e, errno);
+
+  return NULL;
+}
+
+
+static int
+do_test (void)
+{
+  pthread_t th;
+
+  if (pthread_barrier_init (&bar, NULL, 2) != 0)
+    {
+      puts ("barrier_init failed");
+      exit (1);
+    }
+
+  if (sem_init (&sem, 0, 0) != 0)
+    {
+      puts ("sem_init failed");
+      exit (1);
+    }
+
+  if (pthread_create (&th, NULL, tf, NULL) != 0)
+    {
+      puts ("create failed");
+      exit (1);
+    }
+
+  int e = pthread_barrier_wait (&bar);
+  if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD)
+    {
+      puts ("1st barrier_wait failed");
+      exit (1);
+    }
+
+  /* Give the child a chance to go to sleep in sem_wait.  */
+  sleep (1);
+
+  /* Check whether cancellation is honored when waiting in sem_timedwait.  */
+  if (pthread_cancel (th) != 0)
+    {
+      puts ("1st cancel failed");
+      exit (1);
+    }
+
+  void *r;
+  if (pthread_join (th, &r) != 0)
+    {
+      puts ("join failed");
+      exit (1);
+    }
+
+  if (r != PTHREAD_CANCELED)
+    {
+      puts ("thread not canceled");
+      exit (1);
+    }
+
+  return 0;
+}
+
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/nptl/tst-cancelx1.c b/nptl/tst-cancelx1.c
new file mode 100644
index 0000000000..594f095592
--- /dev/null
+++ b/nptl/tst-cancelx1.c
@@ -0,0 +1 @@
+#include "tst-cancel1.c"
diff --git a/nptl/tst-cancelx10.c b/nptl/tst-cancelx10.c
new file mode 100644
index 0000000000..e5bbb34e62
--- /dev/null
+++ b/nptl/tst-cancelx10.c
@@ -0,0 +1 @@
+#include "tst-cancel10.c"
diff --git a/nptl/tst-cancelx11.c b/nptl/tst-cancelx11.c
new file mode 100644
index 0000000000..ffcc2eefc1
--- /dev/null
+++ b/nptl/tst-cancelx11.c
@@ -0,0 +1 @@
+#include "tst-cancel11.c"
diff --git a/nptl/tst-cancelx12.c b/nptl/tst-cancelx12.c
new file mode 100644
index 0000000000..f90ae61bac
--- /dev/null
+++ b/nptl/tst-cancelx12.c
@@ -0,0 +1 @@
+#include "tst-cancel12.c"
diff --git a/nptl/tst-cancelx13.c b/nptl/tst-cancelx13.c
new file mode 100644
index 0000000000..37c4c39c35
--- /dev/null
+++ b/nptl/tst-cancelx13.c
@@ -0,0 +1 @@
+#include "tst-cancel13.c"
diff --git a/nptl/tst-cancelx14.c b/nptl/tst-cancelx14.c
new file mode 100644
index 0000000000..ba4e77584e
--- /dev/null
+++ b/nptl/tst-cancelx14.c
@@ -0,0 +1 @@
+#include "tst-cancel14.c"
diff --git a/nptl/tst-cancelx15.c b/nptl/tst-cancelx15.c
new file mode 100644
index 0000000000..005c1f6e3f
--- /dev/null
+++ b/nptl/tst-cancelx15.c
@@ -0,0 +1 @@
+#include "tst-cancel15.c"
diff --git a/nptl/tst-cancelx2.c b/nptl/tst-cancelx2.c
new file mode 100644
index 0000000000..95dc8a8575
--- /dev/null
+++ b/nptl/tst-cancelx2.c
@@ -0,0 +1 @@
+#include "tst-cancel2.c"
diff --git a/nptl/tst-cancelx3.c b/nptl/tst-cancelx3.c
new file mode 100644
index 0000000000..3937f10b9c
--- /dev/null
+++ b/nptl/tst-cancelx3.c
@@ -0,0 +1 @@
+#include "tst-cancel3.c"
diff --git a/nptl/tst-cancelx4.c b/nptl/tst-cancelx4.c
new file mode 100644
index 0000000000..1c879eba8b
--- /dev/null
+++ b/nptl/tst-cancelx4.c
@@ -0,0 +1 @@
+#include "tst-cancel4.c"
diff --git a/nptl/tst-cancelx5.c b/nptl/tst-cancelx5.c
new file mode 100644
index 0000000000..c0a18840a0
--- /dev/null
+++ b/nptl/tst-cancelx5.c
@@ -0,0 +1 @@
+#include "tst-cancel5.c"
diff --git a/nptl/tst-cancelx6.c b/nptl/tst-cancelx6.c
new file mode 100644
index 0000000000..6926e21c2d
--- /dev/null
+++ b/nptl/tst-cancelx6.c
@@ -0,0 +1 @@
+#include "tst-cancel6.c"
diff --git a/nptl/tst-cancelx7.c b/nptl/tst-cancelx7.c
new file mode 100644
index 0000000000..4df1a58818
--- /dev/null
+++ b/nptl/tst-cancelx7.c
@@ -0,0 +1 @@
+#include "tst-cancel7.c"
diff --git a/nptl/tst-cancelx8.c b/nptl/tst-cancelx8.c
new file mode 100644
index 0000000000..0555c7ceb0
--- /dev/null
+++ b/nptl/tst-cancelx8.c
@@ -0,0 +1 @@
+#include "tst-cancel8.c"
diff --git a/nptl/tst-cancelx9.c b/nptl/tst-cancelx9.c
new file mode 100644
index 0000000000..9d84663d77
--- /dev/null
+++ b/nptl/tst-cancelx9.c
@@ -0,0 +1 @@
+#include "tst-cancel9.c"
diff --git a/nptl/tst-cleanup0.c b/nptl/tst-cleanup0.c
index b6efbf42cf..a73dee3df8 100644
--- a/nptl/tst-cleanup0.c
+++ b/nptl/tst-cleanup0.c
@@ -38,10 +38,20 @@ ch (void *arg)
 }
 
 
+static void
+endfct (void)
+{
+  /* We force exit right here.  */
+  _exit (global);
+}
+
+
 static int
 do_test (void)
 {
-  pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+  atexit (endfct);
+
+  pthread_cancel (pthread_self ());
 
   pthread_cleanup_push (ch, (void *) 1l);
 
@@ -49,7 +59,7 @@ do_test (void)
 
   pthread_cleanup_push (ch, (void *) 3l);
 
-  pthread_cancel (pthread_self ());
+  pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
 
   pthread_cleanup_pop (1);
 
@@ -57,9 +67,10 @@ do_test (void)
 
   pthread_cleanup_pop (1);
 
-  return 0;
+  return 100;
 }
 
 
+#define EXPECTED_STATUS 9
 #define TEST_FUNCTION do_test ()
 #include "../test-skeleton.c"
diff --git a/nptl/tst-cleanup1.c b/nptl/tst-cleanup1.c
index a6b61425a6..2230e0fbe4 100644
--- a/nptl/tst-cleanup1.c
+++ b/nptl/tst-cleanup1.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2002 Free Software Foundation, Inc.
+/* Copyright (C) 2002, 2003 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
 
@@ -41,7 +41,7 @@ ch (void *arg)
 static void *
 tf (void *a)
 {
-  pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+  pthread_cancel (pthread_self ());
 
   pthread_cleanup_push (ch, (void *) 1l);
 
@@ -49,7 +49,7 @@ tf (void *a)
 
   pthread_cleanup_push (ch, (void *) 3l);
 
-  pthread_cancel (pthread_self ());
+  pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
 
   pthread_cleanup_pop (1);
 
diff --git a/nptl/tst-cleanupx0.c b/nptl/tst-cleanupx0.c
new file mode 100644
index 0000000000..0012ab1b25
--- /dev/null
+++ b/nptl/tst-cleanupx0.c
@@ -0,0 +1 @@
+#include "tst-cleanup0.c"
diff --git a/nptl/tst-cleanupx0.expect b/nptl/tst-cleanupx0.expect
new file mode 100644
index 0000000000..4e3c581802
--- /dev/null
+++ b/nptl/tst-cleanupx0.expect
@@ -0,0 +1,3 @@
+ch (3)
+ch (2)
+ch (1)
diff --git a/nptl/tst-cleanupx1.c b/nptl/tst-cleanupx1.c
new file mode 100644
index 0000000000..21e9e58bd6
--- /dev/null
+++ b/nptl/tst-cleanupx1.c
@@ -0,0 +1 @@
+#include "tst-cleanup1.c"
diff --git a/nptl/tst-cleanupx2.c b/nptl/tst-cleanupx2.c
new file mode 100644
index 0000000000..8b9e350935
--- /dev/null
+++ b/nptl/tst-cleanupx2.c
@@ -0,0 +1 @@
+#include "tst-cleanup2.c"
diff --git a/nptl/tst-cleanupx3.c b/nptl/tst-cleanupx3.c
new file mode 100644
index 0000000000..90baf904f9
--- /dev/null
+++ b/nptl/tst-cleanupx3.c
@@ -0,0 +1 @@
+#include "tst-cleanup3.c"