Наконец выяснил, в чем проблема. На RHEL 5.4, если мы вызываем sem_init, а затем делаем sem_timedwait, мы получаем несколько случайное поведение временного ожидания, в зависимости от того, где находится код, находится ли объект, которому принадлежит sem_t, в куче или стеке и т. Д. Иногда временное ожидание возвращается немедленно с errno = 38 (ENOSYS), иногда он корректно ждет перед возвратом.
Запуск его через valgrind дает эту ошибку:
==32459== Thread 2:
==32459== Syscall param futex(op) contains uninitialised byte(s)
==32459== at 0x406C78: sem_timedwait (in /lib/libpthread-2.5.so)
==32459== by 0x8049F2E: TestThread::Run() (in /home/stsadm/semaphore_test/semaphore_test)
==32459== by 0x44B2307: nxThread::_ThreadProc(void*) (in /home/stsadm/semaphore_test/libcore.so)
==32459== by 0x4005AA: start_thread (in /lib/libpthread-2.5.so)
==32459== by 0x355CFD: clone (in /lib/libc-2.5.so)
Если я запускаю точно такой же код на RHEL 5.2, проблема исчезает, и valgrind не сообщает об ошибках.
Если я сделаю memset для переменной sem_t перед вызовом sem_init, проблема исчезнет на RHEL 5.4
memset( &_semaphore, 0, sizeof( sem_t ) );
Итак, похоже, что ошибка была введена с семафорами в RHEL5.4 или чем-то, что он использует внутри, и sem_init неправильно инициализирует память sem_t. Или ожидание sem_timed изменилось, чтобы быть чувствительным к этому так, как раньше.
Интересно, что ни в коем случае sem_init не возвращает ошибку, указывающую, что она не работает.
В качестве альтернативы, если ожидаемое поведение заключается в том, что sem_init не инициализирует память sem_t, и это зависит от вызывающей стороны, то поведение, безусловно, изменилось с RHEL 5.4
pxb
Обновление - вот код теста на случай, если кто-то еще захочет его попробовать. Обратите внимание, что проблема возникает только тогда, когда sem_timedwait вызывается из .so, и только RHEL5.4 (может быть, 5.3 не проверял его), и только когда он построен как 32-битный двоичный файл (конечно же, связывается с 32-битными библиотеками)
1) в semtest.cpp
#include <semaphore.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>
void semtest( int semnum, bool initmem )
{
sem_t sem;
if ( initmem )
{
memset( &sem, 0, sizeof( sem_t ) );
printf( "sem %d: memset size = %d\n", semnum, sizeof( sem_t ) );
}
errno = 0;
int res = sem_init( &sem, 0, 0 );
printf( "sem %d: sem_init res = %d, errno = %d\n", semnum, res, errno );
timespec ts;
clock_gettime( CLOCK_REALTIME, &ts );
ts.tv_sec += 1;
errno = 0;
res = sem_timedwait( &sem, &ts );
printf( "sem %d: sem_timedwait res = %d, errno = %d\n\n", semnum, res, errno );
}
2) в main.cpp (обратите внимание на дублирующую тестовую функцию, чтобы мы могли сравнить выполнение из .so с exe-файлом)
#include <semaphore.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>
extern void semtest( int semnum, bool initmem );
void semtest_in_exe( int semnum, bool initmem )
{
sem_t sem;
if ( initmem )
{
memset( &sem, 0, sizeof( sem_t ) );
printf( "sem %d: memset size = %d\n", semnum, sizeof( sem_t ) );
}
errno = 0;
int res = sem_init( &sem, 0, 0 );
printf( "sem %d: sem_init res = %d, errno = %d\n", semnum, res, errno );
timespec ts;
clock_gettime( CLOCK_REALTIME, &ts );
ts.tv_sec += 1;
errno = 0;
res = sem_timedwait( &sem, &ts );
printf( "sem %d: sem_timedwait res = %d, errno = %d\n\n", semnum, res, errno );
}
int main(int argc, char* argv[], char** envp)
{
semtest( 1, false );
semtest( 2, true );
semtest_in_exe( 3, false );
semtest_in_exe( 4, true );
}
3) вот Makefile
all: main
semtest.o: semtest.cpp
gcc -c -fpic -m32 -I /usr/include/c++/4.1.2 -I /usr/include/c++/4.1.2/i386-redhat-linux semtest.cpp -o semtest.o
libsemtest.so: semtest.o
gcc -shared -m32 -fpic -lstdc++ -lrt semtest.o -o libsemtest.so
main: libsemtest.so
gcc -m32 -L . -lsemtest main.cpp -o semtest
Тестовые случаи:
- запустить изнутри .so, не делая memset
- запустить изнутри .so и сделать memset
- запустить изнутри exe, не делая memset
- запустить изнутри exe и сделать memset
И вот результат на RHEL5.4
sem 1: sem_init res = 0, errno = 0
sem 1: sem_timedwait res = -1, errno = 38
sem 2: memset size = 16
sem 2: sem_init res = 0, errno = 0
sem 2: sem_timedwait res = -1, errno = 110
sem 3: sem_init res = 0, errno = 0
sem 3: sem_timedwait res = -1, errno = 110
sem 4: memset size = 16
sem 4: sem_init res = 0, errno = 0
sem 4: sem_timedwait res = -1, errno = 110
Вы видите, что случай 1 немедленно возвращается с errno = 38.
Если мы запустим точно такой же код на RHEL5.2, мы получим следующее:
sem 1: sem_init res = 0, errno = 0
sem 1: sem_timedwait res = -1, errno = 110
sem 2: memset size = 16
sem 2: sem_init res = 0, errno = 0
sem 2: sem_timedwait res = -1, errno = 110
sem 3: sem_init res = 0, errno = 0
sem 3: sem_timedwait res = -1, errno = 110
sem 4: memset size = 16
sem 4: sem_init res = 0, errno = 0
sem 4: sem_timedwait res = -1, errno = 110
Вы видите, что все дела теперь работают как положено!