2013-01-21 4 views
1

Я пытаюсь перенести приложение с поддержкой C/C++ с поддержкой zeronconf в Linux, однако я получаю связанные с D-BUS segfaults. Я не уверен, что это ошибка в Avahi, неправильное использование Avahi или ошибка в моем коде.многопоточные решения разрешения avahi segfault

Я использую объект ZeroconfResolver, который инкапсулирует AvahiClient, AvahiSimplePoll и AvahiServiceResolver. ZeroconfResolver имеет функцию Resolve, которая сначала создает экземпляр AvahiSimplePoll, затем AvahiClient и, наконец, AvahiServiceResolver. При каждом экземпляре я проверяю ошибки, прежде чем переходить к следующему. После того, как AvahiServiceResolver был успешно создан, он вызывает avahi_simple_poll_loop с AvahiSimplePoll.

Весь этот процесс прекрасно работает, когда сделано синхронно, но не с , когда несколько ошибкой сегментации ZeroconfResolvers используется в то же время асинхронно (то есть у меня есть несколько потоков, создавая свои собственные ZeroconfResolver объектов). Тривиальная адаптация объекта, которую воспроизводит segfaults, может быть видна в приведенном ниже коде (может не сразу выполнить segfault , но в моем случае это случается часто).

Я понимаю, что «из коробки» Avahi не Потокобезопасная, но согласно моей интерпретации [1] безопасно иметь несколько AvahiClient/AvahiPoll объектов в том же процессе, пока они не «доступ» из нескольких потоков. Каждый ZeroconfResolver имеет свой собственный набор объектов Avahi, которые не взаимодействуют друг с другом через границы потоков.

Segfaults происходят в кажущихся случайными функциями в библиотеке Avahi . В общем случае они происходят в функциях avahi_client_new или avahi_service_resolver_new, ссылающихся на dbus. Предполагается ли, что Avaji wiki означает, что «создание» объектов AvahiClient/AvahiPoll равно , а также небезопасный поток?

[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

ли вам решить это? –

+0

@JohanLundberg Nope. Я сдался и решил собрать во встроенной версии Bonjour. В качестве руководства я использовал проект uMundo с открытым исходным кодом. Не обязательно простая задача, но, возможно, мой единственный шаг, поскольку сообщество Avahi, кажется, умирает (почти все вопросы остаются без ответа в рассылке, создатели молчат). Если эта тенденция сохранится, я думаю, что дистрибутивы Linux должны переключаться. – BigMacAttack

+1

В системе не может быть более одного ответчика Zeroconf. Таким образом, у вас действительно нет выбора, кроме как использовать Avahi в системах Linux (если вы не имеете полного контроля над установками целевых систем, я полагаю). –

ответ

0

Одно из решений, кажется, работает хорошо, чтобы добавить свои собственные синхронизации (общий мьютекс) вокруг avahi_client_new, avahi_service_resolver_new и соответствующие свободные операции. Кажется, avahi не требует, чтобы эта операция была внутренне синхронизирована.

Утверждается, что независимые объекты не мешают.

У меня был успех в этом подходе, используя класс-помощник со статическим мьютексом. Конкретно, статическая функция-член (или свободная функция), как это:

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

и замок вокруг любой части кода (как можно) делать бесплатно или новый:

{ 
    std::unique_lock<std::mutex> alock(avahi_mutex()); 
    simple_poll = avahi_simple_poll_new() 
}  
Смежные вопросы