2009-11-16 1 views
2

Je suis dans un fil. J'ai une adresse. Est-ce l'adresse d'une variable sur la même pile que j'utilise?Comment dire par programme si deux variables sont sur la même pile? (sous Windows)

static int *address; 

void A() 
{ 
    int x; 
    atomic::CAS(address, 0, &x); // ie address = &x 
    // ... 
} 

void B() 
{ 
    int y; 
    int * addr = atomic::read(address); // ie addr = address 
    if (addr && on_same_stack(&y, addr)) 
    { 
     // B() called from A() 
    } 
    else 
    { 
     // B() called from different thread than A() 
    } 
} 

Je dois implémenter on_same_stack(addr1, addr2). Je sais que la ou les pile (s) sur Windows grandissent selon les besoins, mais je sais aussi qu'il y a une limite à la croissance, et que (dans le débogage au moins) il y a du code de pile-débordement sur chaque appel de fonction. Donc je pense que cela peut être fait. Maintenant, je sais aussi que je pourrais/devrait utiliser des identifiants de threads, etc. Mais j'essaie d'implémenter un codage sans verrou difficile ici, et je n'ai pas vraiment la place pour stocker les identifiants de threads, juste un seul pointeur. (J'espère éviter CMPXCH16). S'il vous plaît croyez-moi que je sais quelque peu ce que je fais :-).

Ceci est pour Windows seulement pour l'instant. Mais plus c'est portable, mieux c'est. (NT/XP/7/CE?)

P.S. ce site est appelé "stackoverflow" donc ce devrait être le bon endroit à demander, n'est-ce pas? :-)

EDIT: ajout de contexte, puisque tout le monde le demande. J'applique un call_once similaire à pthread_once ou boost.threads call_once. J'essaie de vérifier la récursivité. Je suis très limité avec ce que je dois travailler avec. Je ne peux pas ajouter de paramètres de fonction. Je ne peux pas faire d'hypothèses sur ce que fait le reste du programme, comme la quantité de TLS qu'ils utilisent déjà. Etc. Je ne peux coder que dans ma seule fonction, et ne faire aucun changement ou supposition à propos de quoi que ce soit en dehors de cela.

Merci pour vos questions/réponses.

+2

Quel est le problème avec l'appel GetThreadId()? –

+0

Bien sûr, dès que je poste, je le trouve: Besoin de regarder le bloc d'information de fil, se cachant au registre fs. Quelqu'un sait à quoi cela ressemble dans Win64? – tony

+0

Le code ci-dessus est évidemment une simplification. Dans mon cas, je n'ai pas de place pour stocker le ThreadId. J'ai déjà utilisé toute ma chambre avec un pointeur. En programmation sans verrou, vous n'avez souvent que 32 bits pour travailler avec * atomiquement *. Ou 64 sur les systèmes 64 bits. Ou en fait, généralement deux fois plus (64 sur 32 bits, 128 sur 64), mais pas toujours. Et si vous obtenez ces bits supplémentaires, ils ont tendance à être utilisés pour les compteurs pour éviter les problèmes ABA, etc Donc, si je peux éviter de stocker le threadID, je le ferai. Merci. – tony

Répondre

2

Que diriez-vous quelque chose de fou comme (non testé):

declspec(__thread) void* stackBottom; 

void Thread1Routine(void* arg) 
{ 
    volatile int bottom; 
    stackBottom = ⊥ 

    ... (do stuff which ends up calling on_same_stack()) ... 
} 


bool on_same_stack(void* p) 
{ 
    volatile int top; 
    return ((LONG_PTR)p >= (LONG_PTR)&top) && ((LONG_PTR)p <= (LONG_PTR)stackBottom); 
} 

(modifié pour éliminer les problèmes de passage de arg basés sur les registres théoriques)

+0

Ceci est intéressant. Mon cas est encore plus compliqué en ce que Thread1Routine() appellera récursivement Thread1Routine(), mais je suppose que je peux vérifier si stackBottom a déjà été défini, ne le définissez que la première fois. Ceci est en fait assez portable. Merci. Je modifierais probablement les contrôles> = pour être une forme de is_between() pour gérer les piles qui augmentent ou diminuent. Tant que nous sommes entre le haut et le bas, nous sommes OK. – tony

+0

Les locals de threads (via declspec (__ thread)) sont-ils définis par défaut sur 0 avant l'exécution d'un code de thread? Je suppose que je devrais être capable de trouver ça ... – tony

+0

Tony, peu importe s'il est initialisé. Au lieu de Thread1Routine appelant lui-même récursivement, déplacez tout après l'initialisation dans une autre fonction, Thread1RoutineWorker. Initialisez bottom, puis appelez la fonction worker. Demandez au travailleur de s'appeler récursivement. –

1

de votre commentaire B() called from A(), ne pouvez pas vous passer juste un argument B() et le définir à une valeur spécifique lorsqu'il est appelé de A() ?? (Avec une valeur d'argument par défaut, qui ne nécessiterait pas un grand changement)

votre échantillon est très complet, voici donc une autre tentative fait beaucoup d'hypothèses sur votre problème ... Qu'en est-il:

void A() 
{ 
    B_lockfree(); 
} 

void B() 
{ 
    // acquire a lock here 
    B_lockfree(); 
    // release your lock here 
} 

void B_lockfree() 
{ 
    // do whatever you want here 
} 

(bien, je peux penser à beaucoup de façons, mais sans connaître la grande image, ils peuvent tous être complètement faux ...)

+0

Désolé, il est toujours difficile de donner un petit exemple, et pourtant une grande image. dans A(), imaginez que je ne sais pas ce qui se passe dans la partie '...'. c'est-à-dire que je n'ai aucun contrôle sur la façon dont B() est appelée. Fondamentalement, j'implémente une version de call_once() (voir boost call_once ou pthread_once, etc), et je veux vérifier la récursivité, au moins dans le débogage. Je suis très limité avec ce que je dois travailler avec! – tony

+0

merci pour la clarification, il donne plus d'informations. –

2

en utilisant Win32 Thread Information Block

Ces exemples utilisent t Le thread principal, mais en fait, il doit travailler dans n'importe quel thread.

Exemple # 1:

#include <iostream> 
#include <windows.h> 
#include <winnt.h> 
#include <intrin.h> 

inline size_t get_thread_top_stack_size() 
{ 
    NT_TIB *s = (NT_TIB*)getTib(); 
    return (size_t)s->StackBase ; 
} 

int global_var; 

int main() 
{ 
    size_t sp_value = 0; 
    _asm { mov [sp_value], esp } 
    size_t thread_top_stack = get_thread_top_stack_size(); 

    int my_local_var; 
    size_t my_local_var_addr = (size_t)&my_local_var; 
    if (my_local_var_addr < thread_top_stack && my_local_var_addr > sp_value ) { 
     std::cout << "Yes, on the thread stack"; 
    } else { 
     std::cout << "No, not on the thread stack"; 
    } 

    size_t my_global_var_addr = (size_t)&global_var; 
    if (my_global_var_addr < thread_top_stack && my_global_var_addr> sp_value ) { 
     std::cout << "Yes, on the thread stack"; 
    } else { 
     std::cout << "No, not on the thread stack"; 
    } 
    return 0; 
} 

Exemple # 2:

#include <windows.h> 
#include <winnt.h> 
#include <intrin.h> 

inline NT_TIB* getTib() 
{ 
    return (NT_TIB*)__readfsdword(0x18); 
} 

inline bool is_var_on_the_thread_stack(void* ptr) 
{ 
    NT_TIB *nt_tib = getTib(); 
    return (nt_tib->StackBase >= ptr) && (nt_tib->StackLimit <= ptr); 
} 

int my_global_var; 

int main() 
{ 
    int my_thread_var; 
    if (is_var_on_the_thread_stack(&my_thread_var)) { 
     std::cout << "Yes, on the thread stack" << std::endl; 
    } else { 
     std::cout << "No, not on the thread stack" << std::endl; 
    } 
    if (is_var_on_the_thread_stack(&my_global_var)) { 
     std::cout << "Yes, on the thread stack" << std::endl; 
    } else { 
     std::cout << "No, not on the thread stack" << std::endl; 
    } 
    return 0; 
} 
Questions connexes