restructured file layout
[strongswan.git] / src / charon / processing / 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 <library.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 /**
52 * destroy an event and its job
53 */
54 static void event_destroy(event_t *event)
55 {
56 event->job->destroy(event->job);
57 free(event);
58 }
59
60 typedef struct private_event_queue_t private_event_queue_t;
61
62 /**
63 * Private Variables and Functions of event_queue_t class.
64 */
65 struct private_event_queue_t {
66 /**
67 * Public part.
68 */
69 event_queue_t public;
70
71 /**
72 * The events are stored in a linked list of type linked_list_t.
73 */
74 linked_list_t *list;
75
76 /**
77 * Access to linked_list is locked through this mutex.
78 */
79 pthread_mutex_t mutex;
80
81 /**
82 * If the queue is empty or an event has not to be fired
83 * a thread has to wait.
84 *
85 * This condvar is used to wake up such a thread.
86 */
87 pthread_cond_t condvar;
88 };
89
90 /**
91 * Returns the difference of to timeval structs in milliseconds
92 */
93 static long time_difference(struct timeval *end_time, struct timeval *start_time)
94 {
95 time_t s;
96 suseconds_t us;
97
98 s = (end_time->tv_sec - start_time->tv_sec);
99 us = (end_time->tv_usec - start_time->tv_usec);
100 return ((s * 1000) + us/1000);
101 }
102
103 /**
104 * Implements event_queue_t.get_count
105 */
106 static int get_count(private_event_queue_t *this)
107 {
108 int count;
109 pthread_mutex_lock(&(this->mutex));
110 count = this->list->get_count(this->list);
111 pthread_mutex_unlock(&(this->mutex));
112 return count;
113 }
114
115 /**
116 * Implements event_queue_t.get
117 */
118 static job_t *get(private_event_queue_t *this)
119 {
120 timespec_t timeout;
121 timeval_t current_time;
122 event_t * next_event;
123 job_t *job;
124 int oldstate;
125
126 pthread_mutex_lock(&(this->mutex));
127
128 while (TRUE)
129 {
130 while(this->list->get_count(this->list) == 0)
131 {
132 /* add mutex unlock handler for cancellation, enable cancellation */
133 pthread_cleanup_push((void(*)(void*))pthread_mutex_unlock, (void*)&(this->mutex));
134 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
135
136 pthread_cond_wait( &(this->condvar), &(this->mutex));
137
138 /* reset cancellation, remove mutex-unlock handler (without executing) */
139 pthread_setcancelstate(oldstate, NULL);
140 pthread_cleanup_pop(0);
141 }
142
143 this->list->get_first(this->list, (void **)&next_event);
144
145 gettimeofday(&current_time, NULL);
146 long difference = time_difference(&current_time,&(next_event->time));
147 if (difference <= 0)
148 {
149 timeout.tv_sec = next_event->time.tv_sec;
150 timeout.tv_nsec = next_event->time.tv_usec * 1000;
151
152 /* add mutex unlock handler for cancellation, enable cancellation */
153 pthread_cleanup_push((void(*)(void*))pthread_mutex_unlock, (void*)&(this->mutex));
154 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
155
156 pthread_cond_timedwait(&(this->condvar), &(this->mutex), &timeout);
157
158 /* reset cancellation, remove mutex-unlock handler (without executing) */
159 pthread_setcancelstate(oldstate, NULL);
160 pthread_cleanup_pop(0);
161 }
162 else
163 {
164 /* event available */
165 this->list->remove_first(this->list, (void **)&next_event);
166 job = next_event->job;
167 free(next_event);
168 break;
169 }
170 }
171 pthread_cond_signal( &(this->condvar));
172 pthread_mutex_unlock(&(this->mutex));
173
174 return job;
175 }
176
177 /**
178 * Implements function add_absolute of event_queue_t.
179 * See #event_queue_s.add_absolute for description.
180 */
181 static void add_absolute(private_event_queue_t *this, job_t *job, timeval_t time)
182 {
183 event_t *event;
184 event_t *current_event;
185 iterator_t *iterator;
186
187 /* create event */
188 event = malloc_thing(event_t);
189 event->time = time;
190 event->job = job;
191
192 pthread_mutex_lock(&(this->mutex));
193
194 /* while just used to break out */
195 while(TRUE)
196 {
197 if (this->list->get_count(this->list) == 0)
198 {
199 this->list->insert_first(this->list,event);
200 break;
201 }
202
203 /* check last entry */
204 this->list->get_last(this->list,(void **) &current_event);
205
206 if (time_difference(&(event->time), &(current_event->time)) >= 0)
207 {
208 /* my event has to be fired after the last event in list */
209 this->list->insert_last(this->list,event);
210 break;
211 }
212
213 /* check first entry */
214 this->list->get_first(this->list,(void **) &current_event);
215
216 if (time_difference(&(event->time), &(current_event->time)) < 0)
217 {
218 /* my event has to be fired before the first event in list */
219 this->list->insert_first(this->list,event);
220 break;
221 }
222
223 iterator = this->list->create_iterator(this->list,TRUE);
224 iterator->iterate(iterator, (void**)&current_event);
225 /* first element has not to be checked (already done) */
226 while(iterator->iterate(iterator, (void**)&current_event))
227 {
228 if (time_difference(&(event->time), &(current_event->time)) <= 0)
229 {
230 /* my event has to be fired before the current event in list */
231 iterator->insert_before(iterator,event);
232 break;
233 }
234 }
235 iterator->destroy(iterator);
236 break;
237 }
238
239 pthread_cond_signal( &(this->condvar));
240 pthread_mutex_unlock(&(this->mutex));
241 }
242
243 /**
244 * Implements event_queue_t.add_relative.
245 */
246 static void add_relative(event_queue_t *this, job_t *job, u_int32_t ms)
247 {
248 timeval_t current_time;
249 timeval_t time;
250
251 time_t s = ms / 1000;
252 suseconds_t us = (ms - s * 1000) * 1000;
253
254 gettimeofday(&current_time, NULL);
255
256 time.tv_usec = (current_time.tv_usec + us) % 1000000;
257 time.tv_sec = current_time.tv_sec + (current_time.tv_usec + us)/1000000 + s;
258
259 this->add_absolute(this, job, time);
260 }
261
262
263 /**
264 * Implements event_queue_t.destroy.
265 */
266 static void event_queue_destroy(private_event_queue_t *this)
267 {
268 this->list->destroy_function(this->list, (void*)event_destroy);
269 free(this);
270 }
271
272 /*
273 * Documented in header
274 */
275 event_queue_t *event_queue_create()
276 {
277 private_event_queue_t *this = malloc_thing(private_event_queue_t);
278
279 this->public.get_count = (int (*) (event_queue_t *event_queue)) get_count;
280 this->public.get = (job_t *(*) (event_queue_t *event_queue)) get;
281 this->public.add_absolute = (void (*) (event_queue_t *event_queue, job_t *job, timeval_t time)) add_absolute;
282 this->public.add_relative = (void (*) (event_queue_t *event_queue, job_t *job, u_int32_t ms)) add_relative;
283 this->public.destroy = (void (*) (event_queue_t *event_queue)) event_queue_destroy;
284
285 this->list = linked_list_create();
286 pthread_mutex_init(&(this->mutex), NULL);
287 pthread_cond_init(&(this->condvar), NULL);
288
289 return (&this->public);
290 }