Pull to refresh

Comments 31

Функцию setenv небезопасно вызывать в многопоточной среде. Зачастую это представляет проблему, и данный феномен то и дело переоткрывается

гмм... гуглим setenv и видим:

MT-Unsafe

POSIX.1 does not require setenv() or unsetenv() to be reentrant.

функцию setenv небезопасно вызывать в многопоточной среде? да ладно!

Rust не серебряная пуля? Удивительно.
Более интересно, есть для Rust среда исполнения, в которой он был бы полностью изолирован от наследия libc / posix?

И как же обойтись без libс, учитывая что это практически основной интерфейс для вызова ОС?

glibc имеет свои ограничения. Не. Когда нужно использовать CGO и для статической компиляции приходится еще потанцевать вокруг musl libc

В дотнетах вон SetEnvironmentVariable вообще не влияет никак на environ, setenv выставляется только при порождении дочерних процессов и после fork().

Проблема не в setenv при вызове из раста. Проблема в том, что setenv может вызвать какой-то сишный код.

Т.е. внезапно, setenv вызывать безопастно, а вот getenv требует гарантии, что сишный код из другого потока не вызовет setenv. В отсутствии ffi вызовы getenv и setenv безопасны.

Плюс, хочется, всё же иметь интероп с сишным кодом - видеть изменения из сишного кода и прореагировать изменения в сишный код.

А как в дотнете решают эту проблему? Копируют весь environ при запуске?

В данном случае проблема была как раз в setenv, а getenv вызывал тот самый код на другом языке.

Для Java, например, придумали виртуальную машину. Да и для большинства современных языков есть что-то подобное. Ну или делать libRust, наконец, если упор на системное программирование.

Вот кто бы мог подумать, что возвращать значение из функции через глобальную переменную - плохая идея?

В заголовке погнали на всю libc, а рассказали про cтарую известную проблему с setenv/getenv. Пишут же

The safest solution is to ensure your application only ever calls setenv() at the very top of main(), before your first secondary thread is initialized. 

Мы разрабатывали для EdgeDB новую возможность выборки HTTP.

...тот относительно редкий случай, когда уже второй абзац показывает, что читать надо всё-таки в оригинале.

Обычный программист старой закалки прочтет доку, пожмет плечами, засунет свои вызовы getenv/setenv под мьютекс, и пойдет дальше. Делов на пять минут. Вместо того, чтобы ныть на весь интернет о плохой libc и выдумывания новых языков, которые сами за него будут это делать. Он воспринимает мир таким, каков он есть. Написано же в доке "не потоко безопасна", значит, безопасным вызовом в многопотоке он должен позаботиться сам. Вопросы тут больше не к libc, а почему они используют в многопотоке библиотеку, которая дёргает не потоко-безопасные функции.

засунет свои вызовы getenv/setenv под мьютекс

...и получит точно такой же гонкой в лицо, когда какая-то из зависимостей дёрнет getenv без всякой блокировки.

Фокус в том, что ни один из вызовов getenv/setenv не был своим. Тут setenv делала одна сторонняя библиотека, в то время как getenv делала другая.

Конечно же, тут можно просто выкинуть эти библиотеки и найти более вменяемые. Однако, если, э-э-э, ныть в интернете недостаточно - то вменяемых библиотек просто не будет.

На это опытный программист просто перехватит вызовы get/setenv - (это элементарно делается) и завернёт таки в мьютекс. Судя по минусам, опытных программистов на хабре осталось не много.

Вам мьютекс не особо поможет - getenv возвращает char* который вам не принадлежит и который может быть в любой момент освобождён вызовом setenv из другого потока после того как ваш враппер над getenv уже отпустил мьютекс.

Ну почему же не поможет? Если уж такая сильная нужда будет в библиотеке, которая невозбранно в getenv лазит, что никак не отказаться от неё, то для неё можно уж и расстараться и результат в tls скопировать :) Вот если кто-то напрямую будет environ шерстить, тогда уже да...
Поверьте мне, есть масса способов заставить что-то работать так, как тебе нужно. Это я как автор механизма плагинов для Конфигуратора 1С (которых штатно нет) говорю :)

И самый простой способ - перестать вызывать setenv после инициализации приложения.

Намного проще, чем подмена библиотеки, копирование результата в tls и прочее шаманство.

Ну так всё правильно, лучше быть богатым и здоровым, чем бедным и больным.
Как видим, у авторов статьи не получилось перестать вызывать setenv после инициализации приложения. В этом они обвинили libc, и видимо пошли искать другой дивный язык и библиотеки, которые работают идеально и без ошибок :) Эх, подвёл таки "безопасный Rust", а ведь так надеялись, что нашли серебряную пулю.

С чего вы взяли-то, что у него не получилось? Он же рассказывает как баг был исправлен.

А что, какая то религия запрещает перехватить вызовы этих функций из сторонних библиотек? Или это уже утерянные знания? Под linux это делается элементарным LD_PRELOAD (https://habr.com/ru/articles/479858/). Кривую библиотеку использовать захочешь - не так раскорячишься.
Мне просто странно, что эти разработчики свой гнев не по адресу направили - разработчики libc честно написали, что get/setenv не потокобезопасна, а они теперь плачут, почему в многопотоке у них проблемы.
PS. Ошибся, кому ответ отправлять. Это ответ на коммент выше.

Sign up to leave a comment.

Articles