我正在尝试将启用 zeronconf 的 C/C++ 应用程序移植到 Linux,但是我得到了与 D-BUS 相关的段错误。我不确定这是 Avahi 中的错误、我对 Avahi 的误用,还是我的代码中的错误。
我正在使用一个封装了 AvahiClient、AvahiSimplePoll 和 AvahiServiceResolver 的 ZeroconfResolver 对象。ZeroconfResolver 有一个 Resolve 函数,它首先实例化 AvahiSimplePoll,然后是 AvahiClient,最后是 AvahiServiceResolver。在每次实例化之前,我都会检查错误,然后再继续下一个。在成功创建 AvahiServiceResolver 后,它会使用 AvahiSimplePoll 调用 avahi_simple_poll_loop。
整个过程在同步完成时效果很好,但在同时异步使用多个 ZeroconfResolver 时会因段错误而失败(即,我有多个线程创建自己的 ZeroconfResolver 对象)。可以在下面的代码中看到重现段错误的对象的简单改编(可能不会立即产生段错误,但在我的用例中它经常发生)。
我了解“开箱即用”Avahi 不是线程安全的,但根据我对 [1] 的解释,只要不从超过一根线。每个 ZeroconfResolver 都有自己的一组 Avahi 对象,它们不会跨线程边界相互交互。
段错误发生在 Avahi 库中看似随机的函数中。通常,它们发生在引用 dbus 的 avahi_client_new 或 avahi_service_resolver_new 函数中。Avahi 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;
}