Почему функция strsignal() из GNU GLIBC считается небезопасной для потоков в современных операционных системах?

Вопрос или проблема

Фон

Прежде всего, я знаю, что основной причиной того, что strsignal() не является потокобезопасным, является то, что стандарт говорит, что это может не так и быть. Функция strsignal() из стандартной библиотеки GNU C определяется в спецификации POSIX, и в этой спецификации сказано:

Функция strsignal() не обязана быть потокобезопасной.

https://pubs.opengroup.org/onlinepubs/9699919799.2016edition/functions/strsignal.html

Поскольку стандарт не требует, чтобы strsignal() была потокобезопасной, код, использующий эту функцию, не может предполагать, что она потокобезопасна, если конкретная реализованная версия не указывает, что она потокобезопасна.

Например, спецификация POSIX говорит, что strerror() может быть не потокобезопасной, но изменения в glibc 2.32 означают, что в более новых версиях glibc эта функция теперь является потокобезопасной и указана как таковая в man 3 strerror.

https://lists.gnu.org/archive/html/info-gnu/2020-08/msg00002.html

Вопрос

Страница руководства для версии C стандартной библиотеки GNU Linux для strsignal() указывает, что эта функция не является потокобезопасной:

Linux man-pages 6.7

man 3 strsignal

┌───────────────────────────────┬───────────────┬─────────────────────────────────┐
│ Интерфейс                    │ Атрибут       │ Значение                        │
├───────────────────────────────┼───────────────┼─────────────────────────────────┤
│ strsignal()                   │ Потоковая безопасность │ MT-Unsafe гонка: strsignal локаль │
├───────────────────────────────┼───────────────┼─────────────────────────────────┤
│ sigdescr_np(), sigabbrev_np() │ Потоковая безопасность │ MT-Safe                         │
└───────────────────────────────┴───────────────┴─────────────────────────────────┘

Тем не менее, заглядывая в исходный код, видно, что в частности для glibc начиная с версии 2.32 strsignal() использует локальный для потока буфер, что, как мне кажется, делает его потоко-безопасным.

https://elixir.bootlin.com/glibc/glibc-2.32/source/string/strsignal.c#L27

Итак, начиная с glibc 2.32, почему strerror() считается MT-Safe, а strsignal() все еще документируется здесь как MT-Unsafe?

Исходный код также показывает, что нет специфичных для локали связующих для strsignal(), несмотря на то, что в документации говорится о возможной гонке потоков для локали. Является ли это предостережением на случай изменений, которые могут быть внесены в будущем?

Ответ или решение

Функция strsignal() в GNU C библиотеке (glibc) считается небезопасной для многопоточности по нескольким причинам, несмотря на использование локального буфера, введенного в версии 2.32. Поскольку спецификации POSIX не требуют от реализации функции strsignal() обеспечения потокобезопасности, разработчики glibc оставили функцию с пометкой MT-Unsafe, что отражает возможность возникновения состояний гонки в многопоточной среде.

1. Указания в спецификации

Как вы правильно заметили, спецификация POSIX четко утверждает, что функция strsignal() не обязана быть потокобезопасной. Это значит, что разработчики приложений, использующие данную функцию, не могут полагаться на ее безопасное поведение при вызовах из нескольких потоков.

2. Локальные буферы и их недостатки

Хотя начиная с glibc 2.32 в strsignal() используется локальный буфер, это не делает функцию полностью безопасной. Локальные буферы обеспечивают определенную изоляцию, но стоит учитывать, что:

  • Вызовы могут быть выполнены в один и тот же момент из нескольких потоков. Если несколько потоков одновременно обращаются к strsignal(), то, несмотря на использование локального буфера, может возникнуть ситуация, когда данные будут перезаписаны, если они затрагивают одну и ту же передачу сигнала.
  • Вариации и специфические ситуации, которые могут быть добавлены в разных локалях, могут потенциально создать состояния гонки, если в будущем поддержка локалей будет добавлена.

3. Отсутствие конкретной обработки локалей

Как вы отметили, в исходном коде strsignal() нет конкретной обработки локалей, даже несмотря на то, что документация намекает на возможные гонки, связанные с локалями. Это разнообразие может служить как предостережение, так и средство подготовки к будущим изменениям, если вдруг функции, зависящие от локали, будут добавлены, что может сделать библиотеку более подверженной состояниям гонки.

4. Сравнение с strerror()

В отличие от strsignal(), функция strerror() была обновлена до MT-Safe в glibc 2.32. Это обновление произошло благодаря тому, что она была адаптирована к работе с локальными данными более безопасным образом, обеспечивая механизм, минимизирующий состояние гонки. Эта разница может быть объяснена различиями в реализации и потребностях функций в контексте многопоточности.

Заключение

Таким образом, несмотря на наличие локального буфера в strsignal() в версии glibc 2.32, он все еще остается небезопасным для многопоточности. Это связано с отсутствием явной обработки локалей и спецификациями POSIX, которые делают невозможным однозначное утверждение о потокобезопасности. Поэтому программистам рекомендуется быть осторожными при использовании данной функции в многопоточных приложениях, чтобы избежать непредсказуемого поведения.

Оцените материал
Добавить комментарий

Капча загружается...