summaryrefslogtreecommitdiff
path: root/ot_workqueue.c
diff options
context:
space:
mode:
authorDirk Engling <erdgeist@erdgeist.org>2026-10-31 18:48:00 +0100
committerDirk Engling <erdgeist@erdgeist.org>2026-10-31 18:48:00 +0100
commit5b25f89d00433f1c11576d1ea19304797dc60d1b (patch)
tree3810c8585354f4795bd2f441b64f70e581855214 /ot_workqueue.c
parent0bd368691eb0d9051725d76b2a3721bb902c919b (diff)
Refactor ot_workqueue out of ot_mutex, no behaviour change
Diffstat (limited to 'ot_workqueue.c')
-rw-r--r--ot_workqueue.c243
1 files changed, 243 insertions, 0 deletions
diff --git a/ot_workqueue.c b/ot_workqueue.c
new file mode 100644
index 0000000..aac366c
--- /dev/null
+++ b/ot_workqueue.c
@@ -0,0 +1,243 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <pthread.h>
8#include <stdlib.h>
9#include <string.h>
10#include <sys/uio.h>
11#include <unistd.h>
12
13/* Opentracker */
14#include "ot_iovec.h"
15#include "ot_workqueue.h"
16#include "trackerlogic.h"
17
18/* Pushing a result wakes the thread that will send it */
19extern int g_self_pipe[2];
20
21struct ot_task {
22 ot_taskid taskid;
23 ot_tasktype tasktype;
24 int64_t sock;
25 size_t iovec_entries;
26 struct iovec *iovec;
27 struct ot_task *next;
28};
29
30static ot_taskid next_free_taskid = 1;
31static struct ot_task *tasklist;
32static pthread_mutex_t tasklist_mutex;
33static pthread_cond_t tasklist_being_filled;
34
35int workqueue_pushtask(int64_t sock, ot_tasktype tasktype) {
36 struct ot_task **tmptask, *task;
37
38 task = malloc(sizeof(struct ot_task));
39 if (!task)
40 return -1;
41
42 task->taskid = 0;
43 task->tasktype = tasktype;
44 task->sock = sock;
45 task->iovec_entries = 0;
46 task->iovec = NULL;
47 task->next = 0;
48
49 /* Want exclusive access to tasklist */
50 pthread_mutex_lock(&tasklist_mutex);
51
52 /* Skip to end of list */
53 tmptask = &tasklist;
54 while (*tmptask)
55 tmptask = &(*tmptask)->next;
56 *tmptask = task;
57
58 /* Inform waiting workers and release lock */
59 pthread_cond_broadcast(&tasklist_being_filled);
60 pthread_mutex_unlock(&tasklist_mutex);
61 return 0;
62}
63
64void workqueue_canceltask(int64_t sock) {
65 struct ot_task **task;
66
67 /* Want exclusive access to tasklist */
68 pthread_mutex_lock(&tasklist_mutex);
69
70 for (task = &tasklist; *task; task = &((*task)->next))
71 if ((*task)->sock == sock) {
72 struct iovec *iovec = (*task)->iovec;
73 struct ot_task *ptask = *task;
74 size_t i;
75
76 /* Free task's iovec, the buffers it points at and the vector itself */
77 for (i = 0; i < (*task)->iovec_entries; ++i)
78 free(iovec[i].iov_base);
79 free(iovec);
80
81 *task = (*task)->next;
82 free(ptask);
83 break;
84 }
85
86 /* Release lock */
87 pthread_mutex_unlock(&tasklist_mutex);
88}
89
90ot_taskid workqueue_poptask(ot_tasktype *tasktype) {
91 struct ot_task *task;
92 ot_taskid taskid = 0;
93
94 /* Want exclusive access to tasklist */
95 pthread_mutex_lock(&tasklist_mutex);
96
97 while (!taskid) {
98 /* Skip to the first unassigned task this worker wants to do */
99 for (task = tasklist; task; task = task->next)
100 if (!task->taskid && (TASK_CLASS_MASK & task->tasktype) == *tasktype) {
101 /* If we found an outstanding task, assign a taskid to it
102 and leave the loop */
103 task->taskid = taskid = ++next_free_taskid;
104 *tasktype = task->tasktype;
105 break;
106 }
107
108 /* Wait until the next task is being fed, unless the tracker is on its
109 way out, in which case the worker is meant to leave rather than wait */
110 if (!taskid) {
111 if (!g_opentracker_running)
112 break;
113 pthread_cond_wait(&tasklist_being_filled, &tasklist_mutex);
114 }
115 }
116
117 /* Release lock */
118 pthread_mutex_unlock(&tasklist_mutex);
119
120 return taskid;
121}
122
123void workqueue_pushsuccess(ot_taskid taskid) {
124 struct ot_task **task;
125
126 /* Want exclusive access to tasklist */
127 pthread_mutex_lock(&tasklist_mutex);
128
129 for (task = &tasklist; *task; task = &((*task)->next))
130 if ((*task)->taskid == taskid) {
131 struct ot_task *ptask = *task;
132 *task = (*task)->next;
133 free(ptask);
134 break;
135 }
136
137 /* Release lock */
138 pthread_mutex_unlock(&tasklist_mutex);
139}
140
141int workqueue_pushresult(ot_taskid taskid, size_t iovec_entries, struct iovec *iovec) {
142 struct ot_task *task;
143 const char byte = 'o';
144
145 /* Want exclusive access to tasklist */
146 pthread_mutex_lock(&tasklist_mutex);
147
148 for (task = tasklist; task; task = task->next)
149 if (task->taskid == taskid) {
150 task->iovec_entries = iovec_entries;
151 task->iovec = iovec;
152 task->tasktype = TASK_DONE;
153 break;
154 }
155
156 /* Release lock */
157 pthread_mutex_unlock(&tasklist_mutex);
158
159 /* A full pipe has already done this nudge's job, so a failure is fine */
160 (void)!write(g_self_pipe[1], &byte, 1);
161
162 /* Indicate whether the worker has to throw away results */
163 return task ? 0 : -1;
164}
165
166int workqueue_pushchunked(ot_taskid taskid, struct iovec *iovec) {
167 struct ot_task *task;
168 const char byte = 'o';
169
170 /* Want exclusive access to tasklist */
171 pthread_mutex_lock(&tasklist_mutex);
172
173 for (task = tasklist; task; task = task->next)
174 if (task->taskid == taskid) {
175 if (iovec) {
176 if (iovec_append(&task->iovec_entries, &task->iovec, iovec))
177 task->tasktype = TASK_DONE_PARTIAL;
178 else
179 task = NULL;
180 } else
181 task->tasktype = TASK_DONE;
182 break;
183 }
184
185 /* Release lock */
186 pthread_mutex_unlock(&tasklist_mutex);
187
188 /* A full pipe has already done this nudge's job, so a failure is fine */
189 (void)!write(g_self_pipe[1], &byte, 1);
190
191 /* Indicate whether the worker has to throw away results */
192 return task ? 0 : -1;
193}
194
195int64_t workqueue_popresult(size_t *iovec_entries, struct iovec **iovec, int *is_partial) {
196 struct ot_task **task;
197 int64_t sock = -1;
198
199 *is_partial = 0;
200
201 /* Want exclusive access to tasklist */
202 pthread_mutex_lock(&tasklist_mutex);
203
204 for (task = &tasklist; *task; task = &((*task)->next))
205 if (((*task)->tasktype & TASK_CLASS_MASK) == TASK_DONE) {
206 struct ot_task *ptask = *task;
207 *iovec_entries = ptask->iovec_entries;
208 *iovec = ptask->iovec;
209 sock = ptask->sock;
210
211 if ((*task)->tasktype == TASK_DONE) {
212 *task = ptask->next;
213 free(ptask);
214 } else {
215 ptask->iovec_entries = 0;
216 ptask->iovec = NULL;
217 *is_partial = 1;
218 /* Prevent task from showing up immediately again unless new data was added */
219 (*task)->tasktype = TASK_FULLSCRAPE;
220 }
221 break;
222 }
223
224 /* Release lock */
225 pthread_mutex_unlock(&tasklist_mutex);
226 return sock;
227}
228
229void workqueue_wakeall(void) {
230 pthread_mutex_lock(&tasklist_mutex);
231 pthread_cond_broadcast(&tasklist_being_filled);
232 pthread_mutex_unlock(&tasklist_mutex);
233}
234
235void workqueue_init(void) {
236 pthread_mutex_init(&tasklist_mutex, NULL);
237 pthread_cond_init(&tasklist_being_filled, NULL);
238}
239
240void workqueue_deinit(void) {
241 pthread_mutex_destroy(&tasklist_mutex);
242 pthread_cond_destroy(&tasklist_being_filled);
243}