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

Will Andrews will at FreeBSD.org
Sat Mar 23 15:11:54 UTC 2013


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
+
+typedef void (*taskqueue_callback_fn)(void *context);
+
 /*
  * A notification callback function which is called from
  * taskqueue_enqueue().  The context argument is given in the call to
@@ -76,6 +86,9 @@ void	taskqueue_run(struct taskqueue *que
 void	taskqueue_block(struct taskqueue *queue);
 void	taskqueue_unblock(struct taskqueue *queue);
 int	taskqueue_member(struct taskqueue *queue, struct thread *td);
+void	taskqueue_set_callback(struct taskqueue *queue,
+	    enum taskqueue_callback_type cb_type,
+	    taskqueue_callback_fn callback, void *context);
 
 #define TASK_INITIALIZER(priority, func, context)	\
 	{ .ta_pending = 0,				\


More information about the svn-src-all mailing list