IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

QxOrm Discussion :

Deadlock à l'initialisation des relations


Sujet :

QxOrm

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    151
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 151
    Points : 49
    Points
    49
    Par défaut Deadlock à l'initialisation des relations
    Hello !

    J'ai un dead lock lors de l'utilisation de QxOrm 1.2.3 beta 08, il m'est définitivement impossible de reproduire ça dans QxBlog, je pense que mon modèle de classes est devenu trop compliqué pour que j'arrive à extraire ça et le transposer.

    Mais j'ai quand même regardé un peu et il y a des choses étranges:
    J'ai inséré des qDebug() avec des numéros, dans mon analyse, j'appelerais chacun qN avec N le numéro donné en argument.
    Voici le code où ça bloque (QxSqlRelation.h:100):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
       virtual void init()
       {
          qDebug() << "1";
          if (m_bInitInEvent) { return; }; m_bInitInEvent = true;
          qDebug() << "2";
          QMutexLocker locker(& QxSqlRelation<DataType, Owner>::m_oMutex);
          qDebug() << "3";
          m_pClass = QxClass<type_data>::getSingleton();
          qDebug() << "4";
          m_pClassOwner = QxClass<type_owner>::getSingleton();
          qDebug() << "5";
          m_pDataMemberX = (m_pClass ? m_pClass->getDataMemberX() : NULL);
          qDebug() << "6";
          m_pDataMemberId = (m_pDataMemberX ? m_pDataMemberX->getId_WithDaoStrategy() : NULL);
          qDebug() << "7";
          m_pDataMemberIdOwner = ((m_pClassOwner && m_pClassOwner->getDataMemberX()) ? m_pClassOwner->getDataMemberX()->getId_WithDaoStrategy() : NULL);
          qDebug() << "8";
          m_lstDataMemberPtr = (& QxSqlRelation<DataType, Owner>::m_lstDataMember);
          qDebug() << "9";
          m_lstSqlRelationPtr = (& QxSqlRelation<DataType, Owner>::m_lstSqlRelation);
          qDebug() << "10";
          if (m_pClass) { m_oSoftDelete = m_pClass->getSoftDelete(); }
          qDebug() << "11";
          qAssert(m_pClass && m_pClassOwner && m_pDataMember && m_pDataMemberX && m_pDataMemberId);
          qDebug() << "12";
          if (getDataCount() > 0 || getRelationCount() > 0) { m_bInitInEvent = false; return; }
          qDebug() << "13";
          IxDataMember * p = NULL; long lCount = m_pDataMemberX->count_WithDaoStrategy();
          qDebug() << "14";
          for (long l = 0; l < lCount; ++l) { if ((p = isValid_DataMember(l))) { m_lstDataMember.insert(p->getKey(), p); } }
          qDebug() << "15";
          for (long l = 0; l < lCount; ++l) { if ((p = isValid_SqlRelation(l))) { m_lstSqlRelation.insert(p->getKey(), p->getSqlRelation()); } }
          qDebug() << "16";
          m_bInitInEvent = false;
          qDebug() << "17";
       }
    Il semblerait que ma trace d'execution fasse:
    q1.....q15 puis on rentre dans la boucle et à l'appel de isValid_SqlRelation, on rerentre dans le init(), sauf qu'on passe dans le if juste après q12, ce qui met le booléen m_bInitInEvent à false, et donc, au prochain élément de la boucle q15, le mutex va donc être re-bloqué -> dead lock.

    D'une façon générale, pourquoi avoir mis le mutex en static ? Si c'est pour éviter d'avoir des problèmes de constantness, mieux vaut mettre le mot clé mutable devant.

  2. #2
    Membre du Club
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    151
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 151
    Points : 49
    Points
    49
    Par défaut
    Après une courte analyse, je pense que ceci peut résoudre le problème:


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    for (long l = 0; l < lCount; ++l) { m_bInitInEvent = true; if ((p = isValid_SqlRelation(l))) { m_lstSqlRelation.insert(p->getKey(), p->getSqlRelation()); } }
    A voir, je suis en train de compiler, mais mon projet met 15 minutes à compiler...

  3. #3
    Expert confirmé

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    481
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 481
    Points : 4 238
    Points
    4 238
    Par défaut


    J'avoue que je ne sais pas comment tu arrives à générer ce bug !
    Dans tous les cas, en relisant le code, j'avoue que j'en suis pas très fier
    Donc je vais modifier le code d'initialisation des relations et te proposerais une BETA à télécharger (je vais essayer aujourd'hui mais je ne sais pas si j'aurais du temps)...

    Pour le moment et pour te débloquer, supprime l'appel au mutex.

    Pour info, je me suis permis de changer le titre du sujet pour qu'il soit plus parlant.
    Le site de la bibliothèque QxOrm : bibliothèque C++ de gestion de données (Mapping Objet Relationnel ou ORM) basée sur les frameworks Qt et boost.
    QxEntityEditor : éditeur graphique pour la bibliothèque QxOrm (application multi-plateforme pour gérer graphiquement le modèle d'entités).

    Tutoriel : installer un environnement de développement avec QxOrm sous Windows.
    Tutoriel qxBlog : gestion de blogs en C++/Qt.
    Tutoriel qxClientServer : création d'un serveur d'applications en C++/Qt.

  4. #4
    Membre du Club
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    151
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 151
    Points : 49
    Points
    49
    Par défaut
    Citation Envoyé par QxOrm Voir le message


    J'avoue que je ne sais pas comment tu arrives à générer ce bug !
    Ben... Je pense qu'à terme mon projet devrait couvrir la presque totalité des fonctionnalités de QxOrm, donc ça fait un bon test

    Citation Envoyé par QxOrm Voir le message
    Dans tous les cas, en relisant le code, j'avoue que j'en suis pas très fier
    Donc je vais modifier le code d'initialisation des relations et te proposerais une BETA à télécharger (je vais essayer aujourd'hui mais je ne sais pas si j'aurais du temps)...

    Pour le moment et pour te débloquer, supprime l'appel au mutex.

    Pour info, je me suis permis de changer le titre du sujet pour qu'il soit plus parlant.
    Ok, c'est ce que j'ai fait pour le mutex.
    Merci et bon courage !

  5. #5
    Membre du Club
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    151
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 151
    Points : 49
    Points
    49
    Par défaut
    Pour information, voici le script de création de la base de donnée à partir duquel apparaît le bug (en rouge l'endroit exact):

    A noter que sous linux, ça fonctionne sans problème (j'espère que ça ne veut pas dire que les mutex sont désactivés sous linux).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    void updateDatabaseVersion( const EDatabaseStatus status )
    {
        try {
            int domainVersion = qApp->property( "DomainVersion" ).toInt();
    
            // We connect to the database with an user that has rights for the database creation
            QSqlDatabase db = qx::QxSqlDatabase::getSingleton()->getDatabaseCloned();
            db.setUserName( "root" );
            db.setPassword( "" );
            const bool isSQLite = db.driverName().contains( "SQLITE" );
    
            // Create a transaction that will throw exception on errors
            qx::QxSession session( db, true, true );
    
            // Fetch database version that will lock concurrent access (when multiple users starts the application at the same time)
            DatabaseVersion dbVersion;
    
            if ( status == eDatabaseStatusNotUpTodate )
            {
                QSqlError err = session.fetchByQuery( qx_query( QString( "WHERE name='") + kAppName + ( isSQLite ? "'" : QString("' FOR UPDATE") ) ), dbVersion );
                if ( !err.isValid() )
                {
                    // For other users, when the locking will be over, we check if the update is still needed
                    if ( dbVersion.version >= domainVersion ) { return; }
                }
                else
                {
                    dbVersion.name = kAppName;
                    dbVersion.version = -1;     // Create tables
                }
            }
            else
            {
                dbVersion.name = kAppName;
                dbVersion.version = -1;     // Create tables
            }
            // Execute each query with this object
            QSqlQuery query( db );
    
            // On récupère toutes les classes persistantes C++ enregistrées dans le contexte QxOrm
            qx::QxCollection<QString, qx::IxClass *> * pAllClasses = qx::QxClassX::getAllClasses();
            if ( !pAllClasses ) { qAssert(false); return; }
    
            // Get database tables
            QStringList tables = db.tables();
            for ( long k = 0; k < pAllClasses->count(); k++ )
            {
                qx::IxClass * pClass = pAllClasses->getByIndex( k );
                if ( !pClass ) { continue; }
    
                // Filter non persistant classes
                if ( pClass->isKindOf( "qx::service::IxParameter" ) || pClass->isKindOf( "qx::service::IxService" ) ) { continue; }
    
                if (pClass->getVersion() <= dbVersion.version) { continue; }
    
                // We create tables if not already existing, and we define its owner
                if ( !tables.contains( pClass->getName() ) )
                {
                    // Create attribute query query section
                    QStringList attributes;
                    qx::IxDataMemberX * pDataMemberX = pClass->getDataMemberX();
                    for ( long l = 0; (pDataMemberX && (l < pDataMemberX->count_WithDaoStrategy())); l++ )
                    {
                        QString sql;
                        qx::IxDataMember * p = pDataMemberX->get_WithDaoStrategy(l);
                        if ( p && p->getDao() )
                        {
                            if ( p->hasSqlRelation() && p->getSqlRelation()->getDescription() == "relation one-to-many" )
                                continue;
                            sql += p->getName() + " " + p->getSqlType();
                            if ( p->getNotNull() ) // NOT NULL
                            {
                                sql += " NOT NULL";
                            }
                            if ( p->getIsPrimaryKey() ) // PRIMARY KEY
                            {
                                sql += " PRIMARY KEY";
                            }
                            if ( p->getAutoIncrement() ) // AUTO INCREMENT
                            {
                                sql += " AUTOINCREMENT";
                            }
                            attributes.push_back( sql );
                        }
                    }
    
                    // Append soft delete attribute
                    qx::QxSoftDelete oSoftDelete = pClass->getSoftDelete();
                    // Insert soft delete behaviour to SQL schema
                    if ( !oSoftDelete.isEmpty() )
                    { attributes.push_back( oSoftDelete.buildSqlQueryToCreateTable() ); }
    
                    QString attributeSql;
                    if ( attributes.size() )
                    {
                        for( int i = 0; i < attributes.size() - 1; ++i )
                        {
                            attributeSql += attributes[i] + ", ";
                        }
                        attributeSql = QString( " (" ) + attributeSql + attributes[attributes.size() - 1] + ")";
                        if ( !isSQLite )
                        {
                            attributeSql += "  WITH ( OIDS = FALSE )";
                        }
                    }
                    query.exec( "CREATE TABLE " + pClass->getName() + attributeSql + ";" );
                    session += query.lastError();
                    if ( !isSQLite )
                    {
                        query.exec( "ALTER TABLE " + pClass->getName() + " OWNER TO \"root\";" );
                        session += query.lastError();
                    }
                    // Create sequences & index
                    for ( long l = 0; (pDataMemberX && ( l < pDataMemberX->count_WithDaoStrategy() ) ); l++ )
                    {
                        qx::IxDataMember * p = pDataMemberX->get_WithDaoStrategy(l);
                        if ( p && p->getDao() && !p->hasSqlRelation() )
                        {
    
                            if ( p->getAllPropertyBagKeys().contains( "INDEX" ) ) // INDEX
                            {
                                query.exec( "CREATE INDEX " + pClass->getName() + "_" + p->getName() + "_idx" + 
                                            " ON " + pClass->getName() + " USING " + p->getPropertyBag("INDEX").toString() + " (" + p->getName() + ");" );
                                session += query.lastError();
                            }
    
                            if (p->getAutoIncrement() && !isSQLite ) // AUTO INCREMENT
                            {
                                query.exec( "CREATE SEQUENCE " + pClass->getName() + "_" + p->getName() + "_seq" + "; " );
                                session += query.lastError();
                                if ( !isSQLite )
                                {
                                    query.exec( "ALTER TABLE " + pClass->getName() + "_" + p->getName() + "_seq" + " OWNER TO \"root\"; " );
                                    session += query.lastError();
                                }
                                query.exec( "ALTER TABLE " + pClass->getName() + " ALTER COLUMN " + p->getName() + " " +
                                            "SET DEFAULT nextval('" + pClass->getName() + "_" + p->getName() + "_seq" + "'::regclass);" );
                                session += query.lastError();
                            }
                        }
                    }
    
                    // Insert all relations to SQL schema
                    for (long l = 0; ( pDataMemberX && ( l < pDataMemberX->count_WithDaoStrategy() ) ); l++)
                    {
                        qx::IxDataMember * p = pDataMemberX->get_WithDaoStrategy( l );
                        if ( p && p->hasSqlRelation() && p->getVersion() >= dbVersion.version )
                        {
                            p->getSqlRelation()->init();
                            QString sql = p->getSqlRelation()->createExtraTable();
                            if ( sql.size() )
                            {
                                query.exec( sql );
                                session += query.lastError();
                            }
                        }
                    }
                }
                else
                {
                    // We add columns to table if they don't exists
                    qx::IxDataMemberX * pDataMemberX = pClass->getDataMemberX();
                    for ( long l = 0; (pDataMemberX && (l < pDataMemberX->count_WithDaoStrategy())); l++ )
                    {
                        qx::IxDataMember * p = pDataMemberX->get_WithDaoStrategy(l);
                        if ( !p || ( p->getVersion() <= dbVersion.version ) ) { continue; }
    
                        query.exec( "ALTER TABLE " + pClass->getName() + " ADD COLUMN " + p->getName() + " " + p->getSqlType() + ";" );
                        session += query.lastError();
    
                        if ( p->getIsPrimaryKey() ) // PRIMARY KEY
                        {
                            query.exec( "ALTER TABLE " + pClass->getName() + " ADD PRIMARY KEY (" + p->getName() + ");" );
                            session += query.lastError();
                        }
    
                        if ( p->getAllPropertyBagKeys().contains("INDEX") ) // INDEX
                        {
                            query.exec("CREATE INDEX " + pClass->getName() + "_" + p->getName() + "_idx" + 
                                        " ON " + pClass->getName() + " USING " + p->getPropertyBag("INDEX").toString() + " (" + p->getName() + ");");
                            session += query.lastError();
                        }
    
                        if ( p->getNotNull() ) // NOT NULL
                        {
                            query.exec("ALTER TABLE " + pClass->getName() + " ALTER COLUMN " + p->getName() + " SET NOT NULL;");
                            session += query.lastError();
                        }
    
                        if ( p->getAutoIncrement() && !isSQLite ) // AUTO INCREMENT
                        {
                            query.exec( "CREATE SEQUENCE " + pClass->getName() + "_" + p->getName() + "_seq" + "; " );
                            session += query.lastError();
                            if ( !isSQLite )
                            {
                                query.exec( "ALTER TABLE " + pClass->getName() + "_" + p->getName() + "_seq" + " OWNER TO \"root\"; " );
                                session += query.lastError();
                            }
                            query.exec( "ALTER TABLE " + pClass->getName() + " ALTER COLUMN " + p->getName() + " " +
                                        "SET DEFAULT nextval('" + pClass->getName() + "_" + p->getName() + "_seq" + "'::regclass);" );
                            session += query.lastError();
                        }
    
                        if ( p->getDescription() != "" ) // DESCRIPTION
                        {
                            query.exec("COMMENT ON COLUMN " + pClass->getName() + "." + p->getName() + " IS $$" + p->getDescription() + "$$ ;");
                            session += query.lastError();
                        }
                    }
                }
            }
    
            // Update database version
            dbVersion.version = domainVersion;
            try
            {
                session.save( dbVersion );
            }
            catch (const qx::dao::sql_error &)
            {
                session.insert( dbVersion );
            }
            // End of "try" : Session is destroyed => automatic commit or rollback
            // commit or rollback will unlock locked table (not for SQLite)
       }
        catch (const qx::dao::sql_error & err)
        {
            QSqlError sqlError = err.get();
            qDebug() << sqlError.databaseText();
            qDebug() << sqlError.driverText();
            qDebug() << sqlError.number();
            qDebug() << sqlError.type();
        }
    }

  6. #6
    Expert confirmé

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    481
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 481
    Points : 4 238
    Points
    4 238
    Par défaut
    Voici une version BETA que tu peux télécharger :
    http://www.qxorm.com/version/QxOrm_1.2.3_BETA_11.zip

    Dans cette version, j'ai conservé le code du mutex pour l'initialisation des relations. J'ai juste modifié son type pour qu'il soit récursif. Je pense que ça devrait marcher...
    Tu pourras me faire un retour stp si ça fonctionne ou pas pour toi ?
    Merci
    Le site de la bibliothèque QxOrm : bibliothèque C++ de gestion de données (Mapping Objet Relationnel ou ORM) basée sur les frameworks Qt et boost.
    QxEntityEditor : éditeur graphique pour la bibliothèque QxOrm (application multi-plateforme pour gérer graphiquement le modèle d'entités).

    Tutoriel : installer un environnement de développement avec QxOrm sous Windows.
    Tutoriel qxBlog : gestion de blogs en C++/Qt.
    Tutoriel qxClientServer : création d'un serveur d'applications en C++/Qt.

  7. #7
    Membre du Club
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    151
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 151
    Points : 49
    Points
    49
    Par défaut
    Ok, super merci

    Je vais tester ça dès ce soir, je suis au milieu d'un refactor...

  8. #8
    Membre du Club
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    151
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 151
    Points : 49
    Points
    49
    Par défaut
    Ok, c'est bon ! Je viens de tester, ça m'a tout l'air de fonctionner parfaitement !

    Merci beaucoup ! (je ne comprend pas exactement ce que tu as fait, mais c'est parfait )

  9. #9
    Expert confirmé

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    481
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 481
    Points : 4 238
    Points
    4 238
    Par défaut
    Le site de la bibliothèque QxOrm : bibliothèque C++ de gestion de données (Mapping Objet Relationnel ou ORM) basée sur les frameworks Qt et boost.
    QxEntityEditor : éditeur graphique pour la bibliothèque QxOrm (application multi-plateforme pour gérer graphiquement le modèle d'entités).

    Tutoriel : installer un environnement de développement avec QxOrm sous Windows.
    Tutoriel qxBlog : gestion de blogs en C++/Qt.
    Tutoriel qxClientServer : création d'un serveur d'applications en C++/Qt.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Problème d'initialisation des relations LAZY
    Par Invité dans le forum Hibernate
    Réponses: 10
    Dernier message: 08/08/2014, 09h59
  2. [TADOStoredProc] initialisation des paramètres et lancement
    Par zelastwarrior dans le forum C++Builder
    Réponses: 5
    Dernier message: 16/06/2005, 08h43
  3. Réponses: 5
    Dernier message: 17/05/2005, 09h34
  4. Tables avec des relations & procédures dynamiques
    Par JustMe dans le forum Débuter
    Réponses: 5
    Dernier message: 15/12/2004, 09h58
  5. initialisation des property page
    Par Robleplongeur dans le forum MFC
    Réponses: 3
    Dernier message: 21/04/2004, 10h47

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo