RIP pthread_cancel
curl 8.16.0 внедрил pthread_cancel, чтобы прерывать зависший getaddrinfo, но уже в следующем релизе функцию убирают: отмена потока приводит к утечке памяти.
glibc сначала резолвит имя, выделяя память, затем читает /etc/gai.conf, где встречается fopen — точка отмены. Если поток прервать на этом шаге, выделенные адреса не освобождаются, и утечка повторяется при каждом новом вызове.
Поскольку других «опасных» точек может быть ещё больше, а библиотека не гарантирует чистоту ресурсов, pthread_cancel признан неприемлемым. Возвращаемся к старому выбору: либо ждать pthread_join, либо пускать потоки «в свободное плавание» и накапливать их.
Кто не хочет тормозов — подключает c-ares, но тот не покрывает всех возможностей glibc.
Комментарии (93)
- Проблема: стандартный POSIX-вызов
getaddrinfoблокирующий, не имеет таймаута и плохо сочетается сpthread_cancel, что приводит к утечкам/дедлокам. - Исторически DNS-запросы запускали в отдельном потоке/процессе, но 30 лет спустя ситуация не улучшилась.
- Альтернативы есть:
getaddrinfo_a,c-ares,systemd-resolved,io_uring, но они либо glibc-специфичны, либо нетривиальны в кросс-платформенной разработке. - Разработчики предлагают:
– отказаться отpthread_cancelи использовать пул воркер-потоков с флагом «самоубийства»;
– вынести DNS из libc в системный сервис;
– дождаться нового POSIX-стандарта асинхронного резолвера.
Default musl allocator considered harmful to performance
musl-аллокатор тормозит в 7 раз
Добавь в main.rs:
#[cfg(target_env = "musl")]
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
и в Cargo.toml:
[target.'cfg(target_env = "musl")'.dependencies]
mimalloc = "0.1"
Проблема — конкуренция потоков за malloc. Чем больше потоков, тем хуже.
Замена аллокатора нужна даже для однопоточных программ: забудешь — потом дорого.
Почему musl? Статика + 2 МБ distroless-контейнер = старые RH и быстрый cold-start.
Кейс: сервер обрабатывал данные в 7 раз медленнее хоста.
Виноват 200 000 контекст-переключений/сек vs 1 200 у glibc.
strace показал 6,7 с в futex у musl против 0,5 с у glibc.
Комментарии (53)
- musl-аллокатор медленен в многопоточных Rust-программах из-за одного глобального замка.
- Альпийские образы компактны, но «размер» ≠ «скорость»; в проде чаще берут Debian/RHEL.
- Замена: jemalloc, mimalloc или Wolfi (glibc, apk, busybox) без смены менеджера пакетов.
- glibc тоже фрагментирует память; MALLOC_ARENA_MAX=jemalloc спасает.
- Новый mallocng musl не решает конкуренцию потоков: цель musl — минимум кода и харднинг, а не perf.