Support requeueing for condvars using PI mutex. x86-64 only.
[glibc.git] / nptl / sysdeps / unix / sysv / linux / x86_64 / pthread_cond_wait.S
blobe6323ea3e273dc3dfa906526db9688e3e9b14473
1 /* Copyright (C) 2002-2007, 2009 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, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
20 #include <sysdep.h>
21 #include <shlib-compat.h>
22 #include <lowlevellock.h>
23 #include <lowlevelcond.h>
24 #include <tcb-offsets.h>
25 #include <pthread-pi-defines.h>
27 #include <kernel-features.h>
30         .text
32 /* int pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex)  */
33         .globl  __pthread_cond_wait
34         .type   __pthread_cond_wait, @function
35         .align  16
36 __pthread_cond_wait:
37 .LSTARTCODE:
38         cfi_startproc
39 #ifdef SHARED
40         cfi_personality(DW_EH_PE_pcrel | DW_EH_PE_sdata4 | DW_EH_PE_indirect,
41                         DW.ref.__gcc_personality_v0)
42         cfi_lsda(DW_EH_PE_pcrel | DW_EH_PE_sdata4, .LexceptSTART)
43 #else
44         cfi_personality(DW_EH_PE_udata4, __gcc_personality_v0)
45         cfi_lsda(DW_EH_PE_udata4, .LexceptSTART)
46 #endif
48         pushq   %r12
49         cfi_adjust_cfa_offset(8)
50         cfi_rel_offset(%r12, 0)
51         pushq   %r13
52         cfi_adjust_cfa_offset(8)
53         cfi_rel_offset(%r13, 0)
54 #define FRAME_SIZE 32
55         subq    $FRAME_SIZE, %rsp
56         cfi_adjust_cfa_offset(FRAME_SIZE)
58         /* Stack frame:
60            rsp + 32
61                     +--------------------------+
62            rsp + 24 | old wake_seq value       |
63                     +--------------------------+
64            rsp + 16 | mutex pointer            |
65                     +--------------------------+
66            rsp +  8 | condvar pointer          |
67                     +--------------------------+
68            rsp +  4 | old broadcast_seq value  |
69                     +--------------------------+
70            rsp +  0 | old cancellation mode    |
71                     +--------------------------+
72         */
74         cmpq    $-1, dep_mutex(%rdi)
76                 /* Prepare structure passed to cancellation handler.  */
77         movq    %rdi, 8(%rsp)
78         movq    %rsi, 16(%rsp)
80         je      15f
81         movq    %rsi, dep_mutex(%rdi)
83         /* Get internal lock.  */
84 15:     movl    $1, %esi
85         xorl    %eax, %eax
86         LOCK
87 #if cond_lock == 0
88         cmpxchgl %esi, (%rdi)
89 #else
90         cmpxchgl %esi, cond_lock(%rdi)
91 #endif
92         jne     1f
94         /* Unlock the mutex.  */
95 2:      movq    16(%rsp), %rdi
96         xorl    %esi, %esi
97         callq   __pthread_mutex_unlock_usercnt
99         testl   %eax, %eax
100         jne     12f
102         movq    8(%rsp), %rdi
103         incq    total_seq(%rdi)
104         incl    cond_futex(%rdi)
105         addl    $(1 << nwaiters_shift), cond_nwaiters(%rdi)
107         /* Get and store current wakeup_seq value.  */
108         movq    8(%rsp), %rdi
109         movq    wakeup_seq(%rdi), %r9
110         movl    broadcast_seq(%rdi), %edx
111         movq    %r9, 24(%rsp)
112         movl    %edx, 4(%rsp)
114         /* Unlock.  */
115 8:      movl    cond_futex(%rdi), %r12d
116         LOCK
117 #if cond_lock == 0
118         decl    (%rdi)
119 #else
120         decl    cond_lock(%rdi)
121 #endif
122         jne     3f
124 .LcleanupSTART:
125 4:      callq   __pthread_enable_asynccancel
126         movl    %eax, (%rsp)
128         movq    8(%rsp), %rdi
129         xorq    %r10, %r10
130         movq    %r12, %rdx
131         // XXX reverse + lea
132         addq    $cond_futex, %rdi
133         cmpq    $-1, dep_mutex-cond_futex(%rdi)
134 #ifdef __ASSUME_PRIVATE_FUTEX
135         movl    $FUTEX_WAIT, %eax
136         movl    $(FUTEX_WAIT|FUTEX_PRIVATE_FLAG), %esi
137         cmove   %eax, %esi
138 #else
139         movl    $0, %eax
140         movl    %fs:PRIVATE_FUTEX, %esi
141         cmove   %eax, %esi
142 # if FUTEX_WAIT != 0
143 #  error "cc destroyed by following orl"
144         orl     $FUTEX_WAIT, %esi
145 # endif
146 #endif
147         je      60f
149         movq    dep_mutex-cond_futex(%rdi), %r8
150         /* Requeue to a PI mutex if the PI bit is set.  */
151         testl   $PI_BIT, MUTEX_KIND(%r8)
152         je      60f
154         movl    $(FUTEX_WAIT_REQUEUE_PI|FUTEX_PRIVATE_FLAG), %esi
155         movl    $SYS_futex, %eax
156         syscall
158         movl    $1, %r13d
159 #ifdef __ASSUME_REQUEUE_PI
160         jmp     62f
161 #else
162         cmpq    $-4095, %rax
163         jnae    62f
165         movl    $(FUTEX_WAIT|FUTEX_PRIVATE_FLAG), %esi
166 #endif
168 60:     xorl    %r13d, %r13d
169         movl    $SYS_futex, %eax
170         syscall
172 62:     movl    (%rsp), %edi
173         callq   __pthread_disable_asynccancel
174 .LcleanupEND:
176         /* Lock.  */
177         movq    8(%rsp), %rdi
178         movl    $1, %esi
179         xorl    %eax, %eax
180         LOCK
181 #if cond_lock == 0
182         cmpxchgl %esi, (%rdi)
183 #else
184         cmpxchgl %esi, cond_lock(%rdi)
185 #endif
186         jnz     5f
188 6:      movl    broadcast_seq(%rdi), %edx
190         movq    woken_seq(%rdi), %rax
192         movq    wakeup_seq(%rdi), %r9
194         cmpl    4(%rsp), %edx
195         jne     16f
197         cmpq    24(%rsp), %r9
198         jbe     8b
200         cmpq    %rax, %r9
201         jna     8b
203         incq    woken_seq(%rdi)
205         /* Unlock */
206 16:     subl    $(1 << nwaiters_shift), cond_nwaiters(%rdi)
208         /* Wake up a thread which wants to destroy the condvar object.  */
209         cmpq    $0xffffffffffffffff, total_seq(%rdi)
210         jne     17f
211         movl    cond_nwaiters(%rdi), %eax
212         andl    $~((1 << nwaiters_shift) - 1), %eax
213         jne     17f
215         addq    $cond_nwaiters, %rdi
216         cmpq    $-1, dep_mutex-cond_nwaiters(%rdi)
217         movl    $1, %edx
218 #ifdef __ASSUME_PRIVATE_FUTEX
219         movl    $FUTEX_WAKE, %eax
220         movl    $(FUTEX_WAKE|FUTEX_PRIVATE_FLAG), %esi
221         cmove   %eax, %esi
222 #else
223         movl    $0, %eax
224         movl    %fs:PRIVATE_FUTEX, %esi
225         cmove   %eax, %esi
226         orl     $FUTEX_WAKE, %esi
227 #endif
228         movl    $SYS_futex, %eax
229         syscall
230         subq    $cond_nwaiters, %rdi
232 17:     LOCK
233 #if cond_lock == 0
234         decl    (%rdi)
235 #else
236         decl    cond_lock(%rdi)
237 #endif
238         jne     10f
240         /* If requeue_pi is used the kernel performs the locking of the
241            mutex. */
242 11:     xorl    %eax, %eax
243         testl   %r13d, %r13d
244         jnz     14f
246         movq    16(%rsp), %rdi
247         callq   __pthread_mutex_cond_lock
249 14:     addq    $FRAME_SIZE, %rsp
250         cfi_adjust_cfa_offset(-FRAME_SIZE)
252         popq    %r13
253         cfi_adjust_cfa_offset(-8)
254         cfi_restore(%r13)
255         popq    %r12
256         cfi_adjust_cfa_offset(-8)
257         cfi_restore(%r12)
259         /* We return the result of the mutex_lock operation.  */
260         retq
262         /* Initial locking failed.  */
264         cfi_adjust_cfa_offset(16 + FRAME_SIZE)
265         cfi_rel_offset(%r12, FRAME_SIZE + 8)
266         cfi_rel_offset(%r13, FRAME_SIZE)
267 #if cond_lock != 0
268         addq    $cond_lock, %rdi
269 #endif
270         cmpq    $-1, dep_mutex-cond_lock(%rdi)
271         movl    $LLL_PRIVATE, %eax
272         movl    $LLL_SHARED, %esi
273         cmovne  %eax, %esi
274         callq   __lll_lock_wait
275         jmp     2b
277         /* Unlock in loop requires wakeup.  */
279 #if cond_lock != 0
280         addq    $cond_lock, %rdi
281 #endif
282         cmpq    $-1, dep_mutex-cond_lock(%rdi)
283         movl    $LLL_PRIVATE, %eax
284         movl    $LLL_SHARED, %esi
285         cmovne  %eax, %esi
286         callq   __lll_unlock_wake
287         jmp     4b
289         /* Locking in loop failed.  */
291 #if cond_lock != 0
292         addq    $cond_lock, %rdi
293 #endif
294         cmpq    $-1, dep_mutex-cond_lock(%rdi)
295         movl    $LLL_PRIVATE, %eax
296         movl    $LLL_SHARED, %esi
297         cmovne  %eax, %esi
298         callq   __lll_lock_wait
299 #if cond_lock != 0
300         subq    $cond_lock, %rdi
301 #endif
302         jmp     6b
304         /* Unlock after loop requires wakeup.  */
306 #if cond_lock != 0
307         addq    $cond_lock, %rdi
308 #endif
309         cmpq    $-1, dep_mutex-cond_lock(%rdi)
310         movl    $LLL_PRIVATE, %eax
311         movl    $LLL_SHARED, %esi
312         cmovne  %eax, %esi
313         callq   __lll_unlock_wake
314         jmp     11b
316         /* The initial unlocking of the mutex failed.  */
317 12:     movq    %rax, %r10
318         movq    8(%rsp), %rdi
319         LOCK
320 #if cond_lock == 0
321         decl    (%rdi)
322 #else
323         decl    cond_lock(%rdi)
324 #endif
325         je      13f
327 #if cond_lock != 0
328         addq    $cond_lock, %rdi
329 #endif
330         cmpq    $-1, dep_mutex-cond_lock(%rdi)
331         movl    $LLL_PRIVATE, %eax
332         movl    $LLL_SHARED, %esi
333         cmovne  %eax, %esi
334         callq   __lll_unlock_wake
336 13:     movq    %r10, %rax
337         jmp     14b
338         .size   __pthread_cond_wait, .-__pthread_cond_wait
339 versioned_symbol (libpthread, __pthread_cond_wait, pthread_cond_wait,
340                   GLIBC_2_3_2)
343         .align  16
344         .type   __condvar_cleanup1, @function
345         .globl  __condvar_cleanup1
346         .hidden __condvar_cleanup1
347 __condvar_cleanup1:
348         /* Stack frame:
350            rsp + 48
351                     +--------------------------+
352            rsp + 40 | %r12                     |
353                     +--------------------------+
354            rsp + 32 | %r13                     |
355                     +--------------------------+
356            rsp + 24 | unused                   |
357                     +--------------------------+
358            rsp + 16 | mutex pointer            |
359                     +--------------------------+
360            rsp +  8 | condvar pointer          |
361                     +--------------------------+
362            rsp +  4 | old broadcast_seq value  |
363                     +--------------------------+
364            rsp +  0 | old cancellation mode    |
365                     +--------------------------+
366         */
368         movq    %rax, 24(%rsp)
370         /* Get internal lock.  */
371         movq    8(%rsp), %rdi
372         movl    $1, %esi
373         xorl    %eax, %eax
374         LOCK
375 #if cond_lock == 0
376         cmpxchgl %esi, (%rdi)
377 #else
378         cmpxchgl %esi, cond_lock(%rdi)
379 #endif
380         jz      1f
382 #if cond_lock != 0
383         addq    $cond_lock, %rdi
384 #endif
385         cmpq    $-1, dep_mutex-cond_lock(%rdi)
386         movl    $LLL_PRIVATE, %eax
387         movl    $LLL_SHARED, %esi
388         cmovne  %eax, %esi
389         callq   __lll_lock_wait
390 #if cond_lock != 0
391         subq    $cond_lock, %rdi
392 #endif
394 1:      movl    broadcast_seq(%rdi), %edx
395         cmpl    4(%rsp), %edx
396         jne     3f
398         /* We increment the wakeup_seq counter only if it is lower than
399            total_seq.  If this is not the case the thread was woken and
400            then canceled.  In this case we ignore the signal.  */
401         movq    total_seq(%rdi), %rax
402         cmpq    wakeup_seq(%rdi), %rax
403         jbe     6f
404         incq    wakeup_seq(%rdi)
405         incl    cond_futex(%rdi)
406 6:      incq    woken_seq(%rdi)
408 3:      subl    $(1 << nwaiters_shift), cond_nwaiters(%rdi)
410         /* Wake up a thread which wants to destroy the condvar object.  */
411         xorq    %r12, %r12
412         cmpq    $0xffffffffffffffff, total_seq(%rdi)
413         jne     4f
414         movl    cond_nwaiters(%rdi), %eax
415         andl    $~((1 << nwaiters_shift) - 1), %eax
416         jne     4f
418         cmpq    $-1, dep_mutex(%rdi)
419         leaq    cond_nwaiters(%rdi), %rdi
420         movl    $1, %edx
421 #ifdef __ASSUME_PRIVATE_FUTEX
422         movl    $FUTEX_WAKE, %eax
423         movl    $(FUTEX_WAKE|FUTEX_PRIVATE_FLAG), %esi
424         cmove   %eax, %esi
425 #else
426         movl    $0, %eax
427         movl    %fs:PRIVATE_FUTEX, %esi
428         cmove   %eax, %esi
429         orl     $FUTEX_WAKE, %esi
430 #endif
431         movl    $SYS_futex, %eax
432         syscall
433         subq    $cond_nwaiters, %rdi
434         movl    $1, %r12d
436 4:      LOCK
437 #if cond_lock == 0
438         decl    (%rdi)
439 #else
440         decl    cond_lock(%rdi)
441 #endif
442         je      2f
443 #if cond_lock != 0
444         addq    $cond_lock, %rdi
445 #endif
446         cmpq    $-1, dep_mutex-cond_lock(%rdi)
447         movl    $LLL_PRIVATE, %eax
448         movl    $LLL_SHARED, %esi
449         cmovne  %eax, %esi
450         callq   __lll_unlock_wake
452         /* Wake up all waiters to make sure no signal gets lost.  */
453 2:      testq   %r12, %r12
454         jnz     5f
455         addq    $cond_futex, %rdi
456         cmpq    $-1, dep_mutex-cond_futex(%rdi)
457         movl    $0x7fffffff, %edx
458 #ifdef __ASSUME_PRIVATE_FUTEX
459         movl    $FUTEX_WAKE, %eax
460         movl    $(FUTEX_WAKE|FUTEX_PRIVATE_FLAG), %esi
461         cmove   %eax, %esi
462 #else
463         movl    $0, %eax
464         movl    %fs:PRIVATE_FUTEX, %esi
465         cmove   %eax, %esi
466         orl     $FUTEX_WAKE, %esi
467 #endif
468         movl    $SYS_futex, %eax
469         syscall
471 5:      movq    16(%rsp), %rdi
472         callq   __pthread_mutex_cond_lock
474         movq    24(%rsp), %rdi
475         movq    40(%rsp), %r12
476         movq    32(%rsp), %r13
477 .LcallUR:
478         call    _Unwind_Resume@PLT
479         hlt
480 .LENDCODE:
481         cfi_endproc
482         .size   __condvar_cleanup1, .-__condvar_cleanup1
485         .section .gcc_except_table,"a",@progbits
486 .LexceptSTART:
487         .byte   DW_EH_PE_omit                   # @LPStart format
488         .byte   DW_EH_PE_omit                   # @TType format
489         .byte   DW_EH_PE_uleb128                # call-site format
490         .uleb128 .Lcstend-.Lcstbegin
491 .Lcstbegin:
492         .uleb128 .LcleanupSTART-.LSTARTCODE
493         .uleb128 .LcleanupEND-.LcleanupSTART
494         .uleb128 __condvar_cleanup1-.LSTARTCODE
495         .uleb128  0
496         .uleb128 .LcallUR-.LSTARTCODE
497         .uleb128 .LENDCODE-.LcallUR
498         .uleb128 0
499         .uleb128  0
500 .Lcstend:
503 #ifdef SHARED
504         .hidden DW.ref.__gcc_personality_v0
505         .weak   DW.ref.__gcc_personality_v0
506         .section .gnu.linkonce.d.DW.ref.__gcc_personality_v0,"aw",@progbits
507         .align  8
508         .type   DW.ref.__gcc_personality_v0, @object
509         .size   DW.ref.__gcc_personality_v0, 8
510 DW.ref.__gcc_personality_v0:
511         .quad   __gcc_personality_v0
512 #endif