2 * Copyright (C) 2009 Tobias Brunner
3 * Copyright (C) 2007-2011 Martin Willi
4 * Copyright (C) 2011 revosec AG
5 * Hochschule fuer Technik Rapperswil
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 #include "callback_job.h"
20 #include <semaphore.h>
22 #include <threading/thread.h>
23 #include <threading/condvar.h>
24 #include <threading/mutex.h>
25 #include <utils/linked_list.h>
27 typedef struct private_callback_job_t private_callback_job_t
;
30 * Private data of an callback_job_t Object.
32 struct private_callback_job_t
{
35 * Public callback_job_t interface.
37 callback_job_t
public;
40 * Callback to call on execution
42 callback_job_cb_t callback
;
45 * parameter to supply to callback
50 * cleanup function for data
52 callback_job_cleanup_t cleanup
;
55 * thread of the job, if running
60 * mutex to access jobs interna
65 * list of asociated child jobs
67 linked_list_t
*children
;
70 * parent of this job, or NULL
72 private_callback_job_t
*parent
;
75 * TRUE if the job got cancelled
80 * condvar to synchronize the cancellation/destruction of the job
82 condvar_t
*destroyable
;
85 * semaphore to synchronize the termination of the assigned thread.
87 * separately allocated during cancellation, so that we can wait on it
88 * without risking that it gets freed too early during destruction.
94 * unregister a child from its parent, if any.
95 * note: this->mutex has to be locked
97 static void unregister(private_callback_job_t
*this)
101 this->parent
->mutex
->lock(this->parent
->mutex
);
102 if (this->parent
->cancelled
&& !this->cancelled
)
104 /* if the parent has been cancelled but we have not yet, we do not
105 * unregister until we got cancelled by the parent. */
106 this->parent
->mutex
->unlock(this->parent
->mutex
);
107 this->destroyable
->wait(this->destroyable
, this->mutex
);
108 this->parent
->mutex
->lock(this->parent
->mutex
);
110 this->parent
->children
->remove(this->parent
->children
, this, NULL
);
111 this->parent
->mutex
->unlock(this->parent
->mutex
);
116 METHOD(job_t
, destroy
, void,
117 private_callback_job_t
*this)
119 this->mutex
->lock(this->mutex
);
123 this->cleanup(this->data
);
125 if (this->terminated
)
127 sem_post(this->terminated
);
129 this->children
->destroy(this->children
);
130 this->destroyable
->destroy(this->destroyable
);
131 this->mutex
->unlock(this->mutex
);
132 this->mutex
->destroy(this->mutex
);
136 METHOD(callback_job_t
, cancel
, void,
137 private_callback_job_t
*this)
139 callback_job_t
*child
;
140 sem_t
*terminated
= NULL
;
142 this->mutex
->lock(this->mutex
);
143 this->cancelled
= TRUE
;
144 /* terminate children */
145 while (this->children
->get_first(this->children
, (void**)&child
) == SUCCESS
)
147 this->mutex
->unlock(this->mutex
);
148 child
->cancel(child
);
149 this->mutex
->lock(this->mutex
);
153 /* terminate the thread, if there is currently one executing the job.
154 * we wait for its termination using a semaphore */
155 this->thread
->cancel(this->thread
);
156 terminated
= this->terminated
= malloc_thing(sem_t
);
157 sem_init(terminated
, 0, 0);
161 /* if the job is currently queued, it gets terminated later.
162 * we can't wait, because it might not get executed at all.
163 * we also unregister the queued job manually from its parent (the
164 * others get unregistered during destruction) */
167 this->destroyable
->signal(this->destroyable
);
168 this->mutex
->unlock(this->mutex
);
172 sem_wait(terminated
);
173 sem_destroy(terminated
);
178 METHOD(job_t
, execute
, void,
179 private_callback_job_t
*this)
181 bool cleanup
= FALSE
, requeue
= FALSE
;
183 thread_cleanup_push((thread_cleanup_t
)destroy
, this);
185 this->mutex
->lock(this->mutex
);
186 this->thread
= thread_current();
187 this->mutex
->unlock(this->mutex
);
191 this->mutex
->lock(this->mutex
);
194 this->mutex
->unlock(this->mutex
);
198 this->mutex
->unlock(this->mutex
);
199 switch (this->callback(this->data
))
201 case JOB_REQUEUE_DIRECT
:
203 case JOB_REQUEUE_FAIR
:
208 case JOB_REQUEUE_NONE
:
217 this->mutex
->lock(this->mutex
);
219 this->mutex
->unlock(this->mutex
);
220 /* manually create a cancellation point to avoid that a cancelled thread
221 * goes back into the thread pool */
222 thread_cancellation_point();
225 lib
->processor
->queue_job(lib
->processor
, &this->public.job
);
227 thread_cleanup_pop(cleanup
);
230 METHOD(job_t
, get_priority
, job_priority_t
,
231 private_callback_job_t
*this)
233 return JOB_PRIO_MEDIUM
;
237 * Described in header.
239 callback_job_t
*callback_job_create(callback_job_cb_t cb
, void *data
,
240 callback_job_cleanup_t cleanup
,
241 callback_job_t
*parent
)
243 private_callback_job_t
*this;
249 .get_priority
= _get_priority
,
254 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
258 .children
= linked_list_create(),
259 .parent
= (private_callback_job_t
*)parent
,
260 .destroyable
= condvar_create(CONDVAR_TYPE_DEFAULT
),
263 /* register us at parent */
266 this->parent
->mutex
->lock(this->parent
->mutex
);
267 this->parent
->children
->insert_last(this->parent
->children
, this);
268 this->parent
->mutex
->unlock(this->parent
->mutex
);
271 return &this->public;