2016-04-18 1 views
2

Je regarde un morceau de code qui a fonctionné jusqu'à récemment. Fondamentalement, j'ai une classe C++, dans laquelle je protège une variable avec une macro G_LOCK_DEFINE. Le constructeur est implémenté dans un fichier .cpp distinct.Le comportement de G_LOCK est-il passé de la glib 2.46 à la glib 2.48?

CSomeClass::CSomeClass() 
{ 
    G_LOCK(mSomeCounter); 
    mSomeCounter = 0; 
    G_UNLOCK(mSomeCounter); 
} 

Cette variable est accessible en plusieurs fonctions, mais le principe est toujours le même. Maintenant, comme déjà dit, le code compile bien et en fait a également fonctionné parfaitement dans le passé. Maintenant, depuis peu, je suis dans une impasse, chaque fois que je rencontre une commande G_LOCK. Pour le débogage, j'ai déjà restreint le programme à un seul thread, pour exclure les erreurs logiques.

J'ai récemment mis à jour vers Ubuntu 16.04 bêta, ce qui a poussé ma version glib vers 2.48.0-1ubuntu4. J'ai déjà vérifié le journal des modifications pour des informations pertinentes sur G_LOCK, mais je n'ai rien trouvé. Est-ce que quelqu'un d'autre a remarqué des effets amusants, en utilisant des macros G_LOCK avec la version récente de glib? Ai-je manqué quelques changements ici?

Répondre

4

Tout d'abord, tout ce que G_LOCK_DEFINE fait est de créer une variable GMutex, dont le nom code le nom de la variable qu'il protège, par ex. G_LOCK_DEFINE(mSomeCounter) devient GMutex g__mSomeCounter_lock;. Ainsi, nous pouvons étendre votre code à quelque chose comme:

class CSomeClass { 
    private: 
     gulong mSomeCounter; 
     GMutex g__mSomeCounter_lock; 

    public: 
     CSomeClass(); 
}; 

CSomeClass::CSomeClass() 
{ 
    g_mutex_lock(&g__mSomeCounter_lock); 
    mSomeCounter = 0; 
    g_mutex_unlock(&g__mSomeCounter_lock); 
} 

Le problème fondamental est que vous n'êtes pas une initialisation des membres de la classe CSomeClass. Vous attribuez des valeurs à et d'entre eux dans le constructeur, mais vous ne les initialisez définitivement pas. Il y a une différence entre l'affectation entre accolades, et en utilisant un initialiseur, tels que:

CSomeClass::CSomeClass() : mSomeCounter(0) 

En conséquence, le mutex qui est créé, nommé contre la variable peut contenir des déchets. Il n'y a probablement rien dans le code glib qui aurait changé pour que cela se produise, il est plus probable que les changements apportés aux autres bibliothèques aient changé la disposition de la mémoire de votre application, découvrant le bogue.

Les glib documentation conseils que vous devez g_mutex_init mutex:

qui a été alloué sur la pile, ou dans le cadre d'une structure plus grande

Vous n'avez pas besoin de g_mutex_init mutex que :

Il n'est pas nécessaire d'initialiser un mutex qui a été alloué statiquement

Les instances de classe sont presque toujours et non allouées de manière statique.

Vous devez corriger votre constructeur pour vous assurer qu'il initialise correctement le mutex, par ex.:

CSomeClass::CSomeClass() 
{ 
    g_mutex_init(&G_LOCK_NAME(mSomeCounter)); 
    G_LOCK(mSomeCounter); 
    mSomeCounter = 0; 
    G_UNLOCK(mSomeCounter); 
} 

TBH, je mettrais le mutex dans un support de classe, et l'initialiser dans le cadre de cela, plutôt que la façon dont vous le faites, pour qu'il s'initialisé, verrouillé et déverrouillé partie de la sémantique standard C++ RAII.

Si vous utilisez un petit talon principal, quelque chose comme:

main() { 
    { CSomeClass class1; } 
    { CSomeClass class2; } 
    { CSomeClass class3; } 
} 

et votre code, il y a une bonne chance, il se bloque de toute façon. (Mon mac est écrasé l'exemple avec:. GLib (gthread-posix.c): Unexpected error from C library during 'pthread_mutex_lock': Invalid argument. Aborting.

quelques simples, par exemple, non production emballages pour aider à RAII:

class CGMutex { 
    GMutex mutex; 

    public: 
    CGMutex() { 
     g_mutex_init(&mutex); 
    } 

    ~CGMutex() { 
     g_mutex_clear(&mutex); 
    } 

    GMutex *operator&() { 
     return &mutex; 
    } 
}; 

class CGMutexLocker { 
    CGMutex &mRef; 
    public: 
    CGMutexLocker(CGMutex &mutex) : mRef(mutex) { 
     g_mutex_lock(&mRef); 
    } 
    ~CGMutexLocker() { 
     g_mutex_unlock(&mRef); 
    } 
}; 

class CSomeClass { 
    private: 
     gulong mSomeCounter; 
     CGMutex mSomeCounterLock; 

    public: 
     CSomeClass(); 
}; 

CSomeClass::CSomeClass() 
{ 
    CGMutexLocker locker(mSomeCounterLock); // lock the mutex using the locker 
    mSomeCounter = 0; 
} 

L'initialisation mSomeCounter assure que le compteur s'initialisé, sinon il aura poubelle