1

zeronconf 対応の C/C++ アプリを Linux に移植しようとしていますが、D-BUS 関連の segfault が発生します。これが Avahi のバグなのか、Avahi の誤用なのか、コードのバグなのかはわかりません。

AvahiClient、AvahiSimplePoll、および AvahiServiceResolver をカプセル化する ZeroconfResolver オブジェクトを使用しています。ZeroconfResolver には、最初に AvahiSimplePoll、次に AvahiClient、最後に AvahiServiceResolver をインスタンス化する Resolve 関数があります。インスタンス化のたびに、次の処理に進む前にエラーをチェックしています。AvahiServiceResolver が正常に作成された後、AvahiSimplePoll で avahi_simple_poll_loop を呼び出します。

このプロセス全体は、同期的に実行するとうまく機能しますが、複数の ZeroconfResolver が非同期で同時に使用されている場合 (つまり、複数のスレッドが独自の ZeroconfResolver オブジェクトを作成している場合)、segfaults で失敗します。segfault を再現するオブジェクトの簡単な適応は、以下のコードで見ることができます (すぐに segfault を生成しないかもしれませんが、私の使用例では頻繁に発生します)。

「すぐに使用できる」Avahi はスレッド セーフではないことは理解していますが、[1] の私の解釈によると、同じプロセスに複数の AvahiClient/AvahiPoll オブジェクトを配置しても、2 つ以上から「アクセス」されない限り安全です。 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;
}
4

1 に答える 1

0

正常に機能すると思われる解決策の 1 つは、avahi_client_new、avahi_service_resolver_new、および対応する無料操作の周りに独自の同期 (一般的なミューテックス) を追加することです。avahi は、これらの操作が内部的に同期されているとは主張していないようです。

主張されているのは、独立したオブジェクトは干渉しないということです。

静的ミューテックスを持つヘルパー クラスを使用して、このアプローチで成功しました。具体的には、次のような静的メンバー関数 (またはフリー関数):

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

free または new を実行するコードの任意のセクション (できるだけ小さい部分) をロックします。

{
  std::unique_lock<std::mutex> alock(avahi_mutex());
  simple_poll = avahi_simple_poll_new()
}    
于 2013-04-06T07:41:22.053 に答える