Make umtx_wait and umtx_wake more like linux futex does, it is

more general than previous. It also lets me implement cancelable point
in thread library. Also in theory, umtx_lock and umtx_unlock can
be implemented by using umtx_wait and umtx_wake, all atomic operations
can be done in userland without kernel's casuptr() function.
This commit is contained in:
David Xu 2004-12-30 02:56:17 +00:00
parent cf366589b1
commit cc1000ac5b
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=139427
2 changed files with 16 additions and 50 deletions

View File

@ -608,53 +608,22 @@ do_unlock(struct thread *td, struct umtx *umtx, long id)
} }
static int static int
do_unlock_and_wait(struct thread *td, struct umtx *umtx, long id, void *uaddr, do_wait(struct thread *td, struct umtx *umtx, long id, struct timespec *abstime)
struct timespec *abstime)
{ {
struct umtx_q uq; struct umtx_q uq;
intptr_t owner;
intptr_t old;
struct timespec ts1, ts2; struct timespec ts1, ts2;
struct timeval tv; struct timeval tv;
long tmp;
int timo, error = 0; int timo, error = 0;
if (umtx == uaddr) if ((error = umtxq_queue_me(td, umtx, &uq)) != 0)
return (EINVAL);
/*
* Make sure we own this mtx.
*
* XXX Need a {fu,su}ptr this is not correct on arch where
* sizeof(intptr_t) != sizeof(long).
*/
if ((owner = fuword(&umtx->u_owner)) == -1)
return (EFAULT);
if ((owner & ~UMTX_CONTESTED) != id)
return (EPERM);
if ((error = umtxq_queue_me(td, uaddr, &uq)) != 0)
return (error); return (error);
tmp = fuword(&umtx->u_owner);
old = casuptr((intptr_t *)&umtx->u_owner, id, UMTX_UNOWNED); if (tmp != id) {
if (old == -1) {
umtxq_lock(&uq.uq_key); umtxq_lock(&uq.uq_key);
umtxq_remove(&uq); umtxq_remove(&uq);
umtxq_unlock(&uq.uq_key); umtxq_unlock(&uq.uq_key);
umtx_key_release(&uq.uq_key); } else if (abstime == NULL) {
return (EFAULT);
}
if (old != id) {
error = do_unlock(td, umtx, id);
if (error) {
umtxq_lock(&uq.uq_key);
umtxq_remove(&uq);
umtxq_unlock(&uq.uq_key);
umtx_key_release(&uq.uq_key);
return (error);
}
}
if (abstime == NULL) {
umtxq_lock(&uq.uq_key); umtxq_lock(&uq.uq_key);
if (td->td_flags & TDF_UMTXQ) if (td->td_flags & TDF_UMTXQ)
error = umtxq_sleep(td, &uq.uq_key, error = umtxq_sleep(td, &uq.uq_key,
@ -750,7 +719,7 @@ _umtx_op(struct thread *td, struct _umtx_op_args *uap)
return do_lock(td, uap->umtx, uap->id, ts); return do_lock(td, uap->umtx, uap->id, ts);
case UMTX_OP_UNLOCK: case UMTX_OP_UNLOCK:
return do_unlock(td, uap->umtx, uap->id); return do_unlock(td, uap->umtx, uap->id);
case UMTX_OP_UNLOCK_AND_WAIT: case UMTX_OP_WAIT:
/* Allow a null timespec (wait forever). */ /* Allow a null timespec (wait forever). */
if (uap->uaddr2 == NULL) if (uap->uaddr2 == NULL)
ts = NULL; ts = NULL;
@ -763,10 +732,9 @@ _umtx_op(struct thread *td, struct _umtx_op_args *uap)
return (EINVAL); return (EINVAL);
ts = &abstime; ts = &abstime;
} }
return do_unlock_and_wait(td, uap->umtx, uap->id, return do_wait(td, uap->umtx, uap->id, ts);
uap->uaddr, ts);
case UMTX_OP_WAKE: case UMTX_OP_WAKE:
return do_wake(td, uap->uaddr, uap->id); return do_wake(td, uap->umtx, uap->id);
default: default:
return (EINVAL); return (EINVAL);
} }

View File

@ -46,7 +46,7 @@ struct umtx {
/* op code for _umtx_op */ /* op code for _umtx_op */
#define UMTX_OP_LOCK 0 #define UMTX_OP_LOCK 0
#define UMTX_OP_UNLOCK 1 #define UMTX_OP_UNLOCK 1
#define UMTX_OP_UNLOCK_AND_WAIT 2 #define UMTX_OP_WAIT 2
#define UMTX_OP_WAKE 3 #define UMTX_OP_WAKE 3
#ifndef _KERNEL #ifndef _KERNEL
@ -118,29 +118,27 @@ umtx_unlock(struct umtx *umtx, long id)
/* Unlock umtx and wait on a user address. */ /* Unlock umtx and wait on a user address. */
static __inline int static __inline int
umtx_wait(struct umtx *umtx, long id, void *uaddr) umtx_wait(struct umtx *umtx, long id)
{ {
if (_umtx_op(umtx, UMTX_OP_UNLOCK_AND_WAIT, id, uaddr, 0) == -1) if (_umtx_op(umtx, UMTX_OP_WAIT, id, 0, 0) == -1)
return (errno); return (errno);
return (0); return (0);
} }
static __inline int static __inline int
umtx_timedwait(struct umtx *umtx, long id, void *uaddr, umtx_timedwait(struct umtx *umtx, long id, const struct timespec *abstime)
const struct timespec *abstime)
{ {
if (_umtx_op(umtx, UMTX_OP_UNLOCK_AND_WAIT, id, uaddr, if (_umtx_op(umtx, UMTX_OP_WAIT, id, 0, (void *)abstime) == -1)
(void *)abstime) == -1)
return (errno); return (errno);
return (0); return (0);
} }
/* Wake threads waiting on a user address. */ /* Wake threads waiting on a user address. */
static __inline int static __inline int
umtx_wake(void *uaddr, int nr_wakeup) umtx_wake(struct umtx *umtx, int nr_wakeup)
{ {
/* return how many threads were woke up, -1 if error */ /* return how many threads were woke up, -1 if error */
return _umtx_op(0, UMTX_OP_WAKE, nr_wakeup, uaddr, 0); return _umtx_op(umtx, UMTX_OP_WAKE, nr_wakeup, 0, 0);
} }
#endif /* !_KERNEL */ #endif /* !_KERNEL */