61

In my destructor I want to destroy a thread cleanly.

My goal is to wait for a thread to finish executing and THEN destroy the thread.

The only thing I found about querying the state of a pthread is pthread_attr_setdetachstate but this only tells you if your thread is:

  • PTHREAD_CREATE_DETACHED
  • PTHREAD_CREATE_JOINABLE

Both of those have nothing to do with whether the thread is still running or not.

How do you query a pthread to see if it is still running?

2
  • Also see stackoverflow.com/questions/1693180/…
    – user470379
    Commented Jan 13, 2011 at 21:15
  • 2
    10 years old, but here it goes... what do you mean "wait for a thread to finish executing and then destroy the thread"? When a thread finishes executing it is destroyed.
    – papajony
    Commented May 12, 2020 at 8:41

7 Answers 7

48

It sounds like you have two questions here:

How can I wait until my thread completes?

Answer: This is directly supported by pthreads -- make your thread-to-be-stopped JOINABLE (when it is first started), and use pthread_join() to block your current thread until the thread-to-be-stopped is no longer running.


How can I tell if my thread is still running?

Answer: You can add a "thread_complete" flag to do the trick:

Scenario: Thread A wants to know if Thread B is still alive.

When Thread B is created, it is given a pointer to the "thread_complete" flag address. The "thread_complete" flag should be initialized to NOT_COMPLETED before the thread is created. Thread B's entry point function should immediately call pthread_cleanup_push() to push a "cleanup handler" which sets the "thread_complete" flag to COMPLETED.

See details about cleanup handlers here: pthread cleanup handlers

You'll want to include a corresponding pthread_cleanup_pop(1) call to ensure that the cleanup handler gets called no matter what (i.e. if the thread exits normally OR due to cancellation, etc.).

Then, Thread A can simply check the "thread_complete" flag to see if Thread B has exited yet.

NOTE: Your "thread_complete" flag should be declared "volatile" and should be an atomic type -- the GNU compilers provide the sig_atomic_t for this purpose. This allows the two threads consistent access the same data without the need for synchronization constructs (mutexes/semaphores).

5
  • 4
    The cleanup handlers are entirely unnecessary unless you will be using cancellation (which is generally a rather dangerous tool). Also, you might want to use a pthread condition variable along with the flag variable to enable waiting on it without spinning and burning cpu cycles. Commented Jan 13, 2011 at 1:09
  • If the goal is to know whether or not to call join to "destroy" a pthread_t handle, checking a flag won't work because the thread might not have terminated when you check the flag but still manage to terminate before it is joined (note that joining a thread that has been joined results in undefined behavior, and leaving a joinable thread unjoined results in a leak.)
    – Emil
    Commented Sep 29, 2015 at 5:28
  • To all, whom want to use the aforementioned solution, I have replied below with a problem with this solution. Read it before you apply into your solution. It might not be a problem in lots of cases, but you might want to know about it.
    – newhouse
    Commented Sep 26, 2016 at 11:25
  • @Emil There's no issue. Just make sure to call join once and only once. The race is harmless -- yes, your join might block for a tiny fraction of a second if you happen to check the flag after it's set and before the thread terminates, but the thread is about to terminate anyway. Commented Sep 26, 2016 at 11:30
  • In cases where you compute something and then signal thread_complete, so the computation result can be used, this is wrong. volatile sig_atomic_t can still be reordered, so it might be written before the computation result becomes visible and consumers can see an invalid state.
    – a3f
    Commented May 4, 2019 at 15:26
27
pthread_kill(tid, 0);

No signal is sent, but error checking is still performed so you can use that to check existence of tid.

CAUTION: This answer is incorrect. The standard specifically prohibits passing the ID of a thread whose lifetime has ended. That ID might now specify a different thread or, worse, it might refer to memory that has been freed, causing a crash.

4
  • 2
    For my problem, this was exactly what I needed to solve it, thanks. I used (pthread_kill(tid, 0) != ESRCH). Commented Oct 26, 2012 at 15:21
  • 2
    Please note this: stackoverflow.com/questions/1693180/… Commented Jun 13, 2015 at 14:12
  • 1
    @DavidSchwartz In such cases it is be better to mark your edit clearly as something that is not part of the original answer, as suggested here.
    – Dev-iL
    Commented Mar 20, 2017 at 21:43
  • Additional comment on David Schwartz (correct) edit - the manpage (Linux) says this: 'POSIX.1-2008 recommends that if an implementation detects the use of a thread ID after the end of its lifetime, pthread_kill() should return the error ESRCH. The glibc implementation returns this error in the cases where an invalid thread ID can be detected. But note also that POSIX says that an attempt to use a thread ID whose lifetime has ended produces undefined behavior, and an attempt to use an invalid thread ID in a call to pthread_kill() can, for example, cause a segmentation fault.' Commented Mar 27, 2019 at 8:47
11

I think all you really need is to call pthread_join(). That call won't return until the thread has exited.

If you only want to poll to see whether the thread is still running or not (and note that is usually not what you should be wanting to do!), you could have the thread set a volatile boolean to false just before it exits... then your main-thread could read the boolean and if it's still true, you know the thread is still running. (if it's false, on the other hand, you know the thread is at least almost gone; it may still be running cleanup code that occurs after it sets the boolean to false, though, so even in this case you should still call pthread_join before trying to free any resources the thread might have access to)

3
  • +1 for explaining the potential race condition involved and a simple solution to it. Commented Jan 13, 2011 at 1:10
  • 2
    "then your main-thread could read the boolean and if it's still true, you know the thread is still running." -- In this case, you would only know if the thread has cleanly exited or not, which is not the same as still running or not.
    – user470379
    Commented Jan 13, 2011 at 21:14
  • 2
    Hmm, I'm probably missing a possibility here, but AFAICT the thread is either still running, or it has exited, or it has crashed. And if the thread has crashed, it's taken the rest of the process with it, so there is no second thread still around to detect that anyway. Commented Feb 3, 2013 at 4:30
6

There is not fully portable solution, look if your platform supports pthread_tryjoin_np or pthread_timedjoin_np. So you just check if thread can be joined (of course created with PTHREAD_CREATE_JOINABLE).

2
  • 1
    These interfaces were rejected by POSIX, and should not be used in applications intending to be portable. The same thing can be accomplished portably with a condition variable. Commented Jan 13, 2011 at 1:11
  • 1
    Just because the same thing can be accomplished with a condition variable doesn't mean that the pthread_tryjoin_np isn't a cleaner interface. Commented Jan 9, 2012 at 20:40
4

Let me note on the "winning" answer, which has a huge hidden flaw, and in some contexts it can lead to crashes. Unless you use pthread_join, it will coming up again and again. Assume you are having a process and a shared library. Call the library lib.so.

  1. You dlopen it, you start a thread in it. Assume you don't want it join to it, so you set it detachable.
  2. Process and shared lib's logic doing its work, etc...
  3. You want to load out lib.so, because you don't need it any more.
  4. You call a shutdown on the thread and you say, that you want to read a flag afterwards from your lib.so's thread, that it have finished.
  5. You continue on another thread with dlclose, because you see, that you have saw, that the flag is now showing the thread as "finished"
  6. dlclose will load out all stack and code related memory.
  7. Whops, but dlclose does not stop threads. And you know, even when you are in the last line of the cleanup handler to set the "thread is finished" volatile atomic flag variable, you still have to return from a lot of methods on the stack, giving back values, etc. If a huge thread priority was given to #5+#6's thread, you will receive dlclose before you could REALLY stop on the thread. You will have some nice crashes sometimes.

Let me point out, that this is not a hipothetical problem, I had the same issue on our project.

0

I believe I've come up with a solution that at least works for Linux. Whenever I create a thread I have it save it's LWP (Light Weight Process ID) and assign it a unique name, eg. int lwp = syscall(SYS_gettid); prctl(PR_SET_NAME, (long)"unique name", 0, 0, 0);

Then, to check if the thread exists later I open /proc/pid/task/lwp/comm and read it. If the file exists and it's contents match the unique name I assigned, the thread exists. Note that this does NOT pass a possibly defunct/reused TID to any library function, so no crashes.

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <pthread.h>
#include <sys/prctl.h>
#include <sys/file.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <syscall.h>

pthread_t subthread_tid;
int       subthread_lwp;

#define UNIQUE_NAME "unique name"

bool thread_exists (pthread_t thread_id)
{
    char path[100];
    char thread_name[16];
    FILE *fp;
    bool  thread_exists = false;

    // If the /proc/<pid>/task/<lwp>/comm file exists and it's contents match the "unique name" the
    // thread exists, and it's the original thread (TID has NOT been reused).

    sprintf(path, "/proc/%d/task/%d/comm", getpid(), subthread_lwp);

    fp = fopen(path, "r");

    if( fp != NULL ) {

        fgets(thread_name, 16, fp);
        fclose(fp);

        // Need to trim off the newline
        thread_name[strlen(thread_name)-1] = '\0';

        if( strcmp(UNIQUE_NAME, thread_name) == 0 ) {
            thread_exists = true;
        }
    }

    if( thread_exists ) {
        printf("thread exists\n");
    } else {
        printf("thread does NOT exist\n");
    }

    return thread_exists;
}


void *subthread (void *unused)
{
    subthread_lwp = syscall(SYS_gettid);
    prctl(PR_SET_NAME, (long)UNIQUE_NAME, 0, 0, 0);

    sleep(10000);

    return NULL;
}


int main (int argc, char *argv[], char *envp[])
{
    int error_number;

    pthread_create(&subthread_tid, NULL, subthread, NULL);
    printf("pthread_create()\n");
    sleep(1);
    thread_exists(subthread_tid);

    pthread_cancel(subthread_tid);
    printf("pthread_cancel()\n");
    sleep(1);
    thread_exists(subthread_tid);

    error_number = pthread_join(subthread_tid, NULL);
    if( error_number == 0 ) {
        printf("pthread_join() successful\n");
    } else {
        printf("pthread_join() failed, %d\n", error_number);
    }
    thread_exists(subthread_tid);

    exit(0);
}
-2
#include <string.h>
#include <stdio.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>

void* thread1 (void* arg);
void* thread2 (void* arg);

int main()
{
    pthread_t thr_id;

    pthread_create(&thr_id, NULL, thread1, NULL);

    sleep(10);
}

void* thread1 (void* arg)
{
    pthread_t thr_id = 0;

    pthread_create(&thr_id, NULL, thread2, NULL);

    sleep(5);
    int ret = 0;
    if( (ret = pthread_kill(thr_id, 0)) == 0)
    {
        printf("still running\n");
        pthread_join(thr_id, NULL);
    }
    else
    {
        printf("RIP Thread = %d\n",ret);
    }
}

void* thread2 (void* arg)
{
//  sleep(5);
    printf("I am done\n");
}
2
  • 4
    While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value.
    – JAL
    Commented Oct 23, 2015 at 23:51
  • You might not be aware, but doing a pthread_kill on a dead thread could potentially crash your application or cause unexpected results like deallocating memory used on something else. stackoverflow.com/a/1693235/105539
    – Volomike
    Commented Apr 1, 2016 at 7:32

Not the answer you're looking for? Browse other questions tagged or ask your own question.