moved interface enumeration code to socket, where it belongs
[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-2006 Martin Willi
10 * Copyright (C) 2005 Jan Hutter
11 * Hochschule fuer Technik Rapperswil
12 *
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU General Public License as published by the
15 * Free Software Foundation; either version 2 of the License, or (at your
16 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 * for more details.
22 */
23
24 #include <pthread.h>
25 #include <stdlib.h>
26
27 #include "event_queue.h"
28
29 #include <types.h>
30 #include <utils/linked_list.h>
31
32
33
34 typedef struct event_t event_t;
35
36 /**
37 * Event containing a job and a schedule time
38 */
39 struct event_t {
40 /**
41 * Time to fire the event.
42 */
43 timeval_t time;
44
45 /**
46 * Every event has its assigned job.
47 */
48 job_t * job;
49 };
50
51 typedef struct private_event_queue_t private_event_queue_t;
52
53 /**
54 * Private Variables and Functions of event_queue_t class.
55 */
56 struct private_event_queue_t {
57 /**
58 * Public part.
59 */
60 event_queue_t public;
61
62 /**
63 * The events are stored in a linked list of type linked_list_t.
64 */
65 linked_list_t *list;
66
67 /**
68 * Access to linked_list is locked through this mutex.
69 */
70 pthread_mutex_t mutex;
71
72 /**
73 * If the queue is empty or an event has not to be fired
74 * a thread has to wait.
75 *
76 * This condvar is used to wake up such a thread.
77 */
78 pthread_cond_t condvar;
79 };
80
81 /**
82 * Returns the difference of to timeval structs in milliseconds
83 */
84 static long time_difference(struct timeval *end_time, struct timeval *start_time)
85 {
86 time_t s;
87 suseconds_t us;
88
89 s = (end_time->tv_sec - start_time->tv_sec);
90 us = (end_time->tv_usec - start_time->tv_usec);
91 return ((s * 1000) + us/1000);
92 }
93
94 /**
95 * Implements event_queue_t.get_count
96 */
97 static int get_count(private_event_queue_t *this)
98 {
99 int count;
100 pthread_mutex_lock(&(this->mutex));
101 count = this->list->get_count(this->list);
102 pthread_mutex_unlock(&(this->mutex));
103 return count;
104 }
105
106 /**
107 * Implements event_queue_t.get
108 */
109 static job_t *get(private_event_queue_t *this)
110 {
111 timespec_t timeout;
112 timeval_t current_time;
113 event_t * next_event;
114 job_t *job;
115 int oldstate;
116
117 pthread_mutex_lock(&(this->mutex));
118
119 while (TRUE)
120 {
121 while(this->list->get_count(this->list) == 0)
122 {
123 /* add mutex unlock handler for cancellation, enable cancellation */
124 pthread_cleanup_push((void(*)(void*))pthread_mutex_unlock, (void*)&(this->mutex));
125 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
126
127 pthread_cond_wait( &(this->condvar), &(this->mutex));
128
129 /* reset cancellation, remove mutex-unlock handler (without executing) */
130 pthread_setcancelstate(oldstate, NULL);
131 pthread_cleanup_pop(0);
132 }
133
134 this->list->get_first(this->list, (void **)&next_event);
135
136 gettimeofday(&current_time, NULL);
137 long difference = time_difference(&current_time,&(next_event->time));
138 if (difference <= 0)
139 {
140 timeout.tv_sec = next_event->time.tv_sec;
141 timeout.tv_nsec = next_event->time.tv_usec * 1000;
142
143 /* add mutex unlock handler for cancellation, enable cancellation */
144 pthread_cleanup_push((void(*)(void*))pthread_mutex_unlock, (void*)&(this->mutex));
145 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
146
147 pthread_cond_timedwait(&(this->condvar), &(this->mutex), &timeout);
148
149 /* reset cancellation, remove mutex-unlock handler (without executing) */
150 pthread_setcancelstate(oldstate, NULL);
151 pthread_cleanup_pop(0);
152 }
153 else
154 {
155 /* event available */
156 this->list->remove_first(this->list, (void **)&next_event);
157 job = next_event->job;
158 free(next_event);
159 break;
160 }
161 }
162 pthread_cond_signal( &(this->condvar));
163 pthread_mutex_unlock(&(this->mutex));
164
165 return job;
166 }
167
168 /**
169 * Implements function add_absolute of event_queue_t.
170 * See #event_queue_s.add_absolute for description.
171 */
172 static void add_absolute(private_event_queue_t *this, job_t *job, timeval_t time)
173 {
174 event_t *event;
175 event_t *current_event;
176 status_t status;
177
178 /* create event */
179 event = malloc_thing(event_t);
180 event->time = time;
181 event->job = job;
182
183 pthread_mutex_lock(&(this->mutex));
184
185 /* while just used to break out */
186 while(1)
187 {
188 if (this->list->get_count(this->list) == 0)
189 {
190 this->list->insert_first(this->list,event);
191 break;
192 }
193
194 /* check last entry */
195 this->list->get_last(this->list,(void **) &current_event);
196
197 if (time_difference(&(event->time), &(current_event->time)) >= 0)
198 {
199 /* my event has to be fired after the last event in list */
200 this->list->insert_last(this->list,event);
201 break;
202 }
203
204 /* check first entry */
205 this->list->get_first(this->list,(void **) &current_event);
206
207 if (time_difference(&(event->time), &(current_event->time)) < 0)
208 {
209 /* my event has to be fired before the first event in list */
210 this->list->insert_first(this->list,event);
211 break;
212 }
213
214 iterator_t * iterator;
215
216 iterator = this->list->create_iterator(this->list,TRUE);
217
218 iterator->has_next(iterator);
219 /* first element has not to be checked (already done) */
220
221 while(iterator->has_next(iterator))
222 {
223 status = iterator->current(iterator,(void **) &current_event);
224
225 if (time_difference(&(event->time), &(current_event->time)) <= 0)
226 {
227 /* my event has to be fired before the current event in list */
228 iterator->insert_before(iterator,event);
229 break;
230 }
231 }
232 iterator->destroy(iterator);
233 break;
234 }
235
236 pthread_cond_signal( &(this->condvar));
237 pthread_mutex_unlock(&(this->mutex));
238 }
239
240 /**
241 * Implements event_queue_t.add_relative.
242 */
243 static void add_relative(event_queue_t *this, job_t *job, u_int32_t ms)
244 {
245 timeval_t current_time;
246 timeval_t time;
247
248 time_t s = ms / 1000;
249 suseconds_t us = (ms - s * 1000) * 1000;
250
251 gettimeofday(&current_time, NULL);
252
253 time.tv_usec = (current_time.tv_usec + us) % 1000000;
254 time.tv_sec = current_time.tv_sec + (current_time.tv_usec + us)/1000000 + s;
255
256 this->add_absolute(this, job, time);
257 }
258
259
260 /**
261 * Implements event_queue_t.destroy.
262 */
263 static void event_queue_destroy(private_event_queue_t *this)
264 {
265 event_t *event;
266 while (this->list->remove_last(this->list, (void**)&event) == SUCCESS)
267 {
268 event->job->destroy(event->job);
269 free(event);
270 }
271 this->list->destroy(this->list);
272 pthread_mutex_destroy(&(this->mutex));
273 pthread_cond_destroy(&(this->condvar));
274 free(this);
275 }
276
277 /*
278 * Documented in header
279 */
280 event_queue_t *event_queue_create()
281 {
282 private_event_queue_t *this = malloc_thing(private_event_queue_t);
283
284 this->public.get_count = (int (*) (event_queue_t *event_queue)) get_count;
285 this->public.get = (job_t *(*) (event_queue_t *event_queue)) get;
286 this->public.add_absolute = (void (*) (event_queue_t *event_queue, job_t *job, timeval_t time)) add_absolute;
287 this->public.add_relative = (void (*) (event_queue_t *event_queue, job_t *job, u_int32_t ms)) add_relative;
288 this->public.destroy = (void (*) (event_queue_t *event_queue)) event_queue_destroy;
289
290 this->list = linked_list_create();
291 pthread_mutex_init(&(this->mutex), NULL);
292 pthread_cond_init(&(this->condvar), NULL);
293
294 return (&this->public);
295 }