Leaving a Thread Early

Threads in your code go off and do their own thing, running independently from other parts of the code. Still, the thread is established as a function in your source code file. Like any function, it can leave early.

You can use a return statement to exit a thread early, but the official way to exit a thread is to use the pthread_exit() function:

void pthread_exit(void *value_ptr);

The value_ptr argument is a return value, specified as an address. To capture this value, you use the pthread_join() function, which was covered in last week’s Lesson. This function halts the main thread (or whatever thread it’s running in), waiting for the spawned thread to finish. It’s also the only way to capture the spawned thread’s return value.

The following code launches a thread to output asterisks. The pthread_join() function waits for the launched thread to finish — but the thread exits early after five asterisks are output.

2022_06_11-Lesson-a.c

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

void *thread_funct(void *a)
{
    const int loop=8;
    int x;

    for( x=0; x<loop; x++ )
    {
        if( x==5 )
            pthread_exit("I'm done!");
        printf("*\n");
        sleep(1);
    }

    return(NULL);
}

int main()
{
    char buffer[BUFSIZ];
    int r;
    pthread_t thd;
    void *retval;

    /* spawn the new thread */
    r = pthread_create( &thd, NULL, thread_funct, NULL);
    if( r!=0 )
    {
        perror("Thread");
        exit(1);
    }
    
    /* wait for the thread */
    r = pthread_join( thd, &retval );
    if( r!=0 )
    {
        perror("Thread join");
        exit(1);
    }
    printf("%s\n",(char *)retval);

    /* prompt for your name */
    printf("What is your name? ");
    fgets(buffer,BUFSIZ,stdin);
    printf("Hello, %s",buffer);

    return(0);
}

The pthread_join() function at Line 38 pauses the main thread until the spawned thread runs to completion. The function’s first argument is the thread handle, thd. The second argument is the address of a void pointer, retval. This is the argument’s proper data type, which handles any return value.

The launched thread exits early at Line 13 when the value of variable x is equal to 5. The pthread_exit() function returns the string "I'm done!", which is captured by pthread_join(). The printf() statement at Line 44 outputs the string, casting it from void to a char pointer. Then the rest of the main() function runs.

Here’s a sample run:

*
*
*
*
*
I'm done!
What is your name? Dan
Hello, Dan

If you want to return a value from the thread, say the integer x, you must declare it static. Then you return the value’s address. Here’s the modified version of the thread_funct() function that returns the value in x:

void *thread_funct(void *a)
{
    const int loop=8;
    static int x;

    for( x=0; x<loop; x++ )
    {
        if( x==5 )
            pthread_exit(&x);
        printf("*\n");
        sleep(1);
    }

    return(NULL);
}

The pthread_join() call in the main() function need not be modified, as it accepts a void pointer as its second argument. You must still, however, typecast the thread’s returned value (retval) when used in the main() function. Here’s the updated printf() statement:

printf("The thread returned value: %d\n",*((int *)retval) );

Click here to view the full code on GitHub.

So what happens when you replace pthread_exit() with a simple return statement?

As far as I can tell, the effect appears the same: pthread_exit("I'm done!") becomes return("I'm done!"), and pthread_exit(&x) becomes return(&x). Coders on Stack Overflow write that the Valgrind utility doesn’t report issues when return is used instead of pthread_exit(). I know of no further scintillating details on the differences.

In next week’s Lesson I wrap up my thread on multithreading with coverage of the pthread_cancel() function.

Leave a Reply