bbf1dc1a4b9608f6786e7f24612a9f14bab5be5a
[strongswan.git] / src / charon / processing / scheduler.c
1 /*
2 * Copyright (C) 2005-2006 Martin Willi
3 * Copyright (C) 2005 Jan Hutter
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 *
16 * $Id$
17 */
18
19 #include <stdlib.h>
20 #include <pthread.h>
21 #include <sys/time.h>
22
23 #include "scheduler.h"
24
25 #include <daemon.h>
26 #include <processing/processor.h>
27 #include <processing/jobs/callback_job.h>
28
29 typedef struct event_t event_t;
30
31 /**
32 * Event containing a job and a schedule time
33 */
34 struct event_t {
35 /**
36 * Time to fire the event.
37 */
38 timeval_t time;
39
40 /**
41 * Every event has its assigned job.
42 */
43 job_t *job;
44 };
45
46 /**
47 * destroy an event and its job
48 */
49 static void event_destroy(event_t *event)
50 {
51 event->job->destroy(event->job);
52 free(event);
53 }
54
55 typedef struct private_scheduler_t private_scheduler_t;
56
57 /**
58 * Private data of a scheduler_t object.
59 */
60 struct private_scheduler_t {
61 /**
62 * Public part of a scheduler_t object.
63 */
64 scheduler_t public;
65
66 /**
67 * Job wich schedules
68 */
69 callback_job_t *job;
70
71 /**
72 * The jobs are scheduled in a list.
73 */
74 linked_list_t *list;
75
76 /**
77 * Exclusive access to list
78 */
79 pthread_mutex_t mutex;
80
81 /**
82 * Condvar to wait for next job.
83 */
84 pthread_cond_t condvar;
85
86 bool cancelled;
87 };
88
89 /**
90 * Returns the difference of two timeval structs in milliseconds
91 */
92 static long time_difference(timeval_t *end, timeval_t *start)
93 {
94 time_t s;
95 suseconds_t us;
96
97 s = end->tv_sec - start->tv_sec;
98 us = end->tv_usec - start->tv_usec;
99 return (s * 1000 + us/1000);
100 }
101
102 /**
103 * Get events from the queue and pass it to the processor
104 */
105 static job_requeue_t schedule(private_scheduler_t * this)
106 {
107 timespec_t timeout;
108 timeval_t now;
109 event_t *event;
110 long difference;
111 int oldstate;
112 bool timed = FALSE;
113
114 DBG2(DBG_JOB, "waiting for next event...");
115 pthread_mutex_lock(&this->mutex);
116
117 gettimeofday(&now, NULL);
118
119 if (this->list->get_count(this->list) > 0)
120 {
121 this->list->get_first(this->list, (void **)&event);
122 difference = time_difference(&now, &event->time);
123 if (difference > 0)
124 {
125 DBG2(DBG_JOB, "got event, queueing job for execution");
126 this->list->remove_first(this->list, (void **)&event);
127 pthread_mutex_unlock(&this->mutex);
128 charon->processor->queue_job(charon->processor, event->job);
129 free(event);
130 return JOB_REQUEUE_DIRECT;
131 }
132 timeout.tv_sec = event->time.tv_sec;
133 timeout.tv_nsec = event->time.tv_usec * 1000;
134 timed = TRUE;
135 }
136 pthread_cleanup_push((void*)pthread_mutex_unlock, &this->mutex);
137 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
138
139 if (timed)
140 {
141 pthread_cond_timedwait(&this->condvar, &this->mutex, &timeout);
142 }
143 else
144 {
145 pthread_cond_wait(&this->condvar, &this->mutex);
146 }
147 pthread_setcancelstate(oldstate, NULL);
148 pthread_cleanup_pop(TRUE);
149 return JOB_REQUEUE_DIRECT;
150 }
151
152 /**
153 * Implements scheduler_t.get_job_load
154 */
155 static u_int get_job_load(private_scheduler_t *this)
156 {
157 int count;
158 pthread_mutex_lock(&this->mutex);
159 count = this->list->get_count(this->list);
160 pthread_mutex_unlock(&this->mutex);
161 return count;
162 }
163
164 /**
165 * Implements scheduler_t.schedule_job.
166 */
167 static void schedule_job(private_scheduler_t *this, job_t *job, u_int32_t time)
168 {
169 timeval_t now;
170 event_t *event, *current;
171 iterator_t *iterator;
172 time_t s;
173 suseconds_t us;
174
175 event = malloc_thing(event_t);
176 event->job = job;
177
178 /* calculate absolute time */
179 s = time / 1000;
180 us = (time - s * 1000) * 1000;
181 gettimeofday(&now, NULL);
182 event->time.tv_usec = (now.tv_usec + us) % 1000000;
183 event->time.tv_sec = now.tv_sec + (now.tv_usec + us)/1000000 + s;
184
185 pthread_mutex_lock(&this->mutex);
186 while(TRUE)
187 {
188 if (this->list->get_count(this->list) == 0)
189 {
190 this->list->insert_first(this->list,event);
191 break;
192 }
193
194 this->list->get_last(this->list, (void**)&current);
195 if (time_difference(&event->time, &current->time) >= 0)
196 { /* new event has to be fired after the last event in list */
197 this->list->insert_last(this->list, event);
198 break;
199 }
200
201 this->list->get_first(this->list, (void**)&current);
202 if (time_difference(&event->time, &current->time) < 0)
203 { /* new event has to be fired before the first event in list */
204 this->list->insert_first(this->list, event);
205 break;
206 }
207
208 iterator = this->list->create_iterator(this->list, TRUE);
209 /* first element has not to be checked (already done) */
210 iterator->iterate(iterator, (void**)&current);
211 while(iterator->iterate(iterator, (void**)&current))
212 {
213 if (time_difference(&event->time, &current->time) <= 0)
214 {
215 /* new event has to be fired before the current event in list */
216 iterator->insert_before(iterator, event);
217 break;
218 }
219 }
220 iterator->destroy(iterator);
221 break;
222 }
223 pthread_cond_signal(&this->condvar);
224 pthread_mutex_unlock(&this->mutex);
225 }
226
227 /**
228 * Implementation of scheduler_t.destroy.
229 */
230 static void destroy(private_scheduler_t *this)
231 {
232 this->cancelled = TRUE;
233 this->job->cancel(this->job);
234 this->list->destroy_function(this->list, (void*)event_destroy);
235 free(this);
236 }
237
238 /*
239 * Described in header.
240 */
241 scheduler_t * scheduler_create()
242 {
243 private_scheduler_t *this = malloc_thing(private_scheduler_t);
244
245 this->public.get_job_load = (u_int (*) (scheduler_t *this)) get_job_load;
246 this->public.schedule_job = (void (*) (scheduler_t *this, job_t *job, u_int32_t ms)) schedule_job;
247 this->public.destroy = (void(*)(scheduler_t*)) destroy;
248
249 this->list = linked_list_create();
250 this->cancelled = FALSE;
251 pthread_mutex_init(&this->mutex, NULL);
252 pthread_cond_init(&this->condvar, NULL);
253
254 this->job = callback_job_create((callback_job_cb_t)schedule, this, NULL, NULL);
255 charon->processor->queue_job(charon->processor, (job_t*)this->job);
256
257 return &this->public;
258 }
259