Commit 64b0e185fc for ffmpeg

commit 64b0e185fc081c7adf15a452a613649c74a8e672
Author: Zhao Zhili <quinkblack@foxmail.com>
Date:   Thu Oct 8 01:16:22 2026 +0800

    avformat/udp: stop the rx thread without pthread_cancel()

    The rx thread blocks in recvfrom() and udp_close() cancelled it. That
    only works if recvfrom() is a cancellation point and if a cancel
    request made while cancellation is disabled is still acted on later.
    Darwin loses such a request, so closing an input that is receiving
    data hangs in pthread_join(). On Win32 recvfrom() is not a
    cancellation point at all, which is why udp_close() needed a separate
    shutdown() and CancelIoEx() path.

    Wait with ff_network_wait_fd() instead, as other protocols do. Closing
    an input that receives no data can take up to POLLING_TIME.

    Signed-off-by: Zhao Zhili <zhilizhao@tencent.com>

diff --git a/libavformat/udp.c b/libavformat/udp.c
index 0874d18397..e21c3cd911 100644
--- a/libavformat/udp.c
+++ b/libavformat/udp.c
@@ -530,32 +530,30 @@ static void *circular_buffer_task_rx( void *_URLContext)
 {
     URLContext *h = _URLContext;
     UDPContext *s = h->priv_data;
-    int old_cancelstate;

     ff_thread_setname("udp-rx");

-    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelstate);
     pthread_mutex_lock(&s->mutex);
-    if (ff_socket_nonblock(s->udp_fd, 0) < 0) {
-        av_log(h, AV_LOG_ERROR, "Failed to set blocking mode");
-        s->circular_buffer_error = AVERROR(EIO);
-        goto end;
-    }
     while(1) {
         UDPQueuedPacketHeader pkt_header;
+        int err = 0;
+
+        if (s->close_req)
+            goto end;
+
         pkt_header.addr_len = sizeof(pkt_header.addr);

         pthread_mutex_unlock(&s->mutex);
-        /* Blocking operations are always cancellation points;
-           see "General Information" / "Thread Cancellation Overview"
-           in Single Unix. */
-        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_cancelstate);
         pkt_header.pkt_size = recvfrom(s->udp_fd, s->tmp + sizeof(pkt_header), sizeof(s->tmp) - sizeof(pkt_header), 0, (struct sockaddr *)&pkt_header.addr, &pkt_header.addr_len);
-        pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelstate);
+        if (pkt_header.pkt_size < 0) {
+            err = ff_neterrno();
+            if (err == AVERROR(EAGAIN))
+                err = ff_network_wait_fd(s->udp_fd, 0);
+        }
         pthread_mutex_lock(&s->mutex);
         if (pkt_header.pkt_size < 0) {
-            if (ff_neterrno() != AVERROR(EAGAIN) && ff_neterrno() != AVERROR(EINTR)) {
-                s->circular_buffer_error = ff_neterrno();
+            if (err < 0 && err != AVERROR(EAGAIN) && err != AVERROR(EINTR)) {
+                s->circular_buffer_error = err;
                 goto end;
             }
             continue;
@@ -891,7 +889,10 @@ static int udp_open(URLContext *h, const char *uri, int flags)
         }

         /* make the socket non-blocking */
-        ff_socket_nonblock(udp_fd, 1);
+        if (ff_socket_nonblock(udp_fd, 1) < 0) {
+            ret = ff_neterrno();
+            goto fail;
+        }
     }
     if (s->is_connected) {
         if (connect(udp_fd, (struct sockaddr *) &s->dest_addr, s->dest_addr_len)) {
@@ -1096,8 +1097,7 @@ static int udp_close(URLContext *h)
     UDPContext *s = h->priv_data;

 #if HAVE_PTHREAD_CANCEL
-    // Request close once writing is finished
-    if (s->thread_started && !(h->flags & AVIO_FLAG_READ)) {
+    if (s->thread_started) {
         pthread_mutex_lock(&s->mutex);
         s->close_req = 1;
         pthread_cond_signal(&s->cond);
@@ -1111,18 +1111,6 @@ static int udp_close(URLContext *h)
 #if HAVE_PTHREAD_CANCEL
     if (s->thread_started) {
         int ret;
-        // Cancel only read, as write has been signaled as success to the user
-        if (h->flags & AVIO_FLAG_READ) {
-#ifdef _WIN32
-            /* recvfrom() is not a cancellation point for win32, so we shutdown
-             * the socket and abort pending IO, subsequent recvfrom() calls
-             * will fail with WSAESHUTDOWN causing the thread to exit. */
-            shutdown(s->udp_fd, SD_RECEIVE);
-            CancelIoEx((HANDLE)(SOCKET)s->udp_fd, NULL);
-#else
-            pthread_cancel(s->circular_buffer_thread);
-#endif
-        }
         ret = pthread_join(s->circular_buffer_thread, NULL);
         if (ret != 0)
             av_log(h, AV_LOG_ERROR, "pthread_join(): %s\n", strerror(ret));