From a7720b5e184ed038576e017701911169c7de8f8a Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Fri, 14 Feb 2003 03:26:28 +0000 Subject: Update. * sysdeps/unix/sysv/linux/i386/i486/pthread_cond_wait.S: Fix handling of cancellation and failung pthread_mutex_unlock call. * sysdeps/unix/sysv/linux/i386/i486/pthread_cond_timedwait.S: Likewise. * Makefile (tests): Add tst-cond8 and tst-cond9. * tst-cond8.c: New file. * tst-cond9.c: New file. --- nptl/ChangeLog | 7 + nptl/Makefile | 1 + nptl/allocatestack.c | 3 +- .../sysv/linux/i386/i486/pthread_cond_timedwait.S | 63 +++-- .../unix/sysv/linux/i386/i486/pthread_cond_wait.S | 67 +++-- nptl/tst-cond8.c | 275 +++++++++++++++++++++ nptl/tst-cond9.c | 150 +++++++++++ 7 files changed, 533 insertions(+), 33 deletions(-) create mode 100644 nptl/tst-cond8.c create mode 100644 nptl/tst-cond9.c diff --git a/nptl/ChangeLog b/nptl/ChangeLog index 0bcd27e189..c7d442148c 100644 --- a/nptl/ChangeLog +++ b/nptl/ChangeLog @@ -1,5 +1,12 @@ 2003-02-13 Ulrich Drepper + * sysdeps/unix/sysv/linux/i386/i486/pthread_cond_wait.S: Fix + handling of cancellation and failung pthread_mutex_unlock call. + * sysdeps/unix/sysv/linux/i386/i486/pthread_cond_timedwait.S: Likewise. + * Makefile (tests): Add tst-cond8 and tst-cond9. + * tst-cond8.c: New file. + * tst-cond9.c: New file. + * tst-cond7.c (do_test): Unlock the mutex before canceling the thread. * sysdeps/pthread/pthread.h: Add missing initializers. Protect diff --git a/nptl/Makefile b/nptl/Makefile index ba5ab61a9b..35d77c2361 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -129,6 +129,7 @@ tests = tst-attr1 tst-attr2 \ tst-mutex7 \ tst-spin1 tst-spin2 tst-spin3 \ tst-cond1 tst-cond2 tst-cond3 tst-cond4 tst-cond5 tst-cond6 tst-cond7 \ + tst-cond8 tst-cond9 \ tst-rwlock1 tst-rwlock2 tst-rwlock3 tst-rwlock4 tst-rwlock5 \ tst-rwlock6 tst-rwlock7 \ tst-once1 tst-once2 tst-once3 tst-once4 \ diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c index 94d4525ff0..1a4bf44367 100644 --- a/nptl/allocatestack.c +++ b/nptl/allocatestack.c @@ -465,8 +465,7 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp, 0)) { /* The old guard area is too large. */ - if (mprotect ((char *) mem + guardsize, - pd->guardsize - guardsize, + if (mprotect ((char *) mem + guardsize, pd->guardsize - guardsize, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) goto mprot_error; diff --git a/nptl/sysdeps/unix/sysv/linux/i386/i486/pthread_cond_timedwait.S b/nptl/sysdeps/unix/sysv/linux/i386/i486/pthread_cond_timedwait.S index c520f287d9..b13ad19408 100644 --- a/nptl/sysdeps/unix/sysv/linux/i386/i486/pthread_cond_timedwait.S +++ b/nptl/sysdeps/unix/sysv/linux/i386/i486/pthread_cond_timedwait.S @@ -67,6 +67,9 @@ __pthread_cond_timedwait: 2: pushl 24(%esp) call __pthread_mutex_unlock_internal + testl %eax, %eax + jne 16f + addl $1, total_seq(%ebx) adcl $0, total_seq+4(%ebx) @@ -78,18 +81,23 @@ __pthread_cond_timedwait: #else leal __condvar_cleanup, %eax #endif - subl $32, %esp - leal 16(%esp), %edx - movl %ebx, 8(%esp) + subl $40, %esp + leal 28(%esp), %edx + movl %esp, 8(%esp) movl %eax, 4(%esp) movl %edx, (%esp) call __pthread_cleanup_push + /* Address of the mutex. */ + movl 68(%esp), %ecx /* Get and store current wakeup_seq value. */ movl wakeup_seq(%ebx), %edi movl wakeup_seq+4(%ebx), %edx - movl %edi, 12(%esp) - movl %edx, 16(%esp) + movl %edi, 20(%esp) + movl %edx, 24(%esp) + /* Prepare structure passed to cancellation handler. */ + movl %ebx, 4(%esp) + movl %ecx, 8(%esp) /* Unlock. */ 8: LOCK @@ -105,19 +113,19 @@ __pthread_cond_timedwait: /* Get the current time. */ movl %ebx, %edx - leal 4(%esp), %ebx + leal 12(%esp), %ebx xorl %ecx, %ecx movl $SYS_gettimeofday, %eax ENTER_KERNEL movl %edx, %ebx /* Compute relative timeout. */ - movl 8(%esp), %eax + movl 16(%esp), %eax movl $1000, %edx mul %edx /* Milli seconds to nano seconds. */ movl (%ebp), %ecx movl 4(%ebp), %edx - subl 4(%esp), %ecx + subl 12(%esp), %ecx subl %eax, %edx jns 12f addl $1000000000, %edx @@ -126,9 +134,9 @@ __pthread_cond_timedwait: js 13f /* Store relative timeout. */ - movl %ecx, 4(%esp) - movl %edx, 8(%esp) - leal 4(%esp), %esi + movl %ecx, 12(%esp) + movl %edx, 16(%esp) + leal 12(%esp), %esi xorl %ecx, %ecx /* movl $FUTEX_WAIT, %ecx */ movl %edi, %edx addl $wakeup_seq, %ebx @@ -156,10 +164,10 @@ __pthread_cond_timedwait: movl wakeup_seq(%ebx), %edi movl wakeup_seq+4(%ebx), %edx - cmpl 16(%esp), %ecx + cmpl 24(%esp), %ecx ja 7f jb 15f - cmpl 12(%esp), %eax + cmpl 20(%esp), %eax jb 15f 7: cmpl %ecx, %edx @@ -189,19 +197,19 @@ __pthread_cond_timedwait: jne 10f /* Remove cancellation handler. */ -11: leal 20(%esp), %edx +11: leal 28(%esp), %edx movl $0, 4(%esp) movl %edx, (%esp) call __pthread_cleanup_pop - movl 60(%esp), %ecx - movl %ecx, (%esp) + /* Trick ahead: 8(%esp) contains the address of the mutex. */ + addl $8, %esp call __pthread_mutex_lock_internal addl $36, %esp movl %esi, %eax - popl %ebx +18: popl %ebx popl %esi popl %edi popl %ebp @@ -248,6 +256,27 @@ __pthread_cond_timedwait: #endif call __lll_mutex_unlock_wake jmp 11b + + /* The initial unlocking of the mutex failed. */ +16: movl %eax, (%esp) + LOCK +#if cond_lock == 0 + decl (%ebx) +#else + decl cond_lock(%ebx) +#endif + jne 17f + +#if cond_lock == 0 + movl %ebx, %eax +#else + leal cond_lock(%ebx), %eax +#endif + call __lll_mutex_unlock_wake + +17: popl %eax + jmp 18b + .size __pthread_cond_wait, .-__pthread_cond_wait .size __pthread_cond_timedwait, .-__pthread_cond_timedwait versioned_symbol (libpthread, __pthread_cond_timedwait, pthread_cond_timedwait, GLIBC_2_3_2) diff --git a/nptl/sysdeps/unix/sysv/linux/i386/i486/pthread_cond_wait.S b/nptl/sysdeps/unix/sysv/linux/i386/i486/pthread_cond_wait.S index e43bcbac82..da0483ab1b 100644 --- a/nptl/sysdeps/unix/sysv/linux/i386/i486/pthread_cond_wait.S +++ b/nptl/sysdeps/unix/sysv/linux/i386/i486/pthread_cond_wait.S @@ -20,6 +20,7 @@ #include #include #include +#include #ifdef UP # define LOCK @@ -40,9 +41,11 @@ .hidden __condvar_cleanup __condvar_cleanup: pushl %ebx - movl 8(%esp), %ebx + pushl %esi + movl 12(%esp), %esi /* Get internal lock. */ + movl 4(%esi), %ebx movl $1, %eax LOCK #if cond_lock == 0 @@ -80,7 +83,16 @@ __condvar_cleanup: #endif call __lll_mutex_unlock_wake -2: popl %ebx + /* Lock the mutex unless asnychronous cancellation is in effect. */ +2: testl $2, (%esi) + jne 3f + + pushl 8(%esi) + call __pthread_mutex_lock_internal + popl %eax + +3: popl %esi + popl %ebx ret .size __condvar_cleanup, .-__condvar_cleanup @@ -113,6 +125,9 @@ __pthread_cond_wait: 2: pushl 20(%esp) call __pthread_mutex_unlock_internal + testl %eax, %eax + jne 12f + addl $1, total_seq(%ebx) adcl $0, total_seq+4(%ebx) @@ -124,18 +139,22 @@ __pthread_cond_wait: #else leal __condvar_cleanup, %eax #endif - subl $24, %esp - leal 12(%esp), %edx - movl %ebx, 8(%esp) + subl $32, %esp + leal 20(%esp), %edx + movl %esp, 8(%esp) movl %eax, 4(%esp) movl %edx, (%esp) call __pthread_cleanup_push /* Get and store current wakeup_seq value. */ + movl 56(%esp), %ecx movl wakeup_seq(%ebx), %edi movl wakeup_seq+4(%ebx), %edx - movl %edi, 4(%esp) - movl %edx, 8(%esp) + movl %edi, 12(%esp) + movl %edx, 16(%esp) + /* Prepare structure passed to cancellation handler. */ + movl %ebx, 4(%esp) + movl %ecx, 8(%esp) /* Unlock. */ 8: LOCK @@ -175,10 +194,10 @@ __pthread_cond_wait: movl wakeup_seq(%ebx), %edi movl wakeup_seq+4(%ebx), %edx - cmpl 8(%esp), %ecx + cmpl 16(%esp), %ecx ja 7f jb 8b - cmpl 4(%esp), %eax + cmpl 12(%esp), %eax jb 8b 7: cmpl %ecx, %edx @@ -199,17 +218,17 @@ __pthread_cond_wait: jne 10f /* Remove cancellation handler. */ -11: leal 12(%esp), %edx +11: leal 20(%esp), %edx movl $0, 4(%esp) movl %edx, (%esp) call __pthread_cleanup_pop - movl 48(%esp), %eax - movl %eax, (%esp) + /* Trick ahead: 8(%esp) contains the address of the mutex. */ + addl $8, %esp call __pthread_mutex_lock_internal addl $28, %esp - popl %ebx +14: popl %ebx popl %esi popl %edi @@ -246,7 +265,7 @@ __pthread_cond_wait: call __lll_mutex_lock_wait jmp 6b - /* Unlock after loop requires waekup. */ + /* Unlock after loop requires wakeup. */ 10: #if cond_lock == 0 movl %ebx, %eax @@ -255,6 +274,26 @@ __pthread_cond_wait: #endif call __lll_mutex_unlock_wake jmp 11b + + /* The initial unlocking of the mutex failed. */ +12: movl %eax, (%esp) + LOCK +#if cond_lock == 0 + decl (%ebx) +#else + decl cond_lock(%ebx) +#endif + jne 13f + +#if cond_lock == 0 + movl %ebx, %eax +#else + leal cond_lock(%ebx), %eax +#endif + call __lll_mutex_unlock_wake + +13: popl %eax + jmp 14b .size __pthread_cond_wait, .-__pthread_cond_wait versioned_symbol (libpthread, __pthread_cond_wait, pthread_cond_wait, GLIBC_2_3_2) diff --git a/nptl/tst-cond8.c b/nptl/tst-cond8.c new file mode 100644 index 0000000000..48451e7813 --- /dev/null +++ b/nptl/tst-cond8.c @@ -0,0 +1,275 @@ +/* Copyright (C) 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper , 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 +#include +#include +#include +#include +#include + + +static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t mut = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; + +static pthread_barrier_t bar; + + +static void +ch (void *arg) +{ + int e = pthread_mutex_lock (&mut); + if (e == 0) + { + puts ("mutex not locked at all by cond_wait"); + exit (1); + } + + if (e != EDEADLK) + { + puts ("no deadlock error signaled"); + exit (1); + } + + if (pthread_mutex_unlock (&mut) != 0) + { + puts ("ch: cannot unlock mutex"); + exit (1); + } +} + + +static void * +tf1 (void *p) +{ + int err; + + if (pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL) != 0 + || pthread_setcanceltype (PTHREAD_CANCEL_DEFERRED, NULL) != 0) + { + puts ("cannot set cancellation options"); + exit (1); + } + + err = pthread_mutex_lock (&mut); + if (err != 0) + { + puts ("child: cannot get mutex"); + exit (1); + } + + err = pthread_barrier_wait (&bar); + if (err != 0 && err != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("barrier_wait returned %d\n", err); + exit (1); + } + + puts ("child: got mutex; waiting"); + + pthread_cleanup_push (ch, NULL); + + pthread_cond_wait (&cond, &mut); + + pthread_cleanup_pop (0); + + puts ("child: cond_wait should not have returned"); + + return NULL; +} + + +static void * +tf2 (void *p) +{ + int err; + + if (pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL) != 0 + || pthread_setcanceltype (PTHREAD_CANCEL_DEFERRED, NULL) != 0) + { + puts ("cannot set cancellation options"); + exit (1); + } + + err = pthread_mutex_lock (&mut); + if (err != 0) + { + puts ("child: cannot get mutex"); + exit (1); + } + + err = pthread_barrier_wait (&bar); + if (err != 0 && err != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("barrier_wait returned %d\n", err); + exit (1); + } + + puts ("child: got mutex; waiting"); + + pthread_cleanup_push (ch, NULL); + + /* Current time. */ + struct timeval tv; + (void) gettimeofday (&tv, NULL); + /* +1000 seconds in correct format. */ + struct timespec ts; + TIMEVAL_TO_TIMESPEC (&tv, &ts); + ts.tv_sec += 1000; + + pthread_cond_timedwait (&cond, &mut, &ts); + + pthread_cleanup_pop (0); + + puts ("child: cond_wait should not have returned"); + + return NULL; +} + + +static int +do_test (void) +{ + pthread_t th; + int err; + + printf ("&cond = %p\n&mut = %p\n", &cond, &mut); + + puts ("parent: get mutex"); + + err = pthread_barrier_init (&bar, NULL, 2); + if (err != 0) + { + puts ("parent: cannot init barrier"); + exit (1); + } + + puts ("parent: create child"); + + err = pthread_create (&th, NULL, tf1, NULL); + if (err != 0) + { + puts ("parent: cannot create thread"); + exit (1); + } + + puts ("parent: wait for child to lock mutex"); + + err = pthread_barrier_wait (&bar); + if (err != 0 && err != PTHREAD_BARRIER_SERIAL_THREAD) + { + puts ("parent: cannot wait for barrier"); + exit (1); + } + + err = pthread_mutex_lock (&mut); + if (err != 0) + { + puts ("parent: mutex_lock failed"); + exit (1); + } + + err = pthread_mutex_unlock (&mut); + if (err != 0) + { + puts ("parent: mutex_unlock failed"); + exit (1); + } + + if (pthread_cancel (th) != 0) + { + puts ("cannot cancel thread"); + exit (1); + } + + void *r; + err = pthread_join (th, &r); + if (err != 0) + { + puts ("parent: failed to join"); + exit (1); + } + + if (r != PTHREAD_CANCELED) + { + puts ("child hasn't been canceled"); + exit (1); + } + + + + puts ("parent: create 2nd child"); + + err = pthread_create (&th, NULL, tf2, NULL); + if (err != 0) + { + puts ("parent: cannot create thread"); + exit (1); + } + + puts ("parent: wait for child to lock mutex"); + + err = pthread_barrier_wait (&bar); + if (err != 0 && err != PTHREAD_BARRIER_SERIAL_THREAD) + { + puts ("parent: cannot wait for barrier"); + exit (1); + } + + err = pthread_mutex_lock (&mut); + if (err != 0) + { + puts ("parent: mutex_lock failed"); + exit (1); + } + + err = pthread_mutex_unlock (&mut); + if (err != 0) + { + puts ("parent: mutex_unlock failed"); + exit (1); + } + + if (pthread_cancel (th) != 0) + { + puts ("cannot cancel thread"); + exit (1); + } + + err = pthread_join (th, &r); + if (err != 0) + { + puts ("parent: failed to join"); + exit (1); + } + + if (r != PTHREAD_CANCELED) + { + puts ("child hasn't been canceled"); + exit (1); + } + + puts ("done"); + + return 0; +} + + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/nptl/tst-cond9.c b/nptl/tst-cond9.c new file mode 100644 index 0000000000..2a8477dd8b --- /dev/null +++ b/nptl/tst-cond9.c @@ -0,0 +1,150 @@ +/* Copyright (C) 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper , 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 +#include +#include +#include +#include +#include + + +static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t mut = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; + + +static void * +tf (void *arg) +{ + int err = pthread_cond_wait (&cond, &mut); + if (err == 0) + { + puts ("cond_wait did not fail"); + exit (1); + } + + if (err != EPERM) + { + printf ("cond_wait didn't return EPERM but %d\n", err); + exit (1); + } + + + /* Current time. */ + struct timeval tv; + (void) gettimeofday (&tv, NULL); + /* +1000 seconds in correct format. */ + struct timespec ts; + TIMEVAL_TO_TIMESPEC (&tv, &ts); + ts.tv_sec += 1000; + + err = pthread_cond_timedwait (&cond, &mut, &ts); + if (err == 0) + { + puts ("cond_timedwait did not fail"); + exit (1); + } + + if (err != EPERM) + { + printf ("cond_timedwait didn't return EPERM but %d\n", err); + exit (1); + } + + return (void *) 1l; +} + + +static int +do_test (void) +{ + pthread_t th; + int err; + + printf ("&cond = %p\n&mut = %p\n", &cond, &mut); + + err = pthread_cond_wait (&cond, &mut); + if (err == 0) + { + puts ("cond_wait did not fail"); + exit (1); + } + + if (err != EPERM) + { + printf ("cond_wait didn't return EPERM but %d\n", err); + exit (1); + } + + + /* Current time. */ + struct timeval tv; + (void) gettimeofday (&tv, NULL); + /* +1000 seconds in correct format. */ + struct timespec ts; + TIMEVAL_TO_TIMESPEC (&tv, &ts); + ts.tv_sec += 1000; + + err = pthread_cond_timedwait (&cond, &mut, &ts); + if (err == 0) + { + puts ("cond_timedwait did not fail"); + exit (1); + } + + if (err != EPERM) + { + printf ("cond_timedwait didn't return EPERM but %d\n", err); + exit (1); + } + + if (pthread_mutex_lock (&mut) != 0) + { + puts ("parent: mutex_lock failed"); + exit (1); + } + + puts ("creating thread"); + + if (pthread_create (&th, NULL, tf, NULL) != 0) + { + puts ("create failed"); + exit (1); + } + + void *r; + if (pthread_join (th, &r) != 0) + { + puts ("join failed"); + exit (1); + } + if (r != (void *) 1l) + { + puts ("thread has wrong return value"); + exit (1); + } + + puts ("done"); + + return 0; +} + + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" -- cgit 1.4.1