xref: /freebsd/lib/libthr/thread/thr_umtx.c (revision 02c3c85869f7ceee39a6e721b02df49714b49867)
1a091d823SDavid Xu /*
2a091d823SDavid Xu  * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
3a091d823SDavid Xu  * All rights reserved.
4a091d823SDavid Xu  *
5a091d823SDavid Xu  * Redistribution and use in source and binary forms, with or without
6a091d823SDavid Xu  * modification, are permitted provided that the following conditions
7a091d823SDavid Xu  * are met:
8a091d823SDavid Xu  * 1. Redistributions of source code must retain the above copyright
9a091d823SDavid Xu  *    notice unmodified, this list of conditions, and the following
10a091d823SDavid Xu  *    disclaimer.
11a091d823SDavid Xu  * 2. Redistributions in binary form must reproduce the above copyright
12a091d823SDavid Xu  *    notice, this list of conditions and the following disclaimer in the
13a091d823SDavid Xu  *    documentation and/or other materials provided with the distribution.
14a091d823SDavid Xu  *
15a091d823SDavid Xu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16a091d823SDavid Xu  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17a091d823SDavid Xu  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18a091d823SDavid Xu  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19a091d823SDavid Xu  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20a091d823SDavid Xu  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21a091d823SDavid Xu  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22a091d823SDavid Xu  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23a091d823SDavid Xu  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24a091d823SDavid Xu  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25a091d823SDavid Xu  *
26a091d823SDavid Xu  * $FreeBSD$
27a091d823SDavid Xu  *
28a091d823SDavid Xu  */
29a091d823SDavid Xu 
30a091d823SDavid Xu #include "thr_private.h"
31a091d823SDavid Xu #include "thr_umtx.h"
32a091d823SDavid Xu 
33d6e0eb0aSDavid Xu #ifndef HAS__UMTX_OP_ERR
34d6e0eb0aSDavid Xu int _umtx_op_err(void *obj, int op, u_long val, void *uaddr, void *uaddr2)
35d6e0eb0aSDavid Xu {
36d6e0eb0aSDavid Xu 	if (_umtx_op(obj, op, val, uaddr, uaddr2) == -1)
37d6e0eb0aSDavid Xu 		return (errno);
38d6e0eb0aSDavid Xu 	return (0);
39d6e0eb0aSDavid Xu }
40d6e0eb0aSDavid Xu #endif
41d6e0eb0aSDavid Xu 
428042f26dSDavid Xu void
438042f26dSDavid Xu _thr_umutex_init(struct umutex *mtx)
448042f26dSDavid Xu {
458042f26dSDavid Xu 	static struct umutex default_mtx = DEFAULT_UMUTEX;
468042f26dSDavid Xu 
478042f26dSDavid Xu 	*mtx = default_mtx;
488042f26dSDavid Xu }
498042f26dSDavid Xu 
50a091d823SDavid Xu int
517de1ecefSDavid Xu __thr_umutex_lock(struct umutex *mtx, uint32_t id)
52cf13ecdaSDavid Xu {
537de1ecefSDavid Xu 	uint32_t owner;
547de1ecefSDavid Xu 
557de1ecefSDavid Xu 	if ((mtx->m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0) {
567de1ecefSDavid Xu 		for (;;) {
577de1ecefSDavid Xu 			/* wait in kernel */
587de1ecefSDavid Xu 			_umtx_op_err(mtx, UMTX_OP_MUTEX_WAIT, 0, 0, 0);
597de1ecefSDavid Xu 
607de1ecefSDavid Xu 			owner = mtx->m_owner;
617de1ecefSDavid Xu 			if ((owner & ~UMUTEX_CONTESTED) == 0 &&
627de1ecefSDavid Xu 			     atomic_cmpset_acq_32(&mtx->m_owner, owner, id|owner))
637de1ecefSDavid Xu 				return (0);
647de1ecefSDavid Xu 		}
657de1ecefSDavid Xu 	}
667de1ecefSDavid Xu 
67d6e0eb0aSDavid Xu 	return	_umtx_op_err(mtx, UMTX_OP_MUTEX_LOCK, 0, 0, 0);
68cf13ecdaSDavid Xu }
69cf13ecdaSDavid Xu 
70cf13ecdaSDavid Xu int
717de1ecefSDavid Xu __thr_umutex_timedlock(struct umutex *mtx, uint32_t id,
727de1ecefSDavid Xu 	const struct timespec *ets)
73cf13ecdaSDavid Xu {
747de1ecefSDavid Xu 	struct timespec timo, cts;
757de1ecefSDavid Xu 	uint32_t owner;
767de1ecefSDavid Xu 	int ret;
777de1ecefSDavid Xu 
787de1ecefSDavid Xu 	clock_gettime(CLOCK_REALTIME, &cts);
797de1ecefSDavid Xu 	TIMESPEC_SUB(&timo, ets, &cts);
807de1ecefSDavid Xu 
817de1ecefSDavid Xu 	if (timo.tv_sec < 0)
82cf13ecdaSDavid Xu 		return (ETIMEDOUT);
837de1ecefSDavid Xu 
847de1ecefSDavid Xu 	for (;;) {
857de1ecefSDavid Xu 		if ((mtx->m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0) {
867de1ecefSDavid Xu 
877de1ecefSDavid Xu 			/* wait in kernel */
887de1ecefSDavid Xu 			ret = _umtx_op_err(mtx, UMTX_OP_MUTEX_WAIT, 0, 0, &timo);
897de1ecefSDavid Xu 
907de1ecefSDavid Xu 			/* now try to lock it */
917de1ecefSDavid Xu 			owner = mtx->m_owner;
927de1ecefSDavid Xu 			if ((owner & ~UMUTEX_CONTESTED) == 0 &&
937de1ecefSDavid Xu 			     atomic_cmpset_acq_32(&mtx->m_owner, owner, id|owner))
947de1ecefSDavid Xu 				return (0);
957de1ecefSDavid Xu 		} else {
967de1ecefSDavid Xu 			ret = _umtx_op_err(mtx, UMTX_OP_MUTEX_LOCK, 0, 0, &timo);
977de1ecefSDavid Xu 			if (ret == 0)
987de1ecefSDavid Xu 				break;
997de1ecefSDavid Xu 		}
1007de1ecefSDavid Xu 		if (ret == ETIMEDOUT)
1017de1ecefSDavid Xu 			break;
1027de1ecefSDavid Xu 		clock_gettime(CLOCK_REALTIME, &cts);
1037de1ecefSDavid Xu 		TIMESPEC_SUB(&timo, ets, &cts);
1047de1ecefSDavid Xu 		if (timo.tv_sec < 0 || (timo.tv_sec == 0 && timo.tv_nsec == 0)) {
1057de1ecefSDavid Xu 			ret = ETIMEDOUT;
1067de1ecefSDavid Xu 			break;
1077de1ecefSDavid Xu 		}
1087de1ecefSDavid Xu 	}
1097de1ecefSDavid Xu 	return (ret);
110cf13ecdaSDavid Xu }
111cf13ecdaSDavid Xu 
112cf13ecdaSDavid Xu int
1137de1ecefSDavid Xu __thr_umutex_unlock(struct umutex *mtx, uint32_t id)
114cf13ecdaSDavid Xu {
115e4f141b5SMarcel Moolenaar #ifndef __ia64__
116e4f141b5SMarcel Moolenaar 	/* XXX this logic has a race-condition on ia64. */
1177de1ecefSDavid Xu 	if ((mtx->m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0) {
1187de1ecefSDavid Xu 		atomic_cmpset_rel_32(&mtx->m_owner, id | UMUTEX_CONTESTED, UMUTEX_CONTESTED);
1197de1ecefSDavid Xu 		return _umtx_op_err(mtx, UMTX_OP_MUTEX_WAKE, 0, 0, 0);
1207de1ecefSDavid Xu 	}
121e4f141b5SMarcel Moolenaar #endif /* __ia64__ */
122d6e0eb0aSDavid Xu 	return _umtx_op_err(mtx, UMTX_OP_MUTEX_UNLOCK, 0, 0, 0);
123cf13ecdaSDavid Xu }
124cf13ecdaSDavid Xu 
125cf13ecdaSDavid Xu int
1268042f26dSDavid Xu __thr_umutex_trylock(struct umutex *mtx)
127cf13ecdaSDavid Xu {
128d6e0eb0aSDavid Xu 	return _umtx_op_err(mtx, UMTX_OP_MUTEX_TRYLOCK, 0, 0, 0);
129cf13ecdaSDavid Xu }
130cf13ecdaSDavid Xu 
131cf13ecdaSDavid Xu int
132cf13ecdaSDavid Xu __thr_umutex_set_ceiling(struct umutex *mtx, uint32_t ceiling,
133cf13ecdaSDavid Xu 	uint32_t *oldceiling)
134cf13ecdaSDavid Xu {
135d6e0eb0aSDavid Xu 	return _umtx_op_err(mtx, UMTX_OP_SET_CEILING, ceiling, oldceiling, 0);
136cf13ecdaSDavid Xu }
137cf13ecdaSDavid Xu 
138cf13ecdaSDavid Xu int
1396fdfcacbSDavid Xu _thr_umtx_wait(volatile long *mtx, long id, const struct timespec *timeout)
140a091d823SDavid Xu {
141a091d823SDavid Xu 	if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 &&
142a091d823SDavid Xu 		timeout->tv_nsec <= 0)))
143a091d823SDavid Xu 		return (ETIMEDOUT);
144d6e0eb0aSDavid Xu 	return _umtx_op_err(__DEVOLATILE(void *, mtx), UMTX_OP_WAIT, id, 0,
145d6e0eb0aSDavid Xu 		__DECONST(void*, timeout));
146a091d823SDavid Xu }
147a091d823SDavid Xu 
148a091d823SDavid Xu int
1498d6a11a0SDavid Xu _thr_umtx_wait_uint(volatile u_int *mtx, u_int id, const struct timespec *timeout, int shared)
1506fdfcacbSDavid Xu {
1516fdfcacbSDavid Xu 	if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 &&
1526fdfcacbSDavid Xu 		timeout->tv_nsec <= 0)))
1536fdfcacbSDavid Xu 		return (ETIMEDOUT);
1548d6a11a0SDavid Xu 	return _umtx_op_err(__DEVOLATILE(void *, mtx),
1558d6a11a0SDavid Xu 			shared ? UMTX_OP_WAIT_UINT : UMTX_OP_WAIT_UINT_PRIVATE, id, 0,
156d6e0eb0aSDavid Xu 			__DECONST(void*, timeout));
1576fdfcacbSDavid Xu }
1586fdfcacbSDavid Xu 
1596fdfcacbSDavid Xu int
1608d6a11a0SDavid Xu _thr_umtx_wake(volatile void *mtx, int nr_wakeup, int shared)
161a091d823SDavid Xu {
1628d6a11a0SDavid Xu 	return _umtx_op_err(__DEVOLATILE(void *, mtx), shared ? UMTX_OP_WAKE : UMTX_OP_WAKE_PRIVATE,
163d6e0eb0aSDavid Xu 		nr_wakeup, 0, 0);
164a091d823SDavid Xu }
1652bd2c907SDavid Xu 
166670b44d6SDavid Xu void
167670b44d6SDavid Xu _thr_ucond_init(struct ucond *cv)
168670b44d6SDavid Xu {
169670b44d6SDavid Xu 	bzero(cv, sizeof(struct ucond));
170670b44d6SDavid Xu }
171670b44d6SDavid Xu 
1722bd2c907SDavid Xu int
1732bd2c907SDavid Xu _thr_ucond_wait(struct ucond *cv, struct umutex *m,
1742bd2c907SDavid Xu 	const struct timespec *timeout, int check_unparking)
1752bd2c907SDavid Xu {
1762bd2c907SDavid Xu 	if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 &&
1772bd2c907SDavid Xu 	    timeout->tv_nsec <= 0))) {
1787de1ecefSDavid Xu 		struct pthread *curthread = _get_curthread();
1797de1ecefSDavid Xu 		_thr_umutex_unlock(m, TID(curthread));
1802bd2c907SDavid Xu                 return (ETIMEDOUT);
1812bd2c907SDavid Xu 	}
182d6e0eb0aSDavid Xu 	return _umtx_op_err(cv, UMTX_OP_CV_WAIT,
1833c61d00aSDavid Xu 		     check_unparking ? UMTX_CHECK_UNPARKING : 0,
184d6e0eb0aSDavid Xu 		     m, __DECONST(void*, timeout));
1852bd2c907SDavid Xu }
1862bd2c907SDavid Xu 
1872bd2c907SDavid Xu int
1882bd2c907SDavid Xu _thr_ucond_signal(struct ucond *cv)
1892bd2c907SDavid Xu {
190347126a2SDavid Xu 	if (!cv->c_has_waiters)
191347126a2SDavid Xu 		return (0);
192d6e0eb0aSDavid Xu 	return _umtx_op_err(cv, UMTX_OP_CV_SIGNAL, 0, NULL, NULL);
1932bd2c907SDavid Xu }
1942bd2c907SDavid Xu 
1952bd2c907SDavid Xu int
1962bd2c907SDavid Xu _thr_ucond_broadcast(struct ucond *cv)
1972bd2c907SDavid Xu {
198347126a2SDavid Xu 	if (!cv->c_has_waiters)
199347126a2SDavid Xu 		return (0);
200d6e0eb0aSDavid Xu 	return _umtx_op_err(cv, UMTX_OP_CV_BROADCAST, 0, NULL, NULL);
2012bd2c907SDavid Xu }
2028bf1a48cSDavid Xu 
2038bf1a48cSDavid Xu int
2048bf1a48cSDavid Xu __thr_rwlock_rdlock(struct urwlock *rwlock, int flags, struct timespec *tsp)
2058bf1a48cSDavid Xu {
206d6e0eb0aSDavid Xu 	return _umtx_op_err(rwlock, UMTX_OP_RW_RDLOCK, flags, NULL, tsp);
2078bf1a48cSDavid Xu }
2088bf1a48cSDavid Xu 
2098bf1a48cSDavid Xu int
2108bf1a48cSDavid Xu __thr_rwlock_wrlock(struct urwlock *rwlock, struct timespec *tsp)
2118bf1a48cSDavid Xu {
212d6e0eb0aSDavid Xu 	return _umtx_op_err(rwlock, UMTX_OP_RW_WRLOCK, 0, NULL, tsp);
2138bf1a48cSDavid Xu }
2148bf1a48cSDavid Xu 
2158bf1a48cSDavid Xu int
2168bf1a48cSDavid Xu __thr_rwlock_unlock(struct urwlock *rwlock)
2178bf1a48cSDavid Xu {
218d6e0eb0aSDavid Xu 	return _umtx_op_err(rwlock, UMTX_OP_RW_UNLOCK, 0, NULL, NULL);
2198bf1a48cSDavid Xu }
220*02c3c858SDavid Xu 
221*02c3c858SDavid Xu void
222*02c3c858SDavid Xu _thr_rwl_rdlock(struct urwlock *rwlock)
223*02c3c858SDavid Xu {
224*02c3c858SDavid Xu 	int ret;
225*02c3c858SDavid Xu 
226*02c3c858SDavid Xu 	for (;;) {
227*02c3c858SDavid Xu 		if (_thr_rwlock_tryrdlock(rwlock, URWLOCK_PREFER_READER) == 0)
228*02c3c858SDavid Xu 			return;
229*02c3c858SDavid Xu 		ret = __thr_rwlock_rdlock(rwlock, URWLOCK_PREFER_READER, NULL);
230*02c3c858SDavid Xu 		if (ret == 0)
231*02c3c858SDavid Xu 			return;
232*02c3c858SDavid Xu 		if (ret != EINTR)
233*02c3c858SDavid Xu 			PANIC("rdlock error");
234*02c3c858SDavid Xu 	}
235*02c3c858SDavid Xu }
236*02c3c858SDavid Xu 
237*02c3c858SDavid Xu void
238*02c3c858SDavid Xu _thr_rwl_wrlock(struct urwlock *rwlock)
239*02c3c858SDavid Xu {
240*02c3c858SDavid Xu 	int ret;
241*02c3c858SDavid Xu 
242*02c3c858SDavid Xu 	for (;;) {
243*02c3c858SDavid Xu 		if (_thr_rwlock_trywrlock(rwlock) == 0)
244*02c3c858SDavid Xu 			return;
245*02c3c858SDavid Xu 		ret = __thr_rwlock_wrlock(rwlock, NULL);
246*02c3c858SDavid Xu 		if (ret == 0)
247*02c3c858SDavid Xu 			return;
248*02c3c858SDavid Xu 		if (ret != EINTR)
249*02c3c858SDavid Xu 			PANIC("wrlock error");
250*02c3c858SDavid Xu 	}
251*02c3c858SDavid Xu }
252*02c3c858SDavid Xu 
253*02c3c858SDavid Xu void
254*02c3c858SDavid Xu _thr_rwl_unlock(struct urwlock *rwlock)
255*02c3c858SDavid Xu {
256*02c3c858SDavid Xu 	if (_thr_rwlock_unlock(rwlock))
257*02c3c858SDavid Xu 		PANIC("unlock error");
258*02c3c858SDavid Xu }
259