PostgreSQL La base de donnees la plus sophistiquee au monde.

Forums PostgreSQL.fr

Le forum officiel de la communauté francophone de PostgreSQL

Vous n'êtes pas identifié(e).

#1 22/12/2010 20:35:11

djeauh
Membre

Fonction postgres problème multithreading avec getaddrinfo

Bonsoir,

           Voici mon problème: j'ai une fonction postgresql qui créé X thread de travail, chaque thread commençant par résoudre un nom d'hote avant de pouvoir continuer à travailler. (Voir exemple de code à la fin de ce post.). Le soucis est que postmaster plante de manière régulière au niveau de cette fameuse résolution de noms. Le seul moyen de ne plus faire planter l'appel à la fonction est de protéger par un mutex l'appel à la fonction getaddrinfo.
Mais voilà d'après le man pages de getaddrinfo cette fonction est réentrante! De plus si je prends les fonctions "working_thread" (en chageant elog par printf bien sur!) et "debug_func" du code ci dessous, et que je les compile dans une application, tout fonctionne correctement!!

En générant les coredumps et en les chargeant dans gdb, j'ai systématiquement la pile suivante:

(gdb) bt
#0  0x00007f09efe9cb66 in fgets_unlocked () from /lib/libc.so.6
#1  0x00007f09ed97a577 in ?? () from /lib/libnss_files.so.2
#2  0x00007f09ed97ab78 in _nss_files_gethostbyname2_r () from /lib/libnss_files.so.2
#3  0x00007f09efee752b in ?? () from /lib/libc.so.6
#4  0x00007f09efee8ff9 in getaddrinfo () from /lib/libc.so.6
#5  0x00007f09ed74157c in working_thread (param=0x0) at entry_point.cpp:266
#6  0x00007f09ebf6dfc7 in start_thread () from /lib/libpthread.so.0
#7  0x00007f09efefd64d in clone () from /lib/libc.so.6
#8  0x0000000000000000 in ?? ()

et voici ce que j'obtiens sur stderr:
*** glibc detected *** XXXXX: XXXXX XXXXX XXX.XXX.XXX.XXX(40360) SELECT: double free or corruption (!prev): 0x000000000189ad50 ***
======= Backtrace: =========
/lib/libc.so.6[0x7f09efea19a8]
/lib/libc.so.6(cfree+0x76)[0x7f09efea3ab6]
/lib/libc.so.6(fclose+0x151)[0x7f09efe91e01]
/lib/libnss_files.so.2(_nss_files_gethostbyname2_r+0x17e)[0x7f09ed97abfe]
/lib/libc.so.6[0x7f09efee76cb]
/lib/libc.so.6(getaddrinfo+0x1d9)[0x7f09efee8ff9]
/usr/local/pgsql/lib/XXX/XXX.so(_Z14working_threadPv+0x66)[0x7f09ed74157c]
/lib/libpthread.so.0[0x7f09ebf6dfc7]
/lib/libc.so.6(clone+0x6d)[0x7f09efefd64d]

Note: j'ai aussi essayé gethostbyname_r et j'ai les même soucis sad.

Auriez-vous des pistes à me donner SVP? Merci d'avance!

-----------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------
#include <pthread.h>
#include <iostream>
#include <list>

#ifdef __cplusplus
extern "C" {
#endif
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC
;
#endif

PG_FUNCTION_INFO_V1(test)
;
Datum test( PG_FUNCTION_ARGS );

#ifdef __cplusplus
}
#endif

void *working_thread( void *param ) {
    struct addrinfo *address_info = NULL;
    int resolving_result = 0; //

    elog(INFO, "Démarrage thread %lu", pthread_self());
    resolving_result = getaddrinfo( "www.example.com", "80", NULL, &address_info );
    freeaddrinfo( address_info );
    elog(INFO, "Résultat dans le thread %lu : %d", pthread_self(), resolving_result);
    sleep( 2 ); //On simule un gros traitement
    elog(INFO, "Fin thread %lu", pthread_self());

    return (NULL);
}

void debug_func() {
    //Principe: on veut fait "max_calls" appels à la fonction working_thread en parallèle,
    // en n'autorisant que "max_threads" threads concurrent
    //Nos définitions:
    const int max_threads = 5;
    const int max_calls = 100;
    //Element en cours de traitement
    int current_idx = 0;
    //Une liste de threads + un itérateur pour la parcourir
    std::list<pthread_t> threads;
    std::list<pthread_t>::iterator it;
    //Identifiant de thread temporaire
    pthread_t tmp;
    //Un booléen qui servira dans notre boucle
    bool found = false;

    do {
        if( threads.size() < max_threads ) {
            pthread_create( &tmp, NULL, working_thread, NULL );
            threads.push_back( tmp );
            ++current_idx;
        } else {

            //Les "max_calls" threads sont en cours d'éxécution, on attends qu'il y en ait un qui se libère
            for( it = threads.begin(); it != threads.end(); ++it ) {
                if( pthread_tryjoin_np( *it, NULL ) == 0 ) {
                    //Le thread est terminé: on l'enlève de notre liste
                    threads.erase( it );
                    found = true;
                    break;
                }
            }

            //Si aucun "slot" n'est libre, on attends 1 seconde
            if( !found ) {
                sleep( 1 );
            }
        }
    } while( current_idx < max_calls ); //On créé les threads tant qu'on n'a pas fini nos traitements

    //On attends les derniers threads
    for( it = threads.begin(); it != threads.end(); ++it ) {
        pthread_join( *it, NULL );
    }
}

Datum test( PG_FUNCTION_ARGS ) {
    debug_func();

    PG_RETURN_NULL();
}

Hors ligne

#2 23/12/2010 11:45:38

Marc Cousin
Membre

Re : Fonction postgres problème multithreading avec getaddrinfo

Je ne suis pas vraiment sûr qu'on puisse écrire du code multithread dans une procédure PL, tout simplement. Cela m'étonnerait que le code du backend (le processus serveur) lui-même soit réentrant. elog, par exemple, ne l'est probablement pas. Il y a aussi des algorithmes de gestion de la mémoire, par exemple, qui ne le sont probablement pas.

Bref, je n'ai pas trouvé de trace de quelqu'un exécutant du code multithreadé dans une procédure stockée.


Marc.

Hors ligne

#3 23/12/2010 12:02:35

djeauh
Membre

Re : Fonction postgres problème multithreading avec getaddrinfo

Bonjour et merci de votre réponse. Je vais essayer de faire autrement. Je vous souhaite de bonnes fêtes de fin d'année!

Hors ligne

Pied de page des forums