703b72d65ebe7b9b494a3e26dafb5fb93bbcdbdb
[kopensolaris-gnu/glibc.git] / linuxthreads / spinlock.h
1 /* Linuxthreads - a simple clone()-based implementation of Posix        */
2 /* threads for Linux.                                                   */
3 /* Copyright (C) 1998 Xavier Leroy (Xavier.Leroy@inria.fr)              */
4 /*                                                                      */
5 /* This program is free software; you can redistribute it and/or        */
6 /* modify it under the terms of the GNU Library General Public License  */
7 /* as published by the Free Software Foundation; either version 2       */
8 /* of the License, or (at your option) any later version.               */
9 /*                                                                      */
10 /* This program 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        */
13 /* GNU Library General Public License for more details.                 */
14
15
16 /* There are 2 compare and swap synchronization primitives with
17    different semantics:
18
19         1. compare_and_swap, which has acquire semantics (i.e. it
20         completes befor subsequent writes.)
21         2. compare_and_swap_with_release_semantics, which has release
22         semantics (it completes after previous writes.)
23
24    For those platforms on which they are the same. HAS_COMPARE_AND_SWAP
25    should be defined. For those platforms on which they are different,
26    HAS_COMPARE_AND_SWAP_WITH_RELEASE_SEMANTICS has to be defined.  */
27
28 #ifndef HAS_COMPARE_AND_SWAP
29 #ifdef HAS_COMPARE_AND_SWAP_WITH_RELEASE_SEMANTICS
30 #define HAS_COMPARE_AND_SWAP
31 #endif
32 #endif
33
34 #if defined(TEST_FOR_COMPARE_AND_SWAP)
35
36 extern int __pthread_has_cas;
37 extern int __pthread_compare_and_swap(long * ptr, long oldval, long newval,
38                                       int * spinlock);
39
40 static inline int compare_and_swap(long * ptr, long oldval, long newval,
41                                    int * spinlock)
42 {
43   if (__builtin_expect (__pthread_has_cas, 1))
44     return __compare_and_swap(ptr, oldval, newval);
45   else
46     return __pthread_compare_and_swap(ptr, oldval, newval, spinlock);
47 }
48
49 #elif defined(HAS_COMPARE_AND_SWAP)
50
51 #ifdef HAS_COMPARE_AND_SWAP_WITH_RELEASE_SEMANTICS
52
53 static inline int
54 compare_and_swap_with_release_semantics (long * ptr, long oldval,
55                                          long newval, int * spinlock)
56 {
57   return __compare_and_swap_with_release_semantics (ptr, oldval,
58                                                     newval);
59 }
60
61 #endif
62
63 static inline int compare_and_swap(long * ptr, long oldval, long newval,
64                                    int * spinlock)
65 {
66   return __compare_and_swap(ptr, oldval, newval);
67 }
68
69 #else
70
71 extern int __pthread_compare_and_swap(long * ptr, long oldval, long newval,
72                                       int * spinlock);
73
74 static inline int compare_and_swap(long * ptr, long oldval, long newval,
75                                    int * spinlock)
76 {
77   return __pthread_compare_and_swap(ptr, oldval, newval, spinlock);
78 }
79
80 #endif
81
82 #ifndef HAS_COMPARE_AND_SWAP_WITH_RELEASE_SEMANTICS
83 #define compare_and_swap_with_release_semantics compare_and_swap
84 #define __compare_and_swap_with_release_semantics __compare_and_swap
85 #endif
86
87 /* Internal locks */
88
89 extern void internal_function __pthread_lock(struct _pthread_fastlock * lock,
90                                              pthread_descr self);
91 extern int __pthread_unlock(struct _pthread_fastlock *lock);
92
93 static inline void __pthread_init_lock(struct _pthread_fastlock * lock)
94 {
95   lock->__status = 0;
96   lock->__spinlock = 0;
97 }
98
99 static inline int __pthread_trylock (struct _pthread_fastlock * lock)
100 {
101 #if defined HAS_COMPARE_AND_SWAP
102   long oldstatus;
103 #endif
104
105 #if defined TEST_FOR_COMPARE_AND_SWAP
106   if (!__pthread_has_cas)
107 #endif
108 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
109   {
110     return (testandset(&lock->__spinlock) ? EBUSY : 0);
111   }
112 #endif
113
114 #if defined HAS_COMPARE_AND_SWAP
115   do {
116     oldstatus = lock->__status;
117     if (oldstatus != 0) return EBUSY;
118   } while(! __compare_and_swap(&lock->__status, 0, 1));
119   return 0;
120 #endif
121 }
122
123 /* Variation of internal lock used for pthread_mutex_t, supporting
124    timed-out waits.  Warning: do not mix these operations with the above ones
125    over the same lock object! */
126
127 extern void __pthread_alt_lock(struct _pthread_fastlock * lock,
128                                pthread_descr self);
129
130 extern int __pthread_alt_timedlock(struct _pthread_fastlock * lock,
131                                pthread_descr self, const struct timespec *abstime);
132
133 extern void __pthread_alt_unlock(struct _pthread_fastlock *lock);
134
135 static inline void __pthread_alt_init_lock(struct _pthread_fastlock * lock)
136 {
137   lock->__status = 0;
138   lock->__spinlock = 0;
139 }
140
141 static inline int __pthread_alt_trylock (struct _pthread_fastlock * lock)
142 {
143 #if defined HAS_COMPARE_AND_SWAP
144   long oldstatus;
145 #endif
146
147 #if defined TEST_FOR_COMPARE_AND_SWAP
148   if (!__pthread_has_cas)
149 #endif
150 #if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
151   {
152     int res = EBUSY;
153
154     if (testandset(&lock->__spinlock) == 0)
155       {
156         if (lock->__status == 0)
157           {
158             lock->__status = 1;
159             WRITE_MEMORY_BARRIER();
160             res = 0;
161           }
162         lock->__spinlock = 0;
163       }
164     return res;
165   }
166 #endif
167
168 #if defined HAS_COMPARE_AND_SWAP
169   do {
170     oldstatus = lock->__status;
171     if (oldstatus != 0) return EBUSY;
172   } while(! compare_and_swap(&lock->__status, 0, 1, &lock->__spinlock));
173   return 0;
174 #endif
175 }
176
177 /* Initializers for both lock variants */
178
179 #define LOCK_INITIALIZER {0, 0}
180 #define ALT_LOCK_INITIALIZER {0, 0}
181
182 /* Operations on pthread_atomic, which is defined in internals.h */
183
184 static inline long atomic_increment(struct pthread_atomic *pa)
185 {
186     long oldval;
187
188     do {
189         oldval = pa->p_count;
190     } while (!compare_and_swap(&pa->p_count, oldval, oldval + 1, &pa->p_spinlock));
191
192     return oldval;
193 }
194
195
196 static inline long atomic_decrement(struct pthread_atomic *pa)
197 {
198     long oldval;
199
200     do {
201         oldval = pa->p_count;
202     } while (!compare_and_swap(&pa->p_count, oldval, oldval - 1, &pa->p_spinlock));
203
204     return oldval;
205 }
206
207 #define ATOMIC_INITIALIZER { 0, 0 }