Permutation atomique dans GNU C ++

Je veux vérifier que ma compréhension est correcte. Ce genre de chose est délicat alors je suis presque sûr que je manque quelque chose. J’ai un programme consistant en un thread en temps réel et un thread en temps non réel. Je veux que le thread non-RT puisse échanger un pointeur vers la mémoire utilisée par le thread RT.

D’après les documents, ma compréhension est que cela peut être accompli en g++ avec:

 // global Data *rt_data; Data *swap_data(Data *new_data) { #ifdef __GNUC__ // Atomic pointer swap. Data *old_d = __sync_lock_test_and_set(&rt_data, new_data); #else // Non-atomic, cross your fingers. Data *old_d = rt_data; rt_data = new_data; #endif return old_d; } 

C’est le seul endroit du programme (autre que la configuration initiale) où rt_data est modifié. Lorsque rt_data est utilisé dans le contexte en temps réel, il est copié dans un pointeur local. Pour old_d , plus tard, s’il est certain que l’ancienne mémoire n’est pas utilisée, elle sera libérée dans le thread non RT. Est-ce correct? Ai-je besoin de quelque part volatile ? Existe-t-il d’autres primitives de synchronisation que je devrais appeler?

Soit dit en passant, je le fais en C ++, bien que je sois intéressé de savoir si la réponse est différente pour C.

Merci d’avance.

En général, n’utilisez pas volatile lorsque vous écrivez du code simultané en C/C++ . La sémantique de volatile est si proche de ce que vous voulez qu’elle est tentante, mais à la fin, volatile ne suffit pas . Malheureusement, Java/C# volatile != C/C++ volatile . Herb Sutter a un excellent article expliquant le désordre déroutant.

Ce que vous voulez vraiment, c’est une barrière de mémoire. __sync_lock_test_and_set fournit la clôture pour vous.

Vous aurez également besoin d’une barrière de mémoire lorsque vous copiez (chargez) le pointeur rt_data sur votre copie locale.

Verrouiller la programmation est délicate. Si vous voulez utiliser les extensions c ++ 0x de Gcc, c’est un peu plus facile:

 #include  std::atomic rt_data; Data* swap_data( Data* new_data ) { Data* old_data = rt_data.exchange(new_data); assert( old_data != new_data ); return old_data; } void use_data( ) { Data* local = rt_data.load(); /* ... */ } 

Mise à jour : Cette réponse est incorrecte, car je ne vois pas le fait que volatile garantit que les access aux variables volatile ne sont pas réorganisés, mais ne fournit aucune garantie de ce type vis-à-vis des autres access et manipulations non volatile . Une barrière de mémoire fournit de telles garanties et est nécessaire pour cette application. Ma réponse initiale est ci-dessous, mais n’agissez pas dessus. Voir cette réponse pour une bonne explication dans le trou dans ma compréhension qui a conduit à la réponse incorrecte suivante.

Réponse originale:

Oui, vous avez besoin de volatile sur votre déclaration rt_data ; chaque fois qu’une variable peut être modifiée en dehors du stream de contrôle d’un thread y accédant, elle doit être déclarée volatile . Bien que vous puissiez peut-être vous en sortir sans être volatile puisque vous copiez vers un pointeur local, volatile aide au moins à la documentation et empêche également certaines optimisations du compilateur qui peuvent causer des problèmes. Prenons l’exemple suivant, tiré de DDJ :

 volatile int a; int b; a = 1; b = a; 

S’il est possible a valeur de a change entre a=1 et b=a , alors a devrait être déclaré volatile (à moins, bien sûr, d’affecter une valeur obsolète à b soit acceptable). Le multithreading, en particulier avec les primitives atomiques, constitue une telle situation. La situation est également déclenchée par des variables modifiées par des gestionnaires de signaux et par des variables mappées sur des emplacements mémoire impairs (par exemple, des registres d’E / S matérielles). Voir aussi cette question .

Sinon, ça me semble bien.

Pour ce faire, j’utiliserais probablement les primitives atomiques fournies par GLib . Ils utiliseront une opération atomique si disponible et retomberont sur une implémentation lente mais correcte basée sur un mutex si les opérations atomiques ne sont pas disponibles. Boost peut fournir quelque chose de similaire pour C ++.