replacing the pthread_mutex in scheduler_t with the wrapped implementation.
[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 #include <utils/mutex.h>
29
30 typedef struct event_t event_t;
31
32 /**
33 * Event containing a job and a schedule time
34 */
35 struct event_t {
36 /**
37 * Time to fire the event.
38 */
39 timeval_t time;
40
41 /**
42 * Every event has its assigned job.
43 */
44 job_t *job;
45 };
46
47 /**
48 * destroy an event and its job
49 */
50 static void event_destroy(event_t *event)
51 {
52 event->job->destroy(event->job);
53 free(event);
54 }
55
56 typedef struct private_scheduler_t private_scheduler_t;
57
58 /**
59 * Private data of a scheduler_t object.
60 */
61 struct private_scheduler_t {
62 /**
63 * Public part of a scheduler_t object.
64 */
65 scheduler_t public;
66
67 /**
68 * Job wich schedules
69 */
70 callback_job_t *job;
71
72 /**
73 * The jobs are scheduled in a list.
74 */
75 linked_list_t *list;
76
77 /**
78 * Exclusive access to list
79 */
80 mutex_t *mutex;
81
82 /**
83 * Condvar to wait for next job.
84 */
85 condvar_t *condvar;
86 };
87
88 /**
89 * Returns the difference of two timeval structs in milliseconds
90 */
91 static long time_difference(timeval_t *end, timeval_t *start)
92 {
93 time_t s;
94 suseconds_t us;
95
96 s = end->tv_sec - start->tv_sec;
97 us = end->tv_usec - start->tv_usec;
98 return (s * 1000 + us/1000);
99 }
100
101 /**
102 * Get events from the queue and pass it to the processor
103 */
104 static job_requeue_t schedule(private_scheduler_t * this)
105 {
106 timeval_t now;
107 event_t *event;
108 long difference;
109 int oldstate;
110 bool timed = FALSE;
111
112 DBG2(DBG_JOB, "waiting for next event...");
113 this->mutex->lock(this->mutex);
114
115 gettimeofday(&now, NULL);
116
117 if (this->list->get_count(this->list) > 0)
118 {
119 this->list->get_first(this->list, (void **)&event);
120 difference = time_difference(&now, &event->time);
121 if (difference > 0)
122 {
123 this->list->remove_first(this->list, (void **)&event);
124 this->mutex->unlock(this->mutex);
125 DBG2(DBG_JOB, "got event, queueing job for execution");
126 charon->processor->queue_job(charon->processor, event->job);
127 free(event);
128 return JOB_REQUEUE_DIRECT;
129 }
130 timed = TRUE;
131 }
132 pthread_cleanup_push((void*)this->mutex->unlock, this->mutex);
133 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
134
135 if (timed)
136 {
137 this->condvar->timed_wait_abs(this->condvar, this->mutex, event->time);
138 }
139 else
140 {
141 this->condvar->wait(this->condvar, this->mutex);
142 }
143 pthread_setcancelstate(oldstate, NULL);
144 pthread_cleanup_pop(TRUE);
145 return JOB_REQUEUE_DIRECT;
146 }
147
148 /**
149 * Implements scheduler_t.get_job_load
150 */
151 static u_int get_job_load(private_scheduler_t *this)
152 {
153 int count;
154 this->mutex->lock(this->mutex);
155 count = this->list->get_count(this->list);
156 this->mutex->unlock(this->mutex);
157 return count;
158 }
159
160 /**
161 * Implements scheduler_t.schedule_job.
162 */
163 static void schedule_job(private_scheduler_t *this, job_t *job, u_int32_t time)
164 {
165 timeval_t now;
166 event_t *event, *current;
167 iterator_t *iterator;
168 time_t s;
169 suseconds_t us;
170
171 event = malloc_thing(event_t);
172 event->job = job;
173
174 /* calculate absolute time */
175 s = time / 1000;
176 us = (time - s * 1000) * 1000;
177 gettimeofday(&now, NULL);
178 event->time.tv_usec = (now.tv_usec + us) % 1000000;
179 event->time.tv_sec = now.tv_sec + (now.tv_usec + us)/1000000 + s;
180
181 this->mutex->lock(this->mutex);
182 while(TRUE)
183 {
184 if (this->list->get_count(this->list) == 0)
185 {
186 this->list->insert_first(this->list,event);
187 break;
188 }
189
190 this->list->get_last(this->list, (void**)&current);
191 if (time_difference(&event->time, &current->time) >= 0)
192 { /* new event has to be fired after the last event in list */
193 this->list->insert_last(this->list, event);
194 break;
195 }
196
197 this->list->get_first(this->list, (void**)&current);
198 if (time_difference(&event->time, &current->time) < 0)
199 { /* new event has to be fired before the first event in list */
200 this->list->insert_first(this->list, event);
201 break;
202 }
203
204 iterator = this->list->create_iterator(this->list, TRUE);
205 /* first element has not to be checked (already done) */
206 iterator->iterate(iterator, (void**)&current);
207 while(iterator->iterate(iterator, (void**)&current))
208 {
209 if (time_difference(&event->time, &current->time) <= 0)
210 {
211 /* new event has to be fired before the current event in list */
212 iterator->insert_before(iterator, event);
213 break;
214 }
215 }
216 iterator->destroy(iterator);
217 break;
218 }
219 this->condvar->signal(this->condvar);
220 this->mutex->unlock(this->mutex);
221 }
222
223 /**
224 * Implementation of scheduler_t.destroy.
225 */
226 static void destroy(private_scheduler_t *this)
227 {
228 this->job->cancel(this->job);
229 this->condvar->destroy(this->condvar);
230 this->mutex->destroy(this->mutex);
231 this->list->destroy_function(this->list, (void*)event_destroy);
232 free(this);
233 }
234
235 /*
236 * Described in header.
237 */
238 scheduler_t * scheduler_create()
239 {
240 private_scheduler_t *this = malloc_thing(private_scheduler_t);
241
242 this->public.get_job_load = (u_int (*) (scheduler_t *this)) get_job_load;
243 this->public.schedule_job = (void (*) (scheduler_t *this, job_t *job, u_int32_t ms)) schedule_job;
244 this->public.destroy = (void(*)(scheduler_t*)) destroy;
245
246 this->list = linked_list_create();
247 this->mutex = mutex_create(MUTEX_DEFAULT);
248 this->condvar = condvar_create(CONDVAR_DEFAULT);
249
250 this->job = callback_job_create((callback_job_cb_t)schedule, this, NULL, NULL);
251 charon->processor->queue_job(charon->processor, (job_t*)this->job);
252
253 return &this->public;
254 }
255