sem_timedwait n’est pas correctement pris en charge sous RedHat Enterprise Linux 5.3?

Nous observons un comportement étrange sur les systèmes RedHat Enterprise Linux avec pthreads sem_timedwait. Cela se produit uniquement avec les versions 5.3 et suivantes.

Lorsque nous créons le sémaphore sur un thread d’arrière-plan avec sem_init, aucune erreur n’est renvoyée. Quand on fait sem_timedwait, on obtient un retour immédiat avec errno = 38 (ENOSYS) indiquant qu’il n’est pas supporté.

Si nous faisons la même chose sur le thread principal, cela fonctionne comme prévu et nous n’avons pas d’erreur de sem_timedwait.

Nous ne le voyons pas sur RHEL 5.2 ou avant. Nous avons essayé de comstackr notre code avec gcc 3.2.3 et 4.1.2 et d’obtenir le même résultat, il semble donc que ce soit un problème d’exécution.

Donc, mes questions (enfin;)

1) quelqu’un d’autre a-t-il vu ça? 2) Est-ce un problème connu avec RHEL 5.3? 3) nous utilisons sem_timedwait pour dormir un seul thread. Quelles sont les alternatives sur Linux pour faire la même chose?

S’il s’agit d’une copie d’une autre question, faites-le moi savoir. J’ai regardé mais je ne peux pas en trouver un avec la même question, juste les mêmes pour OSX qui n’est pas ce que nous utilisons.

merci, pxb

Mise à jour: il suffit de faire quelques tests supplémentaires avec les résultats suivants:

  • si je fais une construction 64 bits en utilisant gcc 4.1.2 sur une boîte RHEL5.4 (avec -L / usr / lib64 et -lstdc ++ -lrt) et que je l’exécute sur une installation 64 bits de RHEL5, cela fonctionne bien
  • Si je fais une construction 32 bits en utilisant gcc 4.1.2 sur une boîte RHEL5.1 (avec -L / usr / lib et -lstdc ++ -lrt) et que je l’exécute sur la même boîte RHEL5 64 bits, nous obtenons des erreurs ENOSYS. sem_timedwait

Donc, il semble y avoir une différence entre les bibliothèques d’exécution 64 et 32 ​​bits sur RHEL5.4 (et apparemment RHEL5.3). La seule autre différence était que les versions 32 et 64 bits étaient effectuées respectivement dans les zones RHEL5.1 et RHEL5.4.

Finalement, j’ai découvert quel était le problème. Sur RHEL 5.4, si nous appelons sem_init puis sem_timedwait, nous obtenons un comportement aléatoire de l’attente temporisée, selon l’emplacement du code, si l’object propriétaire de sem_t est sur le tas ou la stack, etc. avec errno = 38 (ENOSYS), parfois il attend correctement avant de revenir.

Le lancer via valgrind donne cette erreur:

==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) 

Si j’exécute exactement le même code sur RHEL 5.2, le problème disparaît et valgrind ne signale aucune erreur.

Si je fais un memset sur la variable sem_t avant d’appeler sem_init, le problème disparaît sur RHEL 5.4

 memset( &_semaphore, 0, sizeof( sem_t ) ); 

Donc, il semble qu’un bogue a été introduit avec des sémaphores sur RHEL5.4 ou quelque chose qu’il utilise en interne, et sem_init n’initialise pas correctement la mémoire sem_t. Ou, l’attente sem_timed a changé pour être sensible à cela d’une manière que ce n’était pas avant.

Fait intéressant, sem_init ne renvoie en aucun cas une erreur pour indiquer que cela n’a pas fonctionné.

Sinon, si le comportement attendu est que sem_init n’intialise pas la mémoire de sem_t et cela dépend de l’appelant, alors le comportement a certainement changé avec RHEL 5.4

pxb

Mettre à jour – voici le code du cas de test au cas où quelqu’un d’autre voudrait l’essayer. Notez que le problème se produit uniquement lorsque sem_timedwait est appelé depuis un .so, et que seul RHEL5.4 (peut-être 5.3 ne l’ont pas testé), et uniquement lorsqu’il est construit en tant que binary 32 bits (liaison sur des librairies 32 bits)

1) dans semtest.cpp

 #include  #include  #include  #include  #include  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) dans main.cpp (notez la fonction de test en double afin que nous puissions comparer l’exécution à partir de .so avec dans l’exe)

 #include  #include  #include  #include  #include  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) voici le 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 

Les cas de test sont les suivants:

  1. courir de l’intérieur .so sans faire memset
  2. courir à partir de .so et faire memset
  3. courir de l’intérieur sans faire memset
  4. courir à partir de exe et faire memset

Et voici le résultat sur 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 

Vous pouvez voir que le cas 1 retourne immédiatement avec errno = 38.

Si nous exécutons exactement le même code sur RHEL5.2, nous obtenons les informations suivantes:

 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 

Vous pouvez voir que tous les cas fonctionnent maintenant comme prévu!

Il semble que semtest appelle sem_init@GLIBC_2.1 , et libsemtest.so appelle sem_init@GLIBC_2.0 .

sem_timedwait() semble nécessiter la version 2.1.

J’ai obtenu des résultats corrects pour les quatre tests en ajoutant -lpthread à la règle qui crée libsemtest.so .

Je l’ai testé sur RH 5.3.