views:

244

answers:

3

I'm in a thread. I have an address. Is that address from a variable on the same stack that I'm using?

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

I need to implement on_same_stack(addr1, addr2). I know that the stack(s) on Windows grow as needed, but I also know that there is a limit to the growth, and that (in debug at least) there is stack-overflow-checking code on every function call. So I think it can be done.

Now, I also know that I could/should use thread IDs, etc. But I'm trying to implement some tricky lock-free coding here, and I don't really have the room to store the thread IDs, just a single pointer. (I'm hoping to avoid CMPXCH16). Please trust me that I somewhat know what I'm doing :-).

This is for Windows-only for now. But the more portable the better. (NT/XP/7/CE?)

P.S. this site is called "stackoverflow" so it should be the right place to ask, shouldn't it? :-)

EDIT: adding context, since everyone is asking. I'm implementing a custom call_once similar to pthread_once or boost.threads call_once. I'm attempting to check for recursion. I am very limited with what I have to work with. I can't add function parameters. I can't make assumptions about what the rest of the program is doing, like how much TLS they are already using. Etc. I can only code inside my one function, and make no changes or assumptions about anything outside of that.

Thanks for your questions/answers.

+1  A: 

How about something crazy like (untested):

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

(edited to remove theoretical register-based arg passing issues)

Aaron
This is interesting.My case is further complicated in that Thread1Routine() will recursively call Thread1Routine(), but I guess I can check if stackBottom has already been set, only set it first time.This is actually quite portable as well.Thanks.I'd probably change the >= checks to be some form of is_between() to handle stacks that grow up or down. As long as we are between top/bottom we are OK.
tony
<sigh>Are thread-locals (via declspec(__thread)) default initted to 0 before any thread code is run?I guess I should be able to find that out...
tony
Tony, it doesn't matter whether it's initialized. Instead of Thread1Routine calling itself recursively, move everything after the initialization into another function, Thread1RoutineWorker. Initialize bottom, and then call the worker function. Have the worker call itself recursively.
Rob Kennedy
so, if anyone is left reading...declspec(__thread) is not allowed in DLLs on OSes before Vista.TlsAlloc() can be used, of course it can fail.It is initted to 0 though.
tony
I'm sorry for the lack of context.I'm implementing call_once(). I can't move initialization to another function, etc. I'm very limited.
tony
I've never seen TlsAlloc() fail unless you're using a lot of per-thread data - in which case you're better off moving it all into a struct and just using a single TLS entry.
Aaron
+1  A: 

from your comment B() called from A(), can't you just pass an argument to B() and set it to a specific value when called from A() ?? (with a default argument value, that would not require a big change)

your sample is not very complete, so here is another attempt making A LOT of assumptions about your problem... what about:

void A()
{
    B_lockfree();
}

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

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

(well, i can think of a lot of ways, but without knowing the big picture, they may all be completely wrong...)

Adrien Plisson
Sorry, it is always hard to give a small example, and yet a big picture.in A(), imagine I don't know what happens in the '...' part. ie I have no control on how B() is called.Basically, I am implementing a version of call_once() (see boost call_once or pthread_once, etc), and I want to check for recursion, at least in debug.I'm very limited with what I have to work with!
tony
thanks for the clarification, it gives some more informations.
Adrien Plisson
+1  A: 

Using Win32 Thread Information Block

These examples use the main thread but actually it must work on in any thread.

Example #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;
}

Example #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;
}
skwllsp