/* * Copyright 2022, Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. */ #include "device.h" #include #include #include #include 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 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 int32 tq_handle_thread(void *data) { struct taskqueue *tq = data; 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); taskqueue_run_locked(tq); TQ_UNLOCK(tq); } taskqueue_run_callback(tq, TASKQUEUE_CALLBACK_TYPE_SHUTDOWN); return 0; } static int _taskqueue_start_threads(struct taskqueue **taskQueue, int count, int priority, const char *name) { struct taskqueue *tq = (*taskQueue); int i, j; if (count == 0) return -1; if (tq->tq_threads != NULL) return -1; if (count == 1) { tq->tq_threads = &tq->tq_thread_storage; } else { tq->tq_threads = malloc(sizeof(thread_id) * count); if (tq->tq_threads == NULL) return B_NO_MEMORY; } tq->tq_sem = create_sem(0, tq->tq_name); if (tq->tq_sem < B_OK) { if (count > 1) free(tq->tq_threads); tq->tq_threads = NULL; return tq->tq_sem; } for (i = 0; i < count; i++) { tq->tq_threads[i] = spawn_kernel_thread(tq_handle_thread, tq->tq_name, priority, tq); if (tq->tq_threads[i] < B_OK) { status_t status = tq->tq_threads[i]; for (j = 0; j < i; j++) kill_thread(tq->tq_threads[j]); if (count > 1) free(tq->tq_threads); tq->tq_threads = NULL; delete_sem(tq->tq_sem); return status; } } tq->tq_threadcount = count; for (i = 0; i < count; i++) resume_thread(tq->tq_threads[i]); return 0; } static void taskqueue_terminate(struct thread **pp, struct taskqueue *tq) { if (tq->tq_sem == -1) return; TQ_UNLOCK(tq); delete_sem(tq->tq_sem); tq->tq_sem = -1; for (int i = 0; i < tq->tq_threadcount; i++) { status_t status; wait_for_thread(tq->tq_threads[i], &status); } if (tq->tq_threadcount > 1) free(tq->tq_threads); tq->tq_threads = NULL; TQ_LOCK(tq); } void taskqueue_drain(struct taskqueue *taskQueue, struct task *task) { if (taskQueue == NULL) return; TQ_LOCK(taskQueue); while (task->ta_pending != 0 || task_is_running(taskQueue, task)) { TQ_UNLOCK(taskQueue); snooze(0); TQ_LOCK(taskQueue); } TQ_UNLOCK(taskQueue); } void taskqueue_drain_all(struct taskqueue *taskQueue) { struct task t_barrier; if (taskQueue == NULL) { printf("taskqueue_drain_all called with NULL taskqueue\n"); return; } TASK_INIT(&t_barrier, USHRT_MAX, taskqueue_task_nop_fn, &t_barrier); taskqueue_enqueue(taskQueue, &t_barrier); taskqueue_drain(taskQueue, &t_barrier); } void taskqueue_thread_enqueue(void *context) { struct taskqueue **tqp = context; release_sem_etc((*tqp)->tq_sem, 1, B_DO_NOT_RESCHEDULE); } void _task_init(struct task *task, int prio, task_fn_t handler, void *context) { task->ta_priority = prio; task->ta_flags = 0; task->ta_func = handler; task->ta_context = context; task->ta_pending = 0; } status_t init_taskqueues() { status_t status = B_NO_MEMORY; if (HAIKU_DRIVER_REQUIRES(FBSD_FAST_TASKQUEUE)) { taskqueue_fast = taskqueue_create_fast("fast taskq", 0, taskqueue_thread_enqueue, &taskqueue_fast); if (taskqueue_fast == NULL) return B_NO_MEMORY; status = taskqueue_start_threads(&taskqueue_fast, 1, B_REAL_TIME_PRIORITY, "fast taskq thread"); if (status < B_OK) goto err_1; } if (HAIKU_DRIVER_REQUIRES(FBSD_SWI_TASKQUEUE)) { taskqueue_swi = taskqueue_create_fast("swi taskq", 0, taskqueue_thread_enqueue, &taskqueue_swi); if (taskqueue_swi == NULL) { status = B_NO_MEMORY; goto err_1; } status = taskqueue_start_threads(&taskqueue_swi, 1, B_REAL_TIME_PRIORITY, "swi taskq"); if (status < B_OK) goto err_2; } if (HAIKU_DRIVER_REQUIRES(FBSD_THREAD_TASKQUEUE)) { taskqueue_thread = taskqueue_create("thread taskq", 0, taskqueue_thread_enqueue, &taskqueue_thread); if (taskqueue_thread == NULL) { status = B_NO_MEMORY; goto err_2; } status = taskqueue_start_threads(&taskqueue_thread, 1, B_REAL_TIME_PRIORITY, "swi taskq"); if (status < B_OK) goto err_3; } return B_OK; err_3: if (taskqueue_thread) taskqueue_free(taskqueue_thread); err_2: if (taskqueue_swi) taskqueue_free(taskqueue_swi); err_1: if (taskqueue_fast) taskqueue_free(taskqueue_fast); return status; } void uninit_taskqueues() { if (HAIKU_DRIVER_REQUIRES(FBSD_THREAD_TASKQUEUE)) taskqueue_free(taskqueue_thread); if (HAIKU_DRIVER_REQUIRES(FBSD_SWI_TASKQUEUE)) taskqueue_free(taskqueue_swi); if (HAIKU_DRIVER_REQUIRES(FBSD_FAST_TASKQUEUE)) taskqueue_free(taskqueue_fast); }