Bug#1008174: libc6: poll() spuriously returns EINTR
Hi, Le lauantaina 26. maaliskuuta 2022, 21.34.38 EET Aurelien Jarno a écrit : > > As far as I understand, POSIX allows (or even requires) thread > > cancellation to be essentially like a signal interruption, save for > > ending the thread. But that is *only* from the moment that cancellation > > is effected. Cancellation cannot be effected while it is disabled by > > definition, so the behaviour from glibc seems wrong here. > > poll() is a cancellation point. It appears to me that POSIX actually > allows this behaviour for cancellation points: I don't think so... > "The side effects of acting upon a cancellation request while suspended > during a call of a function are the same as the side effects that may be > seen in a single-threaded program when a call to a function is > interrupted by a signal and the given function returns [EINTR]. Any such > side effects occur before any cancellation cleanup handlers are called." AFAIU, in POSIX, "acting upon" a cancellation request means to move the cancellation request past the pending state, in other words, actually cancel the thread. That quote clarifies that the signal-like interruption is observed in the cancelled thread flow of execution before the cancellation clean-up handlers. Otherwise the next paragraph would not make much sense, particularly the last sentence: > "Whenever a thread has cancelability enabled and a cancellation request has > been made with that thread as the target, and the thread then calls any > function that is a cancellation point (such as pthread_testcancel() or > read()), the cancellation request shall be acted upon before the function > returns. If a thread has cancelability enabled and a cancellation request is > made with the thread as a target while the thread is suspended at a > cancellation point, the thread shall be awakened and the cancellation > request shall be acted upon." > "However, if the thread is suspended at a cancellation point and the event > for which it is waiting occurs before the cancellation request is acted > upon, it is unspecified whether the cancellation request is acted upon or > whether the cancellation request remains pending and the thread resumes > normal execution." And it would get even more nonsensical / contradictory in the following section: > "When a cancellation request is acted upon, (...) the thread first disables > cancellation by setting its cancelability state to PTHREAD_CANCEL_DISABLE > and its cancelability type to PTHREAD_CANCEL_DEFERRED. The cancelability > state shall remain set to PTHREAD_CANCEL_DISABLE until the thread has > terminated." This paragraph clarifies that cancellation cannot occur recursively / more than once per thread. Assuming that "acting upon a cancellation request" would be permissible with cancellation disabled, this sentence would imply that cancellation is disabled permanently, and the thread will never get cancelled at all. -- Rémi Denis-Courmont http://www.remlab.net/
Bug#1008174: libc6: poll() spuriously returns EINTR
Hi, On 2022-03-23 18:07, Rémi Denis-Courmont wrote: > Package: libc6 > Version: 2.34-0experimental3 > Severity: important > > Dear Maintainer, > > In the example below, glibc 2.34 from experimental causes a spurious > EINTR error in the poll() call from the child thread. It seems that > thread cancellation causes the poll() to be spuriously interrupted, > even though the cancellation is explicitly disabled at that time. Thanks for the example, it's very useful to reproduce and understand the issue. > As far as I understand, POSIX allows (or even requires) thread > cancellation to be essentially like a signal interruption, save for > ending the thread. But that is *only* from the moment that cancellation > is effected. Cancellation cannot be effected while it is disabled by > definition, so the behaviour from glibc seems wrong here. poll() is a cancellation point. It appears to me that POSIX actually allows this behaviour for cancellation points: "The side effects of acting upon a cancellation request while suspended during a call of a function are the same as the side effects that may be seen in a single-threaded program when a call to a function is interrupted by a signal and the given function returns [EINTR]. Any such side effects occur before any cancellation cleanup handlers are called." https://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_09.html > This regression is known to break the test suite from the VLC package. > Rolling back to 2.33 from unstable solves the problem. The regression has been introduced by this commit: https://sourceware.org/git/?p=glibc.git;a=commit;h=26cfbb7162ad364d53d69f6d482f2d87b5950524 Regards, Aurelien -- Aurelien Jarno GPG: 4096R/1DDD8C9B aurel...@aurel32.net http://www.aurel32.net
Bug#1008174: libc6: poll() spuriously returns EINTR
Package: libc6 Version: 2.34-0experimental3 Severity: important Dear Maintainer, In the example below, glibc 2.34 from experimental causes a spurious EINTR error in the poll() call from the child thread. It seems that thread cancellation causes the poll() to be spuriously interrupted, even though the cancellation is explicitly disabled at that time. As far as I understand, POSIX allows (or even requires) thread cancellation to be essentially like a signal interruption, save for ending the thread. But that is *only* from the moment that cancellation is effected. Cancellation cannot be effected while it is disabled by definition, so the behaviour from glibc seems wrong here. This regression is known to break the test suite from the VLC package. Rolling back to 2.33 from unstable solves the problem. 8< #include #include #include #include #include #include static void *thread(void *data) { int canc; (void) data; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &canc); if (poll(NULL, 0, 2000) < 0) { perror("Unexpected poll error"); abort(); } pthread_setcancelstate(canc, NULL); return NULL; } int main(void) { pthread_t th; void *ret; struct timespec ts = { 0, 100*1000*1000 }; if (pthread_create(&th, NULL, thread, NULL)) { perror("pthread_create"); return 1; } clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL); pthread_cancel(th); pthread_join(th, &ret); assert(ret == NULL); return 0; } >8 -- System Information: Debian Release: bookworm/sid APT prefers unstable-debug APT policy: (500, 'unstable-debug'), (500, 'unstable'), (1, 'experimental') Architecture: amd64 (x86_64) Foreign Architectures: i386, arm64 Kernel: Linux 5.16.0-5-amd64 (SMP w/12 CPU threads; PREEMPT) Kernel taint flags: TAINT_PROPRIETARY_MODULE, TAINT_OOT_MODULE, TAINT_UNSIGNED_MODULE Locale: LANG=fr_FR.UTF-8, LC_CTYPE=fr_FR.UTF-8 (charmap=UTF-8) (ignored: LC_ALL set to fi_FI.UTF-8), LANGUAGE=fr:en_GB:fi Shell: /bin/sh linked to /bin/dash Init: systemd (via /run/systemd/system) Versions of packages libc6 depends on: ii libgcc-s1 12-20220319-1 Versions of packages libc6 recommends: ii libidn2-0 2.3.2-2 Versions of packages libc6 suggests: ii debconf [debconf-2.0] 1.5.79 pn glibc-doc ii libc-l10n 2.34-0experimental3 ii libnss-nis 3.1-4 ii libnss-nisplus 1.3-4 ii locales2.33-7 -- debconf information: * libraries/restart-without-asking: true glibc/kernel-too-old: glibc/restart-services: glibc/kernel-not-supported: glibc/restart-failed: * glibc/upgrade: true glibc/disable-screensaver: