1 /* Copyright (C) 2002-2015 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <http://www.gnu.org/licenses/>. */
20 #include <shlib-compat.h>
21 #include <lowlevellock.h>
22 #include <lowlevelcond.h>
23 #include <pthread-pi-defines.h>
24 #include <pthread-errnos.h>
25 #include <stap-probe.h>
27 #include <kernel-features.h>
33 /* int pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex,
34 const struct timespec *abstime) */
35 .globl __pthread_cond_timedwait
36 .type __pthread_cond_timedwait, @function
38 __pthread_cond_timedwait:
42 cfi_personality(DW_EH_PE_pcrel | DW_EH_PE_sdata4 | DW_EH_PE_indirect,
43 DW.ref.__gcc_personality_v0)
44 cfi_lsda(DW_EH_PE_pcrel | DW_EH_PE_sdata4, .LexceptSTART)
46 cfi_personality(DW_EH_PE_udata4, __gcc_personality_v0)
47 cfi_lsda(DW_EH_PE_udata4, .LexceptSTART)
51 cfi_adjust_cfa_offset(8)
52 cfi_rel_offset(%r12, 0)
54 cfi_adjust_cfa_offset(8)
55 cfi_rel_offset(%r13, 0)
57 cfi_adjust_cfa_offset(8)
58 cfi_rel_offset(%r14, 0)
60 cfi_adjust_cfa_offset(8)
61 cfi_rel_offset(%r15, 0)
62 #ifdef __ASSUME_FUTEX_CLOCK_REALTIME
63 # define FRAME_SIZE (32+8)
65 # define FRAME_SIZE (48+8)
67 subq $FRAME_SIZE, %rsp
68 cfi_adjust_cfa_offset(FRAME_SIZE)
71 LIBC_PROBE (cond_timedwait, 3, %rdi, %rsi, %rdx)
73 cmpq $1000000000, 8(%rdx)
80 +--------------------------+
81 rsp + 32 | timeout value |
82 +--------------------------+
83 rsp + 24 | old wake_seq value |
84 +--------------------------+
85 rsp + 16 | mutex pointer |
86 +--------------------------+
87 rsp + 8 | condvar pointer |
88 +--------------------------+
89 rsp + 4 | old broadcast_seq value |
90 +--------------------------+
91 rsp + 0 | old cancellation mode |
92 +--------------------------+
95 LP_OP(cmp) $-1, dep_mutex(%rdi)
97 /* Prepare structure passed to cancellation handler. */
103 mov %RSI_LP, dep_mutex(%rdi)
108 #ifndef __ASSUME_FUTEX_CLOCK_REALTIME
110 cmpl $0, __have_futex_clock_realtime(%rip)
112 cmpl $0, __have_futex_clock_realtime
117 /* Get internal lock. */
122 cmpxchgl %esi, (%rdi)
124 cmpxchgl %esi, cond_lock(%rdi)
128 /* Unlock the mutex. */
129 32: movq 16(%rsp), %rdi
131 callq __pthread_mutex_unlock_usercnt
138 incl cond_futex(%rdi)
139 addl $(1 << nwaiters_shift), cond_nwaiters(%rdi)
141 /* Get and store current wakeup_seq value. */
143 movq wakeup_seq(%rdi), %r9
144 movl broadcast_seq(%rdi), %edx
149 movq $-ETIMEDOUT, %r14
152 38: movl cond_futex(%rdi), %r12d
164 34: callq __pthread_enable_asynccancel
168 movl $FUTEX_WAIT_BITSET, %esi
169 LP_OP(cmp) $-1, dep_mutex(%rdi)
172 mov dep_mutex(%rdi), %R8_LP
173 /* Requeue to a non-robust PI mutex if the PI bit is set and
174 the robust bit is not set. */
175 movl MUTEX_KIND(%r8), %eax
176 andl $(ROBUST_BIT|PI_BIT), %eax
180 movl $(FUTEX_WAIT_REQUEUE_PI|FUTEX_PRIVATE_FLAG), %esi
182 /* The following only works like this because we only support
183 two clocks, represented using a single bit. */
184 testl $1, cond_nwaiters(%rdi)
185 movl $FUTEX_CLOCK_REALTIME, %edx
189 addq $cond_futex, %rdi
190 movl $SYS_futex, %eax
196 #ifdef __ASSUME_REQUEUE_PI
201 /* When a futex syscall with FUTEX_WAIT_REQUEUE_PI returns
202 successfully, it has already locked the mutex for us and the
203 pi_flag (%r15b) is set to denote that fact. However, if another
204 thread changed the futex value before we entered the wait, the
205 syscall may return an EAGAIN and the mutex is not locked. We go
206 ahead with a success anyway since later we look at the pi_flag to
207 decide if we got the mutex or not. The sequence numbers then make
208 sure that only one of the threads actually wake up. We retry using
209 normal FUTEX_WAIT only if the kernel returned ENOSYS, since normal
210 and PI futexes don't mix.
212 Note that we don't check for EAGAIN specifically; we assume that the
213 only other error the futex function could return is EAGAIN (barring
214 the ETIMEOUT of course, for the timeout case in futex) since
215 anything else would mean an error in our function. It is too
216 expensive to do that check for every call (which is quite common in
217 case of a large number of threads), so it has been skipped. */
221 subq $cond_futex, %rdi
224 61: movl $(FUTEX_WAIT_BITSET|FUTEX_PRIVATE_FLAG), %esi
225 60: xorb %r15b, %r15b
227 /* The following only works like this because we only support
228 two clocks, represented using a single bit. */
229 testl $1, cond_nwaiters(%rdi)
230 movl $FUTEX_CLOCK_REALTIME, %edx
231 movl $0xffffffff, %r9d
235 addq $cond_futex, %rdi
236 movl $SYS_futex, %eax
241 callq __pthread_disable_asynccancel
250 cmpxchgl %esi, (%rdi)
252 cmpxchgl %esi, cond_lock(%rdi)
256 36: movl broadcast_seq(%rdi), %edx
258 movq woken_seq(%rdi), %rax
260 movq wakeup_seq(%rdi), %r9
271 45: cmpq $-ETIMEDOUT, %r14
274 /* We need to go back to futex_wait. If we're using requeue_pi, then
275 release the mutex we had acquired and go back. */
279 /* Adjust the mutex values first and then unlock it. The unlock
280 should always succeed or else the kernel did not lock the
283 callq __pthread_mutex_cond_lock_adjust
285 callq __pthread_mutex_unlock_usercnt
286 /* Reload cond_var. */
290 99: incq wakeup_seq(%rdi)
291 incl cond_futex(%rdi)
292 movl $ETIMEDOUT, %r14d
299 44: incq woken_seq(%rdi)
301 54: subl $(1 << nwaiters_shift), cond_nwaiters(%rdi)
303 /* Wake up a thread which wants to destroy the condvar object. */
304 cmpq $0xffffffffffffffff, total_seq(%rdi)
306 movl cond_nwaiters(%rdi), %eax
307 andl $~((1 << nwaiters_shift) - 1), %eax
310 addq $cond_nwaiters, %rdi
311 LP_OP(cmp) $-1, dep_mutex-cond_nwaiters(%rdi)
313 #ifdef __ASSUME_PRIVATE_FUTEX
314 movl $FUTEX_WAKE, %eax
315 movl $(FUTEX_WAKE|FUTEX_PRIVATE_FLAG), %esi
319 movl %fs:PRIVATE_FUTEX, %esi
321 orl $FUTEX_WAKE, %esi
323 movl $SYS_futex, %eax
325 subq $cond_nwaiters, %rdi
335 /* If requeue_pi is used the kernel performs the locking of the
337 41: movq 16(%rsp), %rdi
341 callq __pthread_mutex_cond_lock
346 48: addq $FRAME_SIZE, %rsp
347 cfi_adjust_cfa_offset(-FRAME_SIZE)
349 cfi_adjust_cfa_offset(-8)
352 cfi_adjust_cfa_offset(-8)
355 cfi_adjust_cfa_offset(-8)
358 cfi_adjust_cfa_offset(-8)
365 64: callq __pthread_mutex_cond_lock_adjust
369 /* Initial locking failed. */
372 addq $cond_lock, %rdi
374 LP_OP(cmp) $-1, dep_mutex-cond_lock(%rdi)
375 movl $LLL_PRIVATE, %eax
376 movl $LLL_SHARED, %esi
378 callq __lll_lock_wait
381 /* Unlock in loop requires wakeup. */
384 addq $cond_lock, %rdi
386 LP_OP(cmp) $-1, dep_mutex-cond_lock(%rdi)
387 movl $LLL_PRIVATE, %eax
388 movl $LLL_SHARED, %esi
390 callq __lll_unlock_wake
393 /* Locking in loop failed. */
396 addq $cond_lock, %rdi
398 LP_OP(cmp) $-1, dep_mutex-cond_lock(%rdi)
399 movl $LLL_PRIVATE, %eax
400 movl $LLL_SHARED, %esi
402 callq __lll_lock_wait
404 subq $cond_lock, %rdi
408 /* Unlock after loop requires wakeup. */
411 addq $cond_lock, %rdi
413 LP_OP(cmp) $-1, dep_mutex-cond_lock(%rdi)
414 movl $LLL_PRIVATE, %eax
415 movl $LLL_SHARED, %esi
417 callq __lll_unlock_wake
420 /* The initial unlocking of the mutex failed. */
421 46: movq 8(%rsp), %rdi
432 addq $cond_lock, %rdi
434 LP_OP(cmp) $-1, dep_mutex-cond_lock(%rdi)
435 movl $LLL_PRIVATE, %eax
436 movl $LLL_SHARED, %esi
438 callq __lll_unlock_wake
440 47: movq (%rsp), %rax
444 #ifndef __ASSUME_FUTEX_CLOCK_REALTIME
446 /* Get internal lock. */
451 cmpxchgl %esi, (%rdi)
453 cmpxchgl %esi, cond_lock(%rdi)
457 /* Unlock the mutex. */
458 2: movq 16(%rsp), %rdi
460 callq __pthread_mutex_unlock_usercnt
467 incl cond_futex(%rdi)
468 addl $(1 << nwaiters_shift), cond_nwaiters(%rdi)
470 /* Get and store current wakeup_seq value. */
472 movq wakeup_seq(%rdi), %r9
473 movl broadcast_seq(%rdi), %edx
477 /* Get the current time. */
479 # ifdef __NR_clock_gettime
480 /* Get the clock number. Note that the field in the condvar
481 structure stores the number minus 1. */
483 movl cond_nwaiters(%rdi), %edi
484 andl $((1 << nwaiters_shift) - 1), %edi
485 /* Only clocks 0 and 1 are allowed so far. Both are handled in the
489 mov __vdso_clock_gettime@GOTPCREL(%rip), %RAX_LP
491 PTR_DEMANGLE (%RAX_LP)
494 movl $__NR_clock_gettime, %eax
498 /* Compute relative timeout. */
506 /* This call works because we directly jump to a system call entry
507 which preserves all the registers. */
508 call JUMPTARGET(__gettimeofday)
510 /* Compute relative timeout. */
513 mul %rdx /* Milli seconds to nano seconds. */
520 addq $1000000000, %rdx
524 movq $-ETIMEDOUT, %r14
527 /* Store relative timeout. */
528 21: movq %rcx, 32(%rsp)
531 movl cond_futex(%rdi), %r12d
543 4: callq __pthread_enable_asynccancel
547 LP_OP(cmp) $-1, dep_mutex(%rdi)
549 # ifdef __ASSUME_PRIVATE_FUTEX
550 movl $FUTEX_WAIT, %eax
551 movl $(FUTEX_WAIT|FUTEX_PRIVATE_FLAG), %esi
555 movl %fs:PRIVATE_FUTEX, %esi
558 orl $FUTEX_WAIT, %esi
561 addq $cond_futex, %rdi
562 movl $SYS_futex, %eax
567 callq __pthread_disable_asynccancel
576 cmpxchgl %esi, (%rdi)
578 cmpxchgl %esi, cond_lock(%rdi)
582 6: movl broadcast_seq(%rdi), %edx
584 movq woken_seq(%rdi), %rax
586 movq wakeup_seq(%rdi), %r9
597 15: cmpq $-ETIMEDOUT, %r14
602 /* Initial locking failed. */
605 addq $cond_lock, %rdi
607 LP_OP(cmp) $-1, dep_mutex-cond_lock(%rdi)
608 movl $LLL_PRIVATE, %eax
609 movl $LLL_SHARED, %esi
611 callq __lll_lock_wait
614 /* Unlock in loop requires wakeup. */
617 addq $cond_lock, %rdi
619 LP_OP(cmp) $-1, dep_mutex-cond_lock(%rdi)
620 movl $LLL_PRIVATE, %eax
621 movl $LLL_SHARED, %esi
623 callq __lll_unlock_wake
626 /* Locking in loop failed. */
629 addq $cond_lock, %rdi
631 LP_OP(cmp) $-1, dep_mutex-cond_lock(%rdi)
632 movl $LLL_PRIVATE, %eax
633 movl $LLL_SHARED, %esi
635 callq __lll_lock_wait
637 subq $cond_lock, %rdi
641 .size __pthread_cond_timedwait, .-__pthread_cond_timedwait
642 versioned_symbol (libpthread, __pthread_cond_timedwait, pthread_cond_timedwait,
647 .type __condvar_cleanup2, @function
652 +--------------------------+
654 +--------------------------+
656 +--------------------------+
658 +--------------------------+
660 +--------------------------+
661 rsp + 16 | mutex pointer |
662 +--------------------------+
663 rsp + 8 | condvar pointer |
664 +--------------------------+
665 rsp + 4 | old broadcast_seq value |
666 +--------------------------+
667 rsp + 0 | old cancellation mode |
668 +--------------------------+
673 /* Get internal lock. */
679 cmpxchgl %esi, (%rdi)
681 cmpxchgl %esi, cond_lock(%rdi)
686 addq $cond_lock, %rdi
688 LP_OP(cmp) $-1, dep_mutex-cond_lock(%rdi)
689 movl $LLL_PRIVATE, %eax
690 movl $LLL_SHARED, %esi
692 callq __lll_lock_wait
694 subq $cond_lock, %rdi
697 1: movl broadcast_seq(%rdi), %edx
701 /* We increment the wakeup_seq counter only if it is lower than
702 total_seq. If this is not the case the thread was woken and
703 then canceled. In this case we ignore the signal. */
704 movq total_seq(%rdi), %rax
705 cmpq wakeup_seq(%rdi), %rax
707 incq wakeup_seq(%rdi)
708 incl cond_futex(%rdi)
709 6: incq woken_seq(%rdi)
711 3: subl $(1 << nwaiters_shift), cond_nwaiters(%rdi)
713 /* Wake up a thread which wants to destroy the condvar object. */
715 cmpq $0xffffffffffffffff, total_seq(%rdi)
717 movl cond_nwaiters(%rdi), %eax
718 andl $~((1 << nwaiters_shift) - 1), %eax
721 LP_OP(cmp) $-1, dep_mutex(%rdi)
722 leaq cond_nwaiters(%rdi), %rdi
724 #ifdef __ASSUME_PRIVATE_FUTEX
725 movl $FUTEX_WAKE, %eax
726 movl $(FUTEX_WAKE|FUTEX_PRIVATE_FLAG), %esi
730 movl %fs:PRIVATE_FUTEX, %esi
732 orl $FUTEX_WAKE, %esi
734 movl $SYS_futex, %eax
736 subq $cond_nwaiters, %rdi
747 addq $cond_lock, %rdi
749 LP_OP(cmp) $-1, dep_mutex-cond_lock(%rdi)
750 movl $LLL_PRIVATE, %eax
751 movl $LLL_SHARED, %esi
753 callq __lll_unlock_wake
755 /* Wake up all waiters to make sure no signal gets lost. */
758 addq $cond_futex, %rdi
759 LP_OP(cmp) $-1, dep_mutex-cond_futex(%rdi)
760 movl $0x7fffffff, %edx
761 #ifdef __ASSUME_PRIVATE_FUTEX
762 movl $FUTEX_WAKE, %eax
763 movl $(FUTEX_WAKE|FUTEX_PRIVATE_FLAG), %esi
767 movl %fs:PRIVATE_FUTEX, %esi
769 orl $FUTEX_WAKE, %esi
771 movl $SYS_futex, %eax
774 /* Lock the mutex only if we don't own it already. This only happens
775 in case of PI mutexes, if we got cancelled after a successful
776 return of the futex syscall and before disabling async
778 5: movq 16(%rsp), %rdi
779 movl MUTEX_KIND(%rdi), %eax
780 andl $(ROBUST_BIT|PI_BIT), %eax
788 /* We managed to get the lock. Fix it up before returning. */
789 callq __pthread_mutex_cond_lock_adjust
792 7: callq __pthread_mutex_cond_lock
794 8: movq 24(%rsp), %rdi
795 movq FRAME_SIZE(%rsp), %r15
796 movq FRAME_SIZE+8(%rsp), %r14
797 movq FRAME_SIZE+16(%rsp), %r13
798 movq FRAME_SIZE+24(%rsp), %r12
800 call _Unwind_Resume@PLT
804 .size __condvar_cleanup2, .-__condvar_cleanup2
807 .section .gcc_except_table,"a",@progbits
809 .byte DW_EH_PE_omit # @LPStart format
810 .byte DW_EH_PE_omit # @TType format
811 .byte DW_EH_PE_uleb128 # call-site format
812 .uleb128 .Lcstend-.Lcstbegin
814 .uleb128 .LcleanupSTART1-.LSTARTCODE
815 .uleb128 .LcleanupEND1-.LcleanupSTART1
816 .uleb128 __condvar_cleanup2-.LSTARTCODE
818 #ifndef __ASSUME_FUTEX_CLOCK_REALTIME
819 .uleb128 .LcleanupSTART2-.LSTARTCODE
820 .uleb128 .LcleanupEND2-.LcleanupSTART2
821 .uleb128 __condvar_cleanup2-.LSTARTCODE
824 .uleb128 .LcallUR-.LSTARTCODE
825 .uleb128 .LENDCODE-.LcallUR
832 .hidden DW.ref.__gcc_personality_v0
833 .weak DW.ref.__gcc_personality_v0
834 .section .gnu.linkonce.d.DW.ref.__gcc_personality_v0,"aw",@progbits
836 .type DW.ref.__gcc_personality_v0, @object
837 .size DW.ref.__gcc_personality_v0, LP_SIZE
838 DW.ref.__gcc_personality_v0:
839 ASM_ADDR __gcc_personality_v0