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
15static int _taskqueue_start_threads(struct taskqueue **taskQueue,
16	int count, int priority, const char *name);
17static 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
27struct taskqueue *taskqueue_fast = NULL;
28struct taskqueue *taskqueue_swi = NULL;
29struct taskqueue *taskqueue_thread = NULL;
30
31
32static int32
33tq_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
55static 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
108static void
109taskqueue_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
132void
133taskqueue_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
148void
149taskqueue_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
164void
165taskqueue_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
172void
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
183status_t
184init_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
230err_3:
231	if (taskqueue_thread)
232		taskqueue_free(taskqueue_thread);
233
234err_2:
235	if (taskqueue_swi)
236		taskqueue_free(taskqueue_swi);
237
238err_1:
239	if (taskqueue_fast)
240		taskqueue_free(taskqueue_fast);
241
242	return status;
243}
244
245
246void
247uninit_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