hookenz
hookenz

Reputation: 38897

How to detect early exit from pthread_create without blocking for too long?

I have a thread called mainloop

i.e.

int run_mainloop;

void* mainloop(void* param)
{
    // local vars
    // initialize local vars

    while(run_mainloop)
    {
       // run mainloop
    }

    return 0;
}

The thread is kicked off from a function called client_open, i.e.

int client_open()
{
    run_mainloop = 1;
    return pthread_create(&thread, NULL, mainloop, NULL);     
}

However, in mainloop if initializing local variables fails I need to inform client_open right away of early exit.

pthread_join is inappropriate as it will block and I can't have client_open block. If it was to wait a short time before returning that would be ok.

How could I do this in a nice way without using pthread_join which will block. I want to be able to get the return code.

Upvotes: 5

Views: 1891

Answers (3)

hookenz
hookenz

Reputation: 38897

Ok, I discovered three ways to do this.

1) Initialize and pass variables to mainloop before starting it.

2) Use the Linux specific pthread_tryjoin_np() or pthread_timedjoin_np() I think the timed join version is more appropriate in this case as it allows time for the thread to be created and for initialisation to be done. The timeout need not be long so it will not block the caller to client_open() for very long.

However, as pointed out by @fge they are non-portable. While that isn't too much of a problem I thought about an alternative way which is this.


EDIT: Not such a good solution, but left in here for reference. It'd be better to signal to open using a condition variable that initialization was ok.

3) Check if run_mainloop is non-zero, if it is and pthread_create didn't fail and the thread is running. If it's still zero after a time then it didn't start so we call pthread_join to get the exit code.

int run_mainloop = 0;

void* mainloop(void* param)
{
    // init vars
    // if failure, exit early.

    // everything from this point on is good.
    run_mainloop = 1;
    while (run_mainloop))
    {
        // do styff
    }

    return 0;
}

int client_open()
{
    void* res;
    int rc = pthread_create(&g_thread_id, NULL, mainloop, NULL);
    if (rc != 0)
        return -1;

    usleep(100); // wait a little while, kinda dumb but allows time for init
    if (run_mainloop))
        return 0;

    pthread_join(g_thread_id, &res);
    return -1;
}

Upvotes: -1

Maxim Egorushkin
Maxim Egorushkin

Reputation: 136256

You can use something known as completion variables.

Using which a thread can wait till a newly created thread has finished initialization. The only catch is that the new thread must always signal its initialization completion, even when initialization fails.

Something along the following lines (error handling omitted for clarity):

#include <pthread.h>

// Completion variable definition:    
typedef struct {
    pthread_mutex_t mtx;
    pthread_cond_t cnd;
    int completed;
    int return_code;
} Completion;

#define COMPLETION_INIT { PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0, 0 }

int completion_wait(Completion* c) { // add timeout if necessary
    pthread_mutex_lock(&c->mtx);
    while(!c->completed)
        pthread_cond_wait(&c->cnd, &c->mtx);
    int return_code = c->return_code;
    pthread_mutex_unlock(&c->mtx);
    return return_code;
}

void completion_signal(Completion* c, int return_code) {
    pthread_mutex_lock(&c->mtx);
    c->completed = 1;
    c->return_code = return_code;
    pthread_cond_signal(&c->cnd);
    pthread_mutex_unlock(&c->mtx);
}

// Usage:    

void* mainloop(void* vc) {
    int init_success = 0;
    // initialization
    // ...
    init_success = 1;

init_end:
    Completion* c = (Completion*)vc;
    completion_signal(c, init_success); // always signal
    if(!init_success)
        return NULL;

    // start the main loop
    return NULL;
}

int client_open()
{
    int run_mainloop = 1;
    pthread_t thread;
    Completion c = COMPLETION_INIT;
    pthread_create(&thread, NULL, mainloop, &c);
    pthread_detach(thread);
    return completion_wait(&c);
}

Upvotes: 4

Employed Russian
Employed Russian

Reputation: 213526

Using pthread_tryjoin_np would be incorrect: the new thread could be arbitrarily delayed between pthread_create return, and the new thread actually executing initialization code.

If you pthread_tryjoin_np during that delay, the join will fail and you will decide that everything is "a-ok", when in fact it isn't.

What you want is a condition: client_open will await on it, and the mainloop will signal it (upon being done with initialization).

Upvotes: 4

Related Questions