Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package libdispatch for openSUSE:Factory 
checked in at 2026-10-30 18:16:48
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/libdispatch (Old)
 and      /work/SRC/openSUSE:Factory/.libdispatch.new.1255 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "libdispatch"

Fri Oct 30 18:16:48 2026 rev:15 rq:1393837 version:6.4.2

Changes:
--------
--- /work/SRC/openSUSE:Factory/libdispatch/libdispatch.changes  2026-10-15 
12:22:13.265291998 +0200
+++ /work/SRC/openSUSE:Factory/.libdispatch.new.1255/libdispatch.changes        
2026-10-30 18:23:15.655797336 +0100
@@ -1,0 +2,8 @@
+Fri Oct 30 07:58:54 UTC 2026 - Martin Pluskal <[email protected]>
+
+- Update to version 6.4.2 (swift-6.4.2-RELEASE):
+  * Handle EPOLLHUP like EV_EOF on Linux: fixes CPU hotspinning
+    and races on pipe-backed read sources (upstream issue #949)
+  * Add dispatch_read_hup regression test
+
+-------------------------------------------------------------------

Old:
----
  corelibs-libdispatch-6.4.1.tar.gz

New:
----
  corelibs-libdispatch-6.4.2.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ libdispatch.spec ++++++
--- /var/tmp/diff_new_pack.HsnejE/_old  2026-10-30 18:23:17.383869677 +0100
+++ /var/tmp/diff_new_pack.HsnejE/_new  2026-10-30 18:23:17.389869928 +0100
@@ -18,7 +18,7 @@
 
 %define reltag %{version}-RELEASE
 Name:           libdispatch
-Version:        6.4.1
+Version:        6.4.2
 Release:        0
 Summary:        Apple's Grand Central Dispatch library
 License:        Apache-2.0

++++++ corelibs-libdispatch-6.4.1.tar.gz -> corelibs-libdispatch-6.4.2.tar.gz 
++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/swift-corelibs-libdispatch-swift-6.4.1-RELEASE/src/event/event_epoll.c 
new/swift-corelibs-libdispatch-swift-6.4.2-RELEASE/src/event/event_epoll.c
--- old/swift-corelibs-libdispatch-swift-6.4.1-RELEASE/src/event/event_epoll.c  
2026-05-01 04:25:26.000000000 +0200
+++ new/swift-corelibs-libdispatch-swift-6.4.2-RELEASE/src/event/event_epoll.c  
2026-10-23 04:00:16.000000000 +0200
@@ -553,25 +553,18 @@
 }
 
 static void
-_dispatch_event_merge_hangup(dispatch_unote_t du)
-{
-       // consumed by dux_merge_evt()
-       _dispatch_retain_unote_owner(du);
-       dispatch_unote_state_t du_state = _dispatch_unote_state(du);
-       du_state |= DU_STATE_NEEDS_DELETE;
-       du_state &= ~DU_STATE_ARMED;
-       _dispatch_unote_state_set(du, du_state);
-       uintptr_t data = 0;  // EOF
-       os_atomic_store2o(du._dr, ds_pending_data, ~data, relaxed);
-       dux_merge_evt(du._du, EV_DELETE|EV_DISPATCH, data, 0);
-}
-
-static void
 _dispatch_event_merge_fd(dispatch_muxnote_t dmn, uint32_t events)
 {
        dispatch_unote_linkage_t dul, dul_next;
        uintptr_t data;
 
+       // SR-9033: EPOLLHUP is unmaskable and level-triggered. Deliver it like
+       // EV_EOF on kevent: as a read/write event to whoever is armed, with the
+       // remaining byte count from SIOCINQ (0 once drained).
+       if (events & EPOLLHUP) {
+               events |= _dispatch_muxnote_armed_events(dmn) & (EPOLLIN | 
EPOLLOUT);
+       }
+
        dmn->dmn_disarmed_events |= (events & (EPOLLIN | EPOLLOUT));
 
        if (events & EPOLLIN) {
@@ -600,22 +593,12 @@
                }
        }
 
-       // SR-9033: EPOLLHUP is an unmaskable event which we must respond to
-       if (events & EPOLLHUP) {
-               LIST_FOREACH_SAFE(dul, &dmn->dmn_readers_head, du_link, 
dul_next) {
-                       dispatch_unote_t du = 
_dispatch_unote_linkage_get_unote(dul);
-                       _dispatch_event_merge_hangup(du);
-               }
-               LIST_FOREACH_SAFE(dul, &dmn->dmn_writers_head, du_link, 
dul_next) {
-                       dispatch_unote_t du = 
_dispatch_unote_linkage_get_unote(dul);
-                       _dispatch_event_merge_hangup(du);
-               }
-               epoll_ctl(_dispatch_epfd, EPOLL_CTL_DEL, dmn->dmn_fd, NULL);
-               return;
-       }
-
+       // Only rearm if some unote still wants EPOLLIN or EPOLLOUT: rearming 
with
+       // just EPOLLFREE would immediately redeliver a pending EPOLLHUP and 
spin.
        events = _dispatch_muxnote_armed_events(dmn);
-       if (events) _dispatch_epoll_update(dmn, events, EPOLL_CTL_MOD);
+       if (events & (EPOLLIN | EPOLLOUT)) {
+               _dispatch_epoll_update(dmn, events, EPOLL_CTL_MOD);
+       }
 }
 
 DISPATCH_NOINLINE
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/swift-corelibs-libdispatch-swift-6.4.1-RELEASE/tests/CMakeLists.txt 
new/swift-corelibs-libdispatch-swift-6.4.2-RELEASE/tests/CMakeLists.txt
--- old/swift-corelibs-libdispatch-swift-6.4.1-RELEASE/tests/CMakeLists.txt     
2026-05-01 04:25:26.000000000 +0200
+++ new/swift-corelibs-libdispatch-swift-6.4.2-RELEASE/tests/CMakeLists.txt     
2026-10-23 04:00:16.000000000 +0200
@@ -167,6 +167,11 @@
   list(APPEND DISPATCH_C_TESTS
        thread_name)
 endif()
+# EPOLLHUP handling (SR-9033); kevent platforms get this via EV_EOF
+if(LINUX)
+  list(APPEND DISPATCH_C_TESTS
+       read_hup)
+endif()
 if(APPLE)
   list(APPEND DISPATCH_C_TESTS
        deadname
@@ -183,6 +188,9 @@
 
 set_tests_properties(dispatch_io_pipe PROPERTIES TIMEOUT 15)
 set_tests_properties(dispatch_io_pipe_close PROPERTIES TIMEOUT 5)
+if(LINUX)
+  set_tests_properties(dispatch_read_hup PROPERTIES TIMEOUT 60)
+endif()
 
 # test dispatch API for various C/CXX language variants
 add_unit_test(dispatch_c99 SOURCES dispatch_c99.c)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/swift-corelibs-libdispatch-swift-6.4.1-RELEASE/tests/dispatch_read_hup.c 
new/swift-corelibs-libdispatch-swift-6.4.2-RELEASE/tests/dispatch_read_hup.c
--- 
old/swift-corelibs-libdispatch-swift-6.4.1-RELEASE/tests/dispatch_read_hup.c    
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/swift-corelibs-libdispatch-swift-6.4.2-RELEASE/tests/dispatch_read_hup.c    
    2026-10-23 04:00:16.000000000 +0200
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2026 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+// Read sources on hung-up fds should behave like EV_EOF on kevent: readers
+// see the remaining byte count (0 at EOF) and keep firing until cancelled.
+// On epoll these are delivered as EPOLLHUP (SR-9033).
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <dispatch/dispatch.h>
+
+#include <bsdtests.h>
+#include "dispatch_test.h"
+
+#define HUP_TIMEOUT (10 * NSEC_PER_SEC)
+
+static void
+set_nonblock(int fd)
+{
+       fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
+}
+
+// Returns a nonblocking read end whose writer has already closed, with `len`
+// bytes of `buf` left unread in the pipe.
+static int
+hungup_pipe(const char *buf, size_t len)
+{
+       int p[2];
+       if (pipe(p) == -1) {
+               test_errno("pipe", errno, 0);
+               test_stop();
+       }
+       set_nonblock(p[0]);
+       if (len) {
+               test_long("pipe write", (long)write(p[1], buf, len), (long)len);
+       }
+       close(p[1]);
+       return p[0];
+}
+
+static bool
+wait_done(dispatch_semaphore_t sema, const char *desc)
+{
+       long timedout = dispatch_semaphore_wait(sema,
+                       dispatch_time(DISPATCH_TIME_NOW, HUP_TIMEOUT));
+       test_long(desc, timedout, 0);
+       return !timedout;
+}
+
+// A handler that cancels on data == 0 && read() == 0 gets called and cancels.
+static void
+test_eof_cancel(void)
+{
+       int fd = hungup_pipe(NULL, 0);
+       dispatch_semaphore_t done = dispatch_semaphore_create(0);
+       dispatch_queue_t q = dispatch_queue_create("eof_cancel", NULL);
+       dispatch_source_t ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
+                       (uintptr_t)fd, 0, q);
+       dispatch_source_set_event_handler(ds, ^{
+               char buf[64];
+               if (dispatch_source_get_data(ds) == 0 &&
+                               read(fd, buf, sizeof(buf)) == 0) {
+                       dispatch_source_cancel(ds);
+               }
+       });
+       dispatch_source_set_cancel_handler(ds, ^{
+               close(fd);
+               dispatch_release(ds);
+               dispatch_semaphore_signal(done);
+       });
+       dispatch_resume(ds);
+       wait_done(done, "eof_cancel: cancelled");
+       dispatch_release(q);
+}
+
+// Bytes still in the pipe at hangup are reported first, then 0 at EOF.
+static void
+test_buffered(void)
+{
+       int fd = hungup_pipe("hello", 5);
+       dispatch_semaphore_t done = dispatch_semaphore_create(0);
+       dispatch_queue_t q = dispatch_queue_create("buffered", NULL);
+       dispatch_source_t ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
+                       (uintptr_t)fd, 0, q);
+       __block int calls = 0;
+       __block unsigned long first_data = ~0ul;
+       __block long total = 0;
+       dispatch_source_set_event_handler(ds, ^{
+               unsigned long data = dispatch_source_get_data(ds);
+               if (calls++ == 0) first_data = data;
+               char buf[64];
+               ssize_t n = read(fd, buf, data && data < sizeof(buf) ? data : 
sizeof(buf));
+               if (n > 0) {
+                       total += n;
+               } else if (data == 0 && n == 0) {
+                       dispatch_source_cancel(ds);
+               }
+       });
+       dispatch_source_set_cancel_handler(ds, ^{
+               close(fd);
+               dispatch_release(ds);
+               dispatch_semaphore_signal(done);
+       });
+       dispatch_resume(ds);
+       if (wait_done(done, "buffered: cancelled")) {
+               test_long("buffered: first data", (long)first_data, 5);
+               test_long("buffered: bytes read", total, 5);
+       }
+       dispatch_release(q);
+}
+
+// An uncancelled source at EOF keeps firing, once per rearm.
+static void
+test_refire(void)
+{
+       int fd = hungup_pipe(NULL, 0);
+       dispatch_semaphore_t done = dispatch_semaphore_create(0);
+       dispatch_queue_t q = dispatch_queue_create("refire", NULL);
+       dispatch_source_t ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
+                       (uintptr_t)fd, 0, q);
+       __block int calls = 0;
+       dispatch_source_set_event_handler(ds, ^{
+               if (++calls == 5) dispatch_source_cancel(ds);
+       });
+       dispatch_source_set_cancel_handler(ds, ^{
+               close(fd);
+               dispatch_release(ds);
+               dispatch_semaphore_signal(done);
+       });
+       dispatch_resume(ds);
+       wait_done(done, "refire: fired 5 times");
+       dispatch_release(q);
+}
+
+int
+main(void)
+{
+       dispatch_test_start("Dispatch read sources on hung-up fds");
+
+       test_eof_cancel();
+       test_buffered();
+       test_refire();
+
+       test_stop();
+}

Reply via email to