Implement wait_for_listener in controller_t with semaphores.

This eliminates even the slightest chance of a deadlock.
This commit is contained in:
Tobias Brunner
2012-05-02 14:45:37 +02:00
parent 8f6c13271c
commit 5a073784e2
+17 -53
View File
@@ -24,9 +24,8 @@
#include <daemon.h> #include <daemon.h>
#include <library.h> #include <library.h>
#include <threading/mutex.h>
#include <threading/condvar.h>
#include <threading/thread.h> #include <threading/thread.h>
#include <threading/semaphore.h>
typedef struct private_controller_t private_controller_t; typedef struct private_controller_t private_controller_t;
typedef struct interface_listener_t interface_listener_t; typedef struct interface_listener_t interface_listener_t;
@@ -93,19 +92,9 @@ struct interface_listener_t {
u_int32_t id; u_int32_t id;
/** /**
* mutex to implement wait_for_listener() * semaphore to implement wait_for_listener()
*/ */
mutex_t *mutex; semaphore_t *done;
/**
* condvar to wake a thread waiting in wait_for_listener()
*/
condvar_t *condvar;
/**
* flag to indicate whether a listener is done
*/
bool done;
}; };
@@ -133,12 +122,9 @@ struct interface_job_t {
*/ */
static inline bool listener_done(interface_listener_t *listener) static inline bool listener_done(interface_listener_t *listener)
{ {
if (listener->mutex) if (listener->done)
{ {
listener->mutex->lock(listener->mutex); listener->done->post(listener->done);
listener->condvar->signal(listener->condvar);
listener->done = TRUE;
listener->mutex->unlock(listener->mutex);
} }
return FALSE; return FALSE;
} }
@@ -149,8 +135,7 @@ static inline bool listener_done(interface_listener_t *listener)
static void listener_cleanup(interface_listener_t *listener) static void listener_cleanup(interface_listener_t *listener)
{ {
charon->bus->remove_listener(charon->bus, &listener->public); charon->bus->remove_listener(charon->bus, &listener->public);
listener->condvar->destroy(listener->condvar); listener->done->destroy(listener->done);
listener->mutex->destroy(listener->mutex);
} }
/** /**
@@ -159,53 +144,32 @@ static void listener_cleanup(interface_listener_t *listener)
* *
* @note Use 'return listener_done(listener)' to properly unregister a listener * @note Use 'return listener_done(listener)' to properly unregister a listener
* *
* @returns TRUE in case of a timeout * @param listener listener to register
* @param job job to execute asynchronously when registered, or NULL
* @param timeout max timeout in ms to listen for events, 0 to disable
* @return TRUE if timed out
*/ */
static bool wait_for_listener(interface_listener_t *listener, job_t *job, static bool wait_for_listener(interface_listener_t *listener, job_t *job,
u_int timeout) u_int timeout)
{ {
bool old, timed_out = FALSE; bool old, timed_out = FALSE;
timeval_t tv, add;
if (timeout) listener->done = semaphore_create(0);
{
add.tv_sec = timeout / 1000;
add.tv_usec = (timeout - (add.tv_sec * 1000)) * 1000;
time_monotonic(&tv);
timeradd(&tv, &add, &tv);
}
listener->condvar = condvar_create(CONDVAR_TYPE_DEFAULT);
listener->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
charon->bus->add_listener(charon->bus, &listener->public); charon->bus->add_listener(charon->bus, &listener->public);
lib->processor->queue_job(lib->processor, job); lib->processor->queue_job(lib->processor, job);
listener->mutex->lock(listener->mutex);
thread_cleanup_push((thread_cleanup_t)listener_cleanup, listener); thread_cleanup_push((thread_cleanup_t)listener_cleanup, listener);
thread_cleanup_push((thread_cleanup_t)listener->mutex->unlock,
listener->mutex);
old = thread_cancelability(TRUE); old = thread_cancelability(TRUE);
while (!listener->done) if (timeout)
{ {
if (timeout) timed_out = listener->done->timed_wait(listener->done, timeout);
{ }
if (listener->condvar->timed_wait_abs(listener->condvar, else
listener->mutex, tv)) {
{ listener->done->wait(listener->done);
timed_out = TRUE;
break;
}
}
else
{
listener->condvar->wait(listener->condvar, listener->mutex);
}
} }
thread_cancelability(old); thread_cancelability(old);
/* unlock mutex */
thread_cleanup_pop(TRUE);
thread_cleanup_pop(TRUE); thread_cleanup_pop(TRUE);
return timed_out; return timed_out;
} }