1 /* -*-c-*- */
2 /* This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, see: <http://www.gnu.org/licenses/>
14 */
15
16 /* ---------------------------- included header files ---------------------- */
17
18 #include "config.h"
19
20 #include <stdio.h>
21
22 #include "safemalloc.h"
23 #include "fqueue.h"
24
25 /* ---------------------------- local definitions -------------------------- */
26
27 /* ---------------------------- local macros ------------------------------- */
28
29 /* ---------------------------- imports ------------------------------------ */
30
31 /* ---------------------------- included code files ------------------------ */
32
33 /* ---------------------------- local types -------------------------------- */
34
35 /* ---------------------------- forward declarations ----------------------- */
36
37 /* ---------------------------- local variables ---------------------------- */
38
39 typedef struct fqueue_record
40 {
41 struct fqueue_record *next;
42 void *object;
43 struct
44 {
45 unsigned is_scheduled_for_deletion;
46 unsigned is_just_created;
47 } flags;
48 } fqueue_record;
49
50 /* ---------------------------- exported variables (globals) --------------- */
51
52 /* ---------------------------- local functions ---------------------------- */
53
54 /* ---------------------------- interface functions ------------------------ */
55
56 /* Make newly added items permanent and destroy items scheduled for deletion. */
fqueue_cleanup_queue(fqueue * fq,destroy_fqueue_object_t destroy_func)57 static void fqueue_cleanup_queue(
58 fqueue *fq, destroy_fqueue_object_t destroy_func)
59 {
60 fqueue_record *head;
61 fqueue_record *tail;
62 fqueue_record *prev;
63 fqueue_record *next;
64 fqueue_record *rec;
65
66 for (head = NULL, tail = NULL, prev = NULL, rec = fq->first;
67 rec != NULL; rec = next)
68 {
69 if (rec->flags.is_scheduled_for_deletion)
70 {
71 /* destroy and skip it */
72 next = rec->next;
73 if (rec->object != NULL && destroy_func != NULL)
74 {
75 destroy_func(rec->object);
76 }
77 if (prev != NULL)
78 {
79 prev->next = next;
80 }
81 free(rec);
82 }
83 else
84 {
85 rec->flags.is_just_created = 0;
86 if (head == NULL)
87 {
88 head = rec;
89 }
90 tail = rec;
91 prev = rec;
92 next = rec->next;
93 }
94 }
95 fq->first = head;
96 fq->last = tail;
97
98 return;
99 }
100
101 /* Recursively lock the queue. While locked, objects are not deleted from the
102 * queue but marked for later deletion. New objects are marked as such and are
103 * skipped by the queue functions. */
fqueue_lock_queue(fqueue * fq)104 static void fqueue_lock_queue(fqueue *fq)
105 {
106 fq->lock_level++;
107
108 return;
109 }
110
111 /* Remove one lock level */
fqueue_unlock_queue(fqueue * fq,destroy_fqueue_object_t destroy_func)112 static void fqueue_unlock_queue(
113 fqueue *fq, destroy_fqueue_object_t destroy_func)
114 {
115 switch (fq->lock_level)
116 {
117 case 0:
118 /* bug */
119 break;
120 case 1:
121 if (fq->flags.is_dirty)
122 {
123 fqueue_cleanup_queue(fq, destroy_func);
124 }
125 /* fall through */
126 default:
127 fq->lock_level--;
128 return;
129 }
130
131 return;
132 }
133
134 /* Chack and possibly execute the action associated with a queue object.
135 * Schedule the object for deletion if it was executed. */
fqueue_operate(fqueue * fq,fqueue_record * rec,check_fqueue_object_t check_func,operate_fqueue_object_t operate_func,void * operate_args)136 static void fqueue_operate(
137 fqueue *fq, fqueue_record *rec,
138 check_fqueue_object_t check_func,
139 operate_fqueue_object_t operate_func,
140 void *operate_args)
141 {
142 if (rec == NULL || rec->flags.is_scheduled_for_deletion)
143 {
144 return;
145 }
146 if (check_func == NULL || check_func(rec->object, operate_args) == 1)
147 {
148 if (operate_func != NULL)
149 {
150 operate_func(rec->object, operate_args);
151 }
152 rec->flags.is_scheduled_for_deletion = 1;
153 fq->flags.is_dirty = 1;
154 }
155
156 return;
157 }
158
159 /* ---------------------------- builtin commands --------------------------- */
160
161 /*
162 * Basic queue management
163 */
164
fqueue_init(fqueue * fq)165 void fqueue_init(fqueue *fq)
166 {
167 memset(fq, 0, sizeof(*fq));
168
169 return;
170 }
171
fqueue_get_length(fqueue * fq)172 unsigned int fqueue_get_length(fqueue *fq)
173 {
174 unsigned int len;
175 fqueue_record *t;
176
177 for (t = fq->first, len = 0; t != NULL; t = t->next)
178 {
179 if (!t->flags.is_scheduled_for_deletion)
180 {
181 len++;
182 }
183 }
184
185 return len;
186 }
187
188 /*
189 * Add record to queue
190 */
191
fqueue_add_at_front(fqueue * fq,void * object)192 void fqueue_add_at_front(
193 fqueue *fq, void *object)
194 {
195 fqueue_record *rec;
196
197 rec = fxcalloc(1, sizeof *rec);
198 rec->object = object;
199 rec->next = fq->first;
200 if (fq->lock_level > 0)
201 {
202 rec->flags.is_just_created = 1;
203 fq->flags.is_dirty = 1;
204 }
205 fq->first = rec;
206
207 return;
208 }
209
fqueue_add_at_end(fqueue * fq,void * object)210 void fqueue_add_at_end(
211 fqueue *fq, void *object)
212 {
213 fqueue_record *rec;
214
215 rec = fxcalloc(1, sizeof *rec);
216 rec->object = object;
217 if (fq->lock_level > 0)
218 {
219 rec->flags.is_just_created = 1;
220 fq->flags.is_dirty = 1;
221 }
222 if (fq->first == NULL)
223 {
224 fq->first = rec;
225 }
226 else
227 {
228 fq->last->next = rec;
229 }
230 fq->last = rec;
231 rec->next = NULL;
232
233 return;
234 }
235
fqueue_add_inside(fqueue * fq,void * object,cmp_objects_t cmp_objects,void * cmp_args)236 void fqueue_add_inside(
237 fqueue *fq, void *object, cmp_objects_t cmp_objects, void *cmp_args)
238 {
239 fqueue_record *rec;
240 fqueue_record *p;
241 fqueue_record *t;
242
243 rec = fxcalloc(1, sizeof *rec);
244 rec->object = object;
245 if (fq->lock_level > 0)
246 {
247 rec->flags.is_just_created = 1;
248 fq->flags.is_dirty = 1;
249 }
250
251 /* search place to insert record */
252 for (p = NULL, t = fq->first;
253 t != NULL && cmp_objects(object, t->object, cmp_args) >= 0;
254 p = t, t = t->next)
255 {
256 /* nothing to do here */
257 }
258 /* insert record */
259 if (p == NULL)
260 {
261 /* insert at start */
262 rec->next = fq->first;
263 fq->first = rec;
264 }
265 else
266 {
267 /* insert after p */
268 rec->next = p->next;
269 p->next = rec;
270 }
271 if (t == NULL)
272 {
273 fq->last = rec;
274 }
275
276 return;
277 }
278
279 /*
280 * Fetch queue objects
281 */
282
283 /* Returns the object of the first queue record throuch *ret_object. Returns
284 * 0 if the queue is empty and 1 otherwise. */
fqueue_get_first(fqueue * fq,void ** ret_object)285 int fqueue_get_first(
286 fqueue *fq, void **ret_object)
287 {
288 fqueue_record *rec;
289
290 for (rec = fq->first;
291 rec != NULL && rec->flags.is_scheduled_for_deletion;
292 rec = rec->next)
293 {
294 /* nothing */
295 }
296 if (rec == NULL)
297 {
298 return 0;
299 }
300 *ret_object = rec->object;
301
302 return 1;
303 }
304
305 /*
306 * Operate on queue objects and possibly remove them from the queue
307 */
308
309 /* Runs the operate_func on the first record in the queue. If that function
310 * is NULL or returns 1, the record is removed from the queue. The object of
311 * the queue record must have been freed in operate_func. */
fqueue_remove_or_operate_from_front(fqueue * fq,check_fqueue_object_t check_func,operate_fqueue_object_t operate_func,destroy_fqueue_object_t destroy_func,void * operate_args)312 void fqueue_remove_or_operate_from_front(
313 fqueue *fq,
314 check_fqueue_object_t check_func,
315 operate_fqueue_object_t operate_func,
316 destroy_fqueue_object_t destroy_func,
317 void *operate_args)
318 {
319 fqueue_lock_queue(fq);
320 fqueue_operate(fq, fq->first, check_func, operate_func, operate_args);
321 fqueue_unlock_queue(fq, destroy_func);
322
323 return;
324 }
325
326 /* Same as above but operates on last record in queue */
fqueue_remove_or_operate_from_end(fqueue * fq,check_fqueue_object_t check_func,operate_fqueue_object_t operate_func,destroy_fqueue_object_t destroy_func,void * operate_args)327 void fqueue_remove_or_operate_from_end(
328 fqueue *fq,
329 check_fqueue_object_t check_func,
330 operate_fqueue_object_t operate_func,
331 destroy_fqueue_object_t destroy_func,
332 void *operate_args)
333 {
334 fqueue_lock_queue(fq);
335 fqueue_operate( fq, fq->last, check_func, operate_func, operate_args);
336 fqueue_unlock_queue(fq, destroy_func);
337
338 return;
339 }
340
341 /* Same as above but operates on all records in the queue. */
fqueue_remove_or_operate_all(fqueue * fq,check_fqueue_object_t check_func,operate_fqueue_object_t operate_func,destroy_fqueue_object_t destroy_func,void * operate_args)342 void fqueue_remove_or_operate_all(
343 fqueue *fq,
344 check_fqueue_object_t check_func,
345 operate_fqueue_object_t operate_func,
346 destroy_fqueue_object_t destroy_func,
347 void *operate_args)
348 {
349 fqueue_record *t;
350
351 if (fq->first == NULL)
352 {
353 return;
354 }
355 fqueue_lock_queue(fq);
356 /* search record(s) to remove */
357 for (t = fq->first; t != NULL; t = t->next)
358 {
359 if (t->flags.is_just_created ||
360 t->flags.is_scheduled_for_deletion)
361 {
362 /* skip */
363 continue;
364 }
365 fqueue_operate(fq, t, check_func, operate_func, operate_args);
366 }
367 fqueue_unlock_queue(fq, destroy_func);
368
369 return;
370 }
371