Fix pthread_cond_*wait with requeue-PI on i386.
[glibc.git] / nptl / sysdeps / unix / sysv / linux / i386 / i486 / pthread_cond_wait.S
blob53970d755fadce2b6028901d35d55b89193d6dde
1 /* Copyright (C) 2002-2004,2006-2007,2009,2010 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-errnos.h>
26 #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         pushl   %ebp
49         cfi_adjust_cfa_offset(4)
50         cfi_rel_offset(%ebp, 0)
51         pushl   %edi
52         cfi_adjust_cfa_offset(4)
53         cfi_rel_offset(%edi, 0)
54         pushl   %esi
55         cfi_adjust_cfa_offset(4)
56         cfi_rel_offset(%esi, 0)
57         pushl   %ebx
58         cfi_adjust_cfa_offset(4)
59         cfi_rel_offset(%ebx, 0)
61         xorl    %esi, %esi
62         movl    20(%esp), %ebx
64         /* Get internal lock.  */
65         movl    $1, %edx
66         xorl    %eax, %eax
67         LOCK
68 #if cond_lock == 0
69         cmpxchgl %edx, (%ebx)
70 #else
71         cmpxchgl %edx, cond_lock(%ebx)
72 #endif
73         jnz     1f
75         /* Store the reference to the mutex.  If there is already a
76            different value in there this is a bad user bug.  */
77 2:      cmpl    $-1, dep_mutex(%ebx)
78         movl    24(%esp), %eax
79         je      15f
80         movl    %eax, dep_mutex(%ebx)
82         /* Unlock the mutex.  */
83 15:     xorl    %edx, %edx
84         call    __pthread_mutex_unlock_usercnt
86         testl   %eax, %eax
87         jne     12f
89         addl    $1, total_seq(%ebx)
90         adcl    $0, total_seq+4(%ebx)
91         addl    $1, cond_futex(%ebx)
92         addl    $(1 << nwaiters_shift), cond_nwaiters(%ebx)
94 #define FRAME_SIZE 20
95         subl    $FRAME_SIZE, %esp
96         cfi_adjust_cfa_offset(FRAME_SIZE)
97         cfi_remember_state
99         /* Get and store current wakeup_seq value.  */
100         movl    wakeup_seq(%ebx), %edi
101         movl    wakeup_seq+4(%ebx), %edx
102         movl    broadcast_seq(%ebx), %eax
103         movl    %edi, 4(%esp)
104         movl    %edx, 8(%esp)
105         movl    %eax, 12(%esp)
107         /* Reset the pi-requeued flag.  */
108 8:      movl    $0, 16(%esp)
109         movl    cond_futex(%ebx), %ebp
111         /* Unlock.  */
112         LOCK
113 #if cond_lock == 0
114         subl    $1, (%ebx)
115 #else
116         subl    $1, cond_lock(%ebx)
117 #endif
118         jne     3f
120 .LcleanupSTART:
121 4:      call    __pthread_enable_asynccancel
122         movl    %eax, (%esp)
124         xorl    %ecx, %ecx
125         cmpl    $-1, dep_mutex(%ebx)
126         sete    %cl
127         je      18f
129         movl    dep_mutex(%ebx), %edi
130         /* Requeue to a non-robust PI mutex if the PI bit is set and
131            the robust bit is not set.  */
132         movl    MUTEX_KIND(%edi), %eax
133         andl    $(ROBUST_BIT|PI_BIT), %eax
134         cmpl    $PI_BIT, %eax
135         jne     18f
137         movl    $(FUTEX_WAIT_REQUEUE_PI|FUTEX_PRIVATE_FLAG), %ecx
138         movl    %ebp, %edx
139         xorl    %esi, %esi
140         addl    $cond_futex, %ebx
141         movl    $SYS_futex, %eax
142         ENTER_KERNEL
143         subl    $cond_futex, %ebx
144         /* Set the pi-requeued flag only if the kernel has returned 0. The
145            kernel does not hold the mutex on error.  */
146         cmpl    $0, %eax
147         sete    16(%esp)
148         je      19f
150         /* Normal and PI futexes dont mix. Use normal futex functions only
151            if the kernel does not support the PI futex functions.  */
152         cmpl    $-ENOSYS, %eax
153         jne     19f
154         xorl    %ecx, %ecx
156 18:     subl    $1, %ecx
157 #ifdef __ASSUME_PRIVATE_FUTEX
158         andl    $FUTEX_PRIVATE_FLAG, %ecx
159 #else
160         andl    %gs:PRIVATE_FUTEX, %ecx
161 #endif
162 #if FUTEX_WAIT != 0
163         addl    $FUTEX_WAIT, %ecx
164 #endif
165         movl    %ebp, %edx
166         addl    $cond_futex, %ebx
167 .Ladd_cond_futex:
168         movl    $SYS_futex, %eax
169         ENTER_KERNEL
170         subl    $cond_futex, %ebx
171 .Lsub_cond_futex:
173 19:     movl    (%esp), %eax
174         call    __pthread_disable_asynccancel
175 .LcleanupEND:
177         /* Lock.  */
178         movl    $1, %edx
179         xorl    %eax, %eax
180         LOCK
181 #if cond_lock == 0
182         cmpxchgl %edx, (%ebx)
183 #else
184         cmpxchgl %edx, cond_lock(%ebx)
185 #endif
186         jnz     5f
188 6:      movl    broadcast_seq(%ebx), %eax
189         cmpl    12(%esp), %eax
190         jne     16f
192         movl    woken_seq(%ebx), %eax
193         movl    woken_seq+4(%ebx), %ecx
195         movl    wakeup_seq(%ebx), %edi
196         movl    wakeup_seq+4(%ebx), %edx
198         cmpl    8(%esp), %edx
199         jne     7f
200         cmpl    4(%esp), %edi
201         je      8b
203 7:      cmpl    %ecx, %edx
204         jne     9f
205         cmp     %eax, %edi
206         je      8b
208 9:      addl    $1, woken_seq(%ebx)
209         adcl    $0, woken_seq+4(%ebx)
211         /* Unlock */
212 16:     subl    $(1 << nwaiters_shift), cond_nwaiters(%ebx)
214         /* Wake up a thread which wants to destroy the condvar object.  */
215         movl    total_seq(%ebx), %eax
216         andl    total_seq+4(%ebx), %eax
217         cmpl    $0xffffffff, %eax
218         jne     17f
219         movl    cond_nwaiters(%ebx), %eax
220         andl    $~((1 << nwaiters_shift) - 1), %eax
221         jne     17f
223         addl    $cond_nwaiters, %ebx
224         movl    $SYS_futex, %eax
225 #if FUTEX_PRIVATE_FLAG > 255
226         xorl    %ecx, %ecx
227 #endif
228         cmpl    $-1, dep_mutex-cond_nwaiters(%ebx)
229         sete    %cl
230         subl    $1, %ecx
231 #ifdef __ASSUME_PRIVATE_FUTEX
232         andl    $FUTEX_PRIVATE_FLAG, %ecx
233 #else
234         andl    %gs:PRIVATE_FUTEX, %ecx
235 #endif
236         addl    $FUTEX_WAKE, %ecx
237         movl    $1, %edx
238         ENTER_KERNEL
239         subl    $cond_nwaiters, %ebx
241 17:     LOCK
242 #if cond_lock == 0
243         subl    $1, (%ebx)
244 #else
245         subl    $1, cond_lock(%ebx)
246 #endif
247         jne     10f
249         /* With requeue_pi, the mutex lock is held in the kernel.  */
250 11:     movl    24+FRAME_SIZE(%esp), %eax
251         movl    16(%esp), %ecx
252         testl   %ecx, %ecx
253         jnz     21f
255         call    __pthread_mutex_cond_lock
256 20:     addl    $FRAME_SIZE, %esp
257         cfi_adjust_cfa_offset(-FRAME_SIZE);
259 14:     popl    %ebx
260         cfi_adjust_cfa_offset(-4)
261         cfi_restore(%ebx)
262         popl    %esi
263         cfi_adjust_cfa_offset(-4)
264         cfi_restore(%esi)
265         popl    %edi
266         cfi_adjust_cfa_offset(-4)
267         cfi_restore(%edi)
268         popl    %ebp
269         cfi_adjust_cfa_offset(-4)
270         cfi_restore(%ebp)
272         /* We return the result of the mutex_lock operation.  */
273         ret
275         cfi_restore_state
277 21:     call    __pthread_mutex_cond_lock_adjust
278         xorl    %eax, %eax
279         jmp     20b
281         cfi_adjust_cfa_offset(-FRAME_SIZE);
282         /* Initial locking failed.  */
284 #if cond_lock == 0
285         movl    %ebx, %edx
286 #else
287         leal    cond_lock(%ebx), %edx
288 #endif
289 #if (LLL_SHARED-LLL_PRIVATE) > 255
290         xorl    %ecx, %ecx
291 #endif
292         cmpl    $-1, dep_mutex(%ebx)
293         setne   %cl
294         subl    $1, %ecx
295         andl    $(LLL_SHARED-LLL_PRIVATE), %ecx
296 #if LLL_PRIVATE != 0
297         addl    $LLL_PRIVATE, %ecx
298 #endif
299         call    __lll_lock_wait
300         jmp     2b
302         /* The initial unlocking of the mutex failed.  */
304         LOCK
305 #if cond_lock == 0
306         subl    $1, (%ebx)
307 #else
308         subl    $1, cond_lock(%ebx)
309 #endif
310         jne     14b
312         movl    %eax, %esi
313 #if cond_lock == 0
314         movl    %ebx, %eax
315 #else
316         leal    cond_lock(%ebx), %eax
317 #endif
318 #if (LLL_SHARED-LLL_PRIVATE) > 255
319         xorl    %ecx, %ecx
320 #endif
321         cmpl    $-1, dep_mutex(%ebx)
322         setne   %cl
323         subl    $1, %ecx
324         andl    $(LLL_SHARED-LLL_PRIVATE), %ecx
325 #if LLL_PRIVATE != 0
326         addl    $LLL_PRIVATE, %ecx
327 #endif
328         call    __lll_unlock_wake
330         movl    %esi, %eax
331         jmp     14b
333         cfi_adjust_cfa_offset(FRAME_SIZE)
335         /* Unlock in loop requires wakeup.  */
337 #if cond_lock == 0
338         movl    %ebx, %eax
339 #else
340         leal    cond_lock(%ebx), %eax
341 #endif
342 #if (LLL_SHARED-LLL_PRIVATE) > 255
343         xorl    %ecx, %ecx
344 #endif
345         cmpl    $-1, dep_mutex(%ebx)
346         setne   %cl
347         subl    $1, %ecx
348         andl    $(LLL_SHARED-LLL_PRIVATE), %ecx
349 #if LLL_PRIVATE != 0
350         addl    $LLL_PRIVATE, %ecx
351 #endif
352         call    __lll_unlock_wake
353         jmp     4b
355         /* Locking in loop failed.  */
357 #if cond_lock == 0
358         movl    %ebx, %edx
359 #else
360         leal    cond_lock(%ebx), %edx
361 #endif
362 #if (LLL_SHARED-LLL_PRIVATE) > 255
363         xorl    %ecx, %ecx
364 #endif
365         cmpl    $-1, dep_mutex(%ebx)
366         setne   %cl
367         subl    $1, %ecx
368         andl    $(LLL_SHARED-LLL_PRIVATE), %ecx
369 #if LLL_PRIVATE != 0
370         addl    $LLL_PRIVATE, %ecx
371 #endif
372         call    __lll_lock_wait
373         jmp     6b
375         /* Unlock after loop requires wakeup.  */
377 #if cond_lock == 0
378         movl    %ebx, %eax
379 #else
380         leal    cond_lock(%ebx), %eax
381 #endif
382 #if (LLL_SHARED-LLL_PRIVATE) > 255
383         xorl    %ecx, %ecx
384 #endif
385         cmpl    $-1, dep_mutex(%ebx)
386         setne   %cl
387         subl    $1, %ecx
388         andl    $(LLL_SHARED-LLL_PRIVATE), %ecx
389 #if LLL_PRIVATE != 0
390         addl    $LLL_PRIVATE, %ecx
391 #endif
392         call    __lll_unlock_wake
393         jmp     11b
394         .size   __pthread_cond_wait, .-__pthread_cond_wait
395 versioned_symbol (libpthread, __pthread_cond_wait, pthread_cond_wait,
396                   GLIBC_2_3_2)
399         .type   __condvar_w_cleanup2, @function
400 __condvar_w_cleanup2:
401         subl    $cond_futex, %ebx
402         .size   __condvar_w_cleanup2, .-__condvar_w_cleanup2
403 .LSbl4:
404         .type   __condvar_w_cleanup, @function
405 __condvar_w_cleanup:
406         movl    %eax, %esi
408         /* Get internal lock.  */
409         movl    $1, %edx
410         xorl    %eax, %eax
411         LOCK
412 #if cond_lock == 0
413         cmpxchgl %edx, (%ebx)
414 #else
415         cmpxchgl %edx, cond_lock(%ebx)
416 #endif
417         jz      1f
419 #if cond_lock == 0
420         movl    %ebx, %edx
421 #else
422         leal    cond_lock(%ebx), %edx
423 #endif
424 #if (LLL_SHARED-LLL_PRIVATE) > 255
425         xorl    %ecx, %ecx
426 #endif
427         cmpl    $-1, dep_mutex(%ebx)
428         setne   %cl
429         subl    $1, %ecx
430         andl    $(LLL_SHARED-LLL_PRIVATE), %ecx
431 #if LLL_PRIVATE != 0
432         addl    $LLL_PRIVATE, %ecx
433 #endif
434         call    __lll_lock_wait
436 1:      movl    broadcast_seq(%ebx), %eax
437         cmpl    12(%esp), %eax
438         jne     3f
440         /* We increment the wakeup_seq counter only if it is lower than
441            total_seq.  If this is not the case the thread was woken and
442            then canceled.  In this case we ignore the signal.  */
443         movl    total_seq(%ebx), %eax
444         movl    total_seq+4(%ebx), %edi
445         cmpl    wakeup_seq+4(%ebx), %edi
446         jb      6f
447         ja      7f
448         cmpl    wakeup_seq(%ebx), %eax
449         jbe     7f
451 6:      addl    $1, wakeup_seq(%ebx)
452         adcl    $0, wakeup_seq+4(%ebx)
453         addl    $1, cond_futex(%ebx)
455 7:      addl    $1, woken_seq(%ebx)
456         adcl    $0, woken_seq+4(%ebx)
458 3:      subl    $(1 << nwaiters_shift), cond_nwaiters(%ebx)
460         /* Wake up a thread which wants to destroy the condvar object.  */
461         xorl    %edi, %edi
462         movl    total_seq(%ebx), %eax
463         andl    total_seq+4(%ebx), %eax
464         cmpl    $0xffffffff, %eax
465         jne     4f
466         movl    cond_nwaiters(%ebx), %eax
467         andl    $~((1 << nwaiters_shift) - 1), %eax
468         jne     4f
470         addl    $cond_nwaiters, %ebx
471         movl    $SYS_futex, %eax
472 #if FUTEX_PRIVATE_FLAG > 255
473         xorl    %ecx, %ecx
474 #endif
475         cmpl    $-1, dep_mutex-cond_nwaiters(%ebx)
476         sete    %cl
477         subl    $1, %ecx
478 #ifdef __ASSUME_PRIVATE_FUTEX
479         andl    $FUTEX_PRIVATE_FLAG, %ecx
480 #else
481         andl    %gs:PRIVATE_FUTEX, %ecx
482 #endif
483         addl    $FUTEX_WAKE, %ecx
484         movl    $1, %edx
485         ENTER_KERNEL
486         subl    $cond_nwaiters, %ebx
487         movl    $1, %edi
489 4:      LOCK
490 #if cond_lock == 0
491         subl    $1, (%ebx)
492 #else
493         subl    $1, cond_lock(%ebx)
494 #endif
495         je      2f
497 #if cond_lock == 0
498         movl    %ebx, %eax
499 #else
500         leal    cond_lock(%ebx), %eax
501 #endif
502 #if (LLL_SHARED-LLL_PRIVATE) > 255
503         xorl    %ecx, %ecx
504 #endif
505         cmpl    $-1, dep_mutex(%ebx)
506         setne   %cl
507         subl    $1, %ecx
508         andl    $(LLL_SHARED-LLL_PRIVATE), %ecx
509 #if LLL_PRIVATE != 0
510         addl    $LLL_PRIVATE, %ecx
511 #endif
512         call    __lll_unlock_wake
514         /* Wake up all waiters to make sure no signal gets lost.  */
515 2:      testl   %edi, %edi
516         jnz     5f
517         addl    $cond_futex, %ebx
518 #if FUTEX_PRIVATE_FLAG > 255
519         xorl    %ecx, %ecx
520 #endif
521         cmpl    $-1, dep_mutex-cond_futex(%ebx)
522         sete    %cl
523         subl    $1, %ecx
524 #ifdef __ASSUME_PRIVATE_FUTEX
525         andl    $FUTEX_PRIVATE_FLAG, %ecx
526 #else
527         andl    %gs:PRIVATE_FUTEX, %ecx
528 #endif
529         addl    $FUTEX_WAKE, %ecx
530         movl    $SYS_futex, %eax
531         movl    $0x7fffffff, %edx
532         ENTER_KERNEL
534 5:      movl    24+FRAME_SIZE(%esp), %eax
535         call    __pthread_mutex_cond_lock
537         movl    %esi, (%esp)
538 .LcallUR:
539         call    _Unwind_Resume
540         hlt
541 .LENDCODE:
542         cfi_endproc
543         .size   __condvar_w_cleanup, .-__condvar_w_cleanup
546         .section .gcc_except_table,"a",@progbits
547 .LexceptSTART:
548         .byte   DW_EH_PE_omit                   # @LPStart format (omit)
549         .byte   DW_EH_PE_omit                   # @TType format (omit)
550         .byte   DW_EH_PE_sdata4                 # call-site format
551                                                 # DW_EH_PE_sdata4
552         .uleb128 .Lcstend-.Lcstbegin
553 .Lcstbegin:
554         .long   .LcleanupSTART-.LSTARTCODE
555         .long   .Ladd_cond_futex-.LcleanupSTART
556         .long   __condvar_w_cleanup-.LSTARTCODE
557         .uleb128  0
558         .long   .Ladd_cond_futex-.LSTARTCODE
559         .long   .Lsub_cond_futex-.Ladd_cond_futex
560         .long   __condvar_w_cleanup2-.LSTARTCODE
561         .uleb128  0
562         .long   .Lsub_cond_futex-.LSTARTCODE
563         .long   .LcleanupEND-.Lsub_cond_futex
564         .long   __condvar_w_cleanup-.LSTARTCODE
565         .uleb128  0
566         .long   .LcallUR-.LSTARTCODE
567         .long   .LENDCODE-.LcallUR
568         .long   0
569         .uleb128  0
570 .Lcstend:
572 #ifdef PIC
573         .section .gnu.linkonce.t.__i686.get_pc_thunk.cx,"ax",@progbits
574         .globl  __i686.get_pc_thunk.cx
575         .hidden __i686.get_pc_thunk.cx
576         .type   __i686.get_pc_thunk.cx,@function
577 __i686.get_pc_thunk.cx:
578         movl (%esp), %ecx;
579         ret
580         .size   __i686.get_pc_thunk.cx,.-__i686.get_pc_thunk.cx
581 #endif
583 #ifdef SHARED
584         .hidden DW.ref.__gcc_personality_v0
585         .weak   DW.ref.__gcc_personality_v0
586         .section .gnu.linkonce.d.DW.ref.__gcc_personality_v0,"aw",@progbits
587         .align 4
588         .type   DW.ref.__gcc_personality_v0, @object
589         .size   DW.ref.__gcc_personality_v0, 4
590 DW.ref.__gcc_personality_v0:
591         .long   __gcc_personality_v0
592 #endif