[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