diff options
-rw-r--r-- | ChangeLog | 10 | ||||
-rw-r--r-- | nptl/ChangeLog | 8 | ||||
-rw-r--r-- | nptl/sysdeps/pthread/aio_misc.h | 73 | ||||
-rw-r--r-- | nptl/sysdeps/unix/sysv/linux/i386/lowlevellock.h | 26 | ||||
-rw-r--r-- | nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.h | 25 | ||||
-rw-r--r-- | rt/Makefile | 3 | ||||
-rw-r--r-- | rt/tst-aio9.c | 124 | ||||
-rw-r--r-- | sysdeps/pthread/aio_misc.h | 2 | ||||
-rw-r--r-- | sysdeps/pthread/aio_notify.c | 12 | ||||
-rw-r--r-- | sysdeps/pthread/aio_suspend.c | 36 | ||||
-rw-r--r-- | sysdeps/pthread/lio_listio.c | 12 |
11 files changed, 312 insertions, 19 deletions
diff --git a/ChangeLog b/ChangeLog index 2bbd6c781b..6cebfc3c8f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,15 @@ 2006-01-05 Ulrich Drepper <drepper@redhat.com> + * sysdeps/pthread/aio_misc.h [!DONT_USE_BOOTSTRAP_MAP] + (struct waitlist): Don't add cond. + * sysdeps/pthread/aio_notify.c [!DONT_USE_BOOTSTRAP_MAP] + (__aio_notify): Use AIO_MISC_NOTIFY instead of pthread_cond_signal. + * sysdeps/pthread/aio_suspend.c [!DONT_USE_BOOTSTRAP_MAP]: Don't + use condvar, use AIO_MISC_WAIT. + * sysdeps/pthread/lio_listio.c: Likewise. + * rt/Makefile (tests): Add aio_suspend. + * rt/tst-aio9.c: New file. + * rt/tst-aio3.c: The thread is now supposed to be created. 2006-01-05 Roland McGrath <roland@redhat.com> diff --git a/nptl/ChangeLog b/nptl/ChangeLog index 9772d1307e..0d00473d8c 100644 --- a/nptl/ChangeLog +++ b/nptl/ChangeLog @@ -1,3 +1,11 @@ +2006-01-05 Ulrich Drepper <drepper@redhat.com> + + * sysdeps/unix/sysv/linux/i386/lowlevellock.h (lll_futex_wait): + Return status. + (lll_futex_timed_wait): Define. + * sysdeps/unix/sysv/linux/x86_64/lowlevellock.h: Likewise. + * sysdeps/pthread/aio_misc.h: New file. + 2006-01-03 Joseph S. Myers <joseph@codesourcery.com> * Makefile ($(objpfx)$(multidir)): Use mkdir -p. diff --git a/nptl/sysdeps/pthread/aio_misc.h b/nptl/sysdeps/pthread/aio_misc.h new file mode 100644 index 0000000000..5aeb34eb98 --- /dev/null +++ b/nptl/sysdeps/pthread/aio_misc.h @@ -0,0 +1,73 @@ +/* Copyright (C) 2006 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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. */ + +/* We define a special synchronization primitive for AIO. POSIX + conditional variables would be ideal but the pthread_cond_*wait + operations do not return on EINTR. This is a requirement for + correct aio_suspend and lio_listio implementations. */ + +#include <assert.h> +#include <pthreadP.h> +#include <lowlevellock.h> + +#define DONT_NEED_AIO_MISC_COND 1 + +#define AIO_MISC_NOTIFY(waitlist) \ + do { \ + if (--*waitlist->counterp == 0) \ + lll_futex_wake (waitlist->counterp, 1); \ + } while (0) + +#define AIO_MISC_WAIT(result, futex, timeout, cancel) \ + do { \ + int oldval = futex; \ + \ + if (oldval != 0) \ + { \ + pthread_mutex_unlock (&__aio_requests_mutex); \ + \ + int oldtype; \ + if (cancel) \ + oldtype = LIBC_CANCEL_ASYNC (); \ + \ + int status; \ + do \ + { \ + status = lll_futex_timed_wait (&futex, oldval, timeout); \ + if (status != -EWOULDBLOCK) \ + break; \ + \ + oldval = futex; \ + } \ + while (oldval != 0); \ + \ + if (cancel) \ + LIBC_CANCEL_RESET (oldtype); \ + \ + if (status == -EINTR) \ + result = EINTR; \ + else if (status == -ETIMEDOUT) \ + result = EAGAIN; \ + else \ + assert (status == 0 || status == -EWOULDBLOCK); \ + \ + pthread_mutex_lock (&__aio_requests_mutex); \ + } \ + } while (0) + +#include_next <aio_misc.h> diff --git a/nptl/sysdeps/unix/sysv/linux/i386/lowlevellock.h b/nptl/sysdeps/unix/sysv/linux/i386/lowlevellock.h index b86f11c9b4..fd1ee4569d 100644 --- a/nptl/sysdeps/unix/sysv/linux/i386/lowlevellock.h +++ b/nptl/sysdeps/unix/sysv/linux/i386/lowlevellock.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. +/* Copyright (C) 2002, 2003, 2004, 2006 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@redhat.com>, 2002. @@ -66,17 +66,33 @@ #define lll_futex_wait(futex, val) \ - do { \ - int __ignore; \ + ({ \ + int __status; \ register __typeof (val) _val asm ("edx") = (val); \ __asm __volatile (LLL_EBX_LOAD \ LLL_ENTER_KERNEL \ LLL_EBX_LOAD \ - : "=a" (__ignore) \ + : "=a" (__status) \ : "0" (SYS_futex), LLL_EBX_REG (futex), "S" (0), \ "c" (FUTEX_WAIT), "d" (_val), \ "i" (offsetof (tcbhead_t, sysinfo))); \ - } while (0) + __status; \ + }) + + +#define lll_futex_timed_wait(futex, val, timeout) \ + ({ \ + int __status; \ + register __typeof (val) _val asm ("edx") = (val); \ + __asm __volatile (LLL_EBX_LOAD \ + LLL_ENTER_KERNEL \ + LLL_EBX_LOAD \ + : "=a" (__status) \ + : "0" (SYS_futex), LLL_EBX_REG (futex), "S" (timeout), \ + "c" (FUTEX_WAIT), "d" (_val), \ + "i" (offsetof (tcbhead_t, sysinfo))); \ + __status; \ + }) #define lll_futex_wake(futex, nr) \ diff --git a/nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.h b/nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.h index 40c2518af6..ebcfe6eef1 100644 --- a/nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.h +++ b/nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. +/* Copyright (C) 2002, 2003, 2004, 2006 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@redhat.com>, 2002. @@ -47,16 +47,31 @@ #define lll_futex_wait(futex, val) \ - do { \ - int __ignore; \ + ({ \ + int __status; \ register __typeof (val) _val asm ("edx") = (val); \ __asm __volatile ("xorq %%r10, %%r10\n\t" \ "syscall" \ - : "=a" (__ignore) \ + : "=a" (__status) \ : "0" (SYS_futex), "D" (futex), "S" (FUTEX_WAIT), \ "d" (_val) \ : "memory", "cc", "r10", "r11", "cx"); \ - } while (0) + __status; \ + }) + + +#define lll_futex_timed_wait(futex, val, timeout) \ + ({ \ + register const struct timespec *__to __asm__ ("r10") = timeout; \ + int __status; \ + register __typeof (val) _val asm ("edx") = (val); \ + __asm __volatile ("syscall" \ + : "=a" (__status) \ + : "0" (SYS_futex), "D" (futex), "S" (FUTEX_WAIT), \ + "d" (_val), "r" (__to) \ + : "memory", "cc", "r11", "cx"); \ + __status; \ + }) #define lll_futex_wake(futex, nr) \ diff --git a/rt/Makefile b/rt/Makefile index 68fa9f5c41..4f7d5f9d40 100644 --- a/rt/Makefile +++ b/rt/Makefile @@ -43,7 +43,8 @@ librt-routines = $(aio-routines) \ tests := tst-shm tst-clock tst-clock_nanosleep tst-timer tst-timer2 \ tst-aio tst-aio64 tst-aio2 tst-aio3 tst-aio4 tst-aio5 tst-aio6 \ - tst-aio7 tst-aio8 tst-mqueue1 tst-mqueue2 tst-mqueue3 tst-mqueue4 \ + tst-aio7 tst-aio8 tst-aio9 \ + tst-mqueue1 tst-mqueue2 tst-mqueue3 tst-mqueue4 \ tst-mqueue5 tst-mqueue6 tst-mqueue7 tst-mqueue8 tst-mqueue9 \ tst-timer3 tst-timer4 tst-timer5 \ tst-cpuclock1 tst-cpuclock2 \ diff --git a/rt/tst-aio9.c b/rt/tst-aio9.c new file mode 100644 index 0000000000..eab4f6859a --- /dev/null +++ b/rt/tst-aio9.c @@ -0,0 +1,124 @@ +#include <aio.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <unistd.h> + +static pthread_barrier_t b; +static pthread_t main_thread; +static int flag; + + +static void * +tf (void *arg) +{ + int e = pthread_barrier_wait (&b); + if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD) + { + puts ("child: barrier_wait failed"); + exit (1); + } + + /* There is unfortunately no other way to try to make sure the other + thread reached the aio_suspend call. This test could fail on + highly loaded machines. */ + sleep (2); + + pthread_kill (main_thread, SIGUSR1); + + while (1) + sleep (1000); + + return NULL; +} + + +static void +sh (int sig) +{ + flag = 1; +} + + +static int +do_test (void) +{ + main_thread = pthread_self (); + + struct sigaction sa; + + sa.sa_handler = sh; + sa.sa_flags = 0; + sigemptyset (&sa.sa_mask); + + if (sigaction (SIGUSR1, &sa, NULL) != 0) + { + puts ("sigaction failed"); + return 1; + } + + if (pthread_barrier_init (&b, NULL, 2) != 0) + { + puts ("barrier_init"); + return 1; + } + + int fds[2]; + if (pipe (fds) != 0) + { + puts ("pipe failed"); + return 1; + } + + char buf[42]; + struct aiocb req; + req.aio_fildes = fds[0]; + req.aio_reqprio = 0; + req.aio_offset = 0; + req.aio_buf = buf; + req.aio_nbytes = sizeof (buf); + req.aio_sigevent.sigev_notify = SIGEV_NONE; + + if (aio_read (&req) != 0) + { + puts ("aio_read failed"); + return 1; + } + + pthread_t th; + if (pthread_create (&th, NULL, tf, NULL) != 0) + { + puts ("create failed"); + return 1; + } + + int e = pthread_barrier_wait (&b); + if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD) + { + puts ("parent: barrier_wait failed"); + exit (1); + } + + const struct aiocb *list[1]; + list[0] = &req; + + e = aio_suspend (list, 1, NULL); + if (e != -1) + { + puts ("aio_suspend succeeded"); + return 1; + } + if (errno != EINTR) + { + puts ("aio_suspend did not return EINTR"); + return 1; + } + + return 0; +} + +#define TEST_FUNCTION do_test () +#define TIMEOUT 5 +#include "../test-skeleton.c" diff --git a/sysdeps/pthread/aio_misc.h b/sysdeps/pthread/aio_misc.h index d9aa6e8dd3..50962c7519 100644 --- a/sysdeps/pthread/aio_misc.h +++ b/sysdeps/pthread/aio_misc.h @@ -47,7 +47,9 @@ struct waitlist struct waitlist *next; /* The next two fields is used in synchronous `lio_listio' operations. */ +#ifndef DONT_NEED_AIO_MISC_COND pthread_cond_t *cond; +#endif int *result; volatile int *counterp; diff --git a/sysdeps/pthread/aio_notify.c b/sysdeps/pthread/aio_notify.c index 4d2611f0a1..3f7f70ef7c 100644 --- a/sysdeps/pthread/aio_notify.c +++ b/sysdeps/pthread/aio_notify.c @@ -143,20 +143,24 @@ __aio_notify (struct requestlist *req) { struct waitlist *next = waitlist->next; - /* Decrement the counter. This is used in both cases. */ - --*waitlist->counterp; - if (waitlist->sigevp == NULL) { if (waitlist->result != NULL && aiocbp->__return_value == -1) *waitlist->result = -1; +#ifdef DONT_NEED_AIO_MISC_COND + AIO_MISC_NOTIFY (waitlist); +#else + /* Decrement the counter. */ + --*waitlist->counterp; + pthread_cond_signal (waitlist->cond); +#endif } else /* This is part of a asynchronous `lio_listio' operation. If this request is the last one, send the signal. */ - if (*waitlist->counterp == 0) + if (--*waitlist->counterp == 0) { #ifdef BROKEN_THREAD_SIGNALS __aio_notify_only (waitlist->sigevp, waitlist->caller_pid); diff --git a/sysdeps/pthread/aio_suspend.c b/sysdeps/pthread/aio_suspend.c index 9e3a1ee46e..b85b16d10e 100644 --- a/sysdeps/pthread/aio_suspend.c +++ b/sysdeps/pthread/aio_suspend.c @@ -44,7 +44,9 @@ struct clparam const struct aiocb *const *list; struct waitlist *waitlist; struct requestlist **requestlist; +#ifndef DONT_NEED_AIO_MISC_COND pthread_cond_t *cond; +#endif int nent; }; @@ -52,6 +54,12 @@ struct clparam static void cleanup (void *arg) { +#ifdef DONT_NEED_AIO_MISC_COND + /* Acquire the mutex. If pthread_cond_*wait is used this would + happen implicitly. */ + pthread_mutex_lock (&__aio_requests_mutex); +#endif + const struct clparam *param = (const struct clparam *) arg; /* Now remove the entry in the waiting list for all requests @@ -75,8 +83,10 @@ cleanup (void *arg) *listp = (*listp)->next; } +#ifndef DONT_NEED_AIO_MISC_COND /* Release the conditional variable. */ (void) pthread_cond_destroy (param->cond); +#endif /* Release the mutex. */ pthread_mutex_unlock (&__aio_requests_mutex); @@ -89,13 +99,21 @@ aio_suspend (list, nent, timeout) int nent; const struct timespec *timeout; { + if (__builtin_expect (nent < 0, 0)) + { + __set_errno (EINVAL); + return -1; + } + struct waitlist waitlist[nent]; struct requestlist *requestlist[nent]; +#ifndef DONT_NEED_AIO_MISC_COND pthread_cond_t cond = PTHREAD_COND_INITIALIZER; +#endif int cnt; bool any = false; int result = 0; - int dummy; + int cntr = 1; /* Request the mutex. */ pthread_mutex_lock (&__aio_requests_mutex); @@ -111,10 +129,12 @@ aio_suspend (list, nent, timeout) if (requestlist[cnt] != NULL) { +#ifndef DONT_NEED_AIO_MISC_COND waitlist[cnt].cond = &cond; +#endif waitlist[cnt].result = NULL; waitlist[cnt].next = requestlist[cnt]->waiting; - waitlist[cnt].counterp = &dummy; + waitlist[cnt].counterp = &cntr; waitlist[cnt].sigevp = NULL; #ifdef BROKEN_THREAD_SIGNALS waitlist[cnt].caller_pid = 0; /* Not needed. */ @@ -140,12 +160,17 @@ aio_suspend (list, nent, timeout) .list = list, .waitlist = waitlist, .requestlist = requestlist, +#ifndef DONT_NEED_AIO_MISC_COND .cond = &cond, +#endif .nent = nent }; pthread_cleanup_push (cleanup, &clparam); +#ifdef DONT_NEED_AIO_MISC_COND + AIO_MISC_WAIT (result, cntr, timeout, 1); +#else if (timeout == NULL) result = pthread_cond_wait (&cond, &__aio_requests_mutex); else @@ -167,6 +192,7 @@ aio_suspend (list, nent, timeout) result = pthread_cond_timedwait (&cond, &__aio_requests_mutex, &abstime); } +#endif pthread_cleanup_pop (0); } @@ -190,19 +216,23 @@ aio_suspend (list, nent, timeout) *listp = (*listp)->next; } +#ifndef DONT_NEED_AIO_MISC_COND /* Release the conditional variable. */ if (__builtin_expect (pthread_cond_destroy (&cond) != 0, 0)) /* This must never happen. */ abort (); +#endif if (result != 0) { - /* An error occurred. Possibly it's EINTR. We have to translate +#ifndef DONT_NEED_AIO_MISC_COND + /* An error occurred. Possibly it's ETIMEDOUT. We have to translate the timeout error report of `pthread_cond_timedwait' to the form expected from `aio_suspend'. */ if (result == ETIMEDOUT) __set_errno (EAGAIN); else +#endif __set_errno (result); result = -1; diff --git a/sysdeps/pthread/lio_listio.c b/sysdeps/pthread/lio_listio.c index d6ad5a2b48..39187f3025 100644 --- a/sysdeps/pthread/lio_listio.c +++ b/sysdeps/pthread/lio_listio.c @@ -122,9 +122,11 @@ lio_listio_internal (int mode, struct aiocb *const list[], int nent, } else if (LIO_MODE (mode) == LIO_WAIT) { +#ifndef DONT_NEED_AIO_MISC_COND pthread_cond_t cond = PTHREAD_COND_INITIALIZER; - struct waitlist waitlist[nent]; int oldstate; +#endif + struct waitlist waitlist[nent]; total = 0; for (cnt = 0; cnt < nent; ++cnt) @@ -133,7 +135,9 @@ lio_listio_internal (int mode, struct aiocb *const list[], int nent, if (requests[cnt] != NULL && list[cnt]->aio_lio_opcode != LIO_NOP) { +#ifndef DONT_NEED_AIO_MISC_COND waitlist[cnt].cond = &cond; +#endif waitlist[cnt].result = &result; waitlist[cnt].next = requests[cnt]->waiting; waitlist[cnt].counterp = &total; @@ -146,6 +150,9 @@ lio_listio_internal (int mode, struct aiocb *const list[], int nent, } } +#ifdef DONT_NEED_AIO_MISC_COND + AIO_MISC_WAIT (result, total, NULL, 0); +#else /* Since `pthread_cond_wait'/`pthread_cond_timedwait' are cancellation points we must be careful. We added entries to the waiting lists which we must remove. So defer cancellation for now. */ @@ -161,6 +168,7 @@ lio_listio_internal (int mode, struct aiocb *const list[], int nent, if (pthread_cond_destroy (&cond) != 0) /* This must never happen. */ abort (); +#endif /* If any of the I/O requests failed, return -1 and set errno. */ if (result != 0) @@ -193,7 +201,9 @@ lio_listio_internal (int mode, struct aiocb *const list[], int nent, if (requests[cnt] != NULL && list[cnt]->aio_lio_opcode != LIO_NOP) { +#ifndef DONT_NEED_AIO_MISC_COND waitlist->list[cnt].cond = NULL; +#endif waitlist->list[cnt].result = NULL; waitlist->list[cnt].next = requests[cnt]->waiting; waitlist->list[cnt].counterp = &waitlist->counter; |