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 <threading/thread.h>
21 #include <threading/condvar.h>
22 #include <threading/semaphore.h>
23 #include <threading/mutex.h>
24 #include <utils/linked_list.h>
26 typedef struct private_callback_job_t private_callback_job_t
;
29 * Private data of an callback_job_t Object.
31 struct private_callback_job_t
{
34 * Public callback_job_t interface.
36 callback_job_t
public;
39 * Callback to call on execution
41 callback_job_cb_t callback
;
44 * parameter to supply to callback
49 * cleanup function for data
51 callback_job_cleanup_t cleanup
;
54 * thread of the job, if running
59 * mutex to access jobs interna
64 * list of associated child jobs
66 linked_list_t
*children
;
69 * parent of this job, or NULL
71 private_callback_job_t
*parent
;
74 * TRUE if the job got cancelled
79 * condvar to synchronize the cancellation/destruction of the job
81 condvar_t
*destroyable
;
84 * semaphore to synchronize the termination of the assigned thread.
86 * separately created during cancellation, so that we can wait on it
87 * without risking that it gets destroyed too early during destruction.
89 semaphore_t
*terminated
;
92 * Priority of this job
98 * unregister a child from its parent, if any.
99 * note: this->mutex has to be locked
101 static void unregister(private_callback_job_t
*this)
105 this->parent
->mutex
->lock(this->parent
->mutex
);
106 if (this->parent
->cancelled
&& !this->cancelled
)
108 /* if the parent has been cancelled but we have not yet, we do not
109 * unregister until we got cancelled by the parent. */
110 this->parent
->mutex
->unlock(this->parent
->mutex
);
111 this->destroyable
->wait(this->destroyable
, this->mutex
);
112 this->parent
->mutex
->lock(this->parent
->mutex
);
114 this->parent
->children
->remove(this->parent
->children
, this, NULL
);
115 this->parent
->mutex
->unlock(this->parent
->mutex
);
120 METHOD(job_t
, destroy
, void,
121 private_callback_job_t
*this)
123 this->mutex
->lock(this->mutex
);
127 this->cleanup(this->data
);
129 if (this->terminated
)
131 this->terminated
->post(this->terminated
);
133 this->children
->destroy(this->children
);
134 this->destroyable
->destroy(this->destroyable
);
135 this->mutex
->unlock(this->mutex
);
136 this->mutex
->destroy(this->mutex
);
140 METHOD(callback_job_t
, cancel
, void,
141 private_callback_job_t
*this)
143 callback_job_t
*child
;
144 semaphore_t
*terminated
= NULL
;
146 this->mutex
->lock(this->mutex
);
147 this->cancelled
= TRUE
;
148 /* terminate children */
149 while (this->children
->get_first(this->children
, (void**)&child
) == SUCCESS
)
151 this->mutex
->unlock(this->mutex
);
152 child
->cancel(child
);
153 this->mutex
->lock(this->mutex
);
157 /* terminate the thread, if there is currently one executing the job.
158 * we wait for its termination using a semaphore */
159 this->thread
->cancel(this->thread
);
160 terminated
= this->terminated
= semaphore_create(0);
164 /* if the job is currently queued, it gets terminated later.
165 * we can't wait, because it might not get executed at all.
166 * we also unregister the queued job manually from its parent (the
167 * others get unregistered during destruction) */
170 this->destroyable
->signal(this->destroyable
);
171 this->mutex
->unlock(this->mutex
);
175 terminated
->wait(terminated
);
176 terminated
->destroy(terminated
);
180 METHOD(job_t
, execute
, void,
181 private_callback_job_t
*this)
183 bool cleanup
= FALSE
, requeue
= FALSE
;
185 thread_cleanup_push((thread_cleanup_t
)destroy
, this);
187 this->mutex
->lock(this->mutex
);
188 this->thread
= thread_current();
189 this->mutex
->unlock(this->mutex
);
193 this->mutex
->lock(this->mutex
);
196 this->mutex
->unlock(this->mutex
);
200 this->mutex
->unlock(this->mutex
);
201 switch (this->callback(this->data
))
203 case JOB_REQUEUE_DIRECT
:
205 case JOB_REQUEUE_FAIR
:
210 case JOB_REQUEUE_NONE
:
219 this->mutex
->lock(this->mutex
);
221 this->mutex
->unlock(this->mutex
);
222 /* manually create a cancellation point to avoid that a cancelled thread
223 * goes back into the thread pool */
224 thread_cancellation_point();
227 lib
->processor
->queue_job(lib
->processor
, &this->public.job
);
229 thread_cleanup_pop(cleanup
);
232 METHOD(job_t
, get_priority
, job_priority_t
,
233 private_callback_job_t
*this)
239 * Described in header.
241 callback_job_t
*callback_job_create_with_prio(callback_job_cb_t cb
, void *data
,
242 callback_job_cleanup_t cleanup
, callback_job_t
*parent
,
245 private_callback_job_t
*this;
251 .get_priority
= _get_priority
,
256 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
260 .children
= linked_list_create(),
261 .parent
= (private_callback_job_t
*)parent
,
262 .destroyable
= condvar_create(CONDVAR_TYPE_DEFAULT
),
266 /* register us at parent */
269 this->parent
->mutex
->lock(this->parent
->mutex
);
270 this->parent
->children
->insert_last(this->parent
->children
, this);
271 this->parent
->mutex
->unlock(this->parent
->mutex
);
274 return &this->public;
278 * Described in header.
280 callback_job_t
*callback_job_create(callback_job_cb_t cb
, void *data
,
281 callback_job_cleanup_t cleanup
,
282 callback_job_t
*parent
)
284 return callback_job_create_with_prio(cb
, data
, cleanup
, parent
,