views:

248

answers:

3

I know this is a bad idea! Certainly for safe programming the stack for a given thread should be considered private to that thread. But POSIX at least guarantees that all of a thread's memory is shared and writeable by other threads, which means the stack of one thread can (in theory) be written to by another thread. So I'm curious how one could do that.

The only way I can think of would be to pass the address of a local variable in thread B to thread A and have A start writing to that general area. But this doesn't fully emulate function calls in C. In particular I'm curious if it's possible to say, have thread A sleep while its program counter is set to the first line of some function that takes parameters, and then on thread B actually push those parameters onto the stack, then resume thread A and have it execute as if the function were originally called in thread A with those parameters. I think this requires thread B to be able to access thread A's registers under at least x86 calling conventions, and I'm not sure that's possible.

+1  A: 

It might work to pass a pointer to a volatile local variable in function foo() in thread A to function bar() in thread B, and then letting thread B modify its contents.

But in the face of optimization and thread memory caching, there is no way to guarantee that this will work.

So, yes, this is probably a bad idea. Why do you want to do this? Would semaphores or mutexes be a better solution?

Loadmaster
What is "thread memory caching" and why would optimization break volatile? The point of volatile is to not be broken by optimization.
Joseph Garvin
I've read that multiple threads running on multiple CPUs (or multicore CPUs) have different L1 memory caches, which may interfere with `volatile` semantics.
Loadmaster
A: 

It looks like you are trying to implement your own (fast) user space mutex, so why not explore simply using a futex? They can be tricky, but at least you (as a last resort) have the kernel to arbitrate races.

What Loadmaster suggested could conceivably work, with two threads. More than two and its going to get very, very icky. Additionally, I second what he said about volatile not always being volatile.

Tim Post
+1  A: 

Ok so one way to access thread's stack is to allocate stack memory yourself and assign it while creating the thread. This can be achieved by pthread_attr_setstack() call.

Following code sets threads stack to manually allocated memory (here malloc) instead of system allocating it. Later you can access mystack pointer once thread is created. One usecase of such code is, you can take dump of thread stack to take its snapshot and later you can restore this thread.

CODE:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <limits.h>

   void *thread(void *arg) {
      char *ret;
      printf("thread() entered with argument '%s'\n", (char *)arg);
      if ((ret = (char*) malloc(20)) == NULL) {
        perror("malloc() error");
        exit(2);
      }
      strcpy(ret, "This is a test");
      pthread_exit(ret);
    }

    int main(void)
    {
       pthread_attr_t attr;
       int              rc;

      pthread_t thid;
      void *ret;

       void  *mystack;
       size_t mystacksize = 2 * PTHREAD_STACK_MIN;

       if (pthread_attr_init(&attr) == -1) {
          exit(1);
       }

       /* Get a big enough stack and align it on 4K boundary. */
       mystack = malloc(PTHREAD_STACK_MIN * 3);
       if (mystack != NULL) {
          printf("Using PTHREAD_STACK_MIN to align stackaddr %x.\n", mystack);
          mystack = (void *)((((long)mystack + (PTHREAD_STACK_MIN - 1)) /
                              PTHREAD_STACK_MIN) * PTHREAD_STACK_MIN);
       } else {
          exit(2);
       }

       printf("Setting stackaddr to %x\n", mystack);
       printf("Setting stacksize to %x\n", mystacksize);
       rc = pthread_attr_setstack(&attr, mystack, mystacksize);
       if (rc != 0) {
          printf("pthread_attr_setstack returned: %d\n", rc);
          exit(3);
       } else {
          printf("Set stackaddr to %x\n", mystack);
          printf("Set stacksize to %x\n", mystacksize);
       }


      if (pthread_create(&thid, &attr, thread, "thread 1") != 0) {
        exit(1);
      }

      if (pthread_join(thid, &ret) != 0) {
        exit(3);
      }

      printf("thread exited with '%s'\n", ret);
       rc = pthread_attr_destroy(&attr);
       if (rc != 0) {
          exit(5);
       }

       exit(0);
    }

Let us know if this is what you wanted. Sorry for bad indentation and coding style.

vinit dhatrak
Doh, I forgot that when creating another thread you usually determine what space gets used for it's stack. That makes things simpler.I', still be curious though if it's possible to manipulate the stack used for function calls, which AFAIK requires changing one thread's register values from another thread.
Joseph Garvin