243186c409ded9b903a3700297489e2c386ffac5
[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 int oldstate;
170
171 pthread_mutex_lock(&(this->mutex));
172
173 while (1)
174 {
175 this->list->get_count(this->list,&count);
176 while(count == 0)
177 {
178 /* add mutex unlock handler for cancellation, enable cancellation */
179 pthread_cleanup_push((void(*)(void*))pthread_mutex_unlock, (void*)&(this->mutex));
180 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
181
182 pthread_cond_wait( &(this->condvar), &(this->mutex));
183
184 /* reset cancellation, remove mutex-unlock handler (without executing) */
185 pthread_setcancelstate(oldstate, NULL);
186 pthread_cleanup_pop(0);
187
188 this->list->get_count(this->list,&count);
189 }
190
191 this->list->get_first(this->list,(void **) &next_event);
192
193 gettimeofday(&current_time,NULL);
194 long difference = time_difference(&current_time,&(next_event->time));
195 if (difference <= 0)
196 {
197 timeout.tv_sec = next_event->time.tv_sec;
198 timeout.tv_nsec = next_event->time.tv_usec * 1000;
199
200 pthread_cond_timedwait( &(this->condvar), &(this->mutex),&timeout);
201 }
202 else
203 {
204 /* event available */
205 this->list->remove_first(this->list,(void **) &next_event);
206
207 *job = next_event->job;
208
209 next_event->destroy(next_event);
210 break;
211 }
212
213 }
214 pthread_cond_signal( &(this->condvar));
215
216 pthread_mutex_unlock(&(this->mutex));
217
218 return SUCCESS;
219 }
220
221 /**
222 * @brief implements function add of event_queue_t
223 */
224 static status_t add_absolute(private_event_queue_t *this, job_t *job, timeval_t time)
225 {
226 event_t *event = event_create(time,job);
227 linked_list_element_t * current_list_element;
228 event_t *current_event;
229 status_t status;
230 bool has_next;
231 int count;
232
233 if (event == NULL)
234 {
235 return FAILED;
236 }
237 pthread_mutex_lock(&(this->mutex));
238
239 /* while just used to break out */
240 while(1)
241 {
242 this->list->get_count(this->list,&count);
243 if (count == 0)
244 {
245 status = this->list->insert_first(this->list,event);
246 break;
247 }
248
249 /* check last entry */
250 this->list->get_last(this->list,(void **) &current_event);
251
252 if (time_difference(&(event->time), &(current_event->time)) >= 0)
253 {
254 /* my event has to be fired after the last event in list */
255 status = this->list->insert_last(this->list,event);
256 break;
257 }
258
259 /* check first entry */
260 this->list->get_first(this->list,(void **) &current_event);
261
262 if (time_difference(&(event->time), &(current_event->time)) < 0)
263 {
264 /* my event has to be fired before the first event in list */
265 status = this->list->insert_first(this->list,event);
266 break;
267 }
268
269 linked_list_iterator_t * iterator;
270
271 status = this->list->create_iterator(this->list,&iterator,TRUE);
272 if (status != SUCCESS)
273 {
274 break;
275 }
276
277
278 status = iterator->has_next(iterator,&has_next);
279 /* first element has not to be checked (already done) */
280 status = iterator->has_next(iterator,&has_next);
281 if (status != SUCCESS)
282 {
283 iterator->destroy(iterator);
284 break;
285 }
286
287 while(has_next)
288 {
289 status = iterator->current(iterator,&current_list_element);
290 if (status != SUCCESS)
291 {
292 break;
293 }
294 current_event = (event_t *) current_list_element->value;
295
296 if (time_difference(&(event->time), &(current_event->time)) <= 0)
297 {
298 /* my event has to be fired before the current event in list */
299 status = this->list->insert_before(this->list,current_list_element,event);
300 break;
301 }
302
303 iterator->has_next(iterator,&has_next);
304 if (status != SUCCESS)
305 {
306 break;
307 }
308 }
309 iterator->destroy(iterator);
310 break;
311 }
312
313 pthread_cond_signal( &(this->condvar));
314 pthread_mutex_unlock(&(this->mutex));
315
316 if (status != SUCCESS)
317 {
318 event->destroy(event);
319 }
320 return status;
321 }
322
323 /**
324 * @brief implements function add of event_queue_t
325 */
326 static status_t add_relative(event_queue_t *this, job_t *job, u_int32_t ms)
327 {
328 timeval_t current_time;
329 timeval_t time;
330 int micros = ms * 1000;
331
332 gettimeofday(&current_time, NULL);
333
334 time.tv_usec = ((current_time.tv_usec + micros) % 1000000);
335 time.tv_sec = current_time.tv_sec + ((current_time.tv_usec + micros)/ 1000000);
336
337 return this->add_absolute(this, job, time);
338 }
339
340
341 /**
342 * @brief implements function destroy of event_queue_t
343 */
344 static status_t event_queue_destroy(private_event_queue_t *this)
345 {
346 int count;
347 this->list->get_count(this->list,&count);
348 while (count > 0)
349 {
350 event_t *event;
351
352 if (this->list->remove_first(this->list,(void *) &event) != SUCCESS)
353 {
354 this->list->destroy(this->list);
355 break;
356 }
357 event->job->destroy(event->job);
358 event->destroy(event);
359 this->list->get_count(this->list,&count);
360 }
361 this->list->destroy(this->list);
362
363 pthread_mutex_destroy(&(this->mutex));
364
365 pthread_cond_destroy(&(this->condvar));
366
367 pfree(this);
368 return SUCCESS;
369 }
370
371 /*
372 *
373 * Documented in header
374 */
375 event_queue_t *event_queue_create()
376 {
377 linked_list_t *linked_list = linked_list_create();
378 if (linked_list == NULL)
379 {
380 return NULL;
381 }
382
383 private_event_queue_t *this = alloc_thing(private_event_queue_t, "private_event_queue_t");
384 if (this == NULL)
385 {
386 linked_list->destroy(linked_list);
387 return NULL;
388 }
389
390 this->public.get_count = (status_t (*) (event_queue_t *event_queue, int *count)) get_count;
391 this->public.get = (status_t (*) (event_queue_t *event_queue, job_t **job)) get;
392 this->public.add_absolute = (status_t (*) (event_queue_t *event_queue, job_t *job, timeval_t time)) add_absolute;
393 this->public.add_relative = (status_t (*) (event_queue_t *event_queue, job_t *job, u_int32_t ms)) add_relative;
394 this->public.destroy = (status_t (*) (event_queue_t *event_queue)) event_queue_destroy;
395
396 this->list = linked_list;
397 pthread_mutex_init(&(this->mutex), NULL);
398 pthread_cond_init(&(this->condvar), NULL);
399
400 return (&this->public);
401 }