mirror of
https://sourceware.org/git/glibc.git
synced 2025-10-26 00:57:39 +03:00
Unlock mutex before going back to waiting for PI mutexes
[BZ #14417] A futex call with FUTEX_WAIT_REQUEUE_PI returns with the mutex locked on success. If such a successful thread is pipped to the cond_lock by another spuriously woken waiter, it could be sent back to wait on the futex with the mutex lock held, thus causing a deadlock. So it is necessary that the thread relinquishes the mutex before going back to sleep.
This commit is contained in:
6
NEWS
6
NEWS
@@ -13,9 +13,9 @@ Version 2.17
|
|||||||
10038, 10631, 11438, 11607, 13412, 13542, 13629, 13679, 13696, 13717,
|
10038, 10631, 11438, 11607, 13412, 13542, 13629, 13679, 13696, 13717,
|
||||||
13741, 13939, 13966, 14042, 14090, 14150, 14151, 14154, 14157, 14166,
|
13741, 13939, 13966, 14042, 14090, 14150, 14151, 14154, 14157, 14166,
|
||||||
14173, 14195, 14237, 14251, 14252, 14283, 14298, 14303, 14307, 14328,
|
14173, 14195, 14237, 14251, 14252, 14283, 14298, 14303, 14307, 14328,
|
||||||
14331, 14336, 14337, 14347, 14349, 14376, 14459, 14476, 14477, 14505,
|
14331, 14336, 14337, 14347, 14349, 14376, 14417, 14459, 14476, 14477,
|
||||||
14510, 14516, 14518, 14519, 14530, 14532, 14538, 14543, 14544, 14545,
|
14505, 14510, 14516, 14518, 14519, 14530, 14532, 14538, 14543, 14544,
|
||||||
14562, 14576, 14579, 14583, 14587, 14621, 14638, 14645, 14648.
|
14545, 14562, 14576, 14579, 14583, 14587, 14621, 14638, 14645, 14648.
|
||||||
|
|
||||||
* Support for STT_GNU_IFUNC symbols added for s390 and s390x.
|
* Support for STT_GNU_IFUNC symbols added for s390 and s390x.
|
||||||
Optimized versions of memcpy, memset, and memcmp added for System z10 and
|
Optimized versions of memcpy, memset, and memcmp added for System z10 and
|
||||||
|
|||||||
@@ -1,3 +1,22 @@
|
|||||||
|
2012-10-05 Siddhesh Poyarekar <siddhesh@redhat.com>
|
||||||
|
|
||||||
|
[BZ #14417]
|
||||||
|
* Makefile (tests): New test case tst-cond24.
|
||||||
|
(LDFLAGS-tst-cond24): Link tst-cond24 against librt.
|
||||||
|
* sysdeps/unix/sysv/linux/i386/i486/pthread_cond_timedwait.S
|
||||||
|
(__pthread_cond_timedwait): Unlock mutex before going back to
|
||||||
|
wait in PI case.
|
||||||
|
* sysdeps/unix/sysv/linux/i386/i486/pthread_cond_wait.S
|
||||||
|
(__pthread_cond_wait): Likewise. Revert handling of EAGAIN
|
||||||
|
return from futex_wait.
|
||||||
|
* sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S
|
||||||
|
(__pthread_cond_timedwait): Unlock mutex before going back to
|
||||||
|
wait in PI case. Set requeue_pi flag only if wait returned 0.
|
||||||
|
* sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S
|
||||||
|
(__pthread_cond_wait): Likewise. Revert handling of EAGAIN
|
||||||
|
return from futex_wait.
|
||||||
|
* tst-cond24.c: New test case.
|
||||||
|
|
||||||
2012-10-04 Roland McGrath <roland@hack.frob.com>
|
2012-10-04 Roland McGrath <roland@hack.frob.com>
|
||||||
|
|
||||||
* pthread_create.c (start_thread): Use __madvise, not madvise.
|
* pthread_create.c (start_thread): Use __madvise, not madvise.
|
||||||
|
|||||||
@@ -206,7 +206,8 @@ tests = tst-typesizes \
|
|||||||
tst-cond1 tst-cond2 tst-cond3 tst-cond4 tst-cond5 tst-cond6 tst-cond7 \
|
tst-cond1 tst-cond2 tst-cond3 tst-cond4 tst-cond5 tst-cond6 tst-cond7 \
|
||||||
tst-cond8 tst-cond9 tst-cond10 tst-cond11 tst-cond12 tst-cond13 \
|
tst-cond8 tst-cond9 tst-cond10 tst-cond11 tst-cond12 tst-cond13 \
|
||||||
tst-cond14 tst-cond15 tst-cond16 tst-cond17 tst-cond18 tst-cond19 \
|
tst-cond14 tst-cond15 tst-cond16 tst-cond17 tst-cond18 tst-cond19 \
|
||||||
tst-cond20 tst-cond21 tst-cond22 tst-cond23 tst-cond-except \
|
tst-cond20 tst-cond21 tst-cond22 tst-cond23 tst-cond24 \
|
||||||
|
tst-cond-except \
|
||||||
tst-robust1 tst-robust2 tst-robust3 tst-robust4 tst-robust5 \
|
tst-robust1 tst-robust2 tst-robust3 tst-robust4 tst-robust5 \
|
||||||
tst-robust6 tst-robust7 tst-robust8 tst-robust9 \
|
tst-robust6 tst-robust7 tst-robust8 tst-robust9 \
|
||||||
tst-robustpi1 tst-robustpi2 tst-robustpi3 tst-robustpi4 tst-robustpi5 \
|
tst-robustpi1 tst-robustpi2 tst-robustpi3 tst-robustpi4 tst-robustpi5 \
|
||||||
@@ -274,6 +275,7 @@ gen-as-const-headers = pthread-errnos.sym
|
|||||||
|
|
||||||
LDFLAGS-pthread.so = -Wl,--enable-new-dtags,-z,nodelete,-z,initfirst
|
LDFLAGS-pthread.so = -Wl,--enable-new-dtags,-z,nodelete,-z,initfirst
|
||||||
|
|
||||||
|
LDFLAGS-tst-cond24 = -lrt
|
||||||
|
|
||||||
include ../Makeconfig
|
include ../Makeconfig
|
||||||
|
|
||||||
|
|||||||
@@ -212,8 +212,23 @@ __pthread_cond_timedwait:
|
|||||||
sete 24(%esp)
|
sete 24(%esp)
|
||||||
je 41f
|
je 41f
|
||||||
|
|
||||||
/* Normal and PI futexes dont mix. Use normal futex functions only
|
/* When a futex syscall with FUTEX_WAIT_REQUEUE_PI returns
|
||||||
if the kernel does not support the PI futex functions. */
|
successfully, it has already locked the mutex for us and the
|
||||||
|
pi_flag (24(%esp)) is set to denote that fact. However, if another
|
||||||
|
thread changed the futex value before we entered the wait, the
|
||||||
|
syscall may return an EAGAIN and the mutex is not locked. We go
|
||||||
|
ahead with a success anyway since later we look at the pi_flag to
|
||||||
|
decide if we got the mutex or not. The sequence numbers then make
|
||||||
|
sure that only one of the threads actually wake up. We retry using
|
||||||
|
normal FUTEX_WAIT only if the kernel returned ENOSYS, since normal
|
||||||
|
and PI futexes don't mix.
|
||||||
|
|
||||||
|
Note that we don't check for EAGAIN specifically; we assume that the
|
||||||
|
only other error the futex function could return is EAGAIN (barring
|
||||||
|
the ETIMEOUT of course, for the timeout case in futex) since
|
||||||
|
anything else would mean an error in our function. It is too
|
||||||
|
expensive to do that check for every call (which is quite common in
|
||||||
|
case of a large number of threads), so it has been skipped. */
|
||||||
cmpl $-ENOSYS, %eax
|
cmpl $-ENOSYS, %eax
|
||||||
jne 41f
|
jne 41f
|
||||||
xorl %ecx, %ecx
|
xorl %ecx, %ecx
|
||||||
@@ -273,9 +288,24 @@ __pthread_cond_timedwait:
|
|||||||
jne 9f
|
jne 9f
|
||||||
|
|
||||||
15: cmpl $-ETIMEDOUT, %esi
|
15: cmpl $-ETIMEDOUT, %esi
|
||||||
jne 8b
|
je 28f
|
||||||
|
|
||||||
addl $1, wakeup_seq(%ebx)
|
/* We need to go back to futex_wait. If we're using requeue_pi, then
|
||||||
|
release the mutex we had acquired and go back. */
|
||||||
|
movl 24(%esp), %edx
|
||||||
|
test %edx, %edx
|
||||||
|
jz 8b
|
||||||
|
|
||||||
|
/* Adjust the mutex values first and then unlock it. The unlock
|
||||||
|
should always succeed or else the kernel did not lock the mutex
|
||||||
|
correctly. */
|
||||||
|
movl dep_mutex(%ebx), %eax
|
||||||
|
call __pthread_mutex_cond_lock_adjust
|
||||||
|
xorl %edx, %edx
|
||||||
|
call __pthread_mutex_unlock_usercnt
|
||||||
|
jmp 8b
|
||||||
|
|
||||||
|
28: addl $1, wakeup_seq(%ebx)
|
||||||
adcl $0, wakeup_seq+4(%ebx)
|
adcl $0, wakeup_seq+4(%ebx)
|
||||||
addl $1, cond_futex(%ebx)
|
addl $1, cond_futex(%ebx)
|
||||||
movl $ETIMEDOUT, %esi
|
movl $ETIMEDOUT, %esi
|
||||||
|
|||||||
@@ -136,7 +136,6 @@ __pthread_cond_wait:
|
|||||||
cmpl $PI_BIT, %eax
|
cmpl $PI_BIT, %eax
|
||||||
jne 18f
|
jne 18f
|
||||||
|
|
||||||
90:
|
|
||||||
movl $(FUTEX_WAIT_REQUEUE_PI|FUTEX_PRIVATE_FLAG), %ecx
|
movl $(FUTEX_WAIT_REQUEUE_PI|FUTEX_PRIVATE_FLAG), %ecx
|
||||||
movl %ebp, %edx
|
movl %ebp, %edx
|
||||||
xorl %esi, %esi
|
xorl %esi, %esi
|
||||||
@@ -152,11 +151,22 @@ __pthread_cond_wait:
|
|||||||
sete 16(%esp)
|
sete 16(%esp)
|
||||||
je 19f
|
je 19f
|
||||||
|
|
||||||
cmpl $-EAGAIN, %eax
|
/* When a futex syscall with FUTEX_WAIT_REQUEUE_PI returns
|
||||||
je 91f
|
successfully, it has already locked the mutex for us and the
|
||||||
|
pi_flag (16(%esp)) is set to denote that fact. However, if another
|
||||||
|
thread changed the futex value before we entered the wait, the
|
||||||
|
syscall may return an EAGAIN and the mutex is not locked. We go
|
||||||
|
ahead with a success anyway since later we look at the pi_flag to
|
||||||
|
decide if we got the mutex or not. The sequence numbers then make
|
||||||
|
sure that only one of the threads actually wake up. We retry using
|
||||||
|
normal FUTEX_WAIT only if the kernel returned ENOSYS, since normal
|
||||||
|
and PI futexes don't mix.
|
||||||
|
|
||||||
/* Normal and PI futexes dont mix. Use normal futex functions only
|
Note that we don't check for EAGAIN specifically; we assume that the
|
||||||
if the kernel does not support the PI futex functions. */
|
only other error the futex function could return is EAGAIN since
|
||||||
|
anything else would mean an error in our function. It is too
|
||||||
|
expensive to do that check for every call (which is quite common in
|
||||||
|
case of a large number of threads), so it has been skipped. */
|
||||||
cmpl $-ENOSYS, %eax
|
cmpl $-ENOSYS, %eax
|
||||||
jne 19f
|
jne 19f
|
||||||
xorl %ecx, %ecx
|
xorl %ecx, %ecx
|
||||||
@@ -206,12 +216,12 @@ __pthread_cond_wait:
|
|||||||
cmpl 8(%esp), %edx
|
cmpl 8(%esp), %edx
|
||||||
jne 7f
|
jne 7f
|
||||||
cmpl 4(%esp), %edi
|
cmpl 4(%esp), %edi
|
||||||
je 8b
|
je 22f
|
||||||
|
|
||||||
7: cmpl %ecx, %edx
|
7: cmpl %ecx, %edx
|
||||||
jne 9f
|
jne 9f
|
||||||
cmp %eax, %edi
|
cmp %eax, %edi
|
||||||
je 8b
|
je 22f
|
||||||
|
|
||||||
9: addl $1, woken_seq(%ebx)
|
9: addl $1, woken_seq(%ebx)
|
||||||
adcl $0, woken_seq+4(%ebx)
|
adcl $0, woken_seq+4(%ebx)
|
||||||
@@ -287,6 +297,22 @@ __pthread_cond_wait:
|
|||||||
jmp 20b
|
jmp 20b
|
||||||
|
|
||||||
cfi_adjust_cfa_offset(-FRAME_SIZE);
|
cfi_adjust_cfa_offset(-FRAME_SIZE);
|
||||||
|
|
||||||
|
/* We need to go back to futex_wait. If we're using requeue_pi, then
|
||||||
|
release the mutex we had acquired and go back. */
|
||||||
|
22: movl 16(%esp), %edx
|
||||||
|
test %edx, %edx
|
||||||
|
jz 8b
|
||||||
|
|
||||||
|
/* Adjust the mutex values first and then unlock it. The unlock
|
||||||
|
should always succeed or else the kernel did not lock the mutex
|
||||||
|
correctly. */
|
||||||
|
movl dep_mutex(%ebx), %eax
|
||||||
|
call __pthread_mutex_cond_lock_adjust
|
||||||
|
xorl %edx, %edx
|
||||||
|
call __pthread_mutex_unlock_usercnt
|
||||||
|
jmp 8b
|
||||||
|
|
||||||
/* Initial locking failed. */
|
/* Initial locking failed. */
|
||||||
1:
|
1:
|
||||||
#if cond_lock == 0
|
#if cond_lock == 0
|
||||||
@@ -400,77 +426,6 @@ __pthread_cond_wait:
|
|||||||
call __lll_unlock_wake
|
call __lll_unlock_wake
|
||||||
jmp 11b
|
jmp 11b
|
||||||
|
|
||||||
91:
|
|
||||||
.LcleanupSTART2:
|
|
||||||
/* FUTEX_WAIT_REQUEUE_PI returned EAGAIN. We need to
|
|
||||||
call it again. */
|
|
||||||
|
|
||||||
/* Get internal lock. */
|
|
||||||
movl $1, %edx
|
|
||||||
xorl %eax, %eax
|
|
||||||
LOCK
|
|
||||||
#if cond_lock == 0
|
|
||||||
cmpxchgl %edx, (%ebx)
|
|
||||||
#else
|
|
||||||
cmpxchgl %edx, cond_lock(%ebx)
|
|
||||||
#endif
|
|
||||||
jz 92f
|
|
||||||
|
|
||||||
#if cond_lock == 0
|
|
||||||
movl %ebx, %edx
|
|
||||||
#else
|
|
||||||
leal cond_lock(%ebx), %edx
|
|
||||||
#endif
|
|
||||||
#if (LLL_SHARED-LLL_PRIVATE) > 255
|
|
||||||
xorl %ecx, %ecx
|
|
||||||
#endif
|
|
||||||
cmpl $-1, dep_mutex(%ebx)
|
|
||||||
setne %cl
|
|
||||||
subl $1, %ecx
|
|
||||||
andl $(LLL_SHARED-LLL_PRIVATE), %ecx
|
|
||||||
#if LLL_PRIVATE != 0
|
|
||||||
addl $LLL_PRIVATE, %ecx
|
|
||||||
#endif
|
|
||||||
call __lll_lock_wait
|
|
||||||
|
|
||||||
92:
|
|
||||||
/* Increment the cond_futex value again, so it can be used as a new
|
|
||||||
expected value. */
|
|
||||||
addl $1, cond_futex(%ebx)
|
|
||||||
movl cond_futex(%ebx), %ebp
|
|
||||||
|
|
||||||
/* Unlock. */
|
|
||||||
LOCK
|
|
||||||
#if cond_lock == 0
|
|
||||||
subl $1, (%ebx)
|
|
||||||
#else
|
|
||||||
subl $1, cond_lock(%ebx)
|
|
||||||
#endif
|
|
||||||
je 93f
|
|
||||||
#if cond_lock == 0
|
|
||||||
movl %ebx, %eax
|
|
||||||
#else
|
|
||||||
leal cond_lock(%ebx), %eax
|
|
||||||
#endif
|
|
||||||
#if (LLL_SHARED-LLL_PRIVATE) > 255
|
|
||||||
xorl %ecx, %ecx
|
|
||||||
#endif
|
|
||||||
cmpl $-1, dep_mutex(%ebx)
|
|
||||||
setne %cl
|
|
||||||
subl $1, %ecx
|
|
||||||
andl $(LLL_SHARED-LLL_PRIVATE), %ecx
|
|
||||||
#if LLL_PRIVATE != 0
|
|
||||||
addl $LLL_PRIVATE, %ecx
|
|
||||||
#endif
|
|
||||||
call __lll_unlock_wake
|
|
||||||
|
|
||||||
93:
|
|
||||||
/* Set the rest of SYS_futex args for FUTEX_WAIT_REQUEUE_PI. */
|
|
||||||
xorl %ecx, %ecx
|
|
||||||
movl dep_mutex(%ebx), %edi
|
|
||||||
jmp 90b
|
|
||||||
.LcleanupEND2:
|
|
||||||
|
|
||||||
.size __pthread_cond_wait, .-__pthread_cond_wait
|
.size __pthread_cond_wait, .-__pthread_cond_wait
|
||||||
versioned_symbol (libpthread, __pthread_cond_wait, pthread_cond_wait,
|
versioned_symbol (libpthread, __pthread_cond_wait, pthread_cond_wait,
|
||||||
GLIBC_2_3_2)
|
GLIBC_2_3_2)
|
||||||
@@ -651,10 +606,6 @@ __condvar_w_cleanup:
|
|||||||
.long .LcleanupEND-.Lsub_cond_futex
|
.long .LcleanupEND-.Lsub_cond_futex
|
||||||
.long __condvar_w_cleanup-.LSTARTCODE
|
.long __condvar_w_cleanup-.LSTARTCODE
|
||||||
.uleb128 0
|
.uleb128 0
|
||||||
.long .LcleanupSTART2-.LSTARTCODE
|
|
||||||
.long .LcleanupEND2-.LcleanupSTART2
|
|
||||||
.long __condvar_w_cleanup-.LSTARTCODE
|
|
||||||
.uleb128 0
|
|
||||||
.long .LcallUR-.LSTARTCODE
|
.long .LcallUR-.LSTARTCODE
|
||||||
.long .LENDCODE-.LcallUR
|
.long .LENDCODE-.LcallUR
|
||||||
.long 0
|
.long 0
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ __pthread_cond_timedwait:
|
|||||||
mov %RSI_LP, dep_mutex(%rdi)
|
mov %RSI_LP, dep_mutex(%rdi)
|
||||||
|
|
||||||
22:
|
22:
|
||||||
xorl %r15d, %r15d
|
xorb %r15b, %r15b
|
||||||
|
|
||||||
#ifndef __ASSUME_FUTEX_CLOCK_REALTIME
|
#ifndef __ASSUME_FUTEX_CLOCK_REALTIME
|
||||||
# ifdef PIC
|
# ifdef PIC
|
||||||
@@ -190,18 +190,39 @@ __pthread_cond_timedwait:
|
|||||||
movl $SYS_futex, %eax
|
movl $SYS_futex, %eax
|
||||||
syscall
|
syscall
|
||||||
|
|
||||||
movl $1, %r15d
|
cmpl $0, %eax
|
||||||
|
sete %r15b
|
||||||
|
|
||||||
#ifdef __ASSUME_REQUEUE_PI
|
#ifdef __ASSUME_REQUEUE_PI
|
||||||
jmp 62f
|
jmp 62f
|
||||||
#else
|
#else
|
||||||
cmpq $-4095, %rax
|
je 62f
|
||||||
jnae 62f
|
|
||||||
|
/* When a futex syscall with FUTEX_WAIT_REQUEUE_PI returns
|
||||||
|
successfully, it has already locked the mutex for us and the
|
||||||
|
pi_flag (%r15b) is set to denote that fact. However, if another
|
||||||
|
thread changed the futex value before we entered the wait, the
|
||||||
|
syscall may return an EAGAIN and the mutex is not locked. We go
|
||||||
|
ahead with a success anyway since later we look at the pi_flag to
|
||||||
|
decide if we got the mutex or not. The sequence numbers then make
|
||||||
|
sure that only one of the threads actually wake up. We retry using
|
||||||
|
normal FUTEX_WAIT only if the kernel returned ENOSYS, since normal
|
||||||
|
and PI futexes don't mix.
|
||||||
|
|
||||||
|
Note that we don't check for EAGAIN specifically; we assume that the
|
||||||
|
only other error the futex function could return is EAGAIN (barring
|
||||||
|
the ETIMEOUT of course, for the timeout case in futex) since
|
||||||
|
anything else would mean an error in our function. It is too
|
||||||
|
expensive to do that check for every call (which is quite common in
|
||||||
|
case of a large number of threads), so it has been skipped. */
|
||||||
|
cmpl $-ENOSYS, %eax
|
||||||
|
jne 62f
|
||||||
|
|
||||||
subq $cond_futex, %rdi
|
subq $cond_futex, %rdi
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
61: movl $(FUTEX_WAIT_BITSET|FUTEX_PRIVATE_FLAG), %esi
|
61: movl $(FUTEX_WAIT_BITSET|FUTEX_PRIVATE_FLAG), %esi
|
||||||
60: xorl %r15d, %r15d
|
60: xorb %r15b, %r15b
|
||||||
xorl %eax, %eax
|
xorl %eax, %eax
|
||||||
/* The following only works like this because we only support
|
/* The following only works like this because we only support
|
||||||
two clocks, represented using a single bit. */
|
two clocks, represented using a single bit. */
|
||||||
@@ -248,7 +269,23 @@ __pthread_cond_timedwait:
|
|||||||
ja 39f
|
ja 39f
|
||||||
|
|
||||||
45: cmpq $-ETIMEDOUT, %r14
|
45: cmpq $-ETIMEDOUT, %r14
|
||||||
jne 38b
|
je 99f
|
||||||
|
|
||||||
|
/* We need to go back to futex_wait. If we're using requeue_pi, then
|
||||||
|
release the mutex we had acquired and go back. */
|
||||||
|
test %r15b, %r15b
|
||||||
|
jz 38b
|
||||||
|
|
||||||
|
/* Adjust the mutex values first and then unlock it. The unlock
|
||||||
|
should always succeed or else the kernel did not lock the
|
||||||
|
mutex correctly. */
|
||||||
|
movq %r8, %rdi
|
||||||
|
callq __pthread_mutex_cond_lock_adjust
|
||||||
|
xorl %esi, %esi
|
||||||
|
callq __pthread_mutex_unlock_usercnt
|
||||||
|
/* Reload cond_var. */
|
||||||
|
movq 8(%rsp), %rdi
|
||||||
|
jmp 38b
|
||||||
|
|
||||||
99: incq wakeup_seq(%rdi)
|
99: incq wakeup_seq(%rdi)
|
||||||
incl cond_futex(%rdi)
|
incl cond_futex(%rdi)
|
||||||
@@ -298,7 +335,7 @@ __pthread_cond_timedwait:
|
|||||||
/* If requeue_pi is used the kernel performs the locking of the
|
/* If requeue_pi is used the kernel performs the locking of the
|
||||||
mutex. */
|
mutex. */
|
||||||
41: movq 16(%rsp), %rdi
|
41: movq 16(%rsp), %rdi
|
||||||
testl %r15d, %r15d
|
testb %r15b, %r15b
|
||||||
jnz 64f
|
jnz 64f
|
||||||
|
|
||||||
callq __pthread_mutex_cond_lock
|
callq __pthread_mutex_cond_lock
|
||||||
|
|||||||
@@ -136,19 +136,36 @@ __pthread_cond_wait:
|
|||||||
cmpl $PI_BIT, %eax
|
cmpl $PI_BIT, %eax
|
||||||
jne 61f
|
jne 61f
|
||||||
|
|
||||||
90:
|
|
||||||
movl $(FUTEX_WAIT_REQUEUE_PI|FUTEX_PRIVATE_FLAG), %esi
|
movl $(FUTEX_WAIT_REQUEUE_PI|FUTEX_PRIVATE_FLAG), %esi
|
||||||
movl $SYS_futex, %eax
|
movl $SYS_futex, %eax
|
||||||
syscall
|
syscall
|
||||||
|
|
||||||
movl $1, %r8d
|
cmpl $0, %eax
|
||||||
cmpq $-EAGAIN, %rax
|
sete %r8b
|
||||||
je 91f
|
|
||||||
#ifdef __ASSUME_REQUEUE_PI
|
#ifdef __ASSUME_REQUEUE_PI
|
||||||
jmp 62f
|
jmp 62f
|
||||||
#else
|
#else
|
||||||
cmpq $-4095, %rax
|
je 62f
|
||||||
jnae 62f
|
|
||||||
|
/* When a futex syscall with FUTEX_WAIT_REQUEUE_PI returns
|
||||||
|
successfully, it has already locked the mutex for us and the
|
||||||
|
pi_flag (%r8b) is set to denote that fact. However, if another
|
||||||
|
thread changed the futex value before we entered the wait, the
|
||||||
|
syscall may return an EAGAIN and the mutex is not locked. We go
|
||||||
|
ahead with a success anyway since later we look at the pi_flag to
|
||||||
|
decide if we got the mutex or not. The sequence numbers then make
|
||||||
|
sure that only one of the threads actually wake up. We retry using
|
||||||
|
normal FUTEX_WAIT only if the kernel returned ENOSYS, since normal
|
||||||
|
and PI futexes don't mix.
|
||||||
|
|
||||||
|
Note that we don't check for EAGAIN specifically; we assume that the
|
||||||
|
only other error the futex function could return is EAGAIN since
|
||||||
|
anything else would mean an error in our function. It is too
|
||||||
|
expensive to do that check for every call (which is quite common in
|
||||||
|
case of a large number of threads), so it has been skipped. */
|
||||||
|
cmpl $-ENOSYS, %eax
|
||||||
|
jne 62f
|
||||||
|
|
||||||
# ifndef __ASSUME_PRIVATE_FUTEX
|
# ifndef __ASSUME_PRIVATE_FUTEX
|
||||||
movl $FUTEX_WAIT, %esi
|
movl $FUTEX_WAIT, %esi
|
||||||
@@ -161,7 +178,7 @@ __pthread_cond_wait:
|
|||||||
#else
|
#else
|
||||||
orl %fs:PRIVATE_FUTEX, %esi
|
orl %fs:PRIVATE_FUTEX, %esi
|
||||||
#endif
|
#endif
|
||||||
60: xorl %r8d, %r8d
|
60: xorb %r8b, %r8b
|
||||||
movl $SYS_futex, %eax
|
movl $SYS_futex, %eax
|
||||||
syscall
|
syscall
|
||||||
|
|
||||||
@@ -191,10 +208,10 @@ __pthread_cond_wait:
|
|||||||
jne 16f
|
jne 16f
|
||||||
|
|
||||||
cmpq 24(%rsp), %r9
|
cmpq 24(%rsp), %r9
|
||||||
jbe 8b
|
jbe 19f
|
||||||
|
|
||||||
cmpq %rax, %r9
|
cmpq %rax, %r9
|
||||||
jna 8b
|
jna 19f
|
||||||
|
|
||||||
incq woken_seq(%rdi)
|
incq woken_seq(%rdi)
|
||||||
|
|
||||||
@@ -236,7 +253,7 @@ __pthread_cond_wait:
|
|||||||
/* If requeue_pi is used the kernel performs the locking of the
|
/* If requeue_pi is used the kernel performs the locking of the
|
||||||
mutex. */
|
mutex. */
|
||||||
11: movq 16(%rsp), %rdi
|
11: movq 16(%rsp), %rdi
|
||||||
testl %r8d, %r8d
|
testb %r8b, %r8b
|
||||||
jnz 18f
|
jnz 18f
|
||||||
|
|
||||||
callq __pthread_mutex_cond_lock
|
callq __pthread_mutex_cond_lock
|
||||||
@@ -253,6 +270,23 @@ __pthread_cond_wait:
|
|||||||
xorl %eax, %eax
|
xorl %eax, %eax
|
||||||
jmp 14b
|
jmp 14b
|
||||||
|
|
||||||
|
/* We need to go back to futex_wait. If we're using requeue_pi, then
|
||||||
|
release the mutex we had acquired and go back. */
|
||||||
|
19: testb %r8b, %r8b
|
||||||
|
jz 8b
|
||||||
|
|
||||||
|
/* Adjust the mutex values first and then unlock it. The unlock
|
||||||
|
should always succeed or else the kernel did not lock the mutex
|
||||||
|
correctly. */
|
||||||
|
movq 16(%rsp), %rdi
|
||||||
|
callq __pthread_mutex_cond_lock_adjust
|
||||||
|
movq %rdi, %r8
|
||||||
|
xorl %esi, %esi
|
||||||
|
callq __pthread_mutex_unlock_usercnt
|
||||||
|
/* Reload cond_var. */
|
||||||
|
movq 8(%rsp), %rdi
|
||||||
|
jmp 8b
|
||||||
|
|
||||||
/* Initial locking failed. */
|
/* Initial locking failed. */
|
||||||
1:
|
1:
|
||||||
#if cond_lock != 0
|
#if cond_lock != 0
|
||||||
@@ -331,69 +365,6 @@ __pthread_cond_wait:
|
|||||||
13: movq %r10, %rax
|
13: movq %r10, %rax
|
||||||
jmp 14b
|
jmp 14b
|
||||||
|
|
||||||
91:
|
|
||||||
.LcleanupSTART2:
|
|
||||||
/* FUTEX_WAIT_REQUEUE_PI returned EAGAIN. We need to
|
|
||||||
call it again. */
|
|
||||||
movq 8(%rsp), %rdi
|
|
||||||
|
|
||||||
/* Get internal lock. */
|
|
||||||
movl $1, %esi
|
|
||||||
xorl %eax, %eax
|
|
||||||
LOCK
|
|
||||||
#if cond_lock == 0
|
|
||||||
cmpxchgl %esi, (%rdi)
|
|
||||||
#else
|
|
||||||
cmpxchgl %esi, cond_lock(%rdi)
|
|
||||||
#endif
|
|
||||||
jz 92f
|
|
||||||
|
|
||||||
#if cond_lock != 0
|
|
||||||
addq $cond_lock, %rdi
|
|
||||||
#endif
|
|
||||||
LP_OP(cmp) $-1, dep_mutex-cond_lock(%rdi)
|
|
||||||
movl $LLL_PRIVATE, %eax
|
|
||||||
movl $LLL_SHARED, %esi
|
|
||||||
cmovne %eax, %esi
|
|
||||||
callq __lll_lock_wait
|
|
||||||
#if cond_lock != 0
|
|
||||||
subq $cond_lock, %rdi
|
|
||||||
#endif
|
|
||||||
92:
|
|
||||||
/* Increment the cond_futex value again, so it can be used as a new
|
|
||||||
expected value. */
|
|
||||||
incl cond_futex(%rdi)
|
|
||||||
movl cond_futex(%rdi), %edx
|
|
||||||
|
|
||||||
/* Release internal lock. */
|
|
||||||
LOCK
|
|
||||||
#if cond_lock == 0
|
|
||||||
decl (%rdi)
|
|
||||||
#else
|
|
||||||
decl cond_lock(%rdi)
|
|
||||||
#endif
|
|
||||||
jz 93f
|
|
||||||
|
|
||||||
#if cond_lock != 0
|
|
||||||
addq $cond_lock, %rdi
|
|
||||||
#endif
|
|
||||||
LP_OP(cmp) $-1, dep_mutex-cond_lock(%rdi)
|
|
||||||
movl $LLL_PRIVATE, %eax
|
|
||||||
movl $LLL_SHARED, %esi
|
|
||||||
cmovne %eax, %esi
|
|
||||||
/* The call preserves %rdx. */
|
|
||||||
callq __lll_unlock_wake
|
|
||||||
#if cond_lock != 0
|
|
||||||
subq $cond_lock, %rdi
|
|
||||||
#endif
|
|
||||||
93:
|
|
||||||
/* Set the rest of SYS_futex args for FUTEX_WAIT_REQUEUE_PI. */
|
|
||||||
xorq %r10, %r10
|
|
||||||
mov dep_mutex(%rdi), %R8_LP
|
|
||||||
leaq cond_futex(%rdi), %rdi
|
|
||||||
jmp 90b
|
|
||||||
.LcleanupEND2:
|
|
||||||
|
|
||||||
.size __pthread_cond_wait, .-__pthread_cond_wait
|
.size __pthread_cond_wait, .-__pthread_cond_wait
|
||||||
versioned_symbol (libpthread, __pthread_cond_wait, pthread_cond_wait,
|
versioned_symbol (libpthread, __pthread_cond_wait, pthread_cond_wait,
|
||||||
GLIBC_2_3_2)
|
GLIBC_2_3_2)
|
||||||
@@ -547,10 +518,6 @@ __condvar_cleanup1:
|
|||||||
.uleb128 .LcleanupEND-.LcleanupSTART
|
.uleb128 .LcleanupEND-.LcleanupSTART
|
||||||
.uleb128 __condvar_cleanup1-.LSTARTCODE
|
.uleb128 __condvar_cleanup1-.LSTARTCODE
|
||||||
.uleb128 0
|
.uleb128 0
|
||||||
.uleb128 .LcleanupSTART2-.LSTARTCODE
|
|
||||||
.uleb128 .LcleanupEND2-.LcleanupSTART2
|
|
||||||
.uleb128 __condvar_cleanup1-.LSTARTCODE
|
|
||||||
.uleb128 0
|
|
||||||
.uleb128 .LcallUR-.LSTARTCODE
|
.uleb128 .LcallUR-.LSTARTCODE
|
||||||
.uleb128 .LENDCODE-.LcallUR
|
.uleb128 .LENDCODE-.LcallUR
|
||||||
.uleb128 0
|
.uleb128 0
|
||||||
|
|||||||
249
nptl/tst-cond24.c
Normal file
249
nptl/tst-cond24.c
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
/* Verify that condition variables synchronized by PI mutexes don't hang.
|
||||||
|
Copyright (C) 2012 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, see
|
||||||
|
<http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#define THREADS_NUM 5
|
||||||
|
#define MAXITER 50000
|
||||||
|
|
||||||
|
static pthread_mutex_t mutex;
|
||||||
|
static pthread_mutexattr_t mutex_attr;
|
||||||
|
static pthread_cond_t cond;
|
||||||
|
static pthread_t threads[THREADS_NUM];
|
||||||
|
static int pending = 0;
|
||||||
|
|
||||||
|
typedef void * (*threadfunc) (void *);
|
||||||
|
|
||||||
|
void *
|
||||||
|
thread_fun_timed (void *arg)
|
||||||
|
{
|
||||||
|
int *ret = arg;
|
||||||
|
int rv, i;
|
||||||
|
|
||||||
|
printf ("Started thread_fun_timed[%d]\n", *ret);
|
||||||
|
|
||||||
|
for (i = 0; i < MAXITER / THREADS_NUM; i++)
|
||||||
|
{
|
||||||
|
rv = pthread_mutex_lock (&mutex);
|
||||||
|
if (rv)
|
||||||
|
{
|
||||||
|
printf ("pthread_mutex_lock: %s(%d)\n", strerror (rv), rv);
|
||||||
|
*ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!pending)
|
||||||
|
{
|
||||||
|
struct timespec ts;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &ts);
|
||||||
|
ts.tv_sec += 20;
|
||||||
|
rv = pthread_cond_timedwait (&cond, &mutex, &ts);
|
||||||
|
|
||||||
|
/* There should be no timeout either. */
|
||||||
|
if (rv)
|
||||||
|
{
|
||||||
|
printf ("pthread_cond_wait: %s(%d)\n", strerror (rv), rv);
|
||||||
|
*ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pending--;
|
||||||
|
|
||||||
|
rv = pthread_mutex_unlock (&mutex);
|
||||||
|
if (rv)
|
||||||
|
{
|
||||||
|
printf ("pthread_mutex_unlock: %s(%d)\n", strerror (rv), rv);
|
||||||
|
*ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*ret = 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
thread_fun (void *arg)
|
||||||
|
{
|
||||||
|
int *ret = arg;
|
||||||
|
int rv, i;
|
||||||
|
|
||||||
|
printf ("Started thread_fun[%d]\n", *ret);
|
||||||
|
|
||||||
|
for (i = 0; i < MAXITER / THREADS_NUM; i++)
|
||||||
|
{
|
||||||
|
rv = pthread_mutex_lock (&mutex);
|
||||||
|
if (rv)
|
||||||
|
{
|
||||||
|
printf ("pthread_mutex_lock: %s(%d)\n", strerror (rv), rv);
|
||||||
|
*ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!pending)
|
||||||
|
{
|
||||||
|
rv = pthread_cond_wait (&cond, &mutex);
|
||||||
|
|
||||||
|
if (rv)
|
||||||
|
{
|
||||||
|
printf ("pthread_cond_wait: %s(%d)\n", strerror (rv), rv);
|
||||||
|
*ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pending--;
|
||||||
|
|
||||||
|
rv = pthread_mutex_unlock (&mutex);
|
||||||
|
if (rv)
|
||||||
|
{
|
||||||
|
printf ("pthread_mutex_unlock: %s(%d)\n", strerror (rv), rv);
|
||||||
|
*ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*ret = 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
do_test_wait (threadfunc f)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int rv;
|
||||||
|
int counter = 0;
|
||||||
|
int retval[THREADS_NUM];
|
||||||
|
|
||||||
|
puts ("Starting test");
|
||||||
|
|
||||||
|
rv = pthread_mutexattr_init (&mutex_attr);
|
||||||
|
if (rv)
|
||||||
|
{
|
||||||
|
printf ("pthread_mutexattr_init: %s(%d)\n", strerror (rv), rv);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = pthread_mutexattr_setprotocol (&mutex_attr, PTHREAD_PRIO_INHERIT);
|
||||||
|
if (rv)
|
||||||
|
{
|
||||||
|
printf ("pthread_mutexattr_setprotocol: %s(%d)\n", strerror (rv), rv);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = pthread_mutex_init (&mutex, &mutex_attr);
|
||||||
|
if (rv)
|
||||||
|
{
|
||||||
|
printf ("pthread_mutex_init: %s(%d)\n", strerror (rv), rv);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = pthread_cond_init (&cond, NULL);
|
||||||
|
if (rv)
|
||||||
|
{
|
||||||
|
printf ("pthread_cond_init: %s(%d)\n", strerror (rv), rv);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < THREADS_NUM; i++)
|
||||||
|
{
|
||||||
|
retval[i] = i;
|
||||||
|
rv = pthread_create (&threads[i], NULL, f, &retval[i]);
|
||||||
|
if (rv)
|
||||||
|
{
|
||||||
|
printf ("pthread_create: %s(%d)\n", strerror (rv), rv);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; counter < MAXITER; counter++)
|
||||||
|
{
|
||||||
|
rv = pthread_mutex_lock (&mutex);
|
||||||
|
if (rv)
|
||||||
|
{
|
||||||
|
printf ("pthread_mutex_lock: %s(%d)\n", strerror (rv), rv);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(counter % 100))
|
||||||
|
printf ("counter: %d\n", counter);
|
||||||
|
pending += 1;
|
||||||
|
|
||||||
|
rv = pthread_cond_signal (&cond);
|
||||||
|
if (rv)
|
||||||
|
{
|
||||||
|
printf ("pthread_cond_signal: %s(%d)\n", strerror (rv), rv);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = pthread_mutex_unlock (&mutex);
|
||||||
|
if (rv)
|
||||||
|
{
|
||||||
|
printf ("pthread_mutex_unlock: %s(%d)\n", strerror (rv), rv);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < THREADS_NUM; i++)
|
||||||
|
{
|
||||||
|
void *ret;
|
||||||
|
rv = pthread_join (threads[i], &ret);
|
||||||
|
if (rv)
|
||||||
|
{
|
||||||
|
printf ("pthread_join: %s(%d)\n", strerror (rv), rv);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (ret && *(int *)ret)
|
||||||
|
{
|
||||||
|
printf ("Thread %d returned with an error\n", i);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
do_test (void)
|
||||||
|
{
|
||||||
|
puts ("Testing pthread_cond_wait");
|
||||||
|
int ret = do_test_wait (thread_fun);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
puts ("Testing pthread_cond_timedwait");
|
||||||
|
return do_test_wait (thread_fun_timed);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TIMEOUT 10
|
||||||
|
#define TEST_FUNCTION do_test ()
|
||||||
|
#include "../test-skeleton.c"
|
||||||
Reference in New Issue
Block a user