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 * condvar where active listeners wait
110 * create a listener entry
112 static entry_t
*entry_create(bus_listener_t
*listener
, bool blocker
)
114 entry_t
*this = malloc_thing(entry_t
);
116 this->listener
= listener
;
117 this->blocker
= blocker
;
118 this->condvar
= condvar_create(CONDVAR_DEFAULT
);
126 static void entry_destroy(entry_t
*entry
)
128 entry
->condvar
->destroy(entry
->condvar
);
133 * Get a unique thread number for a calling thread. Since
134 * pthread_self returns large and ugly numbers, use this function
135 * for logging; these numbers are incremental starting at 1
137 static int get_thread_number(private_bus_t
*this)
139 static long current_num
= 0;
142 stored_num
= (long)pthread_getspecific(this->thread_id
);
144 { /* first call of current thread */
145 pthread_setspecific(this->thread_id
, (void*)++current_num
);
155 * Implementation of bus_t.add_listener.
157 static void add_listener(private_bus_t
*this, bus_listener_t
*listener
)
159 this->mutex
->lock(this->mutex
);
160 this->listeners
->insert_last(this->listeners
, entry_create(listener
, FALSE
));
161 this->mutex
->unlock(this->mutex
);
165 * Implementation of bus_t.remove_listener.
167 static void remove_listener(private_bus_t
*this, bus_listener_t
*listener
)
169 iterator_t
*iterator
;
172 this->mutex
->lock(this->mutex
);
173 iterator
= this->listeners
->create_iterator(this->listeners
, TRUE
);
174 while (iterator
->iterate(iterator
, (void**)&entry
))
176 if (entry
->listener
== listener
)
178 iterator
->remove(iterator
);
179 entry_destroy(entry
);
183 iterator
->destroy(iterator
);
184 this->mutex
->unlock(this->mutex
);
187 typedef struct cleanup_data_t cleanup_data_t
;
190 * data to remove a listener using pthread_cleanup handler
192 struct cleanup_data_t
{
195 /** listener entry */
200 * pthread_cleanup handler to remove a listener
202 static void listener_cleanup(cleanup_data_t
*data
)
204 iterator_t
*iterator
;
207 iterator
= data
->this->listeners
->create_iterator(data
->this->listeners
, TRUE
);
208 while (iterator
->iterate(iterator
, (void**)&entry
))
210 if (entry
== data
->entry
)
212 iterator
->remove(iterator
);
213 entry_destroy(entry
);
217 iterator
->destroy(iterator
);
221 * Implementation of bus_t.listen.
223 static void listen_(private_bus_t
*this, bus_listener_t
*listener
, job_t
*job
)
229 data
.entry
= entry_create(listener
, TRUE
);
231 this->mutex
->lock(this->mutex
);
232 this->listeners
->insert_last(this->listeners
, data
.entry
);
233 charon
->processor
->queue_job(charon
->processor
, job
);
234 pthread_cleanup_push((void*)this->mutex
->unlock
, this->mutex
);
235 pthread_cleanup_push((void*)listener_cleanup
, &data
);
236 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE
, &old
);
237 while (data
.entry
->blocker
)
239 data
.entry
->condvar
->wait(data
.entry
->condvar
, this->mutex
);
241 pthread_setcancelstate(old
, NULL
);
242 pthread_cleanup_pop(FALSE
);
244 pthread_cleanup_pop(TRUE
);
245 entry_destroy(data
.entry
);
249 * Implementation of bus_t.set_sa.
251 static void set_sa(private_bus_t
*this, ike_sa_t
*ike_sa
)
253 pthread_setspecific(this->thread_sa
, ike_sa
);
257 * data associated to a signal, passed to callback
260 /** associated IKE_SA */
262 /** invoking thread */
275 * listener invocation as a list remove callback
277 static bool signal_cb(entry_t
*entry
, signal_data_t
*data
)
279 if (!entry
->listener
->signal(entry
->listener
, data
->signal
, data
->level
,
280 data
->thread
, data
->ike_sa
, data
->format
, data
->args
))
284 entry
->blocker
= FALSE
;
285 entry
->condvar
->signal(entry
->condvar
);
289 entry_destroy(entry
);
297 * Implementation of bus_t.vsignal.
299 static void vsignal(private_bus_t
*this, signal_t signal
, level_t level
,
300 char* format
, va_list args
)
304 data
.ike_sa
= pthread_getspecific(this->thread_sa
);
305 data
.thread
= get_thread_number(this);
306 data
.signal
= signal
;
308 data
.format
= format
;
309 va_copy(data
.args
, args
);
311 this->mutex
->lock(this->mutex
);
312 /* we use the remove() method to invoke all listeners with small overhead */
313 this->listeners
->remove(this->listeners
, &data
, (void*)signal_cb
);
314 this->mutex
->unlock(this->mutex
);
320 * Implementation of bus_t.signal.
322 static void signal_(private_bus_t
*this, signal_t signal
, level_t level
,
327 va_start(args
, format
);
328 vsignal(this, signal
, level
, format
, args
);
333 * Implementation of bus_t.destroy.
335 static void destroy(private_bus_t
*this)
337 this->mutex
->destroy(this->mutex
);
338 this->listeners
->destroy_function(this->listeners
, (void*)entry_destroy
);
343 * Described in header.
347 private_bus_t
*this = malloc_thing(private_bus_t
);
349 this->public.add_listener
= (void(*)(bus_t
*,bus_listener_t
*))add_listener
;
350 this->public.remove_listener
= (void(*)(bus_t
*,bus_listener_t
*))remove_listener
;
351 this->public.listen
= (void(*)(bus_t
*, bus_listener_t
*listener
, job_t
*job
))listen_
;
352 this->public.set_sa
= (void(*)(bus_t
*,ike_sa_t
*))set_sa
;
353 this->public.signal
= (void(*)(bus_t
*,signal_t
,level_t
,char*,...))signal_
;
354 this->public.vsignal
= (void(*)(bus_t
*,signal_t
,level_t
,char*,va_list))vsignal
;
355 this->public.destroy
= (void(*)(bus_t
*)) destroy
;
357 this->listeners
= linked_list_create();
358 this->mutex
= mutex_create(MUTEX_DEFAULT
);
359 pthread_key_create(&this->thread_id
, NULL
);
360 pthread_key_create(&this->thread_sa
, NULL
);
362 return &this->public;