4 * @brief Implementation of scheduler_t.
9 * Copyright (C) 2005-2006 Martin Willi
10 * Copyright (C) 2005 Jan Hutter
11 * Hochschule fuer Technik Rapperswil
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>.
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
28 #include "scheduler.h"
31 #include <processing/processor.h>
32 #include <processing/jobs/callback_job.h>
34 typedef struct event_t event_t
;
37 * Event containing a job and a schedule time
41 * Time to fire the event.
46 * Every event has its assigned job.
52 * destroy an event and its job
54 static void event_destroy(event_t
*event
)
56 event
->job
->destroy(event
->job
);
60 typedef struct private_scheduler_t private_scheduler_t
;
63 * Private data of a scheduler_t object.
65 struct private_scheduler_t
{
67 * Public part of a scheduler_t object.
77 * The jobs are scheduled in a list.
82 * Exclusive access to list
84 pthread_mutex_t mutex
;
87 * Condvar to wait for next job.
89 pthread_cond_t condvar
;
93 * Returns the difference of two timeval structs in milliseconds
95 static long time_difference(timeval_t
*end
, timeval_t
*start
)
100 s
= end
->tv_sec
- start
->tv_sec
;
101 us
= end
->tv_usec
- start
->tv_usec
;
102 return (s
* 1000 + us
/1000);
106 * Get events from the queue and pass it to the processor
108 static job_requeue_t
schedule(private_scheduler_t
* this)
117 DBG2(DBG_JOB
, "waiting for next event...");
118 pthread_mutex_lock(&this->mutex
);
120 gettimeofday(&now
, NULL
);
122 if (this->list
->get_count(this->list
) > 0)
124 this->list
->get_first(this->list
, (void **)&event
);
125 difference
= time_difference(&now
, &event
->time
);
128 DBG2(DBG_JOB
, "got event, queueing job for execution");
129 this->list
->remove_first(this->list
, (void **)&event
);
130 pthread_mutex_unlock(&this->mutex
);
131 charon
->processor
->queue_job(charon
->processor
, event
->job
);
133 return JOB_REQUEUE_DIRECT
;
135 timeout
.tv_sec
= event
->time
.tv_sec
;
136 timeout
.tv_nsec
= event
->time
.tv_usec
* 1000;
139 pthread_cleanup_push((void*)pthread_mutex_unlock
, &this->mutex
);
140 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE
, &oldstate
);
144 pthread_cond_timedwait(&this->condvar
, &this->mutex
, &timeout
);
148 pthread_cond_wait(&this->condvar
, &this->mutex
);
150 pthread_setcancelstate(oldstate
, NULL
);
151 pthread_cleanup_pop(0);
153 pthread_mutex_unlock(&this->mutex
);
154 return JOB_REQUEUE_DIRECT
;
158 * Implements scheduler_t.get_job_load
160 static u_int
get_job_load(private_scheduler_t
*this)
163 pthread_mutex_lock(&this->mutex
);
164 count
= this->list
->get_count(this->list
);
165 pthread_mutex_unlock(&this->mutex
);
170 * Implements scheduler_t.schedule_job.
172 static void schedule_job(private_scheduler_t
*this, job_t
*job
, u_int32_t time
)
175 event_t
*event
, *current
;
176 iterator_t
*iterator
;
180 event
= malloc_thing(event_t
);
183 /* calculate absolute time */
185 us
= (time
- s
* 1000) * 1000;
186 gettimeofday(&now
, NULL
);
187 event
->time
.tv_usec
= (now
.tv_usec
+ us
) % 1000000;
188 event
->time
.tv_sec
= now
.tv_sec
+ (now
.tv_usec
+ us
)/1000000 + s
;
190 pthread_mutex_lock(&this->mutex
);
193 if (this->list
->get_count(this->list
) == 0)
195 this->list
->insert_first(this->list
,event
);
199 this->list
->get_last(this->list
, (void**)¤t
);
200 if (time_difference(&event
->time
, ¤t
->time
) >= 0)
201 { /* new event has to be fired after the last event in list */
202 this->list
->insert_last(this->list
, event
);
206 this->list
->get_first(this->list
, (void**)¤t
);
207 if (time_difference(&event
->time
, ¤t
->time
) < 0)
208 { /* new event has to be fired before the first event in list */
209 this->list
->insert_first(this->list
, event
);
213 iterator
= this->list
->create_iterator(this->list
, TRUE
);
214 /* first element has not to be checked (already done) */
215 iterator
->iterate(iterator
, (void**)¤t
);
216 while(iterator
->iterate(iterator
, (void**)¤t
))
218 if (time_difference(&event
->time
, ¤t
->time
) <= 0)
220 /* new event has to be fired before the current event in list */
221 iterator
->insert_before(iterator
, event
);
225 iterator
->destroy(iterator
);
228 pthread_cond_signal(&this->condvar
);
229 pthread_mutex_unlock(&this->mutex
);
233 * Implementation of scheduler_t.destroy.
235 static void destroy(private_scheduler_t
*this)
237 this->job
->cancel(this->job
);
238 this->list
->destroy_function(this->list
, (void*)event_destroy
);
243 * Described in header.
245 scheduler_t
* scheduler_create()
247 private_scheduler_t
*this = malloc_thing(private_scheduler_t
);
249 this->public.get_job_load
= (u_int (*) (scheduler_t
*this)) get_job_load
;
250 this->public.schedule_job
= (void (*) (scheduler_t
*this, job_t
*job
, u_int32_t ms
)) schedule_job
;
251 this->public.destroy
= (void(*)(scheduler_t
*)) destroy
;
253 this->list
= linked_list_create();
254 pthread_mutex_init(&this->mutex
, NULL
);
255 pthread_cond_init(&this->condvar
, NULL
);
257 this->job
= callback_job_create((callback_job_cb_t
)schedule
, this, NULL
, NULL
);
258 charon
->processor
->queue_job(charon
->processor
, (job_t
*)this->job
);
260 return &this->public;