Рассмотрим следующую тривиальную C-программу,
#include <errno.h>
int
main(int argc, char* argv[]) {
return errno;
}
При компиляции в Solaris поведение этого кода зависит от наличия -D_REENTRANT
.
solaris$ cc -E test.c | grep return
return errno;
solaris$ cc -D_REENTRANT -E test.c | grep return
return ( * ( ___errno ( ) ) );
причем последняя версия является потокобезопасной. Если мы скомпилируем тот же код в Linux, мы получим такое же поведение, которое не зависит от -D_REENTRANT
linux$ gcc -E test.c | grep return
return (*__errno_location ());
linux$ gcc -D_REENTRANT -E test.c | grep return
return (*__errno_location ());
Solaris 'cc
имеет опцию -mt
, что подразумевает -D_REENTRANT
, как и gcc
-pthread
. Однако для библиотеки указание этих многопоточных параметров кажется неудовлетворительным, поскольку оно вводит ненужную зависимость от времени выполнения потоковой передачи. Однако, если библиотека должна быть потокобезопасной (включая errno), тогда семантика, защищенная потоками, необходима как во время компиляции библиотеки, так и кода вывода. В Linux это легко, потому что errno всегда является локальным потоком, но не гарантируется на других системах, как только что было продемонстрировано.
В результате возникает вопрос: как правильно скомпилировать и распределить потокобезопасную библиотеку с заголовками? Один из вариантов был бы в #define _REENTRANT
в главном заголовке, но это вызовет проблемы, если #include <errno.h>
произойдет до включения заголовка библиотеки. Другой вариант - скомпилировать библиотеку с -D_REENTRANT
и иметь главный заголовок #error
, если _REENTRANT
не определен.
Какой правильный/лучший способ создать потокобезопасную библиотеку и обеспечить ее правильное взаимодействие с кодом, с которым он связан?