a9823d8d4b57ea0f6d5d6f87fefbb2787f76c1a3
[kopensolaris-gnu/glibc.git] / nptl / sysdeps / unix / sysv / linux / i386 / lowlevellock.h
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.
4
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.
9
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.
14
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.  */
19
20 #ifndef _LOWLEVELLOCK_H
21 #define _LOWLEVELLOCK_H 1
22
23 #include <time.h>
24 #include <sys/param.h>
25 #include <bits/pthreadtypes.h>
26
27 #ifndef LOCK_INSTR
28 # ifdef UP
29 #  define LOCK_INSTR    /* nothing */
30 # else
31 #  define LOCK_INSTR "lock;"
32 # endif
33 #endif
34
35 #define SYS_futex               240
36 #define FUTEX_WAIT              0
37 #define FUTEX_WAKE              1
38
39
40 /* Initializer for compatibility lock.  */
41 #define LLL_MUTEX_LOCK_INITIALIZER              (0)
42 #define LLL_MUTEX_LOCK_INITIALIZER_LOCKED       (1)
43
44
45 #ifdef PIC
46 # define LLL_EBX_LOAD   "xchgl %2, %%ebx\n"
47 # define LLL_EBX_REG    "D"
48 #else
49 # define LLL_EBX_LOAD
50 # define LLL_EBX_REG    "b"
51 #endif
52
53 #ifdef I386_USE_SYSENTER
54 # ifdef SHARED
55 #  define LLL_ENTER_KERNEL      "call *%%gs:%P6\n\t"
56 # else
57 #  define LLL_ENTER_KERNEL      "call *_dl_sysinfo\n\t"
58 # endif
59 #else
60 # define LLL_ENTER_KERNEL       "int $0x80\n\t"
61 #endif
62
63
64 #define lll_futex_wait(futex, val) \
65   do {                                                                        \
66     int __ignore;                                                             \
67     register __typeof (val) _val asm ("edx") = (val);                         \
68     __asm __volatile (LLL_EBX_LOAD                                            \
69                       LLL_ENTER_KERNEL                                        \
70                       LLL_EBX_LOAD                                            \
71                       : "=a" (__ignore)                                       \
72                       : "0" (SYS_futex), LLL_EBX_REG (futex), "S" (0),        \
73                         "c" (FUTEX_WAIT), "d" (_val),                         \
74                         "i" (offsetof (tcbhead_t, sysinfo)));                 \
75   } while (0)
76
77
78 #define lll_futex_wake(futex, nr) \
79   do {                                                                        \
80     int __ignore;                                                             \
81     register __typeof (nr) _nr asm ("edx") = (nr);                            \
82     __asm __volatile (LLL_EBX_LOAD                                            \
83                       LLL_ENTER_KERNEL                                        \
84                       LLL_EBX_LOAD                                            \
85                       : "=a" (__ignore)                                       \
86                       : "0" (SYS_futex), LLL_EBX_REG (futex),                 \
87                         "c" (FUTEX_WAKE), "d" (_nr),                          \
88                         "i" (0) /* phony, to align next arg's number */,      \
89                         "i" (offsetof (tcbhead_t, sysinfo)));                 \
90   } while (0)
91
92
93 /* Does not preserve %eax and %ecx.  */
94 extern int __lll_mutex_lock_wait (int val, int *__futex)
95      __attribute ((regparm (2))) attribute_hidden;
96 /* Does not preserve %eax, %ecx, and %edx.  */
97 extern int __lll_mutex_timedlock_wait (int val, int *__futex,
98                                        const struct timespec *abstime)
99      __attribute ((regparm (3))) attribute_hidden;
100 /* Preserves all registers but %eax.  */
101 extern int __lll_mutex_unlock_wake (int *__futex)
102      __attribute ((regparm (1))) attribute_hidden;
103
104
105 /* NB: in the lll_mutex_trylock macro we simply return the value in %eax
106    after the cmpxchg instruction.  In case the operation succeded this
107    value is zero.  In case the operation failed, the cmpxchg instruction
108    has loaded the current value of the memory work which is guaranteed
109    to be nonzero.  */
110 #define lll_mutex_trylock(futex) \
111   ({ int ret;                                                                 \
112      __asm __volatile (LOCK_INSTR "cmpxchgl %2, %1"                           \
113                        : "=a" (ret), "=m" (futex)                             \
114                        : "r" (LLL_MUTEX_LOCK_INITIALIZER_LOCKED), "m" (futex),\
115                          "0" (LLL_MUTEX_LOCK_INITIALIZER)                     \
116                        : "memory");                                           \
117      ret; })
118
119
120 #define lll_mutex_lock(futex) \
121   (void) ({ int ignore1, ignore2;                                             \
122             __asm __volatile (LOCK_INSTR "cmpxchgl %1, %2\n\t"                \
123                               "jnz _L_mutex_lock_%=\n\t"                      \
124                               ".subsection 1\n\t"                             \
125                               ".type _L_mutex_lock_%=,@function\n"            \
126                               "_L_mutex_lock_%=:\n\t"                         \
127                               "leal %2, %%ecx\n\t"                            \
128                               "call __lll_mutex_lock_wait\n\t"                \
129                               "jmp 1f\n\t"                                    \
130                               ".size _L_mutex_lock_%=,.-_L_mutex_lock_%=\n"   \
131                               ".previous\n"                                   \
132                               "1:"                                            \
133                               : "=a" (ignore1), "=c" (ignore2), "=m" (futex)  \
134                               : "0" (0), "1" (1), "m" (futex)                 \
135                               : "memory"); })
136
137
138 /* Special version of lll_mutex_lock which causes the unlock function to
139    always wakeup waiters.  */
140 #define lll_mutex_cond_lock(futex) \
141   (void) ({ int ignore1, ignore2;                                             \
142             __asm __volatile (LOCK_INSTR "cmpxchgl %1, %2\n\t"                \
143                               "jnz _L_mutex_cond_lock_%=\n\t"                 \
144                               ".subsection 1\n\t"                             \
145                               ".type _L_mutex_cond_lock_%=,@function\n"       \
146                               "_L_mutex_cond_lock_%=:\n\t"                    \
147                               "leal %2, %%ecx\n\t"                            \
148                               "call __lll_mutex_lock_wait\n\t"                \
149                               "jmp 1f\n\t"                                    \
150                               ".size _L_mutex_cond_lock_%=,.-_L_mutex_cond_lock_%=\n"   \
151                               ".previous\n"                                   \
152                               "1:"                                            \
153                               : "=a" (ignore1), "=c" (ignore2), "=m" (futex)  \
154                               : "0" (0), "1" (2), "m" (futex)                 \
155                               : "memory"); })
156
157
158 #define lll_mutex_timedlock(futex, timeout) \
159   ({ int result, ignore1, ignore2;                                            \
160      __asm __volatile (LOCK_INSTR "cmpxchgl %1, %3\n\t"                       \
161                        "jnz _L_mutex_timedlock_%=\n\t"                        \
162                        ".subsection 1\n\t"                                    \
163                        ".type _L_mutex_timedlock_%=,@function\n"              \
164                        "_L_mutex_timedlock_%=:\n\t"                           \
165                        "leal %3, %%ecx\n\t"                                   \
166                        "movl %7, %%edx\n\t"                                   \
167                        "call __lll_mutex_timedlock_wait\n\t"                  \
168                        "jmp 1f\n\t"                                           \
169                        ".size _L_mutex_timedlock_%=,.-_L_mutex_timedlock_%=\n"\
170                        ".previous\n"                                          \
171                        "1:"                                                   \
172                        : "=a" (result), "=c" (ignore1), "=&d" (ignore2),      \
173                          "=m" (futex)                                         \
174                        : "0" (0), "1" (1), "m" (futex), "m" (timeout)         \
175                        : "memory");                                           \
176      result; })
177
178
179 #define lll_mutex_unlock(futex) \
180   (void) ({ int ignore;                                                       \
181             __asm __volatile (LOCK_INSTR "subl $1,%0\n\t"                     \
182                               "jne _L_mutex_unlock_%=\n\t"                    \
183                               ".subsection 1\n\t"                             \
184                               ".type _L_mutex_unlock_%=,@function\n"          \
185                               "_L_mutex_unlock_%=:\n\t"                       \
186                               "leal %0, %%eax\n\t"                            \
187                               "call __lll_mutex_unlock_wake\n\t"              \
188                               "jmp 1f\n\t"                                    \
189                               ".size _L_mutex_unlock_%=,.-_L_mutex_unlock_%=\n" \
190                               ".previous\n"                                   \
191                               "1:"                                            \
192                               : "=m" (futex), "=&a" (ignore)                  \
193                               : "m" (futex)                                   \
194                               : "memory"); })
195
196
197 #define lll_mutex_islocked(futex) \
198   (futex != 0)
199
200
201 /* We have a separate internal lock implementation which is not tied
202    to binary compatibility.  */
203
204 /* Type for lock object.  */
205 typedef int lll_lock_t;
206
207 /* Initializers for lock.  */
208 #define LLL_LOCK_INITIALIZER            (0)
209 #define LLL_LOCK_INITIALIZER_LOCKED     (1)
210
211
212 extern int __lll_lock_wait (int val, int *__futex)
213      __attribute ((regparm (2))) attribute_hidden;
214 extern int __lll_unlock_wake (int *__futex)
215      __attribute ((regparm (1))) attribute_hidden;
216 extern int lll_unlock_wake_cb (int *__futex) attribute_hidden;
217
218
219 /* The states of a lock are:
220     0  -  untaken
221     1  -  taken by one user
222     2  -  taken by more users */
223
224
225 #if defined NOT_IN_libc || defined UP
226 # define lll_trylock(futex) lll_mutex_trylock (futex)
227 # define lll_lock(futex) lll_mutex_lock (futex)
228 # define lll_unlock(futex) lll_mutex_unlock (futex)
229 #else
230 /* Special versions of the macros for use in libc itself.  They avoid
231    the lock prefix when the thread library is not used.
232
233    XXX In future we might even want to avoid it on UP machines.  */
234 # include <tls.h>
235
236 # define lll_trylock(futex) \
237   ({ unsigned char ret;                                                       \
238      __asm __volatile ("cmpl $0, %%gs:%P5\n\t"                                \
239                        "je,pt 0f\n\t"                                         \
240                        "lock\n"                                               \
241                        "0:\tcmpxchgl %2, %1; setne %0"                        \
242                        : "=a" (ret), "=m" (futex)                             \
243                        : "r" (LLL_MUTEX_LOCK_INITIALIZER_LOCKED), "m" (futex),\
244                          "0" (LLL_MUTEX_LOCK_INITIALIZER),                    \
245                          "i" (offsetof (tcbhead_t, multiple_threads))         \
246                        : "memory");                                           \
247      ret; })
248
249
250 # define lll_lock(futex) \
251   (void) ({ int ignore1, ignore2;                                             \
252             __asm __volatile ("cmpl $0, %%gs:%P6\n\t"                         \
253                               "je,pt 0f\n\t"                                  \
254                               "lock\n"                                        \
255                               "0:\tcmpxchgl %1, %2\n\t"                       \
256                               "jnz _L_mutex_lock_%=\n\t"                      \
257                               ".subsection 1\n\t"                             \
258                               ".type _L_mutex_lock_%=,@function\n"            \
259                               "_L_mutex_lock_%=:\n\t"                         \
260                               "leal %2, %%ecx\n\t"                            \
261                               "call __lll_mutex_lock_wait\n\t"                \
262                               "jmp 1f\n\t"                                    \
263                               ".size _L_mutex_lock_%=,.-_L_mutex_lock_%=\n"   \
264                               ".previous\n"                                   \
265                               "1:"                                            \
266                               : "=a" (ignore1), "=c" (ignore2), "=m" (futex)  \
267                               : "0" (0), "1" (1), "m" (futex),                \
268                                 "i" (offsetof (tcbhead_t, multiple_threads))  \
269                               : "memory"); })
270
271
272 # define lll_unlock(futex) \
273   (void) ({ int ignore;                                                       \
274             __asm __volatile ("cmpl $0, %%gs:%P3\n\t"                         \
275                               "je,pt 0f\n\t"                                  \
276                               "lock\n"                                        \
277                               "0:\tsubl $1,%0\n\t"                    \
278                               "jne _L_mutex_unlock_%=\n\t"                    \
279                               ".subsection 1\n\t"                             \
280                               ".type _L_mutex_unlock_%=,@function\n"          \
281                               "_L_mutex_unlock_%=:\n\t"                       \
282                               "leal %0, %%eax\n\t"                            \
283                               "call __lll_mutex_unlock_wake\n\t"              \
284                               "jmp 1f\n\t"                                    \
285                               ".size _L_mutex_unlock_%=,.-_L_mutex_unlock_%=\n" \
286                               ".previous\n"                                   \
287                               "1:"                                            \
288                               : "=m" (futex), "=&a" (ignore)                  \
289                               : "m" (futex),                                  \
290                                 "i" (offsetof (tcbhead_t, multiple_threads))  \
291                               : "memory"); })
292 #endif
293
294
295 #define lll_islocked(futex) \
296   (futex != LLL_LOCK_INITIALIZER)
297
298
299 /* The kernel notifies a process with uses CLONE_CLEARTID via futex
300    wakeup when the clone terminates.  The memory location contains the
301    thread ID while the clone is running and is reset to zero
302    afterwards.
303
304    The macro parameter must not have any side effect.  */
305 #define lll_wait_tid(tid) \
306   do {                                                                        \
307     int __ignore;                                                             \
308     register __typeof (tid) _tid asm ("edx") = (tid);                         \
309     if (_tid != 0)                                                            \
310       __asm __volatile (LLL_EBX_LOAD                                          \
311                         "1:\tmovl %1, %%eax\n\t"                              \
312                         LLL_ENTER_KERNEL                                      \
313                         "cmpl $0, (%%ebx)\n\t"                                \
314                         "jne,pn 1b\n\t"                                       \
315                         LLL_EBX_LOAD                                          \
316                         : "=&a" (__ignore)                                    \
317                         : "i" (SYS_futex), LLL_EBX_REG (&tid), "S" (0),       \
318                           "c" (FUTEX_WAIT), "d" (_tid),                       \
319                           "i" (offsetof (tcbhead_t, sysinfo)));               \
320   } while (0)
321
322 extern int __lll_timedwait_tid (int *tid, const struct timespec *abstime)
323      __attribute__ ((regparm (2))) attribute_hidden;
324 #define lll_timedwait_tid(tid, abstime) \
325   ({                                                                          \
326     int __result = 0;                                                         \
327     if (tid != 0)                                                             \
328       {                                                                       \
329         if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)           \
330           __result = EINVAL;                                                  \
331         else                                                                  \
332           __result = __lll_timedwait_tid (&tid, abstime);                     \
333       }                                                                       \
334     __result; })
335
336
337 /* Conditional variable handling.  */
338
339 extern void __lll_cond_wait (pthread_cond_t *cond)
340      __attribute ((regparm (1))) attribute_hidden;
341 extern int __lll_cond_timedwait (pthread_cond_t *cond,
342                                  const struct timespec *abstime)
343      __attribute ((regparm (2))) attribute_hidden;
344 extern void __lll_cond_wake (pthread_cond_t *cond)
345      __attribute ((regparm (1))) attribute_hidden;
346 extern void __lll_cond_broadcast (pthread_cond_t *cond)
347      __attribute ((regparm (1))) attribute_hidden;
348
349
350 #define lll_cond_wait(cond) \
351   __lll_cond_wait (cond)
352 #define lll_cond_timedwait(cond, abstime) \
353   __lll_cond_timedwait (cond, abstime)
354 #define lll_cond_wake(cond) \
355   __lll_cond_wake (cond)
356 #define lll_cond_broadcast(cond) \
357   __lll_cond_broadcast (cond)
358
359
360 #endif  /* lowlevellock.h */