commit d801022e99a262133faf67859eb521a5d6ef1d8c Author: Ryota Ozaki Date: Wed Dec 20 15:08:25 2017 +0900 Add workqueue_drain that waits for all pending works to finish It is useful when you want to safely call workqueue_destroy that requires that there remains no work in the queue. Without workqueue_drain the caller had to satisfy the constract by itself somehow. workqueue_drain has a contract that the caller must ensure no new work to be enqueued to the workqueue during workqueue_drain. diff --git a/sys/kern/subr_workqueue.c b/sys/kern/subr_workqueue.c index 7e651d1ebda..8a0ad9d5267 100644 --- a/sys/kern/subr_workqueue.c +++ b/sys/kern/subr_workqueue.c @@ -271,6 +271,38 @@ workqueue_create(struct workqueue **wqp, const char *name, return error; } +/* + * Wait for all pending works to finish. The caller must ensure that no new + * work will be enqueued before calling workqueue_drain. + */ +void +workqueue_drain(struct workqueue *wq) +{ + struct workqueue_queue *q; + struct cpu_info *ci; + CPU_INFO_ITERATOR cii; + + for (CPU_INFO_FOREACH(cii, ci)) { + q = workqueue_queue_lookup(wq, ci); + KASSERT(q->q_worker != NULL); + + mutex_enter(&q->q_mutex); + while (!SIMPLEQ_EMPTY(&q->q_queue)) + cv_wait(&q->q_cv, &q->q_mutex); + mutex_exit(&q->q_mutex); + } + +#ifdef DEBUG + /* Check if no new work wasn't enqueued during the above loop. */ + for (CPU_INFO_FOREACH(cii, ci)) { + q = workqueue_queue_lookup(wq, ci); + mutex_enter(&q->q_mutex); + KDASSERT(SIMPLEQ_EMPTY(&q->q_queue)); + mutex_exit(&q->q_mutex); + } +#endif +} + void workqueue_destroy(struct workqueue *wq) { diff --git a/sys/sys/workqueue.h b/sys/sys/workqueue.h index 26d974bced2..10cae3429ce 100644 --- a/sys/sys/workqueue.h +++ b/sys/sys/workqueue.h @@ -51,6 +51,7 @@ struct workqueue; int workqueue_create(struct workqueue **, const char *, void (*)(struct work *, void *), void *, pri_t, int, int); void workqueue_destroy(struct workqueue *); +void workqueue_drain(struct workqueue *); void workqueue_enqueue(struct workqueue *, struct work *, struct cpu_info *);