Короткая версия:
Python предполагает, что sem_timedwait
вернется с EINTR
, если сигнал прервет его во время ожидания. Glibc (libc Debian) делает это, но POSIX говорит, что делать это необязательно, а musl (libc Alpine) этого не делает.
Длинная версия:
Python Event
- это , построенный вокруг Condition
внутри, что само по себе построено вокруг Lock
. Следующая программа демонстрирует то же поведение по той же причине, что и Lock
:
from datetime import datetime
from threading import Lock
lock = Lock()
lock.acquire()
start = datetime.now()
try:
lock.acquire(True, 5)
except KeyboardInterrupt:
print("caught Ctrl+C after %s" % (datetime.now() - start))
Из Документация Python :
Получение блокировки теперь может быть прервано сигналами в POSIX.
Предполагая, что этот фрагмент документации верен, это означает, что поведение в Debian является правильным, а поведение в Alpine - неправильным.
Python acquire
- это , построенный вокруг sem_timedwait
(при условии, что он присутствует, который присутствует как в Debian, так и в Alpine. Если бы его не было, он вместо этого был бы построен вокруг pthread_cond_timedwait
** 1040).
Следующая программа на C демонстрирует несоответствие sem_timedwait
при сборке на каждой из систем:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <time.h>
#include <errno.h>
#include <signal.h>
void handler(int sig) {
puts("in signal handler");
}
int main() {
struct sigaction sa;
sa.sa_handler = handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGALRM, &sa, NULL);
alarm(1);
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 2;
sem_t sem;
sem_init(&sem, 0, 0);
sem_timedwait(&sem, &ts);
if(errno == EINTR) {
puts("Got interrupted by signal");
} else if(errno == ETIMEDOUT) {
puts("Timed out");
}
return 0;
}
В Debian он выходит через 1 секунду с сообщением «Прервано по сигналу». На Alpine он выходит через 2 секунды с «Timed out».
sem_timedwait
- это функция libc , определенная POSIX . В частности, в нем говорится, что он «может» потерпеть неудачу с EINTR, а не «должен». Это означает, что ни glibc (Debian), ни musl (Alpine) не верны.
По историческим причинам из-за ошибок в старых ядрах , musl принял сознательное решение не поддерживать EINTR там, где им не нужно .
По моему мнению, вина здесь лежит на Python, который полагается на опциональную функцию POSIX. Как оказалось, Python был укушен аналогичной проблемой ранее, в случае, когда он использует pthread_cond_timedwait
из-за отсутствия семафоров. Кроме того, эта проблема приводит к сбою одного из тестов Python при сборке с musl. Я открыл Python ошибка # 34004 относительно этого.