2 * Copyright (C) 2005-2006 Martin Willi
3 * Copyright (C) 2005 Jan Hutter
4 * Hochschule fuer Technik Rapperswil
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>.
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
23 #include "scheduler.h"
26 #include <processing/processor.h>
27 #include <processing/jobs/callback_job.h>
29 typedef struct event_t event_t
;
32 * Event containing a job and a schedule time
36 * Time to fire the event.
41 * Every event has its assigned job.
47 * destroy an event and its job
49 static void event_destroy(event_t
*event
)
51 event
->job
->destroy(event
->job
);
55 typedef struct private_scheduler_t private_scheduler_t
;
58 * Private data of a scheduler_t object.
60 struct private_scheduler_t
{
62 * Public part of a scheduler_t object.
72 * The jobs are scheduled in a list.
77 * Exclusive access to list
79 pthread_mutex_t mutex
;
82 * Condvar to wait for next job.
84 pthread_cond_t condvar
;
90 * Returns the difference of two timeval structs in milliseconds
92 static long time_difference(timeval_t
*end
, timeval_t
*start
)
97 s
= end
->tv_sec
- start
->tv_sec
;
98 us
= end
->tv_usec
- start
->tv_usec
;
99 return (s
* 1000 + us
/1000);
103 * Get events from the queue and pass it to the processor
105 static job_requeue_t
schedule(private_scheduler_t
* this)
114 DBG2(DBG_JOB
, "waiting for next event...");
115 pthread_mutex_lock(&this->mutex
);
117 gettimeofday(&now
, NULL
);
119 if (this->list
->get_count(this->list
) > 0)
121 this->list
->get_first(this->list
, (void **)&event
);
122 difference
= time_difference(&now
, &event
->time
);
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
);
130 return JOB_REQUEUE_DIRECT
;
132 timeout
.tv_sec
= event
->time
.tv_sec
;
133 timeout
.tv_nsec
= event
->time
.tv_usec
* 1000;
136 pthread_cleanup_push((void*)pthread_mutex_unlock
, &this->mutex
);
137 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE
, &oldstate
);
141 pthread_cond_timedwait(&this->condvar
, &this->mutex
, &timeout
);
145 pthread_cond_wait(&this->condvar
, &this->mutex
);
147 pthread_setcancelstate(oldstate
, NULL
);
148 pthread_cleanup_pop(TRUE
);
149 return JOB_REQUEUE_DIRECT
;
153 * Implements scheduler_t.get_job_load
155 static u_int
get_job_load(private_scheduler_t
*this)
158 pthread_mutex_lock(&this->mutex
);
159 count
= this->list
->get_count(this->list
);
160 pthread_mutex_unlock(&this->mutex
);
165 * Implements scheduler_t.schedule_job.
167 static void schedule_job(private_scheduler_t
*this, job_t
*job
, u_int32_t time
)
170 event_t
*event
, *current
;
171 iterator_t
*iterator
;
175 event
= malloc_thing(event_t
);
178 /* calculate absolute time */
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
;
185 pthread_mutex_lock(&this->mutex
);
188 if (this->list
->get_count(this->list
) == 0)
190 this->list
->insert_first(this->list
,event
);
194 this->list
->get_last(this->list
, (void**)¤t
);
195 if (time_difference(&event
->time
, ¤t
->time
) >= 0)
196 { /* new event has to be fired after the last event in list */
197 this->list
->insert_last(this->list
, event
);
201 this->list
->get_first(this->list
, (void**)¤t
);
202 if (time_difference(&event
->time
, ¤t
->time
) < 0)
203 { /* new event has to be fired before the first event in list */
204 this->list
->insert_first(this->list
, event
);
208 iterator
= this->list
->create_iterator(this->list
, TRUE
);
209 /* first element has not to be checked (already done) */
210 iterator
->iterate(iterator
, (void**)¤t
);
211 while(iterator
->iterate(iterator
, (void**)¤t
))
213 if (time_difference(&event
->time
, ¤t
->time
) <= 0)
215 /* new event has to be fired before the current event in list */
216 iterator
->insert_before(iterator
, event
);
220 iterator
->destroy(iterator
);
223 pthread_cond_signal(&this->condvar
);
224 pthread_mutex_unlock(&this->mutex
);
228 * Implementation of scheduler_t.destroy.
230 static void destroy(private_scheduler_t
*this)
232 this->cancelled
= TRUE
;
233 this->job
->cancel(this->job
);
234 this->list
->destroy_function(this->list
, (void*)event_destroy
);
239 * Described in header.
241 scheduler_t
* scheduler_create()
243 private_scheduler_t
*this = malloc_thing(private_scheduler_t
);
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
;
249 this->list
= linked_list_create();
250 this->cancelled
= FALSE
;
251 pthread_mutex_init(&this->mutex
, NULL
);
252 pthread_cond_init(&this->condvar
, NULL
);
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
);
257 return &this->public;