version 1.33, 2014/02/11 16:00:13 |
version 1.34, 2016/05/20 13:54:34 |
Line 58 __KERNEL_RCSID(1, "$NetBSD$"); |
|
Line 58 __KERNEL_RCSID(1, "$NetBSD$"); |
|
struct futex; |
struct futex; |
|
|
struct waiting_proc { |
struct waiting_proc { |
lwp_t *wp_l; |
struct futex *wp_futex; |
struct futex *wp_new_futex; |
|
kcondvar_t wp_futex_cv; |
kcondvar_t wp_futex_cv; |
TAILQ_ENTRY(waiting_proc) wp_list; |
TAILQ_ENTRY(waiting_proc) wp_list; |
TAILQ_ENTRY(waiting_proc) wp_rqlist; |
bool wp_onlist; |
}; |
}; |
struct futex { |
struct futex { |
void *f_uaddr; |
void *f_uaddr; |
|
|
uint32_t f_bitset; |
uint32_t f_bitset; |
LIST_ENTRY(futex) f_list; |
LIST_ENTRY(futex) f_list; |
TAILQ_HEAD(, waiting_proc) f_waiting_proc; |
TAILQ_HEAD(, waiting_proc) f_waiting_proc; |
TAILQ_HEAD(, waiting_proc) f_requeue_proc; |
|
}; |
}; |
|
|
static LIST_HEAD(futex_list, futex) futex_list; |
static LIST_HEAD(futex_list, futex) futex_list; |
Line 432 futex_get(void *uaddr, uint32_t bitset) |
|
Line 430 futex_get(void *uaddr, uint32_t bitset) |
|
f->f_bitset = bitset; |
f->f_bitset = bitset; |
f->f_refcount = 1; |
f->f_refcount = 1; |
TAILQ_INIT(&f->f_waiting_proc); |
TAILQ_INIT(&f->f_waiting_proc); |
TAILQ_INIT(&f->f_requeue_proc); |
|
LIST_INSERT_HEAD(&futex_list, f, f_list); |
LIST_INSERT_HEAD(&futex_list, f, f_list); |
|
|
return f; |
return f; |
Line 456 futex_put(struct futex *f) |
|
Line 453 futex_put(struct futex *f) |
|
f->f_refcount--; |
f->f_refcount--; |
if (f->f_refcount == 0) { |
if (f->f_refcount == 0) { |
KASSERT(TAILQ_EMPTY(&f->f_waiting_proc)); |
KASSERT(TAILQ_EMPTY(&f->f_waiting_proc)); |
KASSERT(TAILQ_EMPTY(&f->f_requeue_proc)); |
|
LIST_REMOVE(f, f_list); |
LIST_REMOVE(f, f_list); |
kmem_free(f, sizeof(*f)); |
kmem_free(f, sizeof(*f)); |
} |
} |
Line 465 futex_put(struct futex *f) |
|
Line 461 futex_put(struct futex *f) |
|
static int |
static int |
futex_sleep(struct futex **fp, lwp_t *l, int timeout, struct waiting_proc *wp) |
futex_sleep(struct futex **fp, lwp_t *l, int timeout, struct waiting_proc *wp) |
{ |
{ |
struct futex *f, *newf; |
struct futex *f; |
int ret; |
int ret; |
|
|
FUTEX_LOCKASSERT; |
FUTEX_LOCKASSERT; |
|
|
f = *fp; |
f = *fp; |
wp->wp_l = l; |
wp->wp_futex = f; |
wp->wp_new_futex = NULL; |
|
|
|
requeue: |
|
TAILQ_INSERT_TAIL(&f->f_waiting_proc, wp, wp_list); |
TAILQ_INSERT_TAIL(&f->f_waiting_proc, wp, wp_list); |
|
wp->wp_onlist = true; |
ret = cv_timedwait_sig(&wp->wp_futex_cv, &futex_lock, timeout); |
ret = cv_timedwait_sig(&wp->wp_futex_cv, &futex_lock, timeout); |
TAILQ_REMOVE(&f->f_waiting_proc, wp, wp_list); |
|
|
|
/* if futex_wake() tells us to requeue ... */ |
/* |
newf = wp->wp_new_futex; |
* we may have been requeued to a different futex before we were |
if (ret == 0 && newf != NULL) { |
* woken up, so let the caller know which futex to put. if we were |
/* ... requeue ourselves on the new futex */ |
* woken by futex_wake() then it took us off the waiting list, |
futex_put(f); |
* but if our sleep was interrupted or timed out then we might |
wp->wp_new_futex = NULL; |
* need to take ourselves off the waiting list. |
TAILQ_REMOVE(&newf->f_requeue_proc, wp, wp_rqlist); |
*/ |
*fp = f = newf; |
|
goto requeue; |
f = wp->wp_futex; |
|
if (wp->wp_onlist) { |
|
TAILQ_REMOVE(&f->f_waiting_proc, wp, wp_list); |
} |
} |
|
*fp = f; |
return ret; |
return ret; |
} |
} |
|
|
static int |
static int |
futex_wake(struct futex *f, int n, struct futex *newf, int n2) |
futex_wake(struct futex *f, int n, struct futex *newf, int n2) |
{ |
{ |
struct waiting_proc *wp, *wpnext; |
struct waiting_proc *wp; |
int count; |
int count = 0; |
|
|
FUTEX_LOCKASSERT; |
FUTEX_LOCKASSERT; |
|
|
count = newf ? 0 : 1; |
|
|
|
/* |
/* |
* first, wake up any threads sleeping on this futex. |
* wake up up to n threads waiting on this futex. |
* note that sleeping threads are not in the process of requeueing. |
|
*/ |
*/ |
|
|
TAILQ_FOREACH(wp, &f->f_waiting_proc, wp_list) { |
while (n--) { |
KASSERT(wp->wp_new_futex == NULL); |
wp = TAILQ_FIRST(&f->f_waiting_proc); |
|
if (wp == NULL) |
FUTEXPRINTF(("%s: signal f %p l %p ref %d\n", __func__, |
return count; |
f, wp->wp_l, f->f_refcount)); |
|
|
KASSERT(f == wp->wp_futex); |
|
TAILQ_REMOVE(&f->f_waiting_proc, wp, wp_list); |
|
wp->wp_onlist = false; |
cv_signal(&wp->wp_futex_cv); |
cv_signal(&wp->wp_futex_cv); |
if (count <= n) { |
count++; |
count++; |
|
} else { |
|
if (newf == NULL) |
|
break; |
|
|
|
/* matching futex_put() is called by the other thread. */ |
|
futex_ref(newf); |
|
wp->wp_new_futex = newf; |
|
TAILQ_INSERT_TAIL(&newf->f_requeue_proc, wp, wp_rqlist); |
|
FUTEXPRINTF(("%s: requeue newf %p l %p ref %d\n", |
|
__func__, newf, wp->wp_l, newf->f_refcount)); |
|
if (count - n >= n2) |
|
goto out; |
|
} |
|
} |
} |
|
if (newf == NULL) |
|
return count; |
|
|
/* |
/* |
* next, deal with threads that are requeuing to this futex. |
* then requeue up to n2 additional threads to newf |
* we don't need to signal these threads, any thread on the |
* (without waking them up). |
* requeue list has already been signaled but hasn't had a chance |
|
* to run and requeue itself yet. if we would normally wake |
|
* a thread, just remove the requeue info. if we would normally |
|
* requeue a thread, change the requeue target. |
|
*/ |
*/ |
|
|
TAILQ_FOREACH_SAFE(wp, &f->f_requeue_proc, wp_rqlist, wpnext) { |
while (n2--) { |
KASSERT(wp->wp_new_futex == f); |
wp = TAILQ_FIRST(&f->f_waiting_proc); |
|
if (wp == NULL) |
|
return count; |
|
|
FUTEXPRINTF(("%s: unrequeue f %p l %p ref %d\n", __func__, |
KASSERT(f == wp->wp_futex); |
f, wp->wp_l, f->f_refcount)); |
TAILQ_REMOVE(&f->f_waiting_proc, wp, wp_list); |
wp->wp_new_futex = NULL; |
|
TAILQ_REMOVE(&f->f_requeue_proc, wp, wp_rqlist); |
|
futex_put(f); |
futex_put(f); |
|
|
if (count <= n) { |
wp->wp_futex = newf; |
count++; |
futex_ref(newf); |
} else { |
TAILQ_INSERT_TAIL(&newf->f_waiting_proc, wp, wp_list); |
if (newf == NULL) |
count++; |
break; |
|
|
|
/* matching futex_put() is called by the other thread. */ |
|
futex_ref(newf); |
|
wp->wp_new_futex = newf; |
|
TAILQ_INSERT_TAIL(&newf->f_requeue_proc, wp, wp_rqlist); |
|
FUTEXPRINTF(("%s: rerequeue newf %p l %p ref %d\n", |
|
__func__, newf, wp->wp_l, newf->f_refcount)); |
|
if (count - n >= n2) |
|
break; |
|
} |
|
} |
} |
|
|
out: |
|
return count; |
return count; |
} |
} |
|
|