xref: /haiku/src/libs/compat/freebsd_network/taskqueue.c (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
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