pthread_lock.c revision 1.20.2.1 1 /* $NetBSD: pthread_lock.c,v 1.20.2.1 2007/07/18 13:36:19 skrll Exp $ */
2
3 /*-
4 * Copyright (c) 2001, 2006, 2007 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Nathan J. Williams and Andrew Doran.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #include <sys/cdefs.h>
40 __RCSID("$NetBSD: pthread_lock.c,v 1.20.2.1 2007/07/18 13:36:19 skrll Exp $");
41
42 #include <sys/types.h>
43 #include <sys/lock.h>
44 #include <sys/ras.h>
45
46 #include <errno.h>
47 #include <unistd.h>
48 #include <stdio.h>
49
50 #include "pthread.h"
51 #include "pthread_int.h"
52
53 #ifdef PTHREAD_SPIN_DEBUG_PRINT
54 #define SDPRINTF(x) DPRINTF(x)
55 #else
56 #define SDPRINTF(x)
57 #endif
58
59 /* This does not belong here. */
60 #if defined(i386) || defined(__x86_64__)
61 #define smt_pause() __asm __volatile("rep; nop" ::: "memory")
62 #else
63 #define smt_pause() /* nothing */
64 #endif
65
66 extern int pthread__nspins;
67 static int pthread__atomic;
68
69 RAS_DECL(pthread__lock);
70
71 void
72 pthread__simple_lock_init(__cpu_simple_lock_t *alp)
73 {
74
75 if (pthread__atomic) {
76 __cpu_simple_lock_init(alp);
77 return;
78 }
79
80 __cpu_simple_lock_clear(alp);
81 }
82
83 int
84 pthread__simple_lock_try(__cpu_simple_lock_t *alp)
85 {
86 __cpu_simple_lock_t old;
87 __cpu_simple_lock_t locked = __SIMPLELOCK_LOCKED;
88
89 if (pthread__atomic)
90 return __cpu_simple_lock_try(alp);
91
92 RAS_START(pthread__lock);
93 old = *alp;
94 *alp = locked;
95 RAS_END(pthread__lock);
96
97 return __SIMPLELOCK_UNLOCKED_P(&old);
98 }
99
100 inline void
101 pthread__simple_unlock(__cpu_simple_lock_t *alp)
102 {
103
104 if (pthread__atomic) {
105 __cpu_simple_unlock(alp);
106 return;
107 }
108
109 __cpu_simple_lock_clear(alp);
110 }
111
112 /*
113 * Initialize the locking primitives. On uniprocessors, we always
114 * use Restartable Atomic Sequences if they are available. Otherwise,
115 * we fall back onto machine-dependent atomic lock primitives.
116 */
117 void
118 pthread__lockprim_init(int ncpu)
119 {
120
121 if (ncpu != 1) {
122 pthread__atomic = 1;
123 return;
124 }
125
126 if (rasctl(RAS_ADDR(pthread__lock), RAS_SIZE(pthread__lock),
127 RAS_INSTALL) != 0) {
128 pthread__atomic = 1;
129 return;
130 }
131 }
132
133 void
134 pthread_lockinit(pthread_spin_t *lock)
135 {
136
137 pthread__simple_lock_init(lock);
138 }
139
140 void
141 pthread_spinlock(pthread_t thread, pthread_spin_t *lock)
142 {
143 int count, ret;
144
145 count = pthread__nspins;
146 SDPRINTF(("(pthread_spinlock %p) spinlock %p (count %d)\n",
147 thread, lock, thread->pt_spinlocks));
148 #ifdef PTHREAD_SPIN_DEBUG
149 pthread__assert(thread->pt_spinlocks >= 0);
150 #endif
151
152 thread->pt_spinlocks++;
153 if (__predict_true(pthread__simple_lock_try(lock))) {
154 PTHREADD_ADD(PTHREADD_SPINLOCKS);
155 return;
156 }
157
158 do {
159 while ((ret = pthread__simple_lock_try(lock)) == 0 &&
160 --count) {
161 smt_pause();
162 }
163
164 if (ret == 1)
165 break;
166
167 SDPRINTF(("(pthread_spinlock %p) retrying spinlock %p "
168 "(count %d)\n", thread, lock,
169 thread->pt_spinlocks));
170 thread->pt_spinlocks--;
171
172 /* XXXLWP far from ideal */
173 sched_yield();
174 count = pthread__nspins;
175 thread->pt_spinlocks++;
176 } while (/*CONSTCOND*/ 1);
177
178 PTHREADD_ADD(PTHREADD_SPINLOCKS);
179 }
180
181 int
182 pthread_spintrylock(pthread_t thread, pthread_spin_t *lock)
183 {
184 int ret;
185
186 SDPRINTF(("(pthread_spintrylock %p) spinlock %p (count %d)\n",
187 thread, lock, thread->pt_spinlocks));
188
189 thread->pt_spinlocks++;
190 ret = pthread__simple_lock_try(lock);
191 if (!ret)
192 thread->pt_spinlocks--;
193
194 return ret;
195 }
196
197 void
198 pthread_spinunlock(pthread_t thread, pthread_spin_t *lock)
199 {
200
201 SDPRINTF(("(pthread_spinunlock %p) spinlock %p (count %d)\n",
202 thread, lock, thread->pt_spinlocks));
203
204 pthread__simple_unlock(lock);
205 thread->pt_spinlocks--;
206 #ifdef PTHREAD_SPIN_DEBUG
207 pthread__assert(thread->pt_spinlocks >= 0);
208 #endif
209 PTHREADD_ADD(PTHREADD_SPINUNLOCKS);
210 }
211
212
213 /*
214 * Public (POSIX-specified) spinlocks.
215 */
216 int
217 pthread_spin_init(pthread_spinlock_t *lock, int pshared)
218 {
219
220 #ifdef ERRORCHECK
221 if (lock == NULL || (pshared != PTHREAD_PROCESS_PRIVATE &&
222 pshared != PTHREAD_PROCESS_SHARED))
223 return EINVAL;
224 #endif
225 lock->pts_magic = _PT_SPINLOCK_MAGIC;
226
227 /*
228 * We don't actually use the pshared flag for anything;
229 * CPU simple locks have all the process-shared properties
230 * that we want anyway.
231 */
232 lock->pts_flags = pshared;
233 pthread_lockinit(&lock->pts_spin);
234
235 return 0;
236 }
237
238 int
239 pthread_spin_destroy(pthread_spinlock_t *lock)
240 {
241
242 #ifdef ERRORCHECK
243 if (lock == NULL || lock->pts_magic != _PT_SPINLOCK_MAGIC)
244 return EINVAL;
245 if (!__SIMPLELOCK_UNLOCKED_P(&lock->pts_spin))
246 return EBUSY;
247 #endif
248
249 lock->pts_magic = _PT_SPINLOCK_DEAD;
250
251 return 0;
252 }
253
254 int
255 pthread_spin_lock(pthread_spinlock_t *lock)
256 {
257
258 #ifdef ERRORCHECK
259 if (lock == NULL || lock->pts_magic != _PT_SPINLOCK_MAGIC)
260 return EINVAL;
261 #endif
262
263 while (pthread__simple_lock_try(&lock->pts_spin) == 0) {
264 smt_pause();
265 }
266
267 return 0;
268 }
269
270 int
271 pthread_spin_trylock(pthread_spinlock_t *lock)
272 {
273
274 #ifdef ERRORCHECK
275 if (lock == NULL || lock->pts_magic != _PT_SPINLOCK_MAGIC)
276 return EINVAL;
277 #endif
278
279 if (pthread__simple_lock_try(&lock->pts_spin) == 0)
280 return EBUSY;
281
282 return 0;
283 }
284
285 int
286 pthread_spin_unlock(pthread_spinlock_t *lock)
287 {
288
289 #ifdef ERRORCHECK
290 if (lock == NULL || lock->pts_magic != _PT_SPINLOCK_MAGIC)
291 return EINVAL;
292 #endif
293
294 pthread__simple_unlock(&lock->pts_spin);
295
296 return 0;
297 }
298