1 /* 2 * Copyright 2022, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include "device.h" 7 8 #include <stdio.h> 9 10 #include <compat/sys/callout.h> 11 #include <compat/sys/taskqueue.h> 12 #include <compat/sys/haiku-module.h> 13 14 15 static int _taskqueue_start_threads(struct taskqueue **taskQueue, 16 int count, int priority, const char *name); 17 static void taskqueue_terminate(struct thread **pp, struct taskqueue *tq); 18 19 20 #define malloc kernel_malloc 21 #define free kernel_free 22 #include "fbsd_subr_taskqueue.c" 23 #undef malloc 24 #undef free 25 26 27 struct taskqueue *taskqueue_fast = NULL; 28 struct taskqueue *taskqueue_swi = NULL; 29 struct taskqueue *taskqueue_thread = NULL; 30 31 32 static int32 33 tq_handle_thread(void *data) 34 { 35 struct taskqueue *tq = data; 36 37 taskqueue_run_callback(tq, TASKQUEUE_CALLBACK_TYPE_INIT); 38 39 TQ_LOCK(tq); 40 sem_id sem = tq->tq_sem; 41 TQ_UNLOCK(tq); 42 43 while (acquire_sem(sem) == B_NO_ERROR) { 44 TQ_LOCK(tq); 45 taskqueue_run_locked(tq); 46 TQ_UNLOCK(tq); 47 } 48 49 taskqueue_run_callback(tq, TASKQUEUE_CALLBACK_TYPE_SHUTDOWN); 50 51 return 0; 52 } 53 54 55 static int 56 _taskqueue_start_threads(struct taskqueue **taskQueue, int count, int priority, 57 const char *name) 58 { 59 struct taskqueue *tq = (*taskQueue); 60 int i, j; 61 62 if (count == 0) 63 return -1; 64 65 if (tq->tq_threads != NULL) 66 return -1; 67 68 if (count == 1) { 69 tq->tq_threads = &tq->tq_thread_storage; 70 } else { 71 tq->tq_threads = malloc(sizeof(thread_id) * count); 72 if (tq->tq_threads == NULL) 73 return B_NO_MEMORY; 74 } 75 76 tq->tq_sem = create_sem(0, tq->tq_name); 77 if (tq->tq_sem < B_OK) { 78 if (count > 1) 79 free(tq->tq_threads); 80 tq->tq_threads = NULL; 81 return tq->tq_sem; 82 } 83 84 for (i = 0; i < count; i++) { 85 tq->tq_threads[i] = spawn_kernel_thread(tq_handle_thread, tq->tq_name, 86 priority, tq); 87 if (tq->tq_threads[i] < B_OK) { 88 status_t status = tq->tq_threads[i]; 89 for (j = 0; j < i; j++) 90 kill_thread(tq->tq_threads[j]); 91 if (count > 1) 92 free(tq->tq_threads); 93 tq->tq_threads = NULL; 94 delete_sem(tq->tq_sem); 95 return status; 96 } 97 } 98 99 tq->tq_threadcount = count; 100 101 for (i = 0; i < count; i++) 102 resume_thread(tq->tq_threads[i]); 103 104 return 0; 105 } 106 107 108 static void 109 taskqueue_terminate(struct thread **pp, struct taskqueue *tq) 110 { 111 if (tq->tq_sem == -1) 112 return; 113 114 TQ_UNLOCK(tq); 115 116 delete_sem(tq->tq_sem); 117 tq->tq_sem = -1; 118 119 for (int i = 0; i < tq->tq_threadcount; i++) { 120 status_t status; 121 wait_for_thread(tq->tq_threads[i], &status); 122 } 123 124 if (tq->tq_threadcount > 1) 125 free(tq->tq_threads); 126 tq->tq_threads = NULL; 127 128 TQ_LOCK(tq); 129 } 130 131 132 void 133 taskqueue_drain(struct taskqueue *taskQueue, struct task *task) 134 { 135 if (taskQueue == NULL) 136 return; 137 138 TQ_LOCK(taskQueue); 139 while (task->ta_pending != 0 || task_is_running(taskQueue, task)) { 140 TQ_UNLOCK(taskQueue); 141 snooze(0); 142 TQ_LOCK(taskQueue); 143 } 144 TQ_UNLOCK(taskQueue); 145 } 146 147 148 void 149 taskqueue_drain_all(struct taskqueue *taskQueue) 150 { 151 struct task t_barrier; 152 153 if (taskQueue == NULL) { 154 printf("taskqueue_drain_all called with NULL taskqueue\n"); 155 return; 156 } 157 158 TASK_INIT(&t_barrier, USHRT_MAX, taskqueue_task_nop_fn, &t_barrier); 159 taskqueue_enqueue(taskQueue, &t_barrier); 160 taskqueue_drain(taskQueue, &t_barrier); 161 } 162 163 164 void 165 taskqueue_thread_enqueue(void *context) 166 { 167 struct taskqueue **tqp = context; 168 release_sem_etc((*tqp)->tq_sem, 1, B_DO_NOT_RESCHEDULE); 169 } 170 171 172 void 173 _task_init(struct task *task, int prio, task_fn_t handler, void *context) 174 { 175 task->ta_priority = prio; 176 task->ta_flags = 0; 177 task->ta_func = handler; 178 task->ta_context = context; 179 task->ta_pending = 0; 180 } 181 182 183 status_t 184 init_taskqueues() 185 { 186 status_t status = B_NO_MEMORY; 187 188 if (HAIKU_DRIVER_REQUIRES(FBSD_FAST_TASKQUEUE)) { 189 taskqueue_fast = taskqueue_create_fast("fast taskq", 0, 190 taskqueue_thread_enqueue, &taskqueue_fast); 191 if (taskqueue_fast == NULL) 192 return B_NO_MEMORY; 193 194 status = taskqueue_start_threads(&taskqueue_fast, 1, 195 B_REAL_TIME_PRIORITY, "fast taskq thread"); 196 if (status < B_OK) 197 goto err_1; 198 } 199 200 if (HAIKU_DRIVER_REQUIRES(FBSD_SWI_TASKQUEUE)) { 201 taskqueue_swi = taskqueue_create_fast("swi taskq", 0, 202 taskqueue_thread_enqueue, &taskqueue_swi); 203 if (taskqueue_swi == NULL) { 204 status = B_NO_MEMORY; 205 goto err_1; 206 } 207 208 status = taskqueue_start_threads(&taskqueue_swi, 1, 209 B_REAL_TIME_PRIORITY, "swi taskq"); 210 if (status < B_OK) 211 goto err_2; 212 } 213 214 if (HAIKU_DRIVER_REQUIRES(FBSD_THREAD_TASKQUEUE)) { 215 taskqueue_thread = taskqueue_create("thread taskq", 0, 216 taskqueue_thread_enqueue, &taskqueue_thread); 217 if (taskqueue_thread == NULL) { 218 status = B_NO_MEMORY; 219 goto err_2; 220 } 221 222 status = taskqueue_start_threads(&taskqueue_thread, 1, 223 B_REAL_TIME_PRIORITY, "swi taskq"); 224 if (status < B_OK) 225 goto err_3; 226 } 227 228 return B_OK; 229 230 err_3: 231 if (taskqueue_thread) 232 taskqueue_free(taskqueue_thread); 233 234 err_2: 235 if (taskqueue_swi) 236 taskqueue_free(taskqueue_swi); 237 238 err_1: 239 if (taskqueue_fast) 240 taskqueue_free(taskqueue_fast); 241 242 return status; 243 } 244 245 246 void 247 uninit_taskqueues() 248 { 249 if (HAIKU_DRIVER_REQUIRES(FBSD_THREAD_TASKQUEUE)) 250 taskqueue_free(taskqueue_thread); 251 252 if (HAIKU_DRIVER_REQUIRES(FBSD_SWI_TASKQUEUE)) 253 taskqueue_free(taskqueue_swi); 254 255 if (HAIKU_DRIVER_REQUIRES(FBSD_FAST_TASKQUEUE)) 256 taskqueue_free(taskqueue_fast); 257 } 258