Подтвердить что ты не робот

Почему POSIX указывает wctomb как не потокобезопасный, но не mbtowc?

В XSH 2.9.1, wctomb указан как одна из функций, которые не требуются для потокобезопасности. Однако противоположная функция преобразования mbtowc не отображается в списке. При реализации с кодировками, использующими состояния сдвига, ни один из них не имеет потокобезопасного API, и нет никакого смысла, чтобы требовалось быть потокобезопасным, а другое - нет, хотя ни одно из них не может быть потокобезопасным, не запрещая кодирование с сохранением состояния.

Аналогично для wcstombs (который находится в списке) и mbstowcs (а это не так). Поскольку обе эти функции работают со всеми строками, которые начинаются и заканчиваются в начальном состоянии сдвига, они не имеют состояния, их API-интерфейсы являются потокобезопасными, и снова нет смысла, чтобы одно направление было определено как потокобезопасное, но не другие.

Может ли кто-нибудь пролить свет на это?

4b9b3361

Ответ 1

Как вы отметили в вопросе, wctomb имеет или, по крайней мере, разрешено иметь (скрытое) "состояние сдвига": см. http://pubs.opengroup.org/onlinepubs/009695399/functions/wctomb.html и сравните это с wcrtomb: http://pubs.opengroup.org/onlinepubs/009695399/functions/wcrtomb.html с явным указателем состояния.

POSIX в основном позволяет программисту реализовать wctomb в качестве вызова wcrtomb с помощью переменной static для хранения состояния сдвига. Если эта переменная не является элементом в потоке, она не будет потокобезопасной.

(все это довольно очевидно и содержится в вашем вопросе, я просто повторяю его для ясности здесь)

Обратите внимание, что никакой аргумент wctomb не дает никакого явного контроля за скрытым состоянием переключения (если он есть). В частности, вы не можете reset в исходное состояние.

Но теперь посмотрите mbtowc: http://pubs.opengroup.org/onlinepubs/009695399/functions/mbtowc.html, где текст говорит:

Для зависимого от состояния кодирования эта функция помещается в исходное состояние вызовом, для которого его аргумент указателя символа s является нулевым указателем. Последующие вызовы с s, отличные от нулевого указателя, должны при необходимости изменять внутреннее состояние функции. Вызов с s как нулевой указатель должен заставить эту функцию возвращать ненулевое значение, если кодировки имеют зависимость от состояния, и 0 в противном случае. Если в реализации используются специальные байты для изменения состояния сдвига, эти байты не должны создавать отдельные широкосимвольные коды, но должны быть сгруппированы со смежным символом.

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

Пока я не могу поклясться, я думаю, поэтому mbtowc не указан как не-потокобезопасный.

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

Ответ 2

Основная асимметрия заключается в том, что ISO C требует, чтобы широкие символы имели фиксированную ширину (то же самое для всех символов), и что кодировка не имеет состояний сдвига. Напротив, многобайтовое кодирование зависит от локали и может иметь различную ширину символов, а также состояния сдвига.

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

Существует тонкая разница в том, что состоит из внутреннего состояния в случае преобразования строк. Для mbstowcs целое число широких символов преобразуется в один вызов. Это связано с тем, что широкие символы имеют фиксированную ширину, а также потому, что параметр n вызова указан в символах, а не в байтах. Напротив, для wcstombs параметр n указывается в байтах, а не в многобайтовых символах. Следовательно, состояние, поддерживаемое для wcstombs, должно включать не только состояние сдвига, но и остаток частично выходного многобайтового символа. Поскольку состояние, таким образом, многочастное, операции (загрузка и сохранение) на нем не будут атомарными по типичной архитектуре без дополнительной блокировки.

На этом этапе важно напомнить себе, что "безопасность потоков" имеет довольно технический смысл в POSIX, а именно, что параллельные вызовы логически сериализуются. Это не означает, что параллельное использование обязательно очень полезно. Поскольку все четыре функции сохраняют внутреннее состояние, трудно представить себе вызывающего, который обрабатывает одну линейную строку (за раз) слева направо, но распространяя вызовы на несколько потоков. Это подтверждается введением mbrtowc, wcrtomb, mbsrtowcs и wcstombs в поправке 1 к ISO C 89/90, причем флаг r определен специально для "повторного".

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

Существует еще один улов, который нужно объяснить. Конкретный поток может вызывать setlocale изменение кодировки символов (LC_CTYPE) категории. Стандарт ISO C указывает, что такое действие вызывает текущее состояние (и, кстати, даже состояние, хорошо захваченное с использованием wcrtomb), чтобы стать undefined. Это связано с тем, что состояния сдвига разных локалей могут не сопоставляться друг с другом полезными или определенными способами. Несмотря на то, что это многопоточный сценарий, который может разрушить даже "реентерабельное" семейство функций, он не обязательно создает препятствия для реализации формальной потоковой безопасности, поскольку настройка локали может быть кэширована в течение каждого вызова.

Ответ 3

Общим способом "Unicode-enable" программы C является использование wchar_t вместо char всюду и вызов широкоформатных версий стандартных библиотечных функций узкого символа. Мне нравится этот подход, потому что сразу видно, что переменная типа wchar_t* либо указывает на объект wchar_t, либо широкую строку, но переменная типа char* может указывать на объект char, узкую строку в нативная кодировка char или многобайтовая строка в любом из десятков поддерживаемых кодировок символов. С таким количеством принципиально разных значений char* программист должен быть предельно осторожен, чтобы не передавать, например, многобайтную строку с кодировкой UTF-8 в функцию, ожидающую узкой строки, или передавать многобайтную строку в текущей кодировке к функции, которая ожидает строку с кодировкой UTF-8. Возможно, идея гарантировать, что mbtowc и mbstowcs (функции, которые преобразуют многобайтовые строки в широкие строки) являются потокобезопасными, но не функциями, которые преобразуются из широких строк в многобайтовые строки, заключается в том, чтобы убедить программиста всегда сохранять строковые данные в памяти программы в широком символьном формате, где каждый символ представляет собой ровно один элемент набора символов выполнения, а не сочетание узких строк и многобайтовых строк с использованием, возможно, разных кодировок символов. Возможно, стандартные авторы думали, что это более полезный подход или будет более распространенным.

Если вы считаете, что написано многопоточное серверное программное обеспечение с поддержкой Unicode в C, то, следуя шаблону хранения строковых данных в широкоформатном формате, вы можете обеспечить разделение между строковыми данными, считанными "из провода" и строковыми данными в программной памяти. Всякий раз, когда приходит новое сообщение, содержащее полезную нагрузку строковых данных, подпрограмма C, которая анализирует сообщение и его полезную нагрузку на строку, может использовать функции ввода-вывода с узким char с функциями преобразования символов в многобайтовые символы для чтения строки в программу Память. Если несколько потоков обрабатывают входящие сообщения, как это типично, тогда очень желательно, чтобы mbstowcs был потокобезопасным.

Ответ 4

Я думаю, что это просто основано на предположении, что с набором данных с широким характером кодирования программист может предсказать, сколько памяти должно быть выделено/освобождено на поток из-за фиксированной ширины кодовой точки. Но при переходе в другом направлении, в зависимости от кодировки, может быть не "предсказуемо", сколько памяти нужно будет выделить заранее, тем самым создавая больший объем ошибок.

Обновление: после того, как вы нашли более старую версию стандарта, я заметил, что существует разница в формулировке на странице для функции wctomb(): "Функция wctomb() не обязательно должна быть реентерабельной. Функция, не требующая повторного ввода, не требуется для потоковой защиты". Я думаю, это предполагает другое неявное предположение, сделанное в стандарте: mbtowc() является или должен быть реентерабельным...