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:
parent
cf366589b1
commit
cc1000ac5b
@ -608,53 +608,22 @@ do_unlock(struct thread *td, struct umtx *umtx, long id)
|
||||
}
|
||||
|
||||
static int
|
||||
do_unlock_and_wait(struct thread *td, struct umtx *umtx, long id, void *uaddr,
|
||||
struct timespec *abstime)
|
||||
do_wait(struct thread *td, struct umtx *umtx, long id, struct timespec *abstime)
|
||||
{
|
||||
struct umtx_q uq;
|
||||
intptr_t owner;
|
||||
intptr_t old;
|
||||
struct timespec ts1, ts2;
|
||||
struct timeval tv;
|
||||
long tmp;
|
||||
int timo, error = 0;
|
||||
|
||||
if (umtx == uaddr)
|
||||
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)
|
||||
if ((error = umtxq_queue_me(td, umtx, &uq)) != 0)
|
||||
return (error);
|
||||
|
||||
old = casuptr((intptr_t *)&umtx->u_owner, id, UMTX_UNOWNED);
|
||||
if (old == -1) {
|
||||
tmp = fuword(&umtx->u_owner);
|
||||
if (tmp != id) {
|
||||
umtxq_lock(&uq.uq_key);
|
||||
umtxq_remove(&uq);
|
||||
umtxq_unlock(&uq.uq_key);
|
||||
umtx_key_release(&uq.uq_key);
|
||||
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) {
|
||||
} else if (abstime == NULL) {
|
||||
umtxq_lock(&uq.uq_key);
|
||||
if (td->td_flags & TDF_UMTXQ)
|
||||
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);
|
||||
case UMTX_OP_UNLOCK:
|
||||
return do_unlock(td, uap->umtx, uap->id);
|
||||
case UMTX_OP_UNLOCK_AND_WAIT:
|
||||
case UMTX_OP_WAIT:
|
||||
/* Allow a null timespec (wait forever). */
|
||||
if (uap->uaddr2 == NULL)
|
||||
ts = NULL;
|
||||
@ -763,10 +732,9 @@ _umtx_op(struct thread *td, struct _umtx_op_args *uap)
|
||||
return (EINVAL);
|
||||
ts = &abstime;
|
||||
}
|
||||
return do_unlock_and_wait(td, uap->umtx, uap->id,
|
||||
uap->uaddr, ts);
|
||||
return do_wait(td, uap->umtx, uap->id, ts);
|
||||
case UMTX_OP_WAKE:
|
||||
return do_wake(td, uap->uaddr, uap->id);
|
||||
return do_wake(td, uap->umtx, uap->id);
|
||||
default:
|
||||
return (EINVAL);
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ struct umtx {
|
||||
/* op code for _umtx_op */
|
||||
#define UMTX_OP_LOCK 0
|
||||
#define UMTX_OP_UNLOCK 1
|
||||
#define UMTX_OP_UNLOCK_AND_WAIT 2
|
||||
#define UMTX_OP_WAIT 2
|
||||
#define UMTX_OP_WAKE 3
|
||||
|
||||
#ifndef _KERNEL
|
||||
@ -118,29 +118,27 @@ umtx_unlock(struct umtx *umtx, long id)
|
||||
/* Unlock umtx and wait on a user address. */
|
||||
|
||||
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 (0);
|
||||
}
|
||||
|
||||
static __inline int
|
||||
umtx_timedwait(struct umtx *umtx, long id, void *uaddr,
|
||||
const struct timespec *abstime)
|
||||
umtx_timedwait(struct umtx *umtx, long id, const struct timespec *abstime)
|
||||
{
|
||||
if (_umtx_op(umtx, UMTX_OP_UNLOCK_AND_WAIT, id, uaddr,
|
||||
(void *)abstime) == -1)
|
||||
if (_umtx_op(umtx, UMTX_OP_WAIT, id, 0, (void *)abstime) == -1)
|
||||
return (errno);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Wake threads waiting on a user address. */
|
||||
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 _umtx_op(0, UMTX_OP_WAKE, nr_wakeup, uaddr, 0);
|
||||
return _umtx_op(umtx, UMTX_OP_WAKE, nr_wakeup, 0, 0);
|
||||
}
|
||||
|
||||
#endif /* !_KERNEL */
|
||||
|
Loading…
Reference in New Issue
Block a user