Как программно определить, находятся ли две переменные в одном стеке? (в Windows) - PullRequest
2 голосов
/ 16 ноября 2009

Я в теме. У меня есть адрес. Это адрес из переменной в том же стеке, который я использую?

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()
   }
}

Мне нужно реализовать on_same_stack(addr1, addr2). Я знаю, что стек (-ы) в Windows растут по мере необходимости, но я также знаю, что есть предел для роста, и что (по крайней мере, в отладке) есть код проверки переполнения стека при каждом вызове функции. Поэтому я думаю, что это можно сделать.

Теперь я также знаю, что могу / должен использовать идентификаторы потоков и т. Д. Но я пытаюсь реализовать здесь несколько хитрое кодирование без блокировок, и у меня нет места для хранения идентификаторов потоков, просто один указатель. (Я надеюсь избежать CMPXCH16). Пожалуйста, поверьте мне, что я немного знаю, что я делаю: -).

Пока только для Windows. Но чем портативнее, тем лучше. (NT / XP / 7 / CE?)

P.S. этот сайт называется «stackoverflow», так что это должно быть правильное место, чтобы спросить, не так ли? : -)

РЕДАКТИРОВАТЬ: добавить контекст, так как все спрашивают. Я реализую пользовательский call_once, похожий на pthread_once или boost.threads call_once. Я пытаюсь проверить рекурсию. Я очень ограничен тем, с чем мне приходится работать. Я не могу добавить параметры функции. Я не могу предположить, что делает остальная часть программы, например, сколько TLS они уже используют. И т. Д. Я могу кодировать только внутри своей единственной функции и не делать никаких изменений или предположений ни о чем, кроме этого.

Спасибо за ваши вопросы / ответы.

Ответы [ 3 ]

2 голосов
/ 17 ноября 2009

Использование Блок информации о потоке Win32

В этих примерах используется основной поток, но на самом деле он должен работать в любом потоке.

Пример № 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;
}

Пример № 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;
}
2 голосов
/ 16 ноября 2009

Как насчет чего-то безумного, вроде (не проверено):

declspec(__thread) void* stackBottom;

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

  ... (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);
}

(отредактировано для устранения теоретических проблем с передачей аргументов в регистре)

1 голос
/ 16 ноября 2009

из вашего комментария B() called from A(), вы не можете просто передать аргумент B() и установить для него определенное значение при вызове из A() ?? (со значением аргумента по умолчанию, которое не потребует больших изменений)

Ваш образец не очень полный, поэтому вот еще одна попытка МНОГО предположений о вашей проблеме ... как насчет:

void A()
{
    B_lockfree();
}

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

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

(ну, я могу придумать множество способов, но, не зная общей картины, все они могут быть совершенно не правы ...)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...