svn commit: r248649 - in head: share/man/man9 sys/kern sys/sys

mdf at FreeBSD.org mdf at FreeBSD.org
Sat Mar 23 16:20:46 UTC 2013


On Sat, Mar 23, 2013 at 8:11 AM, Will Andrews <will at freebsd.org> wrote:
> Author: will
> Date: Sat Mar 23 15:11:53 2013
> New Revision: 248649
> URL: http://svnweb.freebsd.org/changeset/base/248649
>
> Log:
>   Extend taskqueue(9) to enable per-taskqueue callbacks.
>
>   The scope of these callbacks is primarily to support actions that affect the
>   taskqueue's thread environments.  They are entirely optional, and
>   consequently are introduced as a new API: taskqueue_set_callback().
>
>   This interface allows the caller to specify that a taskqueue requires a
>   callback and optional context pointer for a given callback type.
>
>   The callback types included in this commit can be used to register a
>   constructor and destructor for thread-local storage using osd(9).  This
>   allows a particular taskqueue to define that its threads require a specific
>   type of TLS, without the need for a specially-orchestrated task-based
>   mechanism for startup and shutdown in order to accomplish it.
>
>   Two callback types are supported at this point:
>
>   - TASKQUEUE_CALLBACK_TYPE_INIT, called by every thread when it starts, prior
>     to processing any tasks.
>   - TASKQUEUE_CALLBACK_TYPE_SHUTDOWN, called by every thread when it exits,
>     after it has processed its last task but before the taskqueue is
>     reclaimed.
>
>   While I'm here:
>
>   - Add two new macros, TQ_ASSERT_LOCKED and TQ_ASSERT_UNLOCKED, and use them
>     in appropriate locations.
>   - Fix taskqueue.9 to mention taskqueue_start_threads(), which is a required
>     interface for all consumers of taskqueue(9).
>
>   Reviewed by:  kib (all), eadler (taskqueue.9), brd (taskqueue.9)
>   Approved by:  ken (mentor)
>   Sponsored by: Spectra Logic
>   MFC after:    1 month
>
> Modified:
>   head/share/man/man9/taskqueue.9
>   head/sys/kern/subr_taskqueue.c
>   head/sys/sys/taskqueue.h
>
> Modified: head/share/man/man9/taskqueue.9
> ==============================================================================
> --- head/share/man/man9/taskqueue.9     Sat Mar 23 14:52:31 2013        (r248648)
> +++ head/share/man/man9/taskqueue.9     Sat Mar 23 15:11:53 2013        (r248649)
> @@ -53,12 +53,23 @@ struct task {
>         void                    *ta_context;    /* argument for handler */
>  };
>
> +enum taskqueue_callback_type {
> +       TASKQUEUE_CALLBACK_TYPE_INIT,
> +       TASKQUEUE_CALLBACK_TYPE_SHUTDOWN,
> +};
> +
> +typedef void (*taskqueue_callback_fn)(void *context);
> +
>  struct timeout_task;
>  .Ed
>  .Ft struct taskqueue *
>  .Fn taskqueue_create "const char *name" "int mflags" "taskqueue_enqueue_fn enqueue" "void *context"
>  .Ft struct taskqueue *
>  .Fn taskqueue_create_fast "const char *name" "int mflags" "taskqueue_enqueue_fn enqueue" "void *context"
> +.Ft int
> +.Fn taskqueue_start_threads "struct taskqueue **tqp" "int count" "int pri" "const char *name" "..."
> +.Ft void
> +.Fn taskqueue_set_callback "struct taskqueue *queue" "enum taskqueue_callback_type cb_type" "taskqueue_callback_fn callback" "void *context"
>  .Ft void
>  .Fn taskqueue_free "struct taskqueue *queue"
>  .Ft int
> @@ -127,6 +138,23 @@ should be used to free the memory used b
>  Any tasks that are on the queue will be executed at this time after
>  which the thread servicing the queue will be signaled that it should exit.
>  .Pp
> +Once a taskqueue has been created, its threads should be started using
> +.Fn taskqueue_start_threads .
> +Callbacks may optionally be registered using
> +.Fn taskqueue_set_callback .
> +Currently, callbacks may be registered for the following purposes:
> +.Bl -tag -width TASKQUEUE_CALLBACK_TYPE_SHUTDOWN
> +.It Dv TASKQUEUE_CALLBACK_TYPE_INIT
> +This callback is called by every thread in the taskqueue, before it executes
> +any tasks.
> +This callback must be set before the taskqueue's threads are started.
> +.It Dv TASKQUEUE_CALLBACK_TYPE_SHUTDOWN
> +This callback is called by every thread in the taskqueue, after it executes
> +its last task.
> +This callback will always be called before the taskqueue structure is
> +reclaimed.
> +.El
> +.Pp
>  To add a task to the list of tasks queued on a taskqueue, call
>  .Fn taskqueue_enqueue
>  with pointers to the queue and task.
>
> Modified: head/sys/kern/subr_taskqueue.c
> ==============================================================================
> --- head/sys/kern/subr_taskqueue.c      Sat Mar 23 14:52:31 2013        (r248648)
> +++ head/sys/kern/subr_taskqueue.c      Sat Mar 23 15:11:53 2013        (r248649)
> @@ -63,6 +63,8 @@ struct taskqueue {
>         int                     tq_spin;
>         int                     tq_flags;
>         int                     tq_callouts;
> +       taskqueue_callback_fn   tq_callbacks[TASKQUEUE_NUM_CALLBACKS];
> +       void                    *tq_cb_contexts[TASKQUEUE_NUM_CALLBACKS];
>  };
>
>  #define        TQ_FLAGS_ACTIVE         (1 << 0)
> @@ -78,6 +80,7 @@ struct taskqueue {
>                 else                                                    \
>                         mtx_lock(&(tq)->tq_mutex);                      \
>         } while (0)
> +#define        TQ_ASSERT_LOCKED(tq)    mtx_assert(&(tq)->tq_mutex, MA_OWNED)
>
>  #define        TQ_UNLOCK(tq)                                                   \
>         do {                                                            \
> @@ -86,6 +89,7 @@ struct taskqueue {
>                 else                                                    \
>                         mtx_unlock(&(tq)->tq_mutex);                    \
>         } while (0)
> +#define        TQ_ASSERT_UNLOCKED(tq)  mtx_assert(&(tq)->tq_mutex, MA_NOTOWNED)
>
>  void
>  _timeout_task_init(struct taskqueue *queue, struct timeout_task *timeout_task,
> @@ -137,6 +141,23 @@ taskqueue_create(const char *name, int m
>                         MTX_DEF, "taskqueue");
>  }
>
> +void
> +taskqueue_set_callback(struct taskqueue *queue,
> +    enum taskqueue_callback_type cb_type, taskqueue_callback_fn callback,
> +    void *context)
> +{
> +
> +       KASSERT(((cb_type >= TASKQUEUE_CALLBACK_TYPE_MIN) &&
> +           (cb_type <= TASKQUEUE_CALLBACK_TYPE_MAX)),
> +           ("Callback type %d not valid, must be %d-%d", cb_type,
> +           TASKQUEUE_CALLBACK_TYPE_MIN, TASKQUEUE_CALLBACK_TYPE_MAX));
> +       KASSERT((queue->tq_callbacks[cb_type] == NULL),
> +           ("Re-initialization of taskqueue callback?"));
> +
> +       queue->tq_callbacks[cb_type] = callback;
> +       queue->tq_cb_contexts[cb_type] = context;
> +}
> +
>  /*
>   * Signal a taskqueue thread to terminate.
>   */
> @@ -293,7 +314,7 @@ taskqueue_run_locked(struct taskqueue *q
>         struct task *task;
>         int pending;
>
> -       mtx_assert(&queue->tq_mutex, MA_OWNED);
> +       TQ_ASSERT_LOCKED(queue);
>         tb.tb_running = NULL;
>         TAILQ_INSERT_TAIL(&queue->tq_active, &tb, tb_link);
>
> @@ -332,7 +353,7 @@ task_is_running(struct taskqueue *queue,
>  {
>         struct taskqueue_busy *tb;
>
> -       mtx_assert(&queue->tq_mutex, MA_OWNED);
> +       TQ_ASSERT_LOCKED(queue);
>         TAILQ_FOREACH(tb, &queue->tq_active, tb_link) {
>                 if (tb->tb_running == task)
>                         return (1);
> @@ -489,6 +510,18 @@ taskqueue_start_threads(struct taskqueue
>         return (0);
>  }
>
> +static inline void
> +taskqueue_run_callback(struct taskqueue *tq,
> +    enum taskqueue_callback_type cb_type)
> +{
> +       taskqueue_callback_fn tq_callback;
> +
> +       TQ_ASSERT_UNLOCKED(tq);
> +       tq_callback = tq->tq_callbacks[cb_type];
> +       if (tq_callback != NULL)
> +               tq_callback(tq->tq_cb_contexts[cb_type]);
> +}
> +
>  void
>  taskqueue_thread_loop(void *arg)
>  {
> @@ -496,6 +529,7 @@ taskqueue_thread_loop(void *arg)
>
>         tqp = arg;
>         tq = *tqp;
> +       taskqueue_run_callback(tq, TASKQUEUE_CALLBACK_TYPE_INIT);
>         TQ_LOCK(tq);
>         while ((tq->tq_flags & TQ_FLAGS_ACTIVE) != 0) {
>                 taskqueue_run_locked(tq);
> @@ -510,6 +544,15 @@ taskqueue_thread_loop(void *arg)
>         }
>         taskqueue_run_locked(tq);
>
> +       /*
> +        * This thread is on its way out, so just drop the lock temporarily
> +        * in order to call the shutdown callback.  This allows the callback
> +        * to look at the taskqueue, even just before it dies.
> +        */
> +       TQ_UNLOCK(tq);
> +       taskqueue_run_callback(tq, TASKQUEUE_CALLBACK_TYPE_SHUTDOWN);
> +       TQ_LOCK(tq);
> +
>         /* rendezvous with thread that asked us to terminate */
>         tq->tq_tcount--;
>         wakeup_one(tq->tq_threads);
> @@ -525,7 +568,7 @@ taskqueue_thread_enqueue(void *context)
>         tqp = context;
>         tq = *tqp;
>
> -       mtx_assert(&tq->tq_mutex, MA_OWNED);
> +       TQ_ASSERT_LOCKED(tq);
>         wakeup_one(tq);
>  }
>
>
> Modified: head/sys/sys/taskqueue.h
> ==============================================================================
> --- head/sys/sys/taskqueue.h    Sat Mar 23 14:52:31 2013        (r248648)
> +++ head/sys/sys/taskqueue.h    Sat Mar 23 15:11:53 2013        (r248649)
> @@ -47,6 +47,16 @@ struct timeout_task {
>         int    f;
>  };
>
> +enum taskqueue_callback_type {
> +       TASKQUEUE_CALLBACK_TYPE_INIT,
> +       TASKQUEUE_CALLBACK_TYPE_SHUTDOWN,
> +};
> +#define        TASKQUEUE_CALLBACK_TYPE_MIN     TASKQUEUE_CALLBACK_TYPE_INIT
> +#define        TASKQUEUE_CALLBACK_TYPE_MAX     TASKQUEUE_CALLBACK_TYPE_SHUTDOWN
> +#define        TASKQUEUE_NUM_CALLBACKS         TASKQUEUE_CALLBACK_TYPE_MAX + 1

This need parentheses to defensively guard against unexpected
precedence of operators.

While here, TASKQUEUE_CALLBACK_ is a very long prefix (19 characters!)
 Can this be named TQCB_ instead so it's not so verbose?

Thanks,
matthew


More information about the svn-src-all mailing list