28

I create a thread and I put it into an infinite loop. I get memory leaks when checking the code with valgrind. Here is my code:

#include <pthread.h>
#include <time.h>

void thread_do(void){
    while(1){}
}

int main(){
    pthread_t th;   
    pthread_create(&th, NULL, (void *)thread_do, NULL);

    sleep(2);
    /* I want to kill thread here */
    sleep(2);
    return 0;
}

So a thread is created in main and just runs thread_do() all the time. Is there a way to kill it from inside main after 2 seconds? I have tried both pthread_detach(th) and pthread_cancel(th) but I still get leaks.

Pithikos
  • 18,827
  • 15
  • 113
  • 136
  • 3
    Cancelling pthreads means the thread has no chance to clean up any memory it allocated. – Lily Ballard Oct 31 '11 at 23:44
  • 2
    Ok I solved this by just having a global `keepalive=1`. Then I changed the `while(1)` to `while(keepalive)`. To kill the thread now I just have to change the value of variable keepalive to 0 and voila! – Pithikos Nov 03 '11 at 14:08

2 Answers2

42

As @sarnold pointed out, by default your thread can't be cancelled with pthread_cancel() without calling any functions that are cancellation points... but this can be changed by using pthread_setcanceltype() to set the thread's cancellation type to asynchronous instead of deferred. To do that, you'd add something like pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL); near the start of your thread function, before you start the loop. You would then be able to terminate the thread by calling pthread_cancel(th) from main().

Note, though, that cancelling threads this way (whether asynchronous or not) doesn't clean up any resources allocated in the thread function (as noted by Kevin in a comment). In order to do this cleanly, you can:

  • Ensure that the thread doesn't do anything it needs to clean up before exit (e.g. using malloc() to allocate a buffer)
  • Ensure that you have some way of cleaning up after the thread elsewhere, after the thread exits
  • Use pthread_cleanup_push() and pthread_cleanup_pop() to add cleanup handlers to clean up resources when the thread is cancelled. Note that this is still risky if the cancellation type is asynchronous, because the thread could be cancelled between allocating a resource and adding the cleanup handler.
  • Avoid using pthread_cancel() and have the thread check some condition to determine when to terminate (which would be checked in long-running loops). Since your thread then checks for termination itself, it can do whatever cleanup it needs to after the check.

One way of implementing the last option is to use a mutex as a flag, and test it with pthread_mutex_trylock() wrapped in a function to use in the loop tests:

#include <pthread.h>
#include <unistd.h>
#include <errno.h>

/* Returns 1 (true) if the mutex is unlocked, which is the
 * thread's signal to terminate. 
 */
int needQuit(pthread_mutex_t *mtx)
{
  switch(pthread_mutex_trylock(mtx)) {
    case 0: /* if we got the lock, unlock and return 1 (true) */
      pthread_mutex_unlock(mtx);
      return 1;
    case EBUSY: /* return 0 (false) if the mutex was locked */
      return 0;
  }
  return 1;
}

/* Thread function, containing a loop that's infinite except that it checks for
 * termination with needQuit() 
 */
void *thread_do(void *arg)
{
  pthread_mutex_t *mx = arg;
  while( !needQuit(mx) ) {}
  return NULL;
}

int main(int argc, char *argv[])
{
  pthread_t th;
  pthread_mutex_t mxq; /* mutex used as quit flag */

  /* init and lock the mutex before creating the thread.  As long as the
     mutex stays locked, the thread should keep running.  A pointer to the
     mutex is passed as the argument to the thread function. */
  pthread_mutex_init(&mxq,NULL);
  pthread_mutex_lock(&mxq);
  pthread_create(&th,NULL,thread_do,&mxq);

  sleep(2);

  /* unlock mxq to tell the thread to terminate, then join the thread */
  pthread_mutex_unlock(&mxq); 
  pthread_join(th,NULL);

  sleep(2);
  return 0;
}

If the thread is not detached (it generally isn't by default), you should call pthread_join() after stopping the thread. If the thread is detached, you don't need to join it, but you won't know exactly when it terminates (or even approximately, unless you add another way to indicate its exit).

Dmitri
  • 9,175
  • 2
  • 27
  • 34
  • to use this mutex solution for multiple pthread I would need one mutex per thread, right? would you recommend this solution in this case? – Ricardo Crudo May 13 '16 at 13:04
  • 2
    @RicardoCrudo No, just one mutex... if `needQuit()` successfully locks it, it unlocks it right after... only `main()` holds the mutex for an extended period of time. Any threads using `needQuit()` to check whether to quit will quit once nothing else has the mutex locked, and they will only lock it themselves for a (very very brief) moment. Only one `needQuit()` call will succeed at a time, but each thread will still get a successful call one after another. – Dmitri May 13 '16 at 13:33
  • is async cancellation supposed to work when waiting in `pthread_cond_wait(&c, &mtx);` in threadFunc , i tried without success – droid192 Jun 25 '18 at 12:01
6

A few small thoughts:

  1. You're trying to cancel your thread, but if the cancellation policy in place is for a deferred cancellation, your thread_do() will never be canceled, because it never calls any functions that are cancellation points:
    A thread's cancellation type, determined by
    pthread_setcanceltype(3), may be either asynchronous or
    deferred (the default for new threads).  Asynchronous
    cancelability means that the thread can be canceled at any
    time (usually immediately, but the system does not guarantee
    this).  Deferred cancelability means that cancellation will
    be delayed until the thread next calls a function that is a
    cancellation point.  A list of functions that are or may be
    cancellation points is provided in pthreads(7).
  1. You're not joining the thread in your simple example code; call pthread_join(3) before the end of your program:
    After a canceled thread has terminated, a join with that
    thread using pthread_join(3) obtains PTHREAD_CANCELED as the
    thread's exit status.  (Joining with a thread is the only way
    to know that cancellation has completed.)
ggorlen
  • 44,755
  • 7
  • 76
  • 106
sarnold
  • 102,305
  • 22
  • 181
  • 238