2 * Copyright (C) 2006 Martin Willi
3 * Hochschule fuer Technik Rapperswil
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 #include <utils/mutex.h>
25 ENUM(signal_names
, SIG_ANY
, SIG_MAX
,
26 /** should not get printed */
28 /** debugging message types */
39 /** should not get printed */
41 /** all level0 signals are AUDIT signals */
50 /** should not get printed */
54 typedef struct private_bus_t private_bus_t
;
57 * Private data of a bus_t object.
59 struct private_bus_t
{
61 * Public part of a bus_t object.
66 * List of registered listeners as entry_t's
68 linked_list_t
*listeners
;
71 * mutex to synchronize active listeners, recursively
76 * Thread local storage for a unique, simple thread ID
78 pthread_key_t thread_id
;
81 * Thread local storage the threads IKE_SA
83 pthread_key_t thread_sa
;
86 typedef struct entry_t entry_t
;
89 * a listener entry, either active or passive
94 * registered listener interface
96 bus_listener_t
*listener
;
99 * is this a active listen() call with a blocking thread
104 * are we currently calling this listener
109 * condvar where active listeners wait
115 * create a listener entry
117 static entry_t
*entry_create(bus_listener_t
*listener
, bool blocker
)
119 entry_t
*this = malloc_thing(entry_t
);
121 this->listener
= listener
;
122 this->blocker
= blocker
;
123 this->calling
= FALSE
;
124 this->condvar
= condvar_create(CONDVAR_DEFAULT
);
132 static void entry_destroy(entry_t
*entry
)
134 entry
->condvar
->destroy(entry
->condvar
);
139 * Get a unique thread number for a calling thread. Since
140 * pthread_self returns large and ugly numbers, use this function
141 * for logging; these numbers are incremental starting at 1
143 static int get_thread_number(private_bus_t
*this)
145 static long current_num
= 0;
148 stored_num
= (long)pthread_getspecific(this->thread_id
);
150 { /* first call of current thread */
151 pthread_setspecific(this->thread_id
, (void*)++current_num
);
161 * Implementation of bus_t.add_listener.
163 static void add_listener(private_bus_t
*this, bus_listener_t
*listener
)
165 this->mutex
->lock(this->mutex
);
166 this->listeners
->insert_last(this->listeners
, entry_create(listener
, FALSE
));
167 this->mutex
->unlock(this->mutex
);
171 * Implementation of bus_t.remove_listener.
173 static void remove_listener(private_bus_t
*this, bus_listener_t
*listener
)
175 iterator_t
*iterator
;
178 this->mutex
->lock(this->mutex
);
179 iterator
= this->listeners
->create_iterator(this->listeners
, TRUE
);
180 while (iterator
->iterate(iterator
, (void**)&entry
))
182 if (entry
->listener
== listener
)
184 iterator
->remove(iterator
);
185 entry_destroy(entry
);
189 iterator
->destroy(iterator
);
190 this->mutex
->unlock(this->mutex
);
193 typedef struct cleanup_data_t cleanup_data_t
;
196 * data to remove a listener using pthread_cleanup handler
198 struct cleanup_data_t
{
201 /** listener entry */
206 * pthread_cleanup handler to remove a listener
208 static void listener_cleanup(cleanup_data_t
*data
)
210 iterator_t
*iterator
;
213 iterator
= data
->this->listeners
->create_iterator(data
->this->listeners
, TRUE
);
214 while (iterator
->iterate(iterator
, (void**)&entry
))
216 if (entry
== data
->entry
)
218 iterator
->remove(iterator
);
219 entry_destroy(entry
);
223 iterator
->destroy(iterator
);
227 * Implementation of bus_t.listen.
229 static void listen_(private_bus_t
*this, bus_listener_t
*listener
, job_t
*job
)
235 data
.entry
= entry_create(listener
, TRUE
);
237 this->mutex
->lock(this->mutex
);
238 this->listeners
->insert_last(this->listeners
, data
.entry
);
239 charon
->processor
->queue_job(charon
->processor
, job
);
240 pthread_cleanup_push((void*)this->mutex
->unlock
, this->mutex
);
241 pthread_cleanup_push((void*)listener_cleanup
, &data
);
242 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE
, &old
);
243 while (data
.entry
->blocker
)
245 data
.entry
->condvar
->wait(data
.entry
->condvar
, this->mutex
);
247 pthread_setcancelstate(old
, NULL
);
248 pthread_cleanup_pop(FALSE
);
250 pthread_cleanup_pop(TRUE
);
251 entry_destroy(data
.entry
);
255 * Implementation of bus_t.set_sa.
257 static void set_sa(private_bus_t
*this, ike_sa_t
*ike_sa
)
259 pthread_setspecific(this->thread_sa
, ike_sa
);
263 * data associated to a signal, passed to callback
266 /** associated IKE_SA */
268 /** invoking thread */
274 /** signal specific user data */
283 * listener invocation as a list remove callback
285 static bool signal_cb(entry_t
*entry
, signal_data_t
*data
)
288 { /* avoid recursive calls */
291 entry
->calling
= TRUE
;
292 if (!entry
->listener
->signal(entry
->listener
, data
->signal
, data
->level
,
293 data
->thread
, data
->ike_sa
, data
->user
, data
->format
, data
->args
))
297 entry
->blocker
= FALSE
;
298 entry
->condvar
->signal(entry
->condvar
);
302 entry_destroy(entry
);
304 entry
->calling
= FALSE
;
307 entry
->calling
= FALSE
;
312 * Implementation of bus_t.vsignal.
314 static void vsignal(private_bus_t
*this, signal_t signal
, level_t level
,
315 void *user
, char* format
, va_list args
)
319 data
.ike_sa
= pthread_getspecific(this->thread_sa
);
320 data
.thread
= get_thread_number(this);
321 data
.signal
= signal
;
324 data
.format
= format
;
325 va_copy(data
.args
, args
);
327 this->mutex
->lock(this->mutex
);
328 /* we use the remove() method to invoke all listeners with small overhead */
329 this->listeners
->remove(this->listeners
, &data
, (void*)signal_cb
);
330 this->mutex
->unlock(this->mutex
);
336 * Implementation of bus_t.signal.
338 static void signal_(private_bus_t
*this, signal_t signal
, level_t level
,
339 void* data
, char* format
, ...)
343 va_start(args
, format
);
344 vsignal(this, signal
, level
, data
, format
, args
);
349 * Implementation of bus_t.destroy.
351 static void destroy(private_bus_t
*this)
353 this->mutex
->destroy(this->mutex
);
354 this->listeners
->destroy_function(this->listeners
, (void*)entry_destroy
);
359 * Described in header.
363 private_bus_t
*this = malloc_thing(private_bus_t
);
365 this->public.add_listener
= (void(*)(bus_t
*,bus_listener_t
*))add_listener
;
366 this->public.remove_listener
= (void(*)(bus_t
*,bus_listener_t
*))remove_listener
;
367 this->public.listen
= (void(*)(bus_t
*, bus_listener_t
*listener
, job_t
*job
))listen_
;
368 this->public.set_sa
= (void(*)(bus_t
*,ike_sa_t
*))set_sa
;
369 this->public.signal
= (void(*)(bus_t
*,signal_t
,level_t
,void*,char*,...))signal_
;
370 this->public.vsignal
= (void(*)(bus_t
*,signal_t
,level_t
,void*,char*,va_list))vsignal
;
371 this->public.destroy
= (void(*)(bus_t
*)) destroy
;
373 this->listeners
= linked_list_create();
374 this->mutex
= mutex_create(MUTEX_RECURSIVE
);
375 pthread_key_create(&this->thread_id
, NULL
);
376 pthread_key_create(&this->thread_sa
, NULL
);
378 return &this->public;