Почему OSX-документ atoi/atof не является потокобезопасным?

Я понимаю, что strtol и strtof предпочтительнее atoi/atof, поскольку первые ошибки обнаружения, а также strtol гораздо более гибкие, чем atoi, когда речь идет о не-base-10.

Но мне все еще интересно что-то: "man atoi" (или atof) на OS X (хотя и не на Linux!) упоминает, что atoi/atof не являются потокобезопасными. Я, откровенно говоря, с трудом представляю возможную реализацию atoi или atof, которая не была бы потоковой. Кто-нибудь знает, почему эта страница говорит об этом? Действительно ли эти функции небезопасны для ОС X или любой другой платформы? И если они есть, то почему, возможно, библиотека не будет определять atoi с точки зрения strtol и, следовательно, быть в безопасности?

Ответы

Ответ 1

Взглянув на страницу руководства на MacOS X 10.6.6, он документирует две функции atof() и atof_l(), и я подозреваю, что дает подсказку о том, почему функция считается небезопасной:

СИНТАКСИС

#include <stdlib.h>
double atof(const char *str);

#include <xlocale.h>
double atof_l(const char *str, locale_t loc);

ОПИСАНИЕ

Функция atof() преобразует начальную часть строки, на которую указывает str, для двойного представления.

Это эквивалентно:

      strtod(str, (char **)NULL);

Символ десятичной точки определяется в локали программы (категория LC_NUMERIC).

В то время как функция atof() использует текущую локаль, функция atof_l() может быть передана локаль напрямую. Для получения дополнительной информации см. Xlocale (3).

ПРИМЕЧАНИЯ ОСУЩЕСТВЛЕНИЯ

Функция atof() не является потокобезопасной, а также не безопасна для асинхронизации.

Функция atof() устарела strtod() и не должна использоваться в новом коде.

ОШИБКИ

Функция atof() не должна влиять на значение errno при ошибке.

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


Я искал окончательное расположение исходного кода библиотеки Дарвина, но не нашел его. Если вы перейдете к исходному коду FreeBSD для atoi(), то ясно, что реализация функции тривиальна:

int
atoi(str)
    const char *str;
{
    return (int)strtol(str, (char **)NULL, 10);
}

(Да, даже не используя прототипированное определение!)

Страница руководства для strtol() не содержит ласковой формулировки о безопасности потоков или безопасности асинхронного отбраковки. Однако, быстрый взгляд на исходный код strtol() показывает, что он использует isspace(), на который влияет локаль:

ISO/IEC 9899: 1999, раздел 7.11.1.1 Функция setlocale

187 Единственные функции в 7.4, поведение которых не зависит от текущей локали, isdigit и isxdigit.

(где §7.4 для <ctype.h>.)

Теперь, хотя я не уверен, что этот код идентичен тому, что в Дарвине (MacOS X), он, вероятно, будет похож. Я думаю, что на страницах руководства может быть место для ошибок: неясно, нужна ли страница, требующая коррекции, для atoi() или для strtol().

Ответ 2

Здесь реализация atoi() в Apple libc (atof() похожа):

int
atoi(str)
    const char *str;
{
    return (int)strtol_l(str, (char **)NULL, 10, __current_locale());
}

И strtol():

long
strtol(const char * __restrict nptr, char ** __restrict endptr, int base)
{
    return strtol_l(nptr, endptr, base, __current_locale());
}

Так как man strtol не упоминает проблему безопасности потока с strtol(), вы можете сделать один или несколько из нескольких выводов:

  • Документы ошибочны в отношении atoi(), не связанных с потоком,
  • они не обращают внимания на то, что strtol() также является небезопасным,
  • они консервативны, документируя, что atoi() не дает обещания безопасности потоков, даже если текущая реализация оказывается потокобезопасной,
  • они устарели (я полагаю, особый случай ошибочного).

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

Ответ 3

После некоторых исследований я считаю, что это просто наследие из старых дней, когда errno была глобальной переменной. Если вы проверите FreeBSD errno.h историю, начиная с первой ревизии, вы увидите, что он был первоначально определен как

extern int errno;           /* global error number */

и теперь это функция. На самом деле я не могу думать о какой-либо другой причине.

Хотя atoi всегда был оберткой вокруг strtol, который также устанавливает errno и должен иметь такую ​​же безопасность потока. Это должна быть проблема с документацией.

Ответ 4

Этот ответ через пару лет после того, как вопрос был задан, и сначала ответил. На моей Mac OS X 10.8.3 (около марта 2013 года) man atoi (или man atof) читается:

IMPLEMENTATION NOTES
  The atof() and atof_l() functions are thread-safe and async-cancel-safe.

  The atof() and atof_l() functions have been deprecated by strtod() and
  strtod_l() and should not be used in new code.

Таким образом, последнее слово, вероятно, заключается в том, что здесь никогда не было проблемы с обеспечением безопасности потоков, а только ошибки в документации.

Ответ 5

Предполагается, что эти функции не устанавливают errno в потоковом безопасном режиме, но это означает, что происходит что-то странное с errno на macos и thread. Обычно errno является локальной переменной потока.

Ответ 6

Предпосылка этого вопроса (в его первоначальном виде, до того, как я отредактировала заголовок) ошибочна. Они потокобезопасны. POSIX указывает, что все функции являются потокобезопасными, если не указано иное (POSIX), и в документации ничего не говорится об этих функциях, которые не являются потокобезопасными. OSX претендует на соответствие POSIX, поэтому они являются потокобезопасными на OSX, иначе это ошибка и большая проблема соответствия. Я собираюсь предположить, что это просто ошибка на страницах man...