Краткий ответ: да, начиная с C11, который является первой версией стандарта C, включающего концепцию потоков, malloc
и его друзья должны быть поточно-ориентированными. Многие операционные системы, которые включали в себя как потоки, так и среду выполнения C, давали эту гарантию задолго до того, как это делал стандарт C, но я не готов поклясться all . Тем не менее, malloc
и друзья не являются и никогда не должны были возвращаться.
Это означает, что безопасно вызывать malloc
и free
из нескольких потоков одновременно и не беспокоиться о блокировке, если вы не нарушаете ни одно из других правил распределения памяти (например, вызов free
один и только один раз на каждый указатель, возвращаемый malloc
). Но не безопасно вызывать эти функции из обработчика сигнала, который мог прервать вызов malloc
или free
в потоке, обрабатывающем сигнал. Иногда, используя функциональность за пределами ISO C, вы можете гарантировать, что поток, обрабатывающий сигнал, не прервал вызов malloc
или free
, например, с sigprocmask
и sigpause
, но постарайтесь не делать этого, если у вас нет другого выбора, потому что трудно получить совершенно правильное решение.
Длинный ответ с цитатами: стандарт C добавил концепцию потоков в 2011 ревизии (ссылка на документ N1570, который является наиболее близким приближением к официальному тексту стандарта 2011 года, который является общедоступным бесплатно). В этой редакции раздел 7.1.4 пункт 5 гласит:
Если в приведенных ниже подробных описаниях явно не указано иное, библиотечные функции должны предотвращать гонки данных следующим образом: библиотечная функция не должна прямо или косвенно обращаться к объектам, доступным для потоков, отличных от текущего потока, если к объектам не осуществляется прямой или косвенный доступ через аргументы функции. Библиотечная функция не должна прямо или косвенно изменять объекты, доступные для потоков, отличных от текущего потока, если к объектам не осуществляется прямой или косвенный доступ через неконстантные аргументы функции. Реализации могут делиться своими внутренними объектами между потоками, если эти объекты не видны пользователям и защищены от скачек данных.
[сноска. Это означает, например, что реализации не разрешается использовать статический объект для внутренних целей без синхронизации, поскольку это может вызвать гонку данных даже в программах, которые не разделяют объекты явно между потоками. Аналогичным образом, реализация memcpy не позволяет копировать байты сверх указанной длины целевого объекта, а затем восстанавливать исходные значения, поскольку это может привести к гонке данных, если программа поделит эти байты между потоками.]
Насколько я понимаю, это многословный способ сказать, что библиотечные функции, определенные стандартом C, должны быть поточно-ориентированными (в обычном смысле: вы можете вызывать их из нескольких потоков одновременно, не делая любые блокировки самостоятельно, до тех пор, пока они не столкнутся с конфликтом данных, переданных в качестве аргументов), если в документации по конкретной функции не указано, что это не так.
Затем 7.22.3p2 подтверждает, что malloc, calloc, realloc, align_alloc и free, в частности, являются поточно-ориентированными:
В целях определения существования гонки данных функции распределения памяти ведут себя так, как будто они обращаются только к тем ячейкам памяти, которые доступны через их аргументы, а не к другому статическому хранилищу продолжительности. Эти функции могут, однако, визуально изменять хранилище, которое они выделяют или освобождают. Вызов free или realloc, который освобождает область p памяти, синхронизируется с любым вызовом выделения, который выделяет всю или часть области p. Эта синхронизация происходит после любого доступа к p с помощью функции освобождения и перед любым таким доступом с помощью функции распределения.
Сравните то, что говорится о strtok, который не является и никогда не был поточно-ориентированным, в 7.24.5.8p6 :
Функция strtok не требуется, чтобы избежать скачек данных при других вызовах функции strtok.
[сноска: вместо этого можно использовать функцию strtok_s, чтобы избежать скачек данных.]
(комментарий к сноске: не используйте strtok_s
, используйте strsep
.)
Более старые версии стандарта C ничего не говорили о безопасности потоков. Однако они что-то говорили о повторном входе, потому что сигналы всегда были частью стандарта C. И это то, что они сказали, возвращаясь к оригинальному 1989 стандарту ANSI C (этот документ имеет почти идентичную формулировку, но очень отличную нумерацию разделов от стандарта ISO C, который вышел в следующем году) :
Если сигнал [a] не является результатом вызова прерывания
или поднять функцию, поведение не определено, если обработчик сигнала
вызывает любую функцию в стандартной библиотеке, кроме сигнала
сама функция или относится к любому объекту со статической продолжительностью хранения
кроме как путем присвоения значения статической переменной продолжительности хранения
типа volatile sig_atomic_t. Кроме того, если такой вызов
сигнальная функция приводит к возврату SIG_ERR, значение errno равно
неопределенный.
Это довольно скучный способ сказать, что функции библиотеки C не требуются для повторного ввода в качестве общего правила. Очень похожая формулировка все еще появляется в C11, 7.14.1.1p5 :
Если сигнал [a] возникает не в результате вызова функции отмены или повышения, поведение не определено, если обработчик сигнала ссылается на какой-либо объект со статическим или потоковым хранением, который не является атомарным объектом без блокировки, другим чем путем присвоения значения объекту, объявленному как volatile sig_atomic_t, или обработчик сигнала вызывает любую функцию в стандартной библиотеке, кроме функции abort, функции _Exit, функции quick_exit или функции signal с первым аргументом, равным сигналу номер, соответствующий сигналу, вызвавшему вызов обработчика. Кроме того, если такой вызов сигнальной функции приводит к возвращению SIG_ERR, значение errno является неопределенным.
[сноска: если какой-либо сигнал генерируется асинхронным обработчиком сигнала, поведение не определено.]
POSIX требует намного дольше, но все же короткого по сравнению с общим размером библиотеки C , списка функций, которые можно безопасно вызывать из «обработчика асинхронного сигнала», а также более детально определяет обстоятельства, при которых сигнал может «возникать не в результате вызова функции отмены или повышения». Если вы делаете что-то нетривиальное с сигналами, вы, вероятно, пишете код, предназначенный для запуска в ОС с природой Unix (в отличие от Windows, MVS или чего-то встроенного, что, вероятно, не имеет полной размещенной реализации C в первое место), и вам следует ознакомиться с требованиями POSIX к ним, а также требованиями ISO C.