Tuesday, October 21, 2014

Unix Prog: Thread Termination(1)

1. Ways to terminate a thread
1) The thread simply return from the start routine. The return value is the exit code.
2) The thread can be cancelled by another thread in the process.
3) The thread can call pthread_exit to terminate itself.

2. pthread_exit, pthread_join
Definition:
 ubuntu@ip-172-31-23-227:~$ less /usr/include/pthread.h  
 ......  
 /* Terminate calling thread.  
   
   The registered cleanup handlers are called via exception handling  
   so we cannot mark this function with __THROW.*/  
 extern void pthread_exit (void *__retval) __attribute__ ((__noreturn__));  
   
 /* Make calling thread wait for termination of the thread TH. The  
   exit status of the thread is stored in *THREAD_RETURN, if THREAD_RETURN  
   is not NULL.  
   
   This function is a cancellation point and therefore not marked with  
   __THROW. */  
 extern int pthread_join (pthread_t __th, void **__thread_return);  
 ......  

pthread_join will automatically put the thread into detached state, then thread's storage can be reclaimed immediately. If the thread is already in the detached state, then pthread_join normally fail, returning EINVAL.

Example:
thread.c:
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<pthread.h>  
 #include<string.h>  
 #include<unistd.h>  
   
 void* thr_fn1(void *arg)  
 {  
  printf("thread 1 returning \n");  
  return((void*)1);  
 }  
   
 void* thr_fn2(void *arg)  
 {  
  printf("thread 2 exiting \n");  
  pthread_exit((void*)2);  
 }  
   
 int main(int argc, char* argv[])  
 {  
  int err;  
  pthread_t tid1, tid2;  
  void *tret;  
   
  // Create the first thread, the first thread starts running  
  err = pthread_create(&tid1, NULL, thr_fn1, NULL);  
  if(err != 0) {  
   printf("can't create thread 1: %s\n", strerror(err));  
   exit(1);  
  }  
   
  // Create the second thread, the second thread starts running  
  err = pthread_create(&tid2, NULL, thr_fn2, NULL);  
  if(err != 0) {  
   printf("can't create thread 2: %s\n", strerror(err));  
   exit(1);  
  }  
   
  // Calling thread, waits for the first thread, if it exits,  
  // Catch the return code of it  
  err = pthread_join(tid1, &tret);  
  if(err != 0) {  
   printf("can't join with thread 1: %s\n", strerror(err));  
   exit(2);  
  }  
  printf("thread 1 return code: %ld\n", (long)tret);  
   
  sleep(2);  
   
  // Calling thread, waits for the second thread, if it exits,  
  // Catch the exit code of it  
  err = pthread_join(tid2, &tret);  
  if(err != 0) {  
   printf("can't join with thread 2: %s\n", strerror(err));  
   exit(2);  
  }  
  printf("thread 2 exit code: %ld\n", (long)tret);  
   
  exit(0);  
 }  

shell:
After running the program, it outputted the first 3 lines. And then after 2 seconds, it outputted last line.
This proves that even if the newly created thread finished running, it is NOT in detached state.It is still there waiting for the pthread_join to pick up its exit code.
 ubuntu@ip-172-31-23-227:~$ ./thread.out  
 thread 2 exiting  
 thread 1 returning  
 thread 1 return code: 1  
 thread 2 exit code: 2  

3. thread "shared" memory overlapping
Thread can return the address of struct to return more complex information, but we need to make sure that the struct is not allocated from stack. Otherwise, after exiting from the thread, the struct's memory maybe written by sth else.

Bad Example:
thread.c:
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<pthread.h>  
 #include<unistd.h>  
 #include<string.h>  
   
 struct foo {  
  int a, b, c, d;  
 };  
   
 void printfoo(const char *s, const struct foo *fp)  
 {  
  printf("%s", s);  
  printf(" structure at 0x%lx\n", (unsigned long)fp);  
  printf(" foo.a = %d\n", fp->a);  
  printf(" foo.b = %d\n", fp->b);  
  printf(" foo.c = %d\n", fp->c);  
  printf(" foo.d = %d\n", fp->d);  
 }  
   
 void *thr_fn1(void *arg)  
 {  
  struct foo foo = {1, 2, 3, 4};  
  printfoo("thread 1:\n", &foo);  
  pthread_exit((void*)&foo);  
 }  
   
 void *thr_fn2(void *arg)  
 {  
  printf("thread 2: ID is %d\n", (int)pthread_self());  
  pthread_exit((void*)0);  
 }  
   
 int main(int argc, char* argv[])  
 {  
  int err;  
  pthread_t tid1, tid2;  
  struct foo *fp;  
   
  // Launch the first thread  
  err = pthread_create(&tid1, NULL, thr_fn1, NULL);  
  if(err != 0) {  
   printf("Can't create thread 1: %s\n", strerror(err));  
   exit(1);  
  }  
   
  // Wait for the first thread finish  
  err = pthread_join(tid1, (void*)&fp);  
  if(err != 0) {  
   printf("Can't join with thread 1: %s\n", strerror(err));  
   exit(2);  
  }  
   
  sleep(1);  
   
  // Launch the second thread  
  printf("Parent starting the second thread\n");  
  err = pthread_create(&tid2, NULL, thr_fn2, NULL);  
  if(err != 0) {  
   printf("Can't create thread 2: %s\n", strerror(err));  
   exit(1);  
  }  
   
  sleep(1);  
  printfoo("parent: \n", fp);  
  exit(0);  
 }  

shell:
After launching the program, it started the first thread, to print out the local struct foo. And then it returned the address of local struct foo.
Next, the program launched the 2nd thread.
Finally, the program print out the struct foo based on the address returned from first thread. But the value is corrupted there(note the address remain the same), this indicate that. pthread_join make first thread's storage immediately reclaimed, and then used for sth else, which corrupted the data there.
 ubuntu@ip-172-31-23-227:~$ ./thread.out  
 thread 1:  
  structure at 0x7ffc17928f00  
  foo.a = 1  
  foo.b = 2  
  foo.c = 3  
  foo.d = 4  
 Parent starting the second thread  
 thread 2: ID is 395482880  
 parent:  
  structure at 0x7ffc17928f00  
  foo.a = 395482880  
  foo.b = 32764  
  foo.c = 395482880  
  foo.d = 32764  

No comments:

Post a Comment