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