2

Here is my program just to find the difference between pthread_exit and return from a thread.

struct foo{
    int a,b,c,d;
    ~foo(){cout<<"foo destructor called"<<endl;}
};
//struct foo foo={1,2,3,4};
void printfoo(const char *s, const struct foo *fp)
{
    cout<<s;
    cout<<"struct at 0x"<<(unsigned)fp<<endl;
    cout<<"foo.a="<<fp->a<<endl;
    cout<<"foo.b="<<fp->b<<endl;
    cout<<"foo.c="<<fp->c<<endl;
    cout<<"foo.d="<<fp->d<<endl;
}
void *thr_fn1(void *arg)
{
    struct foo foo={1,2,3,4};
    printfoo("thread1:\n",&foo);
    pthread_exit((void *)&foo);
    //return((void *)&foo);
}
int main(int argc, char *argv[])
{
    int err;
    pthread_t tid1,tid2;
    struct foo *fp;
    err=pthread_create(&tid1,NULL,thr_fn1,NULL);
    if(err!=0)
            cout<<"can't create thread 1"<<endl;
    err=pthread_join(tid1,(void **)&fp);
    if(err!=0)
            cout<<"can't join with thread 1"<<endl;
    exit(0);
}

In "*thr_fn1" thread function I created an object foo.

According to the site pthread_exit vs. return when I exit the thread function "thr_fun1()" using "return((void *)&foo);" it should call the destructor for the object foo, but it should not call the destructor when I call "pthread_exit((void *)&foo);" to return to main from function "thr_fun1()".

But in both the cases using "return((void *)&foo);" or "pthread_exit((void *)&foo);" the local object "foo" in function "thr_fun1()" is getting called.

This is not the behaviour I guess. Destructor should be called only in "return((void *)&foo);" case only.

Please verify me if I am wrong?

Community
  • 1
  • 1
Santosh Sahu
  • 2,134
  • 6
  • 27
  • 51

3 Answers3

6

Your code has a serious problem. Specifically, you're using a local variable as the exit value for pthread_exit():

void *thr_fn1(void *arg)
{
    struct foo foo={1,2,3,4};
    printfoo("thread1:\n",&foo);
    pthread_exit((void *)&foo);
    //return((void *)&foo);
}

Per the Pthreads spec, "After a thread has terminated, the result of access to local (auto) variables of the thread is undefined."

Therefore, returning the address of a stack-allocated variable from your thread function as the thread exit value (in your case, pthread_exit((void *)&foo) ) will cause problems for any code that retrieves and attempts to dereference this address.

Bukes
  • 3,668
  • 1
  • 18
  • 20
  • As per the Pthreads spec, it's written "The behaviour of pthread_exit() is undefined if called from a cancellation cleanup handler or destructor function that was invoked as a result of either an implicit or explicit call to pthread_exit()." And here in my case i am not calling pthread_exit from destructor or cleanup handler functions. I am calling it from the thread itself. Please provide more correct details – Santosh Sahu Nov 21 '13 at 04:11
  • 1
    @Santosh Sahu The spec specifically says "After a thread has terminated, the result of access to local (auto) variables of the thread is undefined. Thus, references to local variables of the exiting thread should not be used for the pthread_exit() value_ptr parameter value.", which is exactly what your sample is doing. Now, about that downvote.... – Bukes Nov 21 '13 at 18:13
4

Yes, that's right. pthread_exit() immediately exits the current thread, without calling any destructors of objects higher up on the stack. If you're coding in C++, you should make sure to either always return from your thread procedure, or only call pthread_exit() from one of the bottommost stack frames with no objects with destructors still alive in that frame or any higher frames; otherwise, you will leak resources or cause other bad problems.

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • I have changed the question. Please compile in any editor and reply back. – Santosh Sahu Nov 20 '13 at 19:23
  • "Other bad problems" includes such nastiness as not releasing ownership of synchronization objects (mutexes, etc.) acquired/owned by the thread being terminated in such a fashion. – Bukes Nov 20 '13 at 19:25
3

pthread_exit() is throwing an exception which causes the stack to unwind and destructors to be called for locals. See https://stackoverflow.com/a/11452942/12711 for more details.

The exception thrown is of type abi::__forced_unwind (from cxxabi.h); an Internet search can give you more details.


note: as other answers/comments have mentioned, returning the address of a local wouldn't work anyway, but that is besides the point of the question. You get the same behavior regarding destructing foo if some other valid address (or the null pointer) is returned instead of &foo.

Community
  • 1
  • 1
Michael Burr
  • 333,147
  • 50
  • 533
  • 760