- introduced autotools
[strongswan.git] / src / charon / queues / event_queue.c
1 /**
2 * @file event_queue.c
3 *
4 * @brief Implementation of event_queue_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 <pthread.h>
24 #include <stdlib.h>
25
26 #include "event_queue.h"
27
28 #include <types.h>
29 #include <utils/linked_list.h>
30
31
32
33 typedef struct event_t event_t;
34
35 /**
36 * @brief Represents an event as it is stored in the event queue.
37 *
38 * A event consists of a event time and an assigned job object.
39 *
40 */
41 struct event_t{
42 /**
43 * Time to fire the event.
44 */
45 timeval_t time;
46
47 /**
48 * Every event has its assigned job.
49 */
50 job_t * job;
51
52 /**
53 * @brief Destroys a event_t object.
54 *
55 * @param event_t calling object
56 */
57 void (*destroy) (event_t *event);
58 };
59
60
61 /**
62 * implements event_t.destroy
63 */
64 static void event_destroy(event_t *event)
65 {
66 free(event);
67 }
68
69 /**
70 * @brief Creates a event for a specific time
71 *
72 * @param time absolute time to fire the event
73 * @param job job to add to job-queue at specific time
74 *
75 * @returns created event_t object
76 */
77 static event_t *event_create(timeval_t time, job_t *job)
78 {
79 event_t *this = malloc_thing(event_t);
80
81 this->destroy = event_destroy;
82 this->time = time;
83 this->job = job;
84
85 return this;
86 }
87
88
89 typedef struct private_event_queue_t private_event_queue_t;
90
91 /**
92 * Private Variables and Functions of event_queue_t class.
93 *
94 */
95 struct private_event_queue_t {
96 /**
97 * Public part.
98 */
99 event_queue_t public;
100
101 /**
102 * The events are stored in a linked list of type linked_list_t.
103 */
104 linked_list_t *list;
105
106 /**
107 * Access to linked_list is locked through this mutex.
108 */
109 pthread_mutex_t mutex;
110
111 /**
112 * If the queue is empty or an event has not to be fired
113 * a thread has to wait.
114 *
115 * This condvar is used to wake up such a thread.
116 */
117 pthread_cond_t condvar;
118 };
119
120 /**
121 * Returns the difference of to timeval structs in microseconds
122 *
123 * @param end_time end time
124 * @param start_time start time
125 *
126 * @warning this function is also defined in the tester class
127 * In later improvements, this function can be added to a general
128 * class type!
129 *
130 * @return difference in microseconds (end time - start time)
131 */
132 static long time_difference(struct timeval *end_time, struct timeval *start_time)
133 {
134 long seconds, microseconds;
135
136 seconds = (end_time->tv_sec - start_time->tv_sec);
137 microseconds = (end_time->tv_usec - start_time->tv_usec);
138 return ((seconds * 1000000) + microseconds);
139 }
140
141
142 /**
143 * Implements event_queue_t.get_count
144 */
145 static int get_count (private_event_queue_t *this)
146 {
147 int count;
148 pthread_mutex_lock(&(this->mutex));
149 count = this->list->get_count(this->list);
150 pthread_mutex_unlock(&(this->mutex));
151 return count;
152 }
153
154 /**
155 * Implements event_queue_t.get
156 */
157 static job_t *get(private_event_queue_t *this)
158 {
159 timespec_t timeout;
160 timeval_t current_time;
161 event_t * next_event;
162 job_t *job;
163 int oldstate;
164
165 pthread_mutex_lock(&(this->mutex));
166
167 while (1)
168 {
169 while(this->list->get_count(this->list) == 0)
170 {
171 /* add mutex unlock handler for cancellation, enable cancellation */
172 pthread_cleanup_push((void(*)(void*))pthread_mutex_unlock, (void*)&(this->mutex));
173 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
174
175 pthread_cond_wait( &(this->condvar), &(this->mutex));
176
177 /* reset cancellation, remove mutex-unlock handler (without executing) */
178 pthread_setcancelstate(oldstate, NULL);
179 pthread_cleanup_pop(0);
180 }
181
182 this->list->get_first(this->list,(void **) &next_event);
183
184 gettimeofday(&current_time,NULL);
185 long difference = time_difference(&current_time,&(next_event->time));
186 if (difference <= 0)
187 {
188 timeout.tv_sec = next_event->time.tv_sec;
189 timeout.tv_nsec = next_event->time.tv_usec * 1000;
190
191 /* add mutex unlock handler for cancellation, enable cancellation */
192 pthread_cleanup_push((void(*)(void*))pthread_mutex_unlock, (void*)&(this->mutex));
193 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
194
195 pthread_cond_timedwait( &(this->condvar), &(this->mutex),&timeout);
196
197 /* reset cancellation, remove mutex-unlock handler (without executing) */
198 pthread_setcancelstate(oldstate, NULL);
199 pthread_cleanup_pop(0);
200 }
201 else
202 {
203 /* event available */
204 this->list->remove_first(this->list,(void **) &next_event);
205
206 job = next_event->job;
207
208 next_event->destroy(next_event);
209 break;
210 }
211
212 }
213 pthread_cond_signal( &(this->condvar));
214
215 pthread_mutex_unlock(&(this->mutex));
216
217 return job;
218 }
219
220 /**
221 * Implements function add_absolute of event_queue_t.
222 * See #event_queue_s.add_absolute for description.
223 */
224 static void add_absolute(private_event_queue_t *this, job_t *job, timeval_t time)
225 {
226 event_t *event = event_create(time,job);
227 event_t *current_event;
228 status_t status;
229
230 pthread_mutex_lock(&(this->mutex));
231
232 /* while just used to break out */
233 while(1)
234 {
235 if (this->list->get_count(this->list) == 0)
236 {
237 this->list->insert_first(this->list,event);
238 break;
239 }
240
241 /* check last entry */
242 this->list->get_last(this->list,(void **) &current_event);
243
244 if (time_difference(&(event->time), &(current_event->time)) >= 0)
245 {
246 /* my event has to be fired after the last event in list */
247 this->list->insert_last(this->list,event);
248 break;
249 }
250
251 /* check first entry */
252 this->list->get_first(this->list,(void **) &current_event);
253
254 if (time_difference(&(event->time), &(current_event->time)) < 0)
255 {
256 /* my event has to be fired before the first event in list */
257 this->list->insert_first(this->list,event);
258 break;
259 }
260
261 iterator_t * iterator;
262
263 iterator = this->list->create_iterator(this->list,TRUE);
264
265 iterator->has_next(iterator);
266 /* first element has not to be checked (already done) */
267
268 while(iterator->has_next(iterator))
269 {
270 status = iterator->current(iterator,(void **) &current_event);
271
272 if (time_difference(&(event->time), &(current_event->time)) <= 0)
273 {
274 /* my event has to be fired before the current event in list */
275 iterator->insert_before(iterator,event);
276 break;
277 }
278 }
279 iterator->destroy(iterator);
280 break;
281 }
282
283 pthread_cond_signal( &(this->condvar));
284 pthread_mutex_unlock(&(this->mutex));
285 }
286
287 /**
288 * Implements event_queue_t.add_relative.
289 */
290 static void add_relative(event_queue_t *this, job_t *job, u_int32_t ms)
291 {
292 timeval_t current_time;
293 timeval_t time;
294 int micros = ms * 1000;
295
296 gettimeofday(&current_time, NULL);
297
298 time.tv_usec = ((current_time.tv_usec + micros) % 1000000);
299 time.tv_sec = current_time.tv_sec + ((current_time.tv_usec + micros)/ 1000000);
300
301 this->add_absolute(this, job, time);
302 }
303
304
305 /**
306 * Implements event_queue_t.destroy.
307 */
308 static void event_queue_destroy(private_event_queue_t *this)
309 {
310 while (this->list->get_count(this->list) > 0)
311 {
312 event_t *event;
313
314 if (this->list->remove_first(this->list,(void *) &event) != SUCCESS)
315 {
316 this->list->destroy(this->list);
317 break;
318 }
319 event->job->destroy_all(event->job);
320 event->destroy(event);
321 }
322 this->list->destroy(this->list);
323
324 pthread_mutex_destroy(&(this->mutex));
325
326 pthread_cond_destroy(&(this->condvar));
327
328 free(this);
329 }
330
331 /*
332 * Documented in header
333 */
334 event_queue_t *event_queue_create()
335 {
336 private_event_queue_t *this = malloc_thing(private_event_queue_t);
337
338 this->public.get_count = (int (*) (event_queue_t *event_queue)) get_count;
339 this->public.get = (job_t *(*) (event_queue_t *event_queue)) get;
340 this->public.add_absolute = (void (*) (event_queue_t *event_queue, job_t *job, timeval_t time)) add_absolute;
341 this->public.add_relative = (void (*) (event_queue_t *event_queue, job_t *job, u_int32_t ms)) add_relative;
342 this->public.destroy = (void (*) (event_queue_t *event_queue)) event_queue_destroy;
343
344 this->list = linked_list_create();
345 pthread_mutex_init(&(this->mutex), NULL);
346 pthread_cond_init(&(this->condvar), NULL);
347
348 return (&this->public);
349 }