[BZ #357]
[glibc.git] / nptl / sysdeps / unix / sysv / linux / x86_64 / pthread_cond_timedwait.S
blob67bec6caa7791b012d65e2b38c0ffe3019976213
1 /* Copyright (C) 2002, 2003, 2004 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 <lowlevelcond.h>
23 #include <pthread-errnos.h>
25 #ifdef UP
26 # define LOCK
27 #else
28 # define LOCK lock
29 #endif
31 #define SYS_futex               202
32 #define FUTEX_WAIT              0
33 #define FUTEX_WAKE              1
35 /* For the calculation see asm/vsyscall.h.  */
36 #define VSYSCALL_ADDR_vgettimeofday     0xffffffffff600000
39         .text
41 /* int pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex,
42                                const struct timespec *abstime)  */
43         .globl  __pthread_cond_timedwait
44         .type   __pthread_cond_timedwait, @function
45         .align  16
46 __pthread_cond_timedwait:
47 .LSTARTCODE:
48         pushq   %r12
49 .Lpush_r12:
50         pushq   %r13
51 .Lpush_r13:
52         pushq   %r14
53 .Lpush_r14:
54 #define FRAME_SIZE 80
55         subq    $FRAME_SIZE, %rsp
56 .Lsubq:
58         cmpq    $1000000000, 8(%rdx)
59         movq    $EINVAL, %rax
60         jae     18f
62         /* Stack frame:
64            rsp + 80
65                     +--------------------------+
66            rsp + 48 | cleanup buffer           |
67                     +--------------------------+
68            rsp + 40 | old wake_seq value       |
69                     +--------------------------+
70            rsp + 24 | timeout value            |
71                     +--------------------------+
72            rsp + 16 | mutex pointer            |
73                     +--------------------------+
74            rsp +  8 | condvar pointer          |
75                     +--------------------------+
76            rsp +  4 | old broadcast_seq value  |
77                     +--------------------------+
78            rsp +  0 | old cancellation mode    |
79                     +--------------------------+
80         */
82         cmpq    $-1, dep_mutex(%rdi)
84         /* Prepare structure passed to cancellation handler.  */
85         movq    %rdi, 8(%rsp)
86         movq    %rsi, 16(%rsp)
87         movq    %rdx, %r13
89         je      22f
90         movq    %rsi, dep_mutex(%rdi)
92         /* Get internal lock.  */
93 22:     movl    $1, %esi
94         xorl    %eax, %eax
95         LOCK
96 #if cond_lock == 0
97         cmpxchgl %esi, (%rdi)
98 #else
99         cmpxchgl %esi, cond_lock(%rdi)
100 #endif
101         jnz     1f
103         /* Unlock the mutex.  */
104 2:      movq    16(%rsp), %rdi
105         xorq    %rsi, %rsi
106         callq   __pthread_mutex_unlock_usercnt
108         testl   %eax, %eax
109         jne     16f
111         movq    8(%rsp), %rdi
112         incq    total_seq(%rdi)
113         incl    cond_futex(%rdi)
114         addl    $(1 << clock_bits), cond_nwaiters(%rdi)
116         /* Install cancellation handler.  */
117 #ifdef PIC
118         leaq    __condvar_cleanup(%rip), %rsi
119 #else
120         leaq    __condvar_cleanup, %rsi
121 #endif
122         leaq    48(%rsp), %rdi
123         movq    %rsp, %rdx
124         callq   __pthread_cleanup_push
126         /* Get and store current wakeup_seq value.  */
127         movq    8(%rsp), %rdi
128         movq    wakeup_seq(%rdi), %r9
129         movl    broadcast_seq(%rdi), %edx
130         movq    %r9, 40(%rsp)
131         movl    %edx, 4(%rsp)
133         /* Get the current time.  */
135 #ifdef __NR_clock_gettime
136         /* Get the clock number.  Note that the field in the condvar
137            structure stores the number minus 1.  */
138         movq    8(%rsp), %rdi
139         movl    cond_nwaiters(%rdi), %edi
140         andl    $((1 << clock_bits) - 1), %edi
141         /* Only clocks 0 and 1 are allowed so far.  Both are handled in the
142            kernel.  */
143         leaq    24(%rsp), %rsi
144         movq    $__NR_clock_gettime, %rax
145         syscall
146 # ifndef __ASSUME_POSIX_TIMERS
147         cmpq    $-ENOSYS, %rax
148         je      19f
149 # endif
151         /* Compute relative timeout.  */
152         movq    (%r13), %rcx
153         movq    8(%r13), %rdx
154         subq    24(%rsp), %rcx
155         subq    32(%rsp), %rdx
156 #else
157         leaq    24(%rsp), %rdi
158         xorq    %rsi, %rsi
159         movq    $VSYSCALL_ADDR_vgettimeofday, %rax
160         callq   *%rax
162         /* Compute relative timeout.  */
163         movq    32(%rsp), %rax
164         movq    $1000, %rdx
165         mul     %rdx            /* Milli seconds to nano seconds.  */
166         movq    (%r13), %rcx
167         movq    8(%r13), %rdx
168         subq    24(%rsp), %rcx
169         subq    %rax, %rdx
170 #endif
171         jns     12f
172         addq    $1000000000, %rdx
173         decq    %rcx
174 12:     testq   %rcx, %rcx
175         movq    8(%rsp), %rdi
176         movq    $-ETIMEDOUT, %r14
177         js      6f
179         /* Store relative timeout.  */
180 21:     movq    %rcx, 24(%rsp)
181         movq    %rdx, 32(%rsp)
183         movl    cond_futex(%rdi), %r12d
185         /* Unlock.  */
186         LOCK
187 #if cond_lock == 0
188         decl    (%rdi)
189 #else
190         decl    cond_lock(%rdi)
191 #endif
192         jne     3f
194 4:      callq   __pthread_enable_asynccancel
195         movl    %eax, (%rsp)
197         leaq    24(%rsp), %r10
198         xorq    %rsi, %rsi      /* movq $FUTEX_WAIT, %rsi */
199         movq    %r12, %rdx
200         addq    $cond_futex, %rdi
201         movq    $SYS_futex, %rax
202         syscall
203         movq    %rax, %r14
205         movl    (%rsp), %edi
206         callq   __pthread_disable_asynccancel
208         /* Lock.  */
209         movq    8(%rsp), %rdi
210         movl    $1, %esi
211         xorl    %eax, %eax
212         LOCK
213 #if cond_lock == 0
214         cmpxchgl %esi, (%rdi)
215 #else
216         cmpxchgl %esi, cond_lock(%rdi)
217 #endif
218         jne     5f
220 6:      movl    broadcast_seq(%rdi), %edx
222         movq    woken_seq(%rdi), %rax
224         movq    wakeup_seq(%rdi), %r9
226         cmpl    4(%rsp), %edx
227         jne     23f
229         cmpq    40(%rsp), %r9
230         jbe     15f
232         cmpq    %rax, %r9
233         ja      9f
235 15:     cmpq    $-ETIMEDOUT, %r14
236         jne     8b
238 13:     incq    wakeup_seq(%rdi)
239         incl    cond_futex(%rdi)
240         movq    $ETIMEDOUT, %r14
241         jmp     14f
243 23:     xorq    %r14, %r14
244         jmp     24f
246 9:      xorq    %r14, %r14
247 14:     incq    woken_seq(%rdi)
249 24:     subl    $(1 << clock_bits), cond_nwaiters(%rdi)
251         /* Wake up a thread which wants to destroy the condvar object.  */
252         cmpq    $0xffffffffffffffff, total_seq(%rdi)
253         jne     25f
254         movl    cond_nwaiters(%rdi), %eax
255         andl    $~((1 << clock_bits) - 1), %eax
256         jne     25f
258         addq    $cond_nwaiters, %rdi
259         movq    $SYS_futex, %rax
260         movq    $FUTEX_WAKE, %rsi
261         movl    $1, %edx
262         syscall
263         subq    $cond_nwaiters, %rdi
265 25:     LOCK
266 #if cond_lock == 0
267         decl    (%rdi)
268 #else
269         decl    cond_lock(%rdi)
270 #endif
271         jne     10f
273         /* Remove cancellation handler.  */
274 11:     movq    48+CLEANUP_PREV(%rsp), %rdx
275         movq    %rdx, %fs:CLEANUP
277         movq    16(%rsp), %rdi
278         callq   __pthread_mutex_cond_lock
280         testq   %rax, %rax
281         cmoveq  %r14, %rax
283 18:     addq    $FRAME_SIZE, %rsp
284 .Laddq:
285         popq    %r14
286 .Lpop_r14:
287         popq    %r13
288 .Lpop_r13:
289         popq    %r12
290 .Lpop_r12:
292         retq
294         /* Initial locking failed.  */
296 .LSbl1:
297 #if cond_lock != 0
298         addq    $cond_lock, %rdi
299 #endif
300         callq   __lll_mutex_lock_wait
301         jmp     2b
303         /* Unlock in loop requires wakeup.  */
305 #if cond_lock != 0
306         addq    $cond_lock, %rdi
307 #endif
308         callq   __lll_mutex_unlock_wake
309         jmp     4b
311         /* Locking in loop failed.  */
313 #if cond_lock != 0
314         addq    $cond_lock, %rdi
315 #endif
316         callq   __lll_mutex_lock_wait
317 #if cond_lock != 0
318         subq    $cond_lock, %rdi
319 #endif
320         jmp     6b
322         /* Unlock after loop requires wakeup.  */
324 #if cond_lock != 0
325         addq    $cond_lock, %rdi
326 #endif
327         callq   __lll_mutex_unlock_wake
328         jmp     11b
330         /* The initial unlocking of the mutex failed.  */
331 16:     movq    8(%rsp), %rdi
332         movq    %rax, (%rsp)
333         LOCK
334 #if cond_lock == 0
335         decl    (%rdi)
336 #else
337         decl    cond_lock(%rdi)
338 #endif
339         jne     17f
341 #if cond_lock != 0
342         addq    $cond_lock, %rdi
343 #endif
344         callq   __lll_mutex_unlock_wake
346 17:     movq    (%rsp), %rax
347         jmp     18b
349 #if defined __NR_clock_gettime && !defined __ASSUME_POSIX_TIMERS
350         /* clock_gettime not available.  */
351 19:     leaq    24(%rsp), %rdi
352         xorq    %rsi, %rsi
353         movq    $VSYSCALL_ADDR_vgettimeofday, %rax
354         callq   *%rax
356         /* Compute relative timeout.  */
357         movq    32(%rsp), %rax
358         movq    $1000, %rdx
359         mul     %rdx            /* Milli seconds to nano seconds.  */
360         movq    (%r13), %rcx
361         movq    8(%r13), %rdx
362         subq    24(%rsp), %rcx
363         subq    %rax, %rdx
364         jns     20f
365         addq    $1000000000, %rdx
366         decq    %rcx
367 20:     testq   %rcx, %rcx
368         movq    8(%rsp), %rdi
369         movq    $-ETIMEDOUT, %r14
370         js      6b
371         jmp     21b
372 #endif
373 .LENDCODE:
374         .size   __pthread_cond_timedwait, .-__pthread_cond_timedwait
375 versioned_symbol (libpthread, __pthread_cond_timedwait, pthread_cond_timedwait,
376                   GLIBC_2_3_2)
379         .section .eh_frame,"a",@progbits
380 .LSTARTFRAME:
381         .long   L(ENDCIE)-L(STARTCIE)           # Length of the CIE.
382 .LSTARTCIE:
383         .long   0                               # CIE ID.
384         .byte   1                               # Version number.
385 #ifdef SHARED
386         .string "zR"                            # NUL-terminated augmentation
387                                                 # string.
388 #else
389         .ascii  "\0"                            # NUL-terminated augmentation
390                                                 # string.
391 #endif
392         .uleb128 1                              # Code alignment factor.
393         .sleb128 -8                             # Data alignment factor.
394         .byte   16                              # Return address register
395                                                 # column.
396 #ifdef SHARED
397         .uleb128 1                              # Augmentation value length.
398         .byte   0x1b                            # Encoding: DW_EH_PE_pcrel
399                                                 # + DW_EH_PE_sdata4.
400 #endif
401         .byte 0x0c                              # DW_CFA_def_cfa
402         .uleb128 7
403         .uleb128 8
404         .byte   0x90                            # DW_CFA_offset, column 0x8
405         .uleb128 1
406         .align 8
407 .LENDCIE:
409         .long   .LENDFDE-.LSTARTFDE             # Length of the FDE.
410 .LSTARTFDE:
411         .long   .LSTARTFDE-.LSTARTFRAME         # CIE pointer.
412 #ifdef SHARED
413         .long   .LSTARTCODE-.                   # PC-relative start address
414                                                 # of the code
415 #else
416         .long   .LSTARTCODE                     # Start address of the code.
417 #endif
418         .long   .LENDCODE-.LSTARTCODE           # Length of the code.
419 #ifdef SHARED
420         .uleb128 0                              # No augmentation data.
421 #endif
422         .byte   0x40+.Lpush_r12-.LSTARTCODE     # DW_CFA_advance_loc+N
423         .byte   14                              # DW_CFA_def_cfa_offset
424         .uleb128 16
425         .byte   0x8c                            # DW_CFA_offset %r12
426         .uleb128 2
427         .byte   0x40+.Lpush_r13-.Lpush_r12      # DW_CFA_advance_loc+N
428         .byte   14                              # DW_CFA_def_cfa_offset
429         .uleb128 24
430         .byte   0x8d                            # DW_CFA_offset %r13
431         .uleb128 3
432         .byte   0x40+.Lpush_r14-.Lpush_r13      # DW_CFA_advance_loc+N
433         .byte   14                              # DW_CFA_def_cfa_offset
434         .uleb128 32
435         .byte   0x84                            # DW_CFA_offset %r14
436         .uleb128 4
437         .byte   0x40+.Lsubq-.Lpush_r14          # DW_CFA_advance_loc+N
438         .byte   14                              # DW_CFA_def_cfa_offset
439         .uleb128 32+FRAME_SIZE
440         .byte   3                               # DW_CFA_advance_loc2
441         .2byte  .Laddq-.Lsubq
442         .byte   14                              # DW_CFA_def_cfa_offset
443         .uleb128 32
444         .byte   0x40+.Lpop_r14-.Laddq           # DW_CFA_advance_loc+N
445         .byte   14                              # DW_CFA_def_cfa_offset
446         .uleb128 24
447         .byte   0xce                            # DW_CFA_restore %r14
448         .byte   0x40+.Lpop_r13-.Lpop_r14        # DW_CFA_advance_loc+N
449         .byte   14                              # DW_CFA_def_cfa_offset
450         .uleb128 16
451         .byte   0xcd                            # DW_CFA_restore %r13
452         .byte   0x40+.Lpop_r12-.Lpop_r13        # DW_CFA_advance_loc+N
453         .byte   14                              # DW_CFA_def_cfa_offset
454         .uleb128 8
455         .byte   0xcc                            # DW_CFA_restore %r12
456         .byte   0x40+.LSbl1-.Lpop_r12           # DW_CFA_advance_loc+N
457         .byte   14                              # DW_CFA_def_cfa_offset
458         .uleb128 32+FRAME_SIZE
459         .byte   0x8c                            # DW_CFA_offset %r12
460         .uleb128 2
461         .byte   0x8d                            # DW_CFA_offset %r13
462         .uleb128 3
463         .byte   0x84                            # DW_CFA_offset %r14
464         .uleb128 4
465         .align  8
466 .LENDFDE: