Removed strayed code fragment
[strongswan.git] / src / charon / processing / jobs / callback_job.c
1 /*
2 * Copyright (C) 2009 Tobias Brunner
3 * Copyright (C) 2007 Martin Willi
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
17 #include "callback_job.h"
18
19 #include <semaphore.h>
20
21 #include <daemon.h>
22 #include <threading/thread.h>
23 #include <threading/condvar.h>
24 #include <threading/mutex.h>
25
26 typedef struct private_callback_job_t private_callback_job_t;
27
28 /**
29 * Private data of an callback_job_t Object.
30 */
31 struct private_callback_job_t {
32 /**
33 * Public callback_job_t interface.
34 */
35 callback_job_t public;
36
37 /**
38 * Callback to call on execution
39 */
40 callback_job_cb_t callback;
41
42 /**
43 * parameter to supply to callback
44 */
45 void *data;
46
47 /**
48 * cleanup function for data
49 */
50 callback_job_cleanup_t cleanup;
51
52 /**
53 * thread of the job, if running
54 */
55 thread_t *thread;
56
57 /**
58 * mutex to access jobs interna
59 */
60 mutex_t *mutex;
61
62 /**
63 * list of asociated child jobs
64 */
65 linked_list_t *children;
66
67 /**
68 * parent of this job, or NULL
69 */
70 private_callback_job_t *parent;
71
72 /**
73 * TRUE if the job got cancelled
74 */
75 bool cancelled;
76
77 /**
78 * condvar to synchronize the cancellation/destruction of the job
79 */
80 condvar_t *destroyable;
81
82 /**
83 * semaphore to synchronize the termination of the assigned thread.
84 *
85 * separately allocated during cancellation, so that we can wait on it
86 * without risking that it gets freed too early during destruction.
87 */
88 sem_t *terminated;
89 };
90
91 /**
92 * unregister a child from its parent, if any.
93 * note: this->mutex has to be locked
94 */
95 static void unregister(private_callback_job_t *this)
96 {
97 if (this->parent)
98 {
99 this->parent->mutex->lock(this->parent->mutex);
100 if (this->parent->cancelled && !this->cancelled)
101 {
102 /* if the parent has been cancelled but we have not yet, we do not
103 * unregister until we got cancelled by the parent. */
104 this->parent->mutex->unlock(this->parent->mutex);
105 this->destroyable->wait(this->destroyable, this->mutex);
106 this->parent->mutex->lock(this->parent->mutex);
107 }
108 this->parent->children->remove(this->parent->children, this, NULL);
109 this->parent->mutex->unlock(this->parent->mutex);
110 this->parent = NULL;
111 }
112 }
113
114 /**
115 * Implements job_t.destroy.
116 */
117 static void destroy(private_callback_job_t *this)
118 {
119 this->mutex->lock(this->mutex);
120 unregister(this);
121 if (this->cleanup)
122 {
123 this->cleanup(this->data);
124 }
125 if (this->terminated)
126 {
127 sem_post(this->terminated);
128 }
129 this->children->destroy(this->children);
130 this->destroyable->destroy(this->destroyable);
131 this->mutex->unlock(this->mutex);
132 this->mutex->destroy(this->mutex);
133 free(this);
134 }
135
136 /**
137 * Implementation of callback_job_t.cancel.
138 */
139 static void cancel(private_callback_job_t *this)
140 {
141 callback_job_t *child;
142 sem_t *terminated = NULL;
143
144 this->mutex->lock(this->mutex);
145 this->cancelled = TRUE;
146 /* terminate children */
147 while (this->children->get_first(this->children, (void**)&child) == SUCCESS)
148 {
149 this->mutex->unlock(this->mutex);
150 child->cancel(child);
151 this->mutex->lock(this->mutex);
152 }
153 if (this->thread)
154 {
155 /* terminate the thread, if there is currently one executing the job.
156 * we wait for its termination using a semaphore */
157 this->thread->cancel(this->thread);
158 terminated = this->terminated = malloc_thing(sem_t);
159 sem_init(terminated, 0, 0);
160 }
161 else
162 {
163 /* if the job is currently queued, it gets terminated later.
164 * we can't wait, because it might not get executed at all.
165 * we also unregister the queued job manually from its parent (the
166 * others get unregistered during destruction) */
167 unregister(this);
168 }
169 this->destroyable->signal(this->destroyable);
170 this->mutex->unlock(this->mutex);
171
172 if (terminated)
173 {
174 sem_wait(terminated);
175 sem_destroy(terminated);
176 free(terminated);
177 }
178 }
179
180 /**
181 * Implementation of job_t.execute.
182 */
183 static void execute(private_callback_job_t *this)
184 {
185 bool cleanup = FALSE, requeue = FALSE;
186
187 thread_cleanup_push((thread_cleanup_t)destroy, this);
188
189 this->mutex->lock(this->mutex);
190 this->thread = thread_current();
191 this->mutex->unlock(this->mutex);
192
193 while (TRUE)
194 {
195 this->mutex->lock(this->mutex);
196 if (this->cancelled)
197 {
198 this->mutex->unlock(this->mutex);
199 cleanup = TRUE;
200 break;
201 }
202 this->mutex->unlock(this->mutex);
203 switch (this->callback(this->data))
204 {
205 case JOB_REQUEUE_DIRECT:
206 continue;
207 case JOB_REQUEUE_FAIR:
208 {
209 requeue = TRUE;
210 break;
211 }
212 case JOB_REQUEUE_NONE:
213 default:
214 {
215 cleanup = TRUE;
216 break;
217 }
218 }
219 break;
220 }
221 this->mutex->lock(this->mutex);
222 this->thread = NULL;
223 this->mutex->unlock(this->mutex);
224 /* manually create a cancellation point to avoid that a cancelled thread
225 * goes back into the thread pool */
226 thread_cancellation_point();
227 if (requeue)
228 {
229 charon->processor->queue_job(charon->processor,
230 &this->public.job_interface);
231 }
232 thread_cleanup_pop(cleanup);
233 }
234
235 /*
236 * Described in header.
237 */
238 callback_job_t *callback_job_create(callback_job_cb_t cb, void *data,
239 callback_job_cleanup_t cleanup,
240 callback_job_t *parent)
241 {
242 private_callback_job_t *this = malloc_thing(private_callback_job_t);
243
244 /* interface functions */
245 this->public.job_interface.execute = (void (*) (job_t *)) execute;
246 this->public.job_interface.destroy = (void (*) (job_t *)) destroy;
247 this->public.cancel = (void(*)(callback_job_t*))cancel;
248
249 /* private variables */
250 this->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
251 this->callback = cb;
252 this->data = data;
253 this->cleanup = cleanup;
254 this->thread = 0;
255 this->children = linked_list_create();
256 this->parent = (private_callback_job_t*)parent;
257 this->cancelled = FALSE;
258 this->destroyable = condvar_create(CONDVAR_TYPE_DEFAULT);
259 this->terminated = NULL;
260
261 /* register us at parent */
262 if (parent)
263 {
264 this->parent->mutex->lock(this->parent->mutex);
265 this->parent->children->insert_last(this->parent->children, this);
266 this->parent->mutex->unlock(this->parent->mutex);
267 }
268
269 return &this->public;
270 }
271