]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - lib/libthr/thread/thr_umtx.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / lib / libthr / thread / thr_umtx.c
1 /*
2  * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice unmodified, this list of conditions, and the following
10  *    disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  *
28  */
29
30 #include "thr_private.h"
31 #include "thr_umtx.h"
32
33 #ifndef HAS__UMTX_OP_ERR
34 int _umtx_op_err(void *obj, int op, u_long val, void *uaddr, void *uaddr2)
35 {
36         if (_umtx_op(obj, op, val, uaddr, uaddr2) == -1)
37                 return (errno);
38         return (0);
39 }
40 #endif
41
42 void
43 _thr_umutex_init(struct umutex *mtx)
44 {
45         static struct umutex default_mtx = DEFAULT_UMUTEX;
46
47         *mtx = default_mtx;
48 }
49
50 void
51 _thr_urwlock_init(struct urwlock *rwl)
52 {
53         static struct urwlock default_rwl = DEFAULT_URWLOCK;
54         *rwl = default_rwl;
55 }
56
57 int
58 __thr_umutex_lock(struct umutex *mtx, uint32_t id)
59 {
60         uint32_t owner;
61
62         if ((mtx->m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0) {
63                 for (;;) {
64                         /* wait in kernel */
65                         _umtx_op_err(mtx, UMTX_OP_MUTEX_WAIT, 0, 0, 0);
66
67                         owner = mtx->m_owner;
68                         if ((owner & ~UMUTEX_CONTESTED) == 0 &&
69                              atomic_cmpset_acq_32(&mtx->m_owner, owner, id|owner))
70                                 return (0);
71                 }
72         }
73
74         return  _umtx_op_err(mtx, UMTX_OP_MUTEX_LOCK, 0, 0, 0);
75 }
76
77 #define SPINLOOPS 1000
78
79 int
80 __thr_umutex_lock_spin(struct umutex *mtx, uint32_t id)
81 {
82         uint32_t owner;
83
84         if (!_thr_is_smp)
85                 return __thr_umutex_lock(mtx, id);
86
87         if ((mtx->m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0) {
88                 for (;;) {
89                         int count = SPINLOOPS;
90                         while (count--) {
91                                 owner = mtx->m_owner;
92                                 if ((owner & ~UMUTEX_CONTESTED) == 0) {
93                                         if (atomic_cmpset_acq_32(
94                                             &mtx->m_owner,
95                                             owner, id|owner)) {
96                                                 return (0);
97                                         }
98                                 }
99                                 CPU_SPINWAIT;
100                         }
101
102                         /* wait in kernel */
103                         _umtx_op_err(mtx, UMTX_OP_MUTEX_WAIT, 0, 0, 0);
104                 }
105         }
106
107         return  _umtx_op_err(mtx, UMTX_OP_MUTEX_LOCK, 0, 0, 0);
108 }
109
110 int
111 __thr_umutex_timedlock(struct umutex *mtx, uint32_t id,
112         const struct timespec *abstime)
113 {
114         struct _umtx_time *tm_p, timeout;
115         size_t tm_size;
116         uint32_t owner;
117         int ret;
118
119         if (abstime == NULL) {
120                 tm_p = NULL;
121                 tm_size = 0;
122         } else {
123                 timeout._clockid = CLOCK_REALTIME;
124                 timeout._flags = UMTX_ABSTIME;
125                 timeout._timeout = *abstime;
126                 tm_p = &timeout;
127                 tm_size = sizeof(timeout);
128         }
129
130         for (;;) {
131                 if ((mtx->m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0) {
132
133                         /* wait in kernel */
134                         ret = _umtx_op_err(mtx, UMTX_OP_MUTEX_WAIT, 0,
135                                  (void *)tm_size, __DECONST(void *, tm_p));
136
137                         /* now try to lock it */
138                         owner = mtx->m_owner;
139                         if ((owner & ~UMUTEX_CONTESTED) == 0 &&
140                              atomic_cmpset_acq_32(&mtx->m_owner, owner, id|owner))
141                                 return (0);
142                 } else {
143                         ret = _umtx_op_err(mtx, UMTX_OP_MUTEX_LOCK, 0, 
144                                  (void *)tm_size, __DECONST(void *, tm_p));
145                         if (ret == 0)
146                                 break;
147                 }
148                 if (ret == ETIMEDOUT)
149                         break;
150         }
151         return (ret);
152 }
153
154 int
155 __thr_umutex_unlock(struct umutex *mtx, uint32_t id)
156 {
157         return _umtx_op_err(mtx, UMTX_OP_MUTEX_UNLOCK, 0, 0, 0);
158 }
159
160 int
161 __thr_umutex_trylock(struct umutex *mtx)
162 {
163         return _umtx_op_err(mtx, UMTX_OP_MUTEX_TRYLOCK, 0, 0, 0);
164 }
165
166 int
167 __thr_umutex_set_ceiling(struct umutex *mtx, uint32_t ceiling,
168         uint32_t *oldceiling)
169 {
170         return _umtx_op_err(mtx, UMTX_OP_SET_CEILING, ceiling, oldceiling, 0);
171 }
172
173 int
174 _thr_umtx_wait(volatile long *mtx, long id, const struct timespec *timeout)
175 {
176         if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 &&
177                 timeout->tv_nsec <= 0)))
178                 return (ETIMEDOUT);
179         return _umtx_op_err(__DEVOLATILE(void *, mtx), UMTX_OP_WAIT, id, 0,
180                 __DECONST(void*, timeout));
181 }
182
183 int
184 _thr_umtx_wait_uint(volatile u_int *mtx, u_int id, const struct timespec *timeout, int shared)
185 {
186         if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 &&
187                 timeout->tv_nsec <= 0)))
188                 return (ETIMEDOUT);
189         return _umtx_op_err(__DEVOLATILE(void *, mtx), 
190                         shared ? UMTX_OP_WAIT_UINT : UMTX_OP_WAIT_UINT_PRIVATE, id, 0,
191                         __DECONST(void*, timeout));
192 }
193
194 int
195 _thr_umtx_timedwait_uint(volatile u_int *mtx, u_int id, int clockid,
196         const struct timespec *abstime, int shared)
197 {
198         struct _umtx_time *tm_p, timeout;
199         size_t tm_size;
200
201         if (abstime == NULL) {
202                 tm_p = NULL;
203                 tm_size = 0;
204         } else {
205                 timeout._clockid = clockid;
206                 timeout._flags = UMTX_ABSTIME;
207                 timeout._timeout = *abstime;
208                 tm_p = &timeout;
209                 tm_size = sizeof(timeout);
210         }
211
212         return _umtx_op_err(__DEVOLATILE(void *, mtx), 
213                 shared ? UMTX_OP_WAIT_UINT : UMTX_OP_WAIT_UINT_PRIVATE, id, 
214                 (void *)tm_size, __DECONST(void *, tm_p));
215 }
216
217 int
218 _thr_umtx_wake(volatile void *mtx, int nr_wakeup, int shared)
219 {
220         return _umtx_op_err(__DEVOLATILE(void *, mtx), shared ? UMTX_OP_WAKE : UMTX_OP_WAKE_PRIVATE,
221                 nr_wakeup, 0, 0);
222 }
223
224 void
225 _thr_ucond_init(struct ucond *cv)
226 {
227         bzero(cv, sizeof(struct ucond));
228 }
229
230 int
231 _thr_ucond_wait(struct ucond *cv, struct umutex *m,
232         const struct timespec *timeout, int flags)
233 {
234         if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 &&
235             timeout->tv_nsec <= 0))) {
236                 struct pthread *curthread = _get_curthread();
237                 _thr_umutex_unlock(m, TID(curthread));
238                 return (ETIMEDOUT);
239         }
240         return _umtx_op_err(cv, UMTX_OP_CV_WAIT, flags,
241                      m, __DECONST(void*, timeout));
242 }
243  
244 int
245 _thr_ucond_signal(struct ucond *cv)
246 {
247         if (!cv->c_has_waiters)
248                 return (0);
249         return _umtx_op_err(cv, UMTX_OP_CV_SIGNAL, 0, NULL, NULL);
250 }
251
252 int
253 _thr_ucond_broadcast(struct ucond *cv)
254 {
255         if (!cv->c_has_waiters)
256                 return (0);
257         return _umtx_op_err(cv, UMTX_OP_CV_BROADCAST, 0, NULL, NULL);
258 }
259
260 int
261 __thr_rwlock_rdlock(struct urwlock *rwlock, int flags,
262         const struct timespec *tsp)
263 {
264         struct _umtx_time timeout, *tm_p;
265         size_t tm_size;
266
267         if (tsp == NULL) {
268                 tm_p = NULL;
269                 tm_size = 0;
270         } else {
271                 timeout._timeout = *tsp;
272                 timeout._flags = UMTX_ABSTIME;
273                 timeout._clockid = CLOCK_REALTIME;
274                 tm_p = &timeout;
275                 tm_size = sizeof(timeout);
276         }
277         return _umtx_op_err(rwlock, UMTX_OP_RW_RDLOCK, flags, (void *)tm_size, tm_p);
278 }
279
280 int
281 __thr_rwlock_wrlock(struct urwlock *rwlock, const struct timespec *tsp)
282 {
283         struct _umtx_time timeout, *tm_p;
284         size_t tm_size;
285
286         if (tsp == NULL) {
287                 tm_p = NULL;
288                 tm_size = 0;
289         } else {
290                 timeout._timeout = *tsp;
291                 timeout._flags = UMTX_ABSTIME;
292                 timeout._clockid = CLOCK_REALTIME;
293                 tm_p = &timeout;
294                 tm_size = sizeof(timeout);
295         }
296         return _umtx_op_err(rwlock, UMTX_OP_RW_WRLOCK, 0, (void *)tm_size, tm_p);
297 }
298
299 int
300 __thr_rwlock_unlock(struct urwlock *rwlock)
301 {
302         return _umtx_op_err(rwlock, UMTX_OP_RW_UNLOCK, 0, NULL, NULL);
303 }
304
305 void
306 _thr_rwl_rdlock(struct urwlock *rwlock)
307 {
308         int ret;
309
310         for (;;) {
311                 if (_thr_rwlock_tryrdlock(rwlock, URWLOCK_PREFER_READER) == 0)
312                         return;
313                 ret = __thr_rwlock_rdlock(rwlock, URWLOCK_PREFER_READER, NULL);
314                 if (ret == 0)
315                         return;
316                 if (ret != EINTR)
317                         PANIC("rdlock error");
318         }
319 }
320
321 void
322 _thr_rwl_wrlock(struct urwlock *rwlock)
323 {
324         int ret;
325
326         for (;;) {
327                 if (_thr_rwlock_trywrlock(rwlock) == 0)
328                         return;
329                 ret = __thr_rwlock_wrlock(rwlock, NULL);
330                 if (ret == 0)
331                         return;
332                 if (ret != EINTR)
333                         PANIC("wrlock error");
334         }
335 }
336
337 void
338 _thr_rwl_unlock(struct urwlock *rwlock)
339 {
340         if (_thr_rwlock_unlock(rwlock))
341                 PANIC("unlock error");
342 }