Fix Avahi races

Switch from custom thread + SimplePoll to ThreadedPoll, ensuring proper
locking of the polling thread.
This commit is contained in:
Julien BLACHE 2009-04-07 14:03:13 +02:00
parent df0013a166
commit 02f960b25b
1 changed files with 31 additions and 78 deletions

View File

@ -2,6 +2,7 @@
* Rendezvous support with avahi
*
* Copyright (C) 2005 Sebastian Dröge <slomo@ubuntu.com>
* Copyright (C) 2009 Julien BLACHE <jb@jblache.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -37,7 +38,7 @@
#include <avahi-client/client.h>
#include <avahi-common/alternative.h>
#include <avahi-common/error.h>
#include <avahi-common/simple-watch.h>
#include <avahi-common/thread-watch.h>
#include <avahi-common/timeval.h>
#include <avahi-common/malloc.h>
@ -46,7 +47,7 @@
static AvahiClient *mdns_client = NULL;
static AvahiEntryGroup *mdns_group = NULL;
static AvahiSimplePoll *simple_poll = NULL;
static AvahiThreadedPoll *threaded_poll = NULL;
typedef struct tag_rend_avahi_group_entry {
char *name;
@ -59,14 +60,6 @@ typedef struct tag_rend_avahi_group_entry {
static REND_AVAHI_GROUP_ENTRY rend_avahi_entries = { NULL, NULL, 0, NULL };
static pthread_t rend_tid;
static pthread_cond_t rend_avahi_cond;
static pthread_mutex_t rend_avahi_mutex;
static void _rend_avahi_signal(void);
static void _rend_avahi_wait_on(void *what);
static void _rend_avahi_lock(void);
static void _rend_avahi_unlock(void);
static int _rend_avahi_create_services(void);
/* add a new group entry node */
@ -83,23 +76,12 @@ static int _rend_avahi_add_group_entry(char *name, char *type, int port, char *i
pge->txt = strdup(txt);
pge->port = port;
_rend_avahi_lock();
pge->next = rend_avahi_entries.next;
rend_avahi_entries.next = pge;
_rend_avahi_unlock();
return 1;
}
static void *rend_poll(void *arg) {
avahi_simple_poll_loop(simple_poll);
DPRINTF(E_DBG,L_REND,"Avahi poll thread exited\n");
return NULL;
}
static void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
// assert(g == mdns_group);
@ -109,7 +91,6 @@ static void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state,
switch (state) {
case AVAHI_ENTRY_GROUP_ESTABLISHED:
DPRINTF(E_DBG, L_REND, "Successfully added mdns services\n");
_rend_avahi_signal();
break;
case AVAHI_ENTRY_GROUP_COLLISION:
DPRINTF(E_DBG, L_REND, "Group collision\n");
@ -122,7 +103,7 @@ static void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state,
*/
break;
case AVAHI_ENTRY_GROUP_FAILURE :
avahi_simple_poll_quit(simple_poll);
avahi_threaded_poll_quit(threaded_poll);
break;
case AVAHI_ENTRY_GROUP_UNCOMMITED:
case AVAHI_ENTRY_GROUP_REGISTERING:
@ -130,34 +111,9 @@ static void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state,
}
}
void _rend_avahi_lock(void) {
if(pthread_mutex_lock(&rend_avahi_mutex))
DPRINTF(E_FATAL,L_REND,"Could not lock mutex\n");
}
void _rend_avahi_unlock(void) {
pthread_mutex_unlock(&rend_avahi_mutex);
}
void _rend_avahi_signal(void) {
/* kick the condition for waiters */
_rend_avahi_lock();
pthread_cond_signal(&rend_avahi_cond);
_rend_avahi_unlock();
}
void _rend_avahi_wait_on(void *what) {
DPRINTF(E_DBG,L_REND,"Waiting on something...\n");
if(pthread_mutex_lock(&rend_avahi_mutex))
DPRINTF(E_FATAL,L_REND,"Could not lock mutex\n");
while(!what) {
pthread_cond_wait(&rend_avahi_cond,&rend_avahi_mutex);
}
_rend_avahi_unlock();
DPRINTF(E_DBG,L_REND,"Done waiting.\n");
}
int rend_register(char *name, char *type, int port, char *iface, char *txt) {
avahi_threaded_poll_lock(threaded_poll);
DPRINTF(E_DBG,L_REND,"Adding %s/%s\n",name,type);
_rend_avahi_add_group_entry(name,type,port,iface,txt);
if(mdns_group) {
@ -166,6 +122,9 @@ int rend_register(char *name, char *type, int port, char *iface, char *txt) {
}
DPRINTF(E_DBG,L_REND,"Creating service group (again?)\n");
_rend_avahi_create_services();
avahi_threaded_poll_unlock(threaded_poll);
return 0;
}
@ -200,10 +159,6 @@ int _rend_avahi_create_services(void) {
}
}
/* wait for entry group to be created */
_rend_avahi_wait_on(mdns_group);
_rend_avahi_lock();
pentry = rend_avahi_entries.next;
while(pentry) {
/* TODO: honor iface parameter */
@ -241,14 +196,12 @@ int _rend_avahi_create_services(void) {
psl)) < 0) {
DPRINTF(E_WARN, L_REND, "Could not add mdns services: %s\n", avahi_strerror(ret));
avahi_string_list_free(psl);
_rend_avahi_unlock();
return 0;
}
pentry = pentry->next;
}
_rend_avahi_unlock();
if ((ret = avahi_entry_group_commit(mdns_group)) < 0) {
DPRINTF(E_WARN, L_REND, "Could not commit mdns services: %s\n",
avahi_strerror(avahi_client_errno(mdns_client)));
@ -284,19 +237,19 @@ static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UN
avahi_client_free(mdns_client);
mdns_group = NULL;
mdns_client = avahi_client_new(avahi_simple_poll_get(simple_poll),
mdns_client = avahi_client_new(avahi_threaded_poll_get(threaded_poll),
AVAHI_CLIENT_NO_FAIL,
client_callback,NULL,&error);
if (mdns_client == NULL)
{
DPRINTF(E_LOG,L_REND,"Failed to create new Avahi client: %s\n", avahi_strerror(error));
avahi_simple_poll_quit(simple_poll);
avahi_threaded_poll_quit(threaded_poll);
}
}
else
{
DPRINTF(E_LOG,L_REND,"Client failure: %s\n", avahi_strerror(error));
avahi_simple_poll_quit(simple_poll);
avahi_threaded_poll_quit(threaded_poll);
}
break;
case AVAHI_CLIENT_S_REGISTERING:
@ -313,18 +266,8 @@ static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UN
int rend_init(char *user) {
int error;
if(pthread_cond_init(&rend_avahi_cond,NULL)) {
DPRINTF(E_LOG,L_REND,"Could not initialize rendezvous condition\n");
return -1;
}
if(pthread_mutex_init(&rend_avahi_mutex,NULL)) {
DPRINTF(E_LOG,L_REND,"Could not initialize rendezvous mutex\n");
return -1;
}
DPRINTF(E_DBG, L_REND, "Initializing avahi\n");
if(!(simple_poll = avahi_simple_poll_new())) {
if(!(threaded_poll = avahi_threaded_poll_new())) {
DPRINTF(E_LOG,L_REND,"Error starting poll thread\n");
return -1;
}
@ -338,27 +281,37 @@ int rend_init(char *user) {
mdns_interface = AVAHI_IF_UNSPEC;
*/
if (!(mdns_client = avahi_client_new(avahi_simple_poll_get(simple_poll),
if (!(mdns_client = avahi_client_new(avahi_threaded_poll_get(threaded_poll),
AVAHI_CLIENT_NO_FAIL,
client_callback,NULL,&error))) {
DPRINTF(E_WARN, L_REND, "avahi_client_new: Error in avahi: %s\n",
avahi_strerror(avahi_client_errno(mdns_client)));
avahi_simple_poll_free(simple_poll);
avahi_threaded_poll_free(threaded_poll);
return -1;
}
DPRINTF(E_DBG, L_REND, "Starting avahi polling thread\n");
if(pthread_create(&rend_tid, NULL, rend_poll, NULL)) {
DPRINTF(E_FATAL,L_REND,"Could not start avahi polling thread.\n");
}
DPRINTF(E_DBG, L_REND, "Starting Avahi ThreadedPoll\n");
if (avahi_threaded_poll_start(threaded_poll) < 0)
{
DPRINTF(E_WARN, L_REND, "avahi_threaded_poll_start: error: %s\n",
avahi_strerror(avahi_client_errno(mdns_client)));
avahi_threaded_poll_free(threaded_poll);
return -1;
}
return 0;
}
int rend_stop() {
avahi_simple_poll_quit(simple_poll);
avahi_threaded_poll_stop(threaded_poll);
if (mdns_client != NULL)
avahi_client_free(mdns_client);
avahi_threaded_poll_free(threaded_poll);
return 0;
}