std::tuple lookupAddrForHostname()

in tensorpipe/transport/uv/utility.cc [53:136]


std::tuple<Error, std::string> lookupAddrForHostname() {
  // For some operations we need a libuv event loop. We create a fresh one, just
  // for this purpose, which we'll drive inline from this thread. This way we
  // avoid misusing the main event loop in the context impl.
  struct InlineLoop {
    uv_loop_t loop;

    InlineLoop() {
      auto rv = uv_loop_init(&loop);
      TP_THROW_UV_IF(rv < 0, rv);
    }

    ~InlineLoop() {
      auto rv = uv_loop_close(&loop);
      TP_THROW_UV_IF(rv < 0, rv);
    }
  };
  InlineLoop loop;

  struct InlineDeferredExecutor : public DeferredExecutor {
    std::thread::id threadId = std::this_thread::get_id();

    void deferToLoop(TTask fn) override {
      TP_THROW_ASSERT()
          << "How could this be called?! This class is supposed to be "
          << "instantiated as const, and this method isn't const-qualified";
    }

    bool inLoop() const override {
      return std::this_thread::get_id() == threadId;
    }
  };
  const InlineDeferredExecutor executor;

  int rv;
  std::string hostname;
  std::tie(rv, hostname) = getHostname();
  if (rv < 0) {
    return std::make_tuple(TP_CREATE_ERROR(UVError, rv), std::string());
  }

  Addrinfo info;
  std::tie(rv, info) = getAddrinfoFromLoop(&loop.loop, std::move(hostname));
  if (rv < 0) {
    return std::make_tuple(TP_CREATE_ERROR(UVError, rv), std::string());
  }

  Error error;
  for (struct addrinfo* rp = info.get(); rp != nullptr; rp = rp->ai_next) {
    TP_DCHECK(rp->ai_family == AF_INET || rp->ai_family == AF_INET6);
    TP_DCHECK_EQ(rp->ai_socktype, SOCK_STREAM);
    TP_DCHECK_EQ(rp->ai_protocol, IPPROTO_TCP);

    Sockaddr addr = Sockaddr(rp->ai_addr, rp->ai_addrlen);

    TCPHandle handle(&loop.loop, executor);
    handle.initFromLoop();
    rv = handle.bindFromLoop(addr);
    handle.closeFromLoop();

    // The handle will only be closed at the next loop iteration, so run it.
    {
      auto rv = uv_run(&loop.loop, UV_RUN_DEFAULT);
      TP_THROW_ASSERT_IF(rv > 0);
    }

    if (rv < 0) {
      // Record the first binding error we encounter and return that in the end
      // if no working address is found, in order to help with debugging.
      if (!error) {
        error = TP_CREATE_ERROR(UVError, rv);
      }
      continue;
    }

    return std::make_tuple(Error::kSuccess, addr.str());
  }

  if (error) {
    return std::make_tuple(std::move(error), std::string());
  } else {
    return std::make_tuple(TP_CREATE_ERROR(NoAddrFoundError), std::string());
  }
}