703a3a8a69e9ccd536e44438cacdacea6b73c053
[strongswan.git] / Source / charon / event_queue.c
1 /**
2 * @file event_queue.c
3 *
4 * @brief Event-Queue based on linked_list_t
5 *
6 */
7
8 /*
9 * Copyright (C) 2005 Jan Hutter, Martin Willi
10 * Hochschule fuer Technik Rapperswil
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 */
22
23 #include <freeswan.h>
24 #include <pluto/constants.h>
25 #include <pluto/defs.h>
26 #include <pthread.h>
27 #include <stdlib.h>
28
29
30 #include "types.h"
31 #include "event_queue.h"
32 #include "linked_list.h"
33
34
35
36 /**
37 * @brief represents an event as it is stored in the event queue
38 *
39 * A event consists of a event time and an assigned job object
40 *
41 */
42 typedef struct event_s event_t;
43
44 struct event_s{
45 /**
46 * Time to fire the event
47 */
48 timeval_t time;
49
50 /**
51 * Every event has its assigned job
52 */
53 job_t * job;
54
55 /**
56 * @brief Destroys a event_t object
57 *
58 * @param event_t calling object
59 * @returns SUCCESS if succeeded, FAILED otherwise
60 */
61 status_t (*destroy) (event_t *event);
62 };
63
64
65 /**
66 * @brief implements function destroy of event_t
67 */
68 static status_t event_destroy(event_t *event)
69 {
70 if (event == NULL)
71 {
72 return FAILED;
73 }
74 pfree(event);
75 return SUCCESS;
76 }
77
78 /**
79 * @brief Creates a event for a specific time
80 *
81 * @param time to fire the event
82 * @param job job to add to job-queue at specific time
83 *
84 * @return event_t event object
85 */
86 static event_t *event_create(timeval_t time, job_t *job)
87 {
88 event_t *this = alloc_thing(event_t, "event_t");
89
90 this->destroy = event_destroy;
91
92 this->time = time;
93 this->job = job;
94
95 return this;
96 }
97
98
99 /**
100 * @brief Private Variables and Functions of event_queue class
101 *
102 */
103 typedef struct private_event_queue_s private_event_queue_t;
104
105
106 struct private_event_queue_s {
107 event_queue_t public;
108
109 /**
110 * The events are stored in a linked list
111 */
112 linked_list_t *list;
113
114 /**
115 * access to linked_list is locked through this mutex
116 */
117 pthread_mutex_t mutex;
118
119 /**
120 * If the queue is empty or an event has not to be fired
121 * a thread has to wait
122 * This condvar is used to wake up such a thread
123 */
124 pthread_cond_t condvar;
125 };
126
127 /**
128 * Returns the difference of to timeval structs in microseconds
129 *
130 * @param end_time end time
131 * @param start_time start time
132 *
133 * @warning this function is also defined in the tester class
134 * In later improvements, this function can be added to a general
135 * class type!
136 *
137 * @return difference in microseconds
138 */
139 static long time_difference(struct timeval *end_time, struct timeval *start_time)
140 {
141 long seconds, microseconds;
142
143 seconds = (end_time->tv_sec - start_time->tv_sec);
144 microseconds = (end_time->tv_usec - start_time->tv_usec);
145 return ((seconds * 1000000) + microseconds);
146 }
147
148
149 /**
150 * @brief implements function get_count of event_queue_t
151 */
152 static status_t get_count (private_event_queue_t *this, int *count)
153 {
154 pthread_mutex_lock(&(this->mutex));
155 status_t status = this->list->get_count(this->list,count);
156 pthread_mutex_unlock(&(this->mutex));
157 return status;
158 }
159
160 /**
161 * @brief implements function get of event_queue_t
162 */
163 static status_t get(private_event_queue_t *this, job_t **job)
164 {
165 timespec_t timeout;
166 timeval_t current_time;
167 event_t * next_event;
168 int count;
169
170 pthread_mutex_lock(&(this->mutex));
171
172 while (1)
173 {
174 this->list->get_count(this->list,&count);
175 while(count == 0)
176 {
177 pthread_cond_wait( &(this->condvar), &(this->mutex));
178 this->list->get_count(this->list,&count);
179 }
180
181 this->list->get_first(this->list,(void **) &next_event);
182 gettimeofday(&current_time,NULL);
183 long difference = time_difference(&current_time,&(next_event->time));
184 if (difference <= 0)
185 {
186 timeout.tv_sec = next_event->time.tv_sec;
187 timeout.tv_nsec = next_event->time.tv_usec * 1000;
188
189 pthread_cond_timedwait( &(this->condvar), &(this->mutex),&timeout);
190 }
191 else
192 {
193 /* event available */
194 this->list->remove_first(this->list,(void **) &next_event);
195
196 *job = next_event->job;
197
198 next_event->destroy(next_event);
199 break;
200 }
201
202 }
203 pthread_cond_signal( &(this->condvar));
204
205 pthread_mutex_unlock(&(this->mutex));
206
207 return SUCCESS;
208 }
209
210 /**
211 * @brief implements function add of event_queue_t
212 */
213 static status_t add(private_event_queue_t *this, job_t *job, timeval_t time)
214 {
215 event_t *event = event_create(time,job);
216 linked_list_element_t * current_list_element;
217 event_t *current_event;
218 status_t status;
219 bool has_next;
220 int count;
221
222 if (event == NULL)
223 {
224 return FAILED;
225 }
226 pthread_mutex_lock(&(this->mutex));
227
228 /* while just used to break out */
229 while(1)
230 {
231 this->list->get_count(this->list,&count);
232 if (count == 0)
233 {
234 status = this->list->insert_first(this->list,event);
235 break;
236 }
237
238 /* check last entry */
239 this->list->get_last(this->list,(void **) &current_event);
240
241 if (time_difference(&(event->time), &(current_event->time)) >= 0)
242 {
243 /* my event has to be fired after the last event in list */
244 status = this->list->insert_last(this->list,event);
245 break;
246 }
247
248 /* check first entry */
249 this->list->get_first(this->list,(void **) &current_event);
250
251 if (time_difference(&(event->time), &(current_event->time)) < 0)
252 {
253 /* my event has to be fired before the first event in list */
254 status = this->list->insert_first(this->list,event);
255 break;
256 }
257
258 linked_list_iterator_t * iterator;
259
260 status = this->list->create_iterator(this->list,&iterator,TRUE);
261 if (status != SUCCESS)
262 {
263 break;
264 }
265
266
267 status = iterator->has_next(iterator,&has_next);
268 /* first element has not to be checked (already done) */
269 status = iterator->has_next(iterator,&has_next);
270 if (status != SUCCESS)
271 {
272 break;
273 }
274
275 while(has_next)
276 {
277 status = iterator->current(iterator,&current_list_element);
278 if (status != SUCCESS)
279 {
280 break;
281 }
282 current_event = (event_t *) current_list_element->value;
283
284 if (time_difference(&(event->time), &(current_event->time)) <= 0)
285 {
286 /* my event has to be fired before the current event in list */
287 status = this->list->insert_before(this->list,current_list_element,event);
288
289 break;
290 }
291
292 iterator->has_next(iterator,&has_next);
293 if (status != SUCCESS)
294 {
295 break;
296 }
297 }
298 break;
299 }
300
301 pthread_cond_signal( &(this->condvar));
302 pthread_mutex_unlock(&(this->mutex));
303
304 if (status != SUCCESS)
305 {
306 event->destroy(event);
307 }
308 return status;
309 }
310
311
312 /**
313 * @brief implements function destroy of event_queue_t
314 */
315 static status_t event_queue_destroy(private_event_queue_t *this)
316 {
317 int count;
318 this->list->get_count(this->list,&count);
319 while (count > 0)
320 {
321 event_t *event;
322
323 if (this->list->remove_first(this->list,(void *) &event) != SUCCESS)
324 {
325 this->list->destroy(this->list);
326 break;
327 }
328 event->job->destroy(event->job);
329 event->destroy(event);
330 this->list->get_count(this->list,&count);
331 }
332 this->list->destroy(this->list);
333
334 pthread_mutex_destroy(&(this->mutex));
335
336 pthread_cond_destroy(&(this->condvar));
337
338 pfree(this);
339 return SUCCESS;
340 }
341
342 /*
343 *
344 * Documented in header
345 */
346 event_queue_t *event_queue_create()
347 {
348 linked_list_t *linked_list = linked_list_create();
349 if (linked_list == NULL)
350 {
351 return NULL;
352 }
353
354 private_event_queue_t *this = alloc_thing(private_event_queue_t, "private_event_queue_t");
355 if (this == NULL)
356 {
357 linked_list->destroy(linked_list);
358 return NULL;
359 }
360
361 this->public.get_count = (status_t (*) (event_queue_t *event_queue, int *count)) get_count;
362 this->public.get = (status_t (*) (event_queue_t *event_queue, job_t **job)) get;
363 this->public.add = (status_t (*) (event_queue_t *event_queue, job_t *job, timeval_t time)) add;
364 this->public.destroy = (status_t (*) (event_queue_t *event_queue)) event_queue_destroy;
365
366 this->list = linked_list;
367 pthread_mutex_init(&(this->mutex), NULL);
368 pthread_cond_init(&(this->condvar), NULL);
369
370 return (&this->public);
371 }