freebsd_network: Rework taskqueues implementation.
* Use FreeBSD's queue management directly, only retaining thread creation and synchronization functions from Haiku. * Use FreeBSD's task structure declarations. * Use a standard non-FAST taskqueue for taskqueue_thread. * Rewrite header and adjust consumers as appropriate.
This commit is contained in:
@@ -85,10 +85,6 @@ void taskqgroup_config_gtask_init(void *ctx, struct grouptask *gtask,
|
||||
gtask_fn_t *fn, const char *name);
|
||||
void taskqgroup_config_gtask_deinit(struct grouptask *gtask);
|
||||
|
||||
#define TASK_ENQUEUED 0x1
|
||||
#define TASK_SKIP_WAKEUP 0x2
|
||||
#define TASK_NOENQUEUE 0x4
|
||||
|
||||
#define GTASK_INIT(gtask, flags, priority, func, context) do { \
|
||||
(gtask)->ta_flags = flags; \
|
||||
(gtask)->ta_priority = (priority); \
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018, Haiku Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef _FBSD_IFLIB_SYS_TASKQUEUE_H_
|
||||
#define _FBSD_IFLIB_SYS_TASKQUEUE_H_
|
||||
|
||||
/* include the real sys/taskqueue.h */
|
||||
#include_next <sys/taskqueue.h>
|
||||
|
||||
typedef void (*taskqueue_callback_fn)(void *context);
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
#endif /* _FBSD_IFLIB_SYS_TASKQUEUE_H_ */
|
||||
@@ -1,29 +1,63 @@
|
||||
/*
|
||||
* Copyright 2007 Haiku Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2000 Doug Rabson
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
#ifndef _FBSD_COMPAT_SYS__TASK_H_
|
||||
#define _FBSD_COMPAT_SYS__TASK_H_
|
||||
|
||||
#ifndef _SYS__TASK_H_
|
||||
#define _SYS__TASK_H_
|
||||
|
||||
/* Haiku's list management */
|
||||
#include <util/list.h>
|
||||
#include <sys/queue.h>
|
||||
|
||||
|
||||
typedef void (*task_fn_t)(void *context, int pending);
|
||||
/*
|
||||
* Each task includes a function which is called from
|
||||
* taskqueue_run(). The first argument is taken from the 'ta_context'
|
||||
* field of struct task and the second argument is a count of how many
|
||||
* times the task was enqueued before the call to taskqueue_run().
|
||||
*
|
||||
* List of locks
|
||||
* (c) const after init
|
||||
* (q) taskqueue lock
|
||||
*/
|
||||
typedef void task_fn_t(void *context, int pending);
|
||||
|
||||
struct task {
|
||||
int ta_pending;
|
||||
int ta_priority;
|
||||
int ta_flags;
|
||||
task_fn_t ta_handler;
|
||||
void *ta_argument;
|
||||
|
||||
struct list_link ta_link;
|
||||
STAILQ_ENTRY(task) ta_link; /* (q) link for queue */
|
||||
uint16_t ta_pending; /* (q) count times queued */
|
||||
uint8_t ta_priority; /* (c) Priority */
|
||||
uint8_t ta_flags; /* (c) Flags */
|
||||
task_fn_t *ta_func; /* (c) task handler */
|
||||
void *ta_context; /* (c) argument for handler */
|
||||
};
|
||||
|
||||
#define TASK_ENQUEUED (1<<0)
|
||||
#define TASK_NOENQUEUE (1<<1)
|
||||
#define TASK_SKIP_WAKEUP (1<<2)
|
||||
|
||||
#define TASK_NEEDSGIANT (1 << 0) /* Haiku extension, OpenBSD compatibility */
|
||||
#define TASK_NEEDSGIANT (1<<3) /* Haiku extension, OpenBSD compatibility */
|
||||
|
||||
|
||||
#endif
|
||||
#endif /* !_SYS__TASK_H_ */
|
||||
|
||||
@@ -1,15 +1,45 @@
|
||||
/*
|
||||
* Copyright 2007-2018, Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
/*-
|
||||
* Copyright (c) 2000 Doug Rabson
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
#ifndef _FBSD_COMPAT_SYS_TASKQUEUE_H_
|
||||
#define _FBSD_COMPAT_SYS_TASKQUEUE_H_
|
||||
|
||||
#ifndef _SYS_TASKQUEUE_H_
|
||||
#define _SYS_TASKQUEUE_H_
|
||||
|
||||
#ifndef _KERNEL
|
||||
#error "no user-servicable parts inside"
|
||||
#endif
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <sys/_task.h>
|
||||
#include <sys/callout.h>
|
||||
#include <sys/_callout.h>
|
||||
|
||||
struct taskqueue;
|
||||
struct taskqgroup;
|
||||
struct thread;
|
||||
|
||||
struct timeout_task {
|
||||
struct taskqueue *q;
|
||||
@@ -18,48 +48,148 @@ struct timeout_task {
|
||||
int f;
|
||||
};
|
||||
|
||||
#define TASKQUEUE_NAMELEN 64
|
||||
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
|
||||
#define TASKQUEUE_NAMELEN 32
|
||||
|
||||
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
|
||||
* taskqueue_create(). This function would normally be used to allow the
|
||||
* queue to arrange to run itself later (e.g., by scheduling a software
|
||||
* interrupt or waking a kernel thread).
|
||||
*/
|
||||
typedef void (*taskqueue_enqueue_fn)(void *context);
|
||||
|
||||
|
||||
struct taskqueue;
|
||||
struct taskqueue *taskqueue_create(const char *name, int mflags,
|
||||
taskqueue_enqueue_fn enqueue, void *context);
|
||||
int taskqueue_start_threads(struct taskqueue **tq, int count, int pri,
|
||||
const char *name, ...) __printflike(4, 5);
|
||||
void taskqueue_free(struct taskqueue *tq);
|
||||
void taskqueue_drain(struct taskqueue *tq, struct task *task);
|
||||
void taskqueue_drain_timeout(struct taskqueue *queue,
|
||||
struct timeout_task *timeout_task);
|
||||
void taskqueue_drain_all(struct taskqueue *tq);
|
||||
void taskqueue_block(struct taskqueue *queue);
|
||||
void taskqueue_unblock(struct taskqueue *queue);
|
||||
int taskqueue_enqueue(struct taskqueue *tq, struct task *task);
|
||||
int taskqueue_enqueue_timeout(struct taskqueue *queue,
|
||||
struct timeout_task *ttask, int _ticks);
|
||||
int taskqueue_cancel(struct taskqueue *queue, struct task *task,
|
||||
u_int *pendp);
|
||||
int taskqueue_cancel_timeout(struct taskqueue *queue,
|
||||
struct timeout_task *timeout_task, u_int *pendp);
|
||||
taskqueue_enqueue_fn enqueue,
|
||||
void *context);
|
||||
int taskqueue_start_threads(struct taskqueue **tqp, int count, int pri,
|
||||
const char *name, ...) __printflike(4, 5);
|
||||
int taskqueue_enqueue(struct taskqueue *queue, struct task *task);
|
||||
int taskqueue_enqueue_timeout(struct taskqueue *queue,
|
||||
struct timeout_task *timeout_task, int _ticks);
|
||||
int taskqueue_cancel(struct taskqueue *queue, struct task *task,
|
||||
u_int *pendp);
|
||||
int taskqueue_cancel_timeout(struct taskqueue *queue,
|
||||
struct timeout_task *timeout_task, u_int *pendp);
|
||||
void taskqueue_drain(struct taskqueue *queue, struct task *task);
|
||||
void taskqueue_drain_timeout(struct taskqueue *queue,
|
||||
struct timeout_task *timeout_task);
|
||||
void taskqueue_drain_all(struct taskqueue *queue);
|
||||
void taskqueue_free(struct taskqueue *queue);
|
||||
void taskqueue_run(struct taskqueue *queue);
|
||||
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);
|
||||
|
||||
void taskqueue_thread_enqueue(void *context);
|
||||
/*
|
||||
* Functions for dedicated thread taskqueues
|
||||
*/
|
||||
void taskqueue_thread_loop(void *arg);
|
||||
void taskqueue_thread_enqueue(void *context);
|
||||
|
||||
extern struct taskqueue *taskqueue_fast;
|
||||
extern struct taskqueue *taskqueue_swi;
|
||||
extern struct taskqueue *taskqueue_thread;
|
||||
|
||||
int taskqueue_enqueue_fast(struct taskqueue *queue, struct task *task);
|
||||
struct taskqueue *taskqueue_create_fast(const char *name, int mflags,
|
||||
taskqueue_enqueue_fn enqueue, void *context);
|
||||
|
||||
void task_init(struct task *, int prio, task_fn_t handler, void *arg);
|
||||
#define TASK_INIT(taskp, prio, hand, arg) task_init(taskp, prio, hand, arg)
|
||||
/*
|
||||
* Initialise a task structure.
|
||||
*/
|
||||
void _task_init(struct task *, int prio, task_fn_t handler, void *arg);
|
||||
#define TASK_INIT(taskp, prio, hand, arg) _task_init(taskp, prio, hand, arg)
|
||||
#define NET_TASK_INIT TASK_INIT
|
||||
|
||||
void timeout_task_init(struct taskqueue *queue, struct timeout_task *timeout_task,
|
||||
int priority, task_fn_t func, void *context);
|
||||
void _timeout_task_init(struct taskqueue *queue,
|
||||
struct timeout_task *timeout_task, int priority, task_fn_t func,
|
||||
void *context);
|
||||
#define TIMEOUT_TASK_INIT(queue, timeout_task, priority, func, context) \
|
||||
timeout_task_init(queue, timeout_task, priority, func, context);
|
||||
_timeout_task_init(queue, timeout_task, priority, func, context);
|
||||
|
||||
#endif
|
||||
/*
|
||||
* Declare a reference to a taskqueue.
|
||||
*/
|
||||
#define TASKQUEUE_DECLARE(name) \
|
||||
extern struct taskqueue *taskqueue_##name
|
||||
|
||||
/*
|
||||
* Define and initialise a global taskqueue that uses sleep mutexes.
|
||||
*/
|
||||
#define TASKQUEUE_DEFINE(name, enqueue, context, init) \
|
||||
\
|
||||
struct taskqueue *taskqueue_##name; \
|
||||
\
|
||||
static void \
|
||||
taskqueue_define_##name(void *arg) \
|
||||
{ \
|
||||
taskqueue_##name = \
|
||||
taskqueue_create(#name, M_WAITOK, (enqueue), (context)); \
|
||||
init; \
|
||||
} \
|
||||
\
|
||||
SYSINIT(taskqueue_##name, SI_SUB_INIT_IF, SI_ORDER_SECOND, \
|
||||
taskqueue_define_##name, NULL); \
|
||||
\
|
||||
struct __hack
|
||||
#define TASKQUEUE_DEFINE_THREAD(name) \
|
||||
TASKQUEUE_DEFINE(name, taskqueue_thread_enqueue, &taskqueue_##name, \
|
||||
taskqueue_start_threads(&taskqueue_##name, 1, PWAIT, \
|
||||
"%s taskq", #name))
|
||||
|
||||
/*
|
||||
* Define and initialise a global taskqueue that uses spin mutexes.
|
||||
*/
|
||||
#define TASKQUEUE_FAST_DEFINE(name, enqueue, context, init) \
|
||||
\
|
||||
struct taskqueue *taskqueue_##name; \
|
||||
\
|
||||
static void \
|
||||
taskqueue_define_##name(void *arg) \
|
||||
{ \
|
||||
taskqueue_##name = \
|
||||
taskqueue_create_fast(#name, M_WAITOK, (enqueue), \
|
||||
(context)); \
|
||||
init; \
|
||||
} \
|
||||
\
|
||||
SYSINIT(taskqueue_##name, SI_SUB_INIT_IF, SI_ORDER_SECOND, \
|
||||
taskqueue_define_##name, NULL); \
|
||||
\
|
||||
struct __hack
|
||||
#define TASKQUEUE_FAST_DEFINE_THREAD(name) \
|
||||
TASKQUEUE_FAST_DEFINE(name, taskqueue_thread_enqueue, \
|
||||
&taskqueue_##name, taskqueue_start_threads(&taskqueue_##name \
|
||||
1, PWAIT, "%s taskq", #name))
|
||||
|
||||
/*
|
||||
* These queues are serviced by software interrupt handlers. To enqueue
|
||||
* a task, call taskqueue_enqueue(taskqueue_swi, &task) or
|
||||
* taskqueue_enqueue(taskqueue_swi_giant, &task).
|
||||
*/
|
||||
TASKQUEUE_DECLARE(swi_giant);
|
||||
TASKQUEUE_DECLARE(swi);
|
||||
|
||||
/*
|
||||
* This queue is serviced by a kernel thread. To enqueue a task, call
|
||||
* taskqueue_enqueue(taskqueue_thread, &task).
|
||||
*/
|
||||
TASKQUEUE_DECLARE(thread);
|
||||
|
||||
/*
|
||||
* Queue for swi handlers dispatched from fast interrupt handlers.
|
||||
* These are necessarily different from the above because the queue
|
||||
* must be locked with spinlocks since sleep mutex's cannot be used
|
||||
* from a fast interrupt handler context.
|
||||
*/
|
||||
TASKQUEUE_DECLARE(fast);
|
||||
struct taskqueue *taskqueue_create_fast(const char *name, int mflags,
|
||||
taskqueue_enqueue_fn enqueue,
|
||||
void *context);
|
||||
|
||||
#endif /* !_SYS_TASKQUEUE_H_ */
|
||||
|
||||
@@ -0,0 +1,519 @@
|
||||
/*-
|
||||
* Copyright (c) 2000 Doug Rabson
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/kthread.h>
|
||||
#include <sys/libkern.h>
|
||||
#include <sys/limits.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/smp.h>
|
||||
#include <sys/taskqueue.h>
|
||||
#include <machine/stdarg.h>
|
||||
|
||||
static MALLOC_DEFINE(M_TASKQUEUE, "taskqueue", "Task Queues");
|
||||
static void *taskqueue_giant_ih;
|
||||
static void *taskqueue_ih;
|
||||
|
||||
struct taskqueue_busy {
|
||||
struct task *tb_running;
|
||||
TAILQ_ENTRY(taskqueue_busy) tb_link;
|
||||
};
|
||||
|
||||
struct task * const TB_DRAIN_WAITER = (struct task *)0x1;
|
||||
|
||||
struct taskqueue {
|
||||
STAILQ_HEAD(, task) tq_queue;
|
||||
taskqueue_enqueue_fn tq_enqueue;
|
||||
void *tq_context;
|
||||
char *tq_name;
|
||||
TAILQ_HEAD(, taskqueue_busy) tq_active;
|
||||
struct mtx tq_mutex;
|
||||
#ifdef __HAIKU__
|
||||
sem_id tq_sem;
|
||||
thread_id *tq_threads;
|
||||
thread_id tq_thread_storage;
|
||||
int tq_threadcount;
|
||||
#else
|
||||
struct thread **tq_threads;
|
||||
#endif
|
||||
int tq_tcount;
|
||||
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)
|
||||
#define TQ_FLAGS_BLOCKED (1 << 1)
|
||||
#define TQ_FLAGS_UNLOCKED_ENQUEUE (1 << 2)
|
||||
|
||||
#define DT_CALLOUT_ARMED (1 << 0)
|
||||
#define DT_DRAIN_IN_PROGRESS (1 << 1)
|
||||
|
||||
#define TQ_LOCK(tq) \
|
||||
do { \
|
||||
if ((tq)->tq_spin) \
|
||||
mtx_lock_spin(&(tq)->tq_mutex); \
|
||||
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 { \
|
||||
if ((tq)->tq_spin) \
|
||||
mtx_unlock_spin(&(tq)->tq_mutex); \
|
||||
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,
|
||||
int priority, task_fn_t func, void *context)
|
||||
{
|
||||
|
||||
TASK_INIT(&timeout_task->t, priority, func, context);
|
||||
callout_init_mtx(&timeout_task->c, &queue->tq_mutex,
|
||||
CALLOUT_RETURNUNLOCKED);
|
||||
timeout_task->q = queue;
|
||||
timeout_task->f = 0;
|
||||
}
|
||||
|
||||
static struct taskqueue *
|
||||
_taskqueue_create(const char *name, int mflags,
|
||||
taskqueue_enqueue_fn enqueue, void *context,
|
||||
int mtxflags, const char *mtxname __unused)
|
||||
{
|
||||
struct taskqueue *queue;
|
||||
char *tq_name;
|
||||
|
||||
tq_name = malloc(TASKQUEUE_NAMELEN, M_TASKQUEUE, mflags | M_ZERO);
|
||||
if (tq_name == NULL)
|
||||
return (NULL);
|
||||
|
||||
queue = malloc(sizeof(struct taskqueue), M_TASKQUEUE, mflags | M_ZERO);
|
||||
if (queue == NULL) {
|
||||
free(tq_name, M_TASKQUEUE);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
snprintf(tq_name, TASKQUEUE_NAMELEN, "%s", (name) ? name : "taskqueue");
|
||||
|
||||
STAILQ_INIT(&queue->tq_queue);
|
||||
TAILQ_INIT(&queue->tq_active);
|
||||
queue->tq_enqueue = enqueue;
|
||||
queue->tq_context = context;
|
||||
queue->tq_name = tq_name;
|
||||
queue->tq_spin = (mtxflags & MTX_SPIN) != 0;
|
||||
queue->tq_flags |= TQ_FLAGS_ACTIVE;
|
||||
if (enqueue == taskqueue_thread_enqueue)
|
||||
queue->tq_flags |= TQ_FLAGS_UNLOCKED_ENQUEUE;
|
||||
mtx_init(&queue->tq_mutex, tq_name, NULL, mtxflags);
|
||||
|
||||
return (queue);
|
||||
}
|
||||
|
||||
struct taskqueue *
|
||||
taskqueue_create(const char *name, int mflags,
|
||||
taskqueue_enqueue_fn enqueue, void *context)
|
||||
{
|
||||
|
||||
return _taskqueue_create(name, mflags, enqueue, context,
|
||||
MTX_DEF, name);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void
|
||||
taskqueue_free(struct taskqueue *queue)
|
||||
{
|
||||
|
||||
TQ_LOCK(queue);
|
||||
queue->tq_flags &= ~TQ_FLAGS_ACTIVE;
|
||||
taskqueue_terminate(queue->tq_threads, queue);
|
||||
KASSERT(TAILQ_EMPTY(&queue->tq_active), ("Tasks still running?"));
|
||||
KASSERT(queue->tq_callouts == 0, ("Armed timeout tasks"));
|
||||
mtx_destroy(&queue->tq_mutex);
|
||||
free(queue->tq_threads, M_TASKQUEUE);
|
||||
free(queue->tq_name, M_TASKQUEUE);
|
||||
free(queue, M_TASKQUEUE);
|
||||
}
|
||||
|
||||
static int
|
||||
taskqueue_enqueue_locked(struct taskqueue *queue, struct task *task)
|
||||
{
|
||||
struct task *ins;
|
||||
struct task *prev;
|
||||
|
||||
KASSERT(task->ta_func != NULL, ("enqueueing task with NULL func"));
|
||||
/*
|
||||
* Count multiple enqueues.
|
||||
*/
|
||||
if (task->ta_pending) {
|
||||
if (task->ta_pending < USHRT_MAX)
|
||||
task->ta_pending++;
|
||||
TQ_UNLOCK(queue);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Optimise the case when all tasks have the same priority.
|
||||
*/
|
||||
prev = STAILQ_LAST(&queue->tq_queue, task, ta_link);
|
||||
if (!prev || prev->ta_priority >= task->ta_priority) {
|
||||
STAILQ_INSERT_TAIL(&queue->tq_queue, task, ta_link);
|
||||
} else {
|
||||
prev = NULL;
|
||||
for (ins = STAILQ_FIRST(&queue->tq_queue); ins;
|
||||
prev = ins, ins = STAILQ_NEXT(ins, ta_link))
|
||||
if (ins->ta_priority < task->ta_priority)
|
||||
break;
|
||||
|
||||
if (prev)
|
||||
STAILQ_INSERT_AFTER(&queue->tq_queue, prev, task, ta_link);
|
||||
else
|
||||
STAILQ_INSERT_HEAD(&queue->tq_queue, task, ta_link);
|
||||
}
|
||||
|
||||
task->ta_pending = 1;
|
||||
if ((queue->tq_flags & TQ_FLAGS_UNLOCKED_ENQUEUE) != 0)
|
||||
TQ_UNLOCK(queue);
|
||||
if ((queue->tq_flags & TQ_FLAGS_BLOCKED) == 0)
|
||||
queue->tq_enqueue(queue->tq_context);
|
||||
if ((queue->tq_flags & TQ_FLAGS_UNLOCKED_ENQUEUE) == 0)
|
||||
TQ_UNLOCK(queue);
|
||||
|
||||
/* Return with lock released. */
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
taskqueue_enqueue(struct taskqueue *queue, struct task *task)
|
||||
{
|
||||
int res;
|
||||
|
||||
TQ_LOCK(queue);
|
||||
res = taskqueue_enqueue_locked(queue, task);
|
||||
/* The lock is released inside. */
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
static void
|
||||
taskqueue_timeout_func(void *arg)
|
||||
{
|
||||
struct taskqueue *queue;
|
||||
struct timeout_task *timeout_task;
|
||||
|
||||
timeout_task = arg;
|
||||
queue = timeout_task->q;
|
||||
KASSERT((timeout_task->f & DT_CALLOUT_ARMED) != 0, ("Stray timeout"));
|
||||
timeout_task->f &= ~DT_CALLOUT_ARMED;
|
||||
queue->tq_callouts--;
|
||||
taskqueue_enqueue_locked(timeout_task->q, &timeout_task->t);
|
||||
/* The lock is released inside. */
|
||||
}
|
||||
|
||||
int
|
||||
taskqueue_enqueue_timeout(struct taskqueue *queue,
|
||||
struct timeout_task *timeout_task, int _ticks)
|
||||
{
|
||||
int res;
|
||||
|
||||
TQ_LOCK(queue);
|
||||
KASSERT(timeout_task->q == NULL || timeout_task->q == queue,
|
||||
("Migrated queue"));
|
||||
KASSERT(!queue->tq_spin, ("Timeout for spin-queue"));
|
||||
timeout_task->q = queue;
|
||||
res = timeout_task->t.ta_pending;
|
||||
if (timeout_task->f & DT_DRAIN_IN_PROGRESS) {
|
||||
/* Do nothing */
|
||||
TQ_UNLOCK(queue);
|
||||
res = -1;
|
||||
} else if (_ticks == 0) {
|
||||
taskqueue_enqueue_locked(queue, &timeout_task->t);
|
||||
/* The lock is released inside. */
|
||||
} else {
|
||||
if ((timeout_task->f & DT_CALLOUT_ARMED) != 0) {
|
||||
res++;
|
||||
} else {
|
||||
queue->tq_callouts++;
|
||||
timeout_task->f |= DT_CALLOUT_ARMED;
|
||||
if (_ticks < 0)
|
||||
_ticks = -_ticks; /* Ignore overflow. */
|
||||
}
|
||||
if (_ticks > 0) {
|
||||
callout_reset(&timeout_task->c, _ticks,
|
||||
taskqueue_timeout_func, timeout_task);
|
||||
}
|
||||
TQ_UNLOCK(queue);
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
static void
|
||||
taskqueue_task_nop_fn(void *context, int pending)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
taskqueue_block(struct taskqueue *queue)
|
||||
{
|
||||
|
||||
TQ_LOCK(queue);
|
||||
queue->tq_flags |= TQ_FLAGS_BLOCKED;
|
||||
TQ_UNLOCK(queue);
|
||||
}
|
||||
|
||||
void
|
||||
taskqueue_unblock(struct taskqueue *queue)
|
||||
{
|
||||
|
||||
TQ_LOCK(queue);
|
||||
queue->tq_flags &= ~TQ_FLAGS_BLOCKED;
|
||||
if (!STAILQ_EMPTY(&queue->tq_queue))
|
||||
queue->tq_enqueue(queue->tq_context);
|
||||
TQ_UNLOCK(queue);
|
||||
}
|
||||
|
||||
static void
|
||||
taskqueue_run_locked(struct taskqueue *queue)
|
||||
{
|
||||
struct taskqueue_busy tb;
|
||||
struct taskqueue_busy *tb_first;
|
||||
struct task *task;
|
||||
int pending;
|
||||
|
||||
KASSERT(queue != NULL, ("tq is NULL"));
|
||||
TQ_ASSERT_LOCKED(queue);
|
||||
tb.tb_running = NULL;
|
||||
|
||||
while (STAILQ_FIRST(&queue->tq_queue)) {
|
||||
TAILQ_INSERT_TAIL(&queue->tq_active, &tb, tb_link);
|
||||
|
||||
/*
|
||||
* Carefully remove the first task from the queue and
|
||||
* zero its pending count.
|
||||
*/
|
||||
task = STAILQ_FIRST(&queue->tq_queue);
|
||||
KASSERT(task != NULL, ("task is NULL"));
|
||||
STAILQ_REMOVE_HEAD(&queue->tq_queue, ta_link);
|
||||
pending = task->ta_pending;
|
||||
task->ta_pending = 0;
|
||||
tb.tb_running = task;
|
||||
TQ_UNLOCK(queue);
|
||||
|
||||
KASSERT(task->ta_func != NULL, ("task->ta_func is NULL"));
|
||||
#ifdef __HAIKU__
|
||||
if ((task->ta_flags & TASK_NEEDSGIANT) != 0)
|
||||
mtx_lock(&Giant);
|
||||
#endif
|
||||
task->ta_func(task->ta_context, pending);
|
||||
#ifdef __HAIKU__
|
||||
if ((task->ta_flags & TASK_NEEDSGIANT) != 0)
|
||||
mtx_unlock(&Giant);
|
||||
#endif
|
||||
|
||||
TQ_LOCK(queue);
|
||||
tb.tb_running = NULL;
|
||||
|
||||
TAILQ_REMOVE(&queue->tq_active, &tb, tb_link);
|
||||
tb_first = TAILQ_FIRST(&queue->tq_active);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
taskqueue_run(struct taskqueue *queue)
|
||||
{
|
||||
|
||||
TQ_LOCK(queue);
|
||||
taskqueue_run_locked(queue);
|
||||
TQ_UNLOCK(queue);
|
||||
}
|
||||
|
||||
static int
|
||||
task_is_running(struct taskqueue *queue, struct task *task)
|
||||
{
|
||||
struct taskqueue_busy *tb;
|
||||
|
||||
TQ_ASSERT_LOCKED(queue);
|
||||
TAILQ_FOREACH(tb, &queue->tq_active, tb_link) {
|
||||
if (tb->tb_running == task)
|
||||
return (1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
taskqueue_cancel_locked(struct taskqueue *queue, struct task *task,
|
||||
u_int *pendp)
|
||||
{
|
||||
|
||||
if (task->ta_pending > 0)
|
||||
STAILQ_REMOVE(&queue->tq_queue, task, task, ta_link);
|
||||
if (pendp != NULL)
|
||||
*pendp = task->ta_pending;
|
||||
task->ta_pending = 0;
|
||||
return (task_is_running(queue, task) ? EBUSY : 0);
|
||||
}
|
||||
|
||||
int
|
||||
taskqueue_cancel(struct taskqueue *queue, struct task *task, u_int *pendp)
|
||||
{
|
||||
int error;
|
||||
|
||||
TQ_LOCK(queue);
|
||||
error = taskqueue_cancel_locked(queue, task, pendp);
|
||||
TQ_UNLOCK(queue);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
taskqueue_cancel_timeout(struct taskqueue *queue,
|
||||
struct timeout_task *timeout_task, u_int *pendp)
|
||||
{
|
||||
u_int pending, pending1;
|
||||
int error;
|
||||
|
||||
TQ_LOCK(queue);
|
||||
pending = !!(callout_stop(&timeout_task->c) > 0);
|
||||
error = taskqueue_cancel_locked(queue, &timeout_task->t, &pending1);
|
||||
if ((timeout_task->f & DT_CALLOUT_ARMED) != 0) {
|
||||
timeout_task->f &= ~DT_CALLOUT_ARMED;
|
||||
queue->tq_callouts--;
|
||||
}
|
||||
TQ_UNLOCK(queue);
|
||||
|
||||
if (pendp != NULL)
|
||||
*pendp = pending + pending1;
|
||||
return (error);
|
||||
}
|
||||
|
||||
void
|
||||
taskqueue_drain_timeout(struct taskqueue *queue,
|
||||
struct timeout_task *timeout_task)
|
||||
{
|
||||
|
||||
/*
|
||||
* Set flag to prevent timer from re-starting during drain:
|
||||
*/
|
||||
TQ_LOCK(queue);
|
||||
KASSERT((timeout_task->f & DT_DRAIN_IN_PROGRESS) == 0,
|
||||
("Drain already in progress"));
|
||||
timeout_task->f |= DT_DRAIN_IN_PROGRESS;
|
||||
TQ_UNLOCK(queue);
|
||||
|
||||
callout_drain(&timeout_task->c);
|
||||
taskqueue_drain(queue, &timeout_task->t);
|
||||
|
||||
/*
|
||||
* Clear flag to allow timer to re-start:
|
||||
*/
|
||||
TQ_LOCK(queue);
|
||||
timeout_task->f &= ~DT_DRAIN_IN_PROGRESS;
|
||||
TQ_UNLOCK(queue);
|
||||
}
|
||||
|
||||
int
|
||||
taskqueue_start_threads(struct taskqueue **tqp, int count, int pri,
|
||||
const char *format, ...)
|
||||
{
|
||||
char name[64];
|
||||
int error;
|
||||
va_list vl;
|
||||
|
||||
va_start(vl, format);
|
||||
vsnprintf(name, sizeof(name), format, vl);
|
||||
va_end(vl);
|
||||
|
||||
error = _taskqueue_start_threads(tqp, count, pri, name);
|
||||
return (error);
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
int
|
||||
taskqueue_member(struct taskqueue *queue, struct thread *td)
|
||||
{
|
||||
int i, j, ret = 0;
|
||||
|
||||
for (i = 0, j = 0; ; i++) {
|
||||
if (queue->tq_threads[i] == NULL)
|
||||
continue;
|
||||
if (queue->tq_threads[i] == td) {
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
if (++j >= queue->tq_tcount)
|
||||
break;
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
struct taskqueue *
|
||||
taskqueue_create_fast(const char *name, int mflags,
|
||||
taskqueue_enqueue_fn enqueue, void *context)
|
||||
{
|
||||
return _taskqueue_create(name, mflags, enqueue, context,
|
||||
MTX_SPIN, "fast_taskqueue");
|
||||
}
|
||||
@@ -1,13 +1,8 @@
|
||||
/*
|
||||
* Copyright 2009, Colin Günther, [email protected]
|
||||
* Copyright 2007, Hugo Santos. All Rights Reserved.
|
||||
* Copyright 2022, Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Hugo Santos, [email protected]
|
||||
*/
|
||||
|
||||
|
||||
#include "device.h"
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -17,125 +12,42 @@
|
||||
#include <compat/sys/haiku-module.h>
|
||||
|
||||
|
||||
#define TQ_FLAGS_ACTIVE (1 << 0)
|
||||
#define TQ_FLAGS_BLOCKED (1 << 1)
|
||||
#define TQ_FLAGS_PENDING (1 << 2)
|
||||
static int _taskqueue_start_threads(struct taskqueue **taskQueue,
|
||||
int count, int priority, const char *name);
|
||||
static void taskqueue_terminate(struct thread **pp, struct taskqueue *tq);
|
||||
|
||||
#define DT_CALLOUT_ARMED (1 << 0)
|
||||
#define DT_DRAIN_IN_PROGRESS (1 << 1)
|
||||
|
||||
struct taskqueue {
|
||||
char tq_name[TASKQUEUE_NAMELEN];
|
||||
struct mtx tq_mutex;
|
||||
struct list tq_list;
|
||||
taskqueue_enqueue_fn tq_enqueue;
|
||||
void *tq_arg;
|
||||
int tq_fast;
|
||||
sem_id tq_sem;
|
||||
thread_id *tq_threads;
|
||||
thread_id tq_thread_storage;
|
||||
int tq_threadcount;
|
||||
int tq_flags;
|
||||
int tq_callouts;
|
||||
};
|
||||
#define malloc kernel_malloc
|
||||
#define free kernel_free
|
||||
#include "fbsd_subr_taskqueue.c"
|
||||
#undef malloc
|
||||
#undef free
|
||||
|
||||
|
||||
struct taskqueue *taskqueue_fast = NULL;
|
||||
struct taskqueue *taskqueue_swi = NULL;
|
||||
struct taskqueue *taskqueue_thread = NULL;
|
||||
|
||||
|
||||
static struct taskqueue *
|
||||
_taskqueue_create(const char *name, int mflags, int fast,
|
||||
taskqueue_enqueue_fn enqueueFunction, void *context)
|
||||
{
|
||||
struct taskqueue *tq = malloc(sizeof(struct taskqueue));
|
||||
if (tq == NULL)
|
||||
return NULL;
|
||||
|
||||
tq->tq_fast = fast;
|
||||
|
||||
if (fast) {
|
||||
mtx_init(&tq->tq_mutex, name, NULL, MTX_SPIN);
|
||||
} else {
|
||||
mtx_init(&tq->tq_mutex, name, NULL, MTX_DEF);
|
||||
}
|
||||
|
||||
strlcpy(tq->tq_name, name, sizeof(tq->tq_name));
|
||||
list_init_etc(&tq->tq_list, offsetof(struct task, ta_link));
|
||||
tq->tq_enqueue = enqueueFunction;
|
||||
tq->tq_arg = context;
|
||||
|
||||
tq->tq_sem = -1;
|
||||
tq->tq_threads = NULL;
|
||||
tq->tq_threadcount = 0;
|
||||
tq->tq_flags = TQ_FLAGS_ACTIVE;
|
||||
tq->tq_callouts = 0;
|
||||
|
||||
return tq;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
tq_lock(struct taskqueue *taskQueue)
|
||||
{
|
||||
if (taskQueue->tq_fast) {
|
||||
mtx_lock_spin(&taskQueue->tq_mutex);
|
||||
} else {
|
||||
mtx_lock(&taskQueue->tq_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
tq_unlock(struct taskqueue *taskQueue)
|
||||
{
|
||||
if (taskQueue->tq_fast) {
|
||||
mtx_unlock_spin(&taskQueue->tq_mutex);
|
||||
} else {
|
||||
mtx_unlock(&taskQueue->tq_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct taskqueue *
|
||||
taskqueue_create(const char *name, int mflags,
|
||||
taskqueue_enqueue_fn enqueueFunction, void *context)
|
||||
{
|
||||
return _taskqueue_create(name, mflags, 0, enqueueFunction, context);
|
||||
}
|
||||
|
||||
|
||||
static int32
|
||||
tq_handle_thread(void *data)
|
||||
{
|
||||
struct taskqueue *tq = data;
|
||||
struct task *t;
|
||||
int pending;
|
||||
sem_id sem;
|
||||
|
||||
/* just a synchronization point */
|
||||
tq_lock(tq);
|
||||
sem = tq->tq_sem;
|
||||
tq_unlock(tq);
|
||||
taskqueue_run_callback(tq, TASKQUEUE_CALLBACK_TYPE_INIT);
|
||||
|
||||
TQ_LOCK(tq);
|
||||
sem_id sem = tq->tq_sem;
|
||||
TQ_UNLOCK(tq);
|
||||
|
||||
while (acquire_sem(sem) == B_NO_ERROR) {
|
||||
tq_lock(tq);
|
||||
t = list_remove_head_item(&tq->tq_list);
|
||||
tq_unlock(tq);
|
||||
if (t == NULL)
|
||||
continue;
|
||||
pending = t->ta_pending;
|
||||
t->ta_pending = 0;
|
||||
|
||||
if ((t->ta_flags & TASK_NEEDSGIANT) != 0)
|
||||
mtx_lock(&Giant);
|
||||
|
||||
t->ta_handler(t->ta_argument, pending);
|
||||
|
||||
if ((t->ta_flags & TASK_NEEDSGIANT) != 0)
|
||||
mtx_unlock(&Giant);
|
||||
TQ_LOCK(tq);
|
||||
taskqueue_run_locked(tq);
|
||||
TQ_UNLOCK(tq);
|
||||
}
|
||||
|
||||
taskqueue_run_callback(tq, TASKQUEUE_CALLBACK_TYPE_SHUTDOWN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -193,56 +105,21 @@ _taskqueue_start_threads(struct taskqueue **taskQueue, int count, int priority,
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
taskqueue_start_threads(struct taskqueue **taskQueue, int count, int priority,
|
||||
const char *format, ...)
|
||||
static void
|
||||
taskqueue_terminate(struct thread **pp, struct taskqueue *tq)
|
||||
{
|
||||
/* we assume that start_threads is called in a sane place, and thus
|
||||
* don't need to be locked. This is mostly due to the fact that if
|
||||
* the TQ is 'fast', locking the TQ disables interrupts... and then
|
||||
* we can't create semaphores, threads and bananas. */
|
||||
|
||||
char name[64];
|
||||
int result;
|
||||
va_list vl;
|
||||
|
||||
va_start(vl, format);
|
||||
vsnprintf(name, sizeof(name), format, vl);
|
||||
va_end(vl);
|
||||
|
||||
/*tq_lock(*tqp);*/
|
||||
result = _taskqueue_start_threads(taskQueue, count, priority, name);
|
||||
/*tq_unlock(*tqp);*/
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
taskqueue_free(struct taskqueue *taskQueue)
|
||||
{
|
||||
if (taskQueue == NULL)
|
||||
if (tq->tq_sem == -1)
|
||||
return;
|
||||
|
||||
/* lock and drain list? */
|
||||
taskQueue->tq_flags &= ~TQ_FLAGS_ACTIVE;
|
||||
if (!taskQueue->tq_fast)
|
||||
mtx_destroy(&taskQueue->tq_mutex);
|
||||
if (taskQueue->tq_sem != -1) {
|
||||
int i;
|
||||
delete_sem(tq->tq_sem);
|
||||
|
||||
delete_sem(taskQueue->tq_sem);
|
||||
|
||||
for (i = 0; i < taskQueue->tq_threadcount; i++) {
|
||||
status_t status;
|
||||
wait_for_thread(taskQueue->tq_threads[i], &status);
|
||||
}
|
||||
|
||||
if (taskQueue->tq_threadcount > 1)
|
||||
free(taskQueue->tq_threads);
|
||||
for (int i = 0; i < tq->tq_threadcount; i++) {
|
||||
status_t status;
|
||||
wait_for_thread(tq->tq_threads[i], &status);
|
||||
}
|
||||
|
||||
free(taskQueue);
|
||||
if (tq->tq_threadcount > 1)
|
||||
free(tq->tq_threads);
|
||||
}
|
||||
|
||||
|
||||
@@ -252,44 +129,13 @@ taskqueue_drain(struct taskqueue *taskQueue, struct task *task)
|
||||
if (taskQueue == NULL)
|
||||
return;
|
||||
|
||||
tq_lock(taskQueue);
|
||||
while (task->ta_pending != 0) {
|
||||
tq_unlock(taskQueue);
|
||||
TQ_LOCK(taskQueue);
|
||||
while (task->ta_pending != 0 || task_is_running(taskQueue, task)) {
|
||||
TQ_UNLOCK(taskQueue);
|
||||
snooze(0);
|
||||
tq_lock(taskQueue);
|
||||
TQ_LOCK(taskQueue);
|
||||
}
|
||||
tq_unlock(taskQueue);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
taskqueue_drain_timeout(struct taskqueue *queue,
|
||||
struct timeout_task *timeout_task)
|
||||
{
|
||||
/*
|
||||
* Set flag to prevent timer from re-starting during drain:
|
||||
*/
|
||||
tq_lock(queue);
|
||||
KASSERT((timeout_task->f & DT_DRAIN_IN_PROGRESS) == 0,
|
||||
("Drain already in progress"));
|
||||
timeout_task->f |= DT_DRAIN_IN_PROGRESS;
|
||||
tq_unlock(queue);
|
||||
|
||||
callout_drain(&timeout_task->c);
|
||||
taskqueue_drain(queue, &timeout_task->t);
|
||||
|
||||
/*
|
||||
* Clear flag to allow timer to re-start:
|
||||
*/
|
||||
tq_lock(queue);
|
||||
timeout_task->f &= ~DT_DRAIN_IN_PROGRESS;
|
||||
tq_unlock(queue);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
taskqueue_task_nop_fn(void* context, int pending)
|
||||
{
|
||||
TQ_UNLOCK(taskQueue);
|
||||
}
|
||||
|
||||
|
||||
@@ -309,165 +155,6 @@ taskqueue_drain_all(struct taskqueue *taskQueue)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
taskqueue_enqueue_locked(struct taskqueue *taskQueue, struct task *task)
|
||||
{
|
||||
/* we don't really support priorities */
|
||||
if (task->ta_pending) {
|
||||
task->ta_pending++;
|
||||
} else {
|
||||
list_add_item(&taskQueue->tq_list, task);
|
||||
task->ta_pending = 1;
|
||||
if ((taskQueue->tq_flags & TQ_FLAGS_BLOCKED) == 0)
|
||||
taskQueue->tq_enqueue(taskQueue->tq_arg);
|
||||
else
|
||||
taskQueue->tq_flags |= TQ_FLAGS_PENDING;
|
||||
}
|
||||
tq_unlock(taskQueue);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
taskqueue_enqueue(struct taskqueue *taskQueue, struct task *task)
|
||||
{
|
||||
tq_lock(taskQueue);
|
||||
taskqueue_enqueue_locked(taskQueue, task);
|
||||
/* The lock is released inside. */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
taskqueue_timeout_func(void *arg)
|
||||
{
|
||||
struct taskqueue *queue;
|
||||
struct timeout_task *timeout_task;
|
||||
|
||||
timeout_task = arg;
|
||||
queue = timeout_task->q;
|
||||
KASSERT((timeout_task->f & DT_CALLOUT_ARMED) != 0,
|
||||
("stray timeout ('%s')", timeout_task->q->tq_name));
|
||||
timeout_task->f &= ~DT_CALLOUT_ARMED;
|
||||
queue->tq_callouts--;
|
||||
taskqueue_enqueue_locked(timeout_task->q, &timeout_task->t);
|
||||
/* The lock is released inside. */
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
taskqueue_enqueue_timeout(struct taskqueue *queue,
|
||||
struct timeout_task *ttask, int _ticks)
|
||||
{
|
||||
int res;
|
||||
|
||||
tq_lock(queue);
|
||||
KASSERT(ttask->q == NULL || ttask->q == queue,
|
||||
("Migrated queue"));
|
||||
ttask->q = queue;
|
||||
res = ttask->t.ta_pending;
|
||||
if (ttask->f & DT_DRAIN_IN_PROGRESS) {
|
||||
/* Do nothing */
|
||||
tq_unlock(queue);
|
||||
res = -1;
|
||||
} else if (_ticks == 0) {
|
||||
tq_unlock(queue);
|
||||
taskqueue_enqueue(queue, &ttask->t);
|
||||
} else {
|
||||
if ((ttask->f & DT_CALLOUT_ARMED) != 0) {
|
||||
res++;
|
||||
} else {
|
||||
queue->tq_callouts++;
|
||||
ttask->f |= DT_CALLOUT_ARMED;
|
||||
if (_ticks < 0)
|
||||
_ticks = -_ticks; /* Ignore overflow. */
|
||||
}
|
||||
tq_unlock(queue);
|
||||
if (_ticks > 0) {
|
||||
callout_reset(&ttask->c, _ticks,
|
||||
taskqueue_timeout_func, ttask);
|
||||
}
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
taskqueue_cancel_locked(struct taskqueue *queue, struct task *task,
|
||||
u_int *pendp)
|
||||
{
|
||||
if (task->ta_pending > 0)
|
||||
list_remove_item(&queue->tq_list, task);
|
||||
if (pendp != NULL)
|
||||
*pendp = task->ta_pending;
|
||||
task->ta_pending = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
taskqueue_cancel(struct taskqueue *queue, struct task *task, u_int *pendp)
|
||||
{
|
||||
int error;
|
||||
|
||||
tq_lock(queue);
|
||||
error = taskqueue_cancel_locked(queue, task, pendp);
|
||||
tq_unlock(queue);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
taskqueue_cancel_timeout(struct taskqueue *queue,
|
||||
struct timeout_task *timeout_task, u_int *pendp)
|
||||
{
|
||||
u_int pending, pending1;
|
||||
int error;
|
||||
|
||||
tq_lock(queue);
|
||||
pending = !!(callout_stop(&timeout_task->c) > 0);
|
||||
error = taskqueue_cancel_locked(queue, &timeout_task->t, &pending1);
|
||||
if ((timeout_task->f & DT_CALLOUT_ARMED) != 0) {
|
||||
timeout_task->f &= ~DT_CALLOUT_ARMED;
|
||||
queue->tq_callouts--;
|
||||
}
|
||||
tq_unlock(queue);
|
||||
|
||||
if (pendp != NULL)
|
||||
*pendp = pending + pending1;
|
||||
return (error);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
taskqueue_block(struct taskqueue *taskQueue)
|
||||
{
|
||||
if (taskQueue == NULL)
|
||||
return;
|
||||
|
||||
tq_lock(taskQueue);
|
||||
taskQueue->tq_flags |= TQ_FLAGS_BLOCKED;
|
||||
tq_unlock(taskQueue);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
taskqueue_unblock(struct taskqueue *taskQueue)
|
||||
{
|
||||
if (taskQueue == NULL)
|
||||
return;
|
||||
|
||||
tq_lock(taskQueue);
|
||||
taskQueue->tq_flags &= ~TQ_FLAGS_BLOCKED;
|
||||
if (taskQueue->tq_flags & TQ_FLAGS_PENDING) {
|
||||
taskQueue->tq_flags &= ~TQ_FLAGS_PENDING;
|
||||
taskQueue->tq_enqueue(taskQueue->tq_arg);
|
||||
}
|
||||
tq_unlock(taskQueue);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
taskqueue_thread_enqueue(void *context)
|
||||
{
|
||||
@@ -476,44 +163,17 @@ taskqueue_thread_enqueue(void *context)
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
taskqueue_enqueue_fast(struct taskqueue *taskQueue, struct task *task)
|
||||
{
|
||||
return taskqueue_enqueue(taskQueue, task);
|
||||
}
|
||||
|
||||
|
||||
struct taskqueue *
|
||||
taskqueue_create_fast(const char *name, int mflags,
|
||||
taskqueue_enqueue_fn enqueueFunction, void *context)
|
||||
{
|
||||
return _taskqueue_create(name, mflags, 1, enqueueFunction, context);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
task_init(struct task *task, int prio, task_fn_t handler, void *context)
|
||||
_task_init(struct task *task, int prio, task_fn_t handler, void *context)
|
||||
{
|
||||
task->ta_priority = prio;
|
||||
task->ta_flags = 0;
|
||||
task->ta_handler = handler;
|
||||
task->ta_argument = context;
|
||||
task->ta_func = handler;
|
||||
task->ta_context = context;
|
||||
task->ta_pending = 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
timeout_task_init(struct taskqueue *queue, struct timeout_task *timeout_task,
|
||||
int priority, task_fn_t func, void *context)
|
||||
{
|
||||
TASK_INIT(&timeout_task->t, priority, func, context);
|
||||
callout_init_mtx(&timeout_task->c, &queue->tq_mutex,
|
||||
CALLOUT_RETURNUNLOCKED);
|
||||
timeout_task->q = queue;
|
||||
timeout_task->f = 0;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
init_taskqueues()
|
||||
{
|
||||
@@ -546,7 +206,7 @@ init_taskqueues()
|
||||
}
|
||||
|
||||
if (HAIKU_DRIVER_REQUIRES(FBSD_THREAD_TASKQUEUE)) {
|
||||
taskqueue_thread = taskqueue_create_fast("thread taskq", 0,
|
||||
taskqueue_thread = taskqueue_create("thread taskq", 0,
|
||||
taskqueue_thread_enqueue, &taskqueue_thread);
|
||||
if (taskqueue_thread == NULL) {
|
||||
status = B_NO_MEMORY;
|
||||
|
||||
@@ -20,9 +20,7 @@ struct taskq {
|
||||
static void
|
||||
task_set(struct task *t, void (*fn)(void *), void *arg)
|
||||
{
|
||||
t->ta_priority = 0;
|
||||
t->ta_handler = fn;
|
||||
t->ta_argument = arg;
|
||||
TASK_INIT(t, 0, fn, arg);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +39,7 @@ task_add(struct taskq* tasq, struct task *w)
|
||||
w->ta_flags |= TASK_NEEDSGIANT;
|
||||
if (task_pending(w))
|
||||
return 0;
|
||||
return (taskqueue_enqueue_fast(tq, w) == 0);
|
||||
return (taskqueue_enqueue(tq, w) == 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user