Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 14 May 2000 11:31:32 -0600
From:      Chuck Paterson <cp@bsdi.com>
To:        Doug Rabson <dfr@nlsystems.com>
Cc:        Arun Sharma <adsharma@sharmas.dhs.org>, freebsd-arch@freebsd.org
Subject:   Re: A new api for asynchronous task execution 
Message-ID:  <200005141731.LAA04793@berserker.bsdi.com>

next in thread | raw e-mail | index | archive | help
	While writing up this I noticed a trivial bug 

taskqueue_enqueue(struct taskqueue *queue, struct task *task)
{
	int s = splhigh();

	/*
	 * Count multiple enqueues.
	 */
	if (task->pending) {
		task->pending++;
		return;			<---- really need a splx before this
	}


}
}I agree that the queue should have a mutex to protect itself. Since we
}haven't finalised the api for mutex locking (we will almost certainly use
}the BSD/OS one), I can't add anything yet but I will when the new SMP
}work starts.

	FYI, the current BSD/OS mutex's have a string associated
with them, ie there name. This name is used for debugging (checking
lock order) and perhaps for blowing locks open at panic time.
Mutex's which aren't part of a group of a unique name, an example
of this might be "ipqlock". For mutex's which are all really  the
same they share the same name such as "vnode interlock". There is
also the issue of what flags are passed in to initialize the mutex,
and for that matter flags used in acquiring the mutex. I guess I
should for now just point out some of the issues.

1)	It would be useful to have meaningful name associated with
a mutex when initializing the mutex associated with a taskqueue.

2)	There are flags needed with initializing the mutex associated
with a taskqueue.

3)      When acquiring the mutex associated with a queue it wants to
in general not be of type M_SPIN, but sometimes it will.

4)	As proposed now the mutex protecting the queue often does not
want to be held while the enqueueing function is called. This is
particularly true if the enqueueing function may cause an immediate
context switch.

5)	Even the taskqueue_run really wants to just know what
type of mutex is present.

	In some ways the implementation of taskqueue_enqueue() is
not as important to get right from the start as the calling sequence
to it. There are almost certainly going to be lots more users of
the swi taskqueue than anything else and changing the few actual
implementation of the enqueuer function and init function seem like
not a big deal.

	I have included just some sketches of how you could deal
with some of this. Just something to think about

	The following is very slightly slower because the type of
mutex is not known at compile times, but it makes it possible
to not have to totally change to model.

Chuck

struct taskqueue {
	STAILQ_HEAD(, task)  queue;
	mtx_t		     mtx;
	int		     mtx_flags;
	void		     (*enqueue)(struct taskqueue *queue);
};


void
taskqueue_init(struct taskqueue *queue,
	       void (*enqueue)(struct taskqueue *queue), char *name, int flags)
{
	STAILQ_INIT(&queue->queue);
	queue->enqueue = enqueue;
	queue->mtx_flags = flags & TQ_SPIN ? M_SPIN, M_DEF;
	mtx_init(&queue->mtx, name, queueu->mtx_flags;
}

void
taskqueue_enqueue(struct taskqueue *queue, struct task *task)
{
	mtx_enter(&queue->mtx, queue->mtx_flags);
	/*
	 * Count multiple enqueues.
	 */
	if (task->pending) {
		task->pending++;
		mtx_exit(&queue->mtx, queue->mtx_flags);
		return;
	}
	STAILQ_INSERT_TAIL(&queue->queue, task, link);
	task->pending = 1;
	mtx_exit(&queue->mtx, queue->mtx_flags);
	if (queue->enqueue)
		queue->enqueue(queue);
}

void
taskqueue_run(struct taskqueue *queue)
{
	int s;
	struct task *task;
	int pending;

	mtx_enter(&queue->mtx, queue->mtx_flags); 
	while (STAILQ_FIRST(&queue->queue)) {
		/*
		 * Carefully remove the first task from the queue and
		 * zero its pending count.
		 */
		task = STAILQ_FIRST(&queue->queue);
		STAILQ_REMOVE_HEAD(&queue->queue, link);
		pending = task->pending;
		task->pending = 0;
		mtx_exit(&queue->mtx, queue->mtx_flags);
		task->func(task->arg, pending);
		mtx_enter(&queue->mtx, queue->mtx_flags);
	}
	mtx_exit(&queue->mtx, queue->mtx_flags);
}

/*
 * The following will probably work, but cheating
 * on locking probably ought not to be done until it has been
 * shown that in the particular path that the performance
 * is worth the potential risk.
 */

void
taskqueue_run(struct taskqueue *queue)
{
	int s;
	struct task *task;
	int pending;

	while (STAILQ_FIRST(&queue->queue)) {
		mtx_enter(&queue->mtx, queue->mtx_flags); 
		/*
		 * Carefully remove the first task from the queue and
		 * zero its pending count.
		 */
		task = STAILQ_FIRST(&queue->queue);
		STAILQ_REMOVE_HEAD(&queue->queue, link);
		pending = task->pending;
		task->pending = 0;
		mtx_exit(&queue->mtx, queue->mtx_flags);
		task->func(task->arg, pending);
	}
}



To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-arch" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200005141731.LAA04793>