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