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(); +}
