2013-01-21 2 views
1

Je tente de porter mon application C/C++ compatible zeronconf vers Linux, mais je reçois des erreurs de segmentation liées au D-BUS. Je ne suis pas sûr si c'est un bug dans Avahi, mon abus d'Avahi, ou un bug dans mon code. J'utilise un objet ZeroconfResolver qui encapsule un AvahiClient, AvahiSimplePoll et AvahiServiceResolver. Le ZeroconfResolver a une fonction Resolve qui instancie d'abord le AvahiSimplePoll, puis AvahiClient, et enfin le AvahiServiceResolver. À chaque instantiation je vérifie les erreurs avant de passer à la suivante. Une fois AvahiServiceResolver créé, il appelle avahi_simple_poll_loop avec AvahiSimplePoll.La résolution d'avahi multithread provoque la segfault

Ce processus fonctionne très bien lorsque vous avez terminé de manière synchrone, mais échoue avec segfaults lorsque plusieurs ZeroconfResolvers sont utilisés en même temps de manière asynchrone (i.e. j'ai plusieurs threads créant leurs propres ZeroconfResolver objets). Une adaptation triviale de l'objet qui reproduit les erreurs de segmentation peut être vu dans le code ci-dessous (peut ne pas produire un segfault tout de suite, mais dans mon cas d'utilisation, il arrive souvent).

Je comprends que « hors de la boîte » Avahi est pas thread-safe, mais selon mon interprétation de [1], il est sûr d'avoir plusieurs objets AvahiClient/AvahiPoll dans le même processus aussi longtemps qu'ils sont pas 'accédé' à partir de plus d'un thread. Chaque ZeroconfResolver possède son propre ensemble d'objets Avahi qui n'interagissent pas les uns avec les autres entre les limites de fil. Les erreurs se produisent dans des fonctions apparemment aléatoires dans la bibliothèque Avahi . En général, ils se produisent dans les fonctions avahi_client_new ou avahi_service_resolver_new faisant référence à dbus. Est-ce que le wiki Avahi signifie implicitement que la 'création' des objets AvahiClient/AvahiPoll est également pas thread sûre?

[1] http://avahi.org/wiki/RunningAvahiClientAsThread

#include <dispatch/dispatch.h> 
#include <cstdio> 

#include <sys/types.h> 
#include <netinet/in.h> 

#include <avahi-client/lookup.h> 
#include <avahi-client/client.h> 
#include <avahi-client/publish.h> 
#include <avahi-common/alternative.h> 
#include <avahi-common/simple-watch.h> 
#include <avahi-common/malloc.h> 
#include <avahi-common/error.h> 
#include <avahi-common/timeval.h> 

void resolve_reply(
    AvahiServiceResolver *r, 
    AVAHI_GCC_UNUSED AvahiIfIndex interface, 
    AVAHI_GCC_UNUSED AvahiProtocol protocol, 
    AvahiResolverEvent event, 
    const char *name, 
    const char *type, 
    const char *domain, 
    const char *host_name, 
    const AvahiAddress *address, 
    uint16_t port, 
    AvahiStringList *txt, 
    AvahiLookupResultFlags flags, 
    void * context) { 

    assert(r); 

    if (event == AVAHI_RESOLVER_FOUND) 
     printf("resolve_reply(%s, %s, %s, %s)[FOUND]\n", name, type, domain, host_name); 

    avahi_service_resolver_free(r); 
    avahi_simple_poll_quit((AvahiSimplePoll*)context); 
} 


int main() { 
    // Run until segfault 
    while (true) { 
    // Adding block to conccurent GCD queue (managed thread pool) 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), [=]{ 
     char name[] = "SomeHTTPServerToResolve"; 
     char domain[] = "local."; 
     char type[] = "_http._tcp."; 

     AvahiSimplePoll * simple_poll = NULL; 
     if ((simple_poll = avahi_simple_poll_new())) { 
     int error; 
     AvahiClient * client = NULL; 
     if ((client = avahi_client_new(avahi_simple_poll_get(simple_poll), AVAHI_CLIENT_NO_FAIL, NULL, NULL, &error))) { 
      AvahiServiceResolver * resolver = NULL; 
      if ((resolver = avahi_service_resolver_new(client, AVAHI_IF_UNSPEC,  AVAHI_PROTO_UNSPEC, name, type, domain, AVAHI_PROTO_UNSPEC, AVAHI_LOOKUP_NO_ADDRESS,  (AvahiServiceResolverCallback)resolve_reply, simple_poll))) { 
       avahi_simple_poll_loop(simple_poll); 
       printf("Exit Loop(%p)\n", simple_poll); 
      } else { 
       printf("Resolve(%s, %s, %s)[%s]\n", name, type, domain, avahi_strerror(avahi_client_errno(client))); 
      } 
      avahi_client_free(client); 
     } else { 
      printf("avahi_client_new()[%s]\n", avahi_strerror(error)); 
     } 
     avahi_simple_poll_free(simple_poll); 
     } else { 
     printf("avahi_simple_poll_new()[Failed]\n"); 
     } 
    }); 
    } 

    // Never reached 
    return 0; 
} 
+0

Avez-vous résoudre ceci? –

+0

@JohanLundberg Non. J'ai abandonné et j'ai décidé de compiler dans une version embarquée de Bonjour à la place. J'ai utilisé le projet open source uMundo comme guide. Ce n'est pas forcément une tâche facile mais peut-être mon seul geste puisque la communauté Avahi semble être en train de mourir (presque toutes les questions restent sans réponse sur la liste de diffusion, les créateurs sont silencieux). Si cette tendance se poursuit, je pense que les distributions Linux devraient changer. – BigMacAttack

+1

Vous ne pouvez pas avoir plus d'un répondeur Zeroconf sur un système. Vous n'avez donc pas d'autre choix que d'utiliser Avahi sur les systèmes Linux (à moins que vous n'ayez un contrôle complet sur les installations des systèmes cibles, je suppose). –

Répondre

0

Une solution qui semble fonctionner très bien est d'ajouter votre propre synchronisation (un mutex commun) autour avahi_client_new, avahi_service_resolver_new et les opérations libres correspondantes. Il semble qu'Avahi ne prétende pas que ces opérations soient synchronisées en interne.

Ce qui est revendiqué est que les objets indépendants n'interfèrent pas.

J'ai eu du succès avec cette approche, en utilisant une classe d'aide avec un mutex statique. Pour être plus précis, une fonction membre statique (ou fonction libre) comme ceci:

std::mutex& avahi_mutex(){ 
    static std::mutex mtx; 
    return mtx; 
} 

et un verrou autour de toute section de code (aussi petit que possible) faire libre ou nouvelle:

{ 
    std::unique_lock<std::mutex> alock(avahi_mutex()); 
    simple_poll = avahi_simple_poll_new() 
}  
Questions connexes