[FFmpeg-devel] [PATCH 1/1] avformat/tcp: support timeout for getaddrinfo For fixing stucking when resolving addrinfo

XinZheng Zhang zhangxzheng at gmail.com
Tue May 31 13:56:06 CEST 2016


Sorry, This a wrong patch from a private branch. I will post the correct
one later.

On Tue, May 31, 2016 at 7:05 PM, Xinzheng Zhang <zhangxzheng at gmail.com>
wrote:

> From: xinzhengzhang <zhangxzheng at gmail.com>
>
> ---
>  libavformat/tcp.c | 194
> ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 194 insertions(+)
>
> diff --git a/libavformat/tcp.c b/libavformat/tcp.c
> index 4ac061a..dc3e0bd 100644
> --- a/libavformat/tcp.c
> +++ b/libavformat/tcp.c
> @@ -32,11 +32,15 @@
>  #if HAVE_POLL_H
>  #include <poll.h>
>  #endif
> +#if HAVE_PTHREADS
> +#include <pthread.h>
> +#endif
>
>  typedef struct TCPContext {
>      const AVClass *class;
>      int fd;
>      int listen;
> +    int addrinfo_timeout;
>      int open_timeout;
>      int rw_timeout;
>      int listen_timeout;
> @@ -52,6 +56,7 @@ static const AVOption options[] = {
>      { "listen",          "Listen for incoming connections",
> OFFSET(listen),         AV_OPT_TYPE_INT, { .i64 = 0 },     0,       2,
>  .flags = D|E },
>      { "timeout",     "set timeout (in microseconds) of socket I/O
> operations", OFFSET(rw_timeout),     AV_OPT_TYPE_INT, { .i64 = -1 },
>  -1, INT_MAX, .flags = D|E },
>      { "listen_timeout",  "Connection awaiting timeout (in
> milliseconds)",      OFFSET(listen_timeout), AV_OPT_TYPE_INT, { .i64 = -1
> },         -1, INT_MAX, .flags = D|E },
> +    { "addrinfo_timeout", "set timeout (in microseconds) for
> getaddrinfo()",   OFFSET(addrinfo_timeout), AV_OPT_TYPE_INT, { .i64 = -1
> },       -1, INT_MAX, .flags = D|E },
>      { "send_buffer_size", "Socket send buffer size (in bytes)",
>       OFFSET(send_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 },         -1,
> INT_MAX, .flags = D|E },
>      { "recv_buffer_size", "Socket receive buffer size (in bytes)",
>      OFFSET(recv_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 },         -1,
> INT_MAX, .flags = D|E },
>      { "ijkapplication",  "AVApplicationContext",
>  OFFSET(app_ctx),        AV_OPT_TYPE_INT64, { .i64 = 0 },   INT64_MIN,
> INT64_MAX, .flags = D },
> @@ -65,6 +70,189 @@ static const AVClass tcp_class = {
>      .version    = LIBAVUTIL_VERSION_INT,
>  };
>
> +#ifdef HAVE_PTHREADS
> +
> +typedef struct TCPAddrinfoRequest
> +{
> +    AVBufferRef *buffer;
> +
> +    pthread_mutex_t mutex;
> +    pthread_cond_t cond;
> +
> +    int64_t timeout;    // in microseconds;
> +    AVIOInterruptCB interrupt_callback;
> +
> +    char            *hostname;
> +    char            *servname;
> +    struct addrinfo  hints;
> +    struct addrinfo *res;
> +
> +    volatile int     finished;
> +    int              ret;
> +} TCPAddrinfoRequest;
> +
> +static void tcp_getaddrinfo_request_free(TCPAddrinfoRequest *req)
> +{
> +    freeaddrinfo(req->res);
> +
> +    av_freep(&req->servname);
> +    av_freep(&req->hostname);
> +    pthread_cond_destroy(&req->cond);
> +    pthread_mutex_destroy(&req->mutex);
> +    av_freep(&req);
> +}
> +
> +static void tcp_getaddrinfo_request_free_buffer(void *opaque, uint8_t
> *data)
> +{
> +    TCPAddrinfoRequest *req = (TCPAddrinfoRequest *)opaque;
> +    tcp_getaddrinfo_request_free(req);
> +}
> +
> +static int tcp_getaddrinfo_request_create(TCPAddrinfoRequest **request,
> +                                          const char *hostname,
> +                                          const char *servname,
> +                                          const struct addrinfo *hints,
> +                                          int64_t timeout,
> +                                          const AVIOInterruptCB *int_cb)
> +{
> +    TCPAddrinfoRequest *req = (TCPAddrinfoRequest *)
> av_mallocz(sizeof(TCPAddrinfoRequest));
> +    if (!req)
> +        return AVERROR(ENOMEM);
> +
> +    if (pthread_mutex_init(&req->mutex, NULL)) {
> +        av_freep(&req);
> +        return AVERROR(ENOMEM);
> +    }
> +
> +    if (pthread_cond_init(&req->cond, NULL)) {
> +        pthread_mutex_destroy(&req->mutex);
> +        av_freep(&req);
> +        return AVERROR(ENOMEM);
> +    }
> +
> +    req->timeout            = timeout;
> +    req->interrupt_callback = *int_cb;
> +
> +    if (hostname && *hostname) {
> +        req->hostname = av_strdup(hostname);
> +        if (!req->hostname)
> +            goto fail;
> +    }
> +
> +    if (servname) {
> +        req->servname = av_strdup(servname);
> +        if (!req->hostname)
> +            goto fail;
> +    }
> +
> +    if (hints) {
> +        req->hints.ai_family   = hints->ai_family;
> +        req->hints.ai_socktype = hints->ai_socktype;
> +        req->hints.ai_protocol = hints->ai_protocol;
> +        req->hints.ai_flags    = hints->ai_flags;
> +    }
> +
> +    req->buffer = av_buffer_create(NULL, 0,
> tcp_getaddrinfo_request_free_buffer, req, 0);
> +    if (!req->buffer)
> +        goto fail;
> +
> +    *request = req;
> +    return 0;
> +fail:
> +    tcp_getaddrinfo_request_free(req);
> +    return AVERROR(ENOMEM);
> +}
> +
> +static void *tcp_getaddrinfo_worker(void *arg)
> +{
> +    TCPAddrinfoRequest *req = arg;
> +
> +    req->ret = getaddrinfo(req->hostname, req->servname, &req->hints,
> &req->res);
> +
> +    pthread_mutex_lock(&req->mutex);
> +    req->finished = 1;
> +    pthread_cond_signal(&req->cond);
> +    pthread_mutex_unlock(&req->mutex);
> +
> +    av_buffer_unref(&req->buffer);
> +    return NULL;
> +}
> +
> +static int tcp_getaddrinfo_nonblock(const char *hostname, const char
> *servname,
> +                                 const struct addrinfo *hints, struct
> addrinfo **res,
> +                                 int64_t timeout,
> +                                 const AVIOInterruptCB *int_cb)
> +{
> +    int     ret;
> +    int64_t start;
> +    int64_t now;
> +    AVBufferRef        *req_ref = NULL;
> +    TCPAddrinfoRequest *req     = NULL;
> +    pthread_t work_thread;
> +
> +    if (hostname && !hostname[0])
> +        hostname = NULL;
> +
> +    if (timeout <= 0)
> +        return getaddrinfo(hostname, servname, hints, res);
> +
> +    ret = tcp_getaddrinfo_request_create(&req, hostname, servname, hints,
> timeout, int_cb);
> +    if (ret)
> +        goto fail;
> +
> +    req_ref = av_buffer_ref(req->buffer);
> +    if (ret)
> +        goto fail;
> +
> +    /* FIXME: using a thread pool would be better. */
> +    ret = pthread_create(&work_thread, NULL, tcp_getaddrinfo_worker, req);
> +    if (ret) {
> +        ret = AVERROR(ret);
> +        goto fail;
> +    }
> +
> +    pthread_detach(work_thread);
> +
> +    start = av_gettime();
> +    now   = start;
> +
> +    pthread_mutex_lock(&req->mutex);
> +    while (!req->finished && start + timeout > now) {
> +        int64_t wait_time = now + 100000;
> +        struct timespec tv = { .tv_sec  =  wait_time / 1000000,
> +                               .tv_nsec = (wait_time % 1000000) * 1000 };
> +#if HAVE_PTHREAD_COND_TIMEWAIT_MONOTONIC_NP
> +        ret = pthread_cond_timedwait_monotonic_np(&req->cond,
> &req->mutex, &tv);
> +else
> +        ret = pthread_cond_timedwait(&req->cond, &req->mutex, &tv);
> +#endif
> +        if (ret == 0 || ret != ETIMEDOUT)
> +            break;
> +
> +        if (ff_check_interrupt(&req->interrupt_callback))
> +            return AVERROR_EXIT;
> +
> +        now = av_gettime();
> +    }
> +    pthread_mutex_unlock(&req->mutex);
> +
> +    if (!req->finished && ret != 0) {
> +        ret = AVERROR(ret);
> +        goto fail;
> +    }
> +
> +    ret      = req->ret;
> +    *res     = req->res;
> +    req->res = NULL;
> +    av_buffer_unref(&req_ref);
> +    return ret;
> +fail:
> +    av_buffer_unref(&req_ref);
> +    return ret;
> +}
> +
> +#endif
> +
>  /* return non zero if error */
>  static int tcp_open(URLContext *h, const char *uri, int flags)
>  {
> @@ -111,10 +299,16 @@ static int tcp_open(URLContext *h, const char *uri,
> int flags)
>      snprintf(portstr, sizeof(portstr), "%d", port);
>      if (s->listen)
>          hints.ai_flags |= AI_PASSIVE;
> +#ifdef HAVE_PTHREADS
> +    ret = tcp_getaddrinfo_nonblock(hostname, portstr, &hints, &ai,
> s->addrinfo_timeout, &h->interrupt_callback);
> +#else
> +    if (s->addrinfo_timeout > 0)
> +        av_log(h, AV_LOG_WARNING, "Ignore addrinfo_timeout without
> pthreads support.\n")
>      if (!hostname[0])
>          ret = getaddrinfo(NULL, portstr, &hints, &ai);
>      else
>          ret = getaddrinfo(hostname, portstr, &hints, &ai);
> +#endif
>      if (ret) {
>          av_log(h, AV_LOG_ERROR,
>                 "Failed to resolve hostname %s: %s\n",
> --
> 2.6.4 (Apple Git-63)
>
>


More information about the ffmpeg-devel mailing list