Question

When I try to create a class instance with a virtual method and pass it to pthread_create, I get a race condition, causing the caller to sometimes call the base method instead of the derived method like it should. After googling pthread vtable race, I found out this is fairly well-known behavior. My question is, what's a good way to get around it?

The code below exhibits this behavior at any optimization setting. Note that the MyThread object is completely constructed before being passed to pthread_create.

#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Thread {
    pthread_t thread;

    void start() {
        int s = pthread_create(&thread, NULL, callback, this);
        if (s) {
            fprintf(stderr, "pthread_create: %s\n", strerror(errno));
            exit(EXIT_FAILURE);
        }
    }
    static void *callback(void *ctx) {
        Thread *thread = static_cast<Thread*> (ctx);
        thread->routine();
        return NULL;
    }
    ~Thread() {
        pthread_join(thread, NULL);
    }

    virtual void routine() {
        puts("Base");
    }
};

struct MyThread : public Thread {
    virtual void routine() {

    }
};

int main() {
    const int count = 20;
    int loop = 1000;

    while (loop--) {
        MyThread *thread[count];
        int i;
        for (i=0; i<count; i++) {
            thread[i] = new MyThread;
            thread[i]->start();
        }
        for (i=0; i<count; i++)
            delete thread[i];
    }

    return 0;
}
Was it helpful?

Solution

The only problem here is that you are deleting the objects before the spawned thread executes the method, so by that time the child destructor already fired and the object is not there anymore.

So it has nothing to do with pthread_create or whatever, its your timing, you can't spawn a thread, give it some resources and delete them before he has the chance of using them.

Try this, it'll show how the objs are destructed by main thread before spawned thread uses them:

struct Thread {
pthread_t thread;
bool deleted;

void start() {
    deleted=false;
    int s = pthread_create(&thread, NULL, callback, this);
    if (s) {
            fprintf(stderr, "pthread_create: %s\n", strerror(errno));
            exit(EXIT_FAILURE);
    }
}
static void *callback(void *ctx) {
    Thread *thread = static_cast<Thread*> (ctx);
    thread->routine();
    return NULL;
}
~Thread() {
    pthread_join(thread, NULL);
}

virtual void routine() {
    if(deleted){
        puts("My child deleted me");
    }
    puts("Base");
}
};

struct MyThread : public Thread {
virtual void routine() {

}
~MyThread(){
    deleted=true;
}

};

In the other hand if you just place a sleep in main before deleting them you'll never have that problem because the spawned thread is using valid resources.

int main() {
const int count = 20;
int loop = 1000;

while (loop--) {
    MyThread *thread[count];
    int i;
    for (i=0; i<count; i++) {
            thread[i] = new MyThread;
            thread[i]->start();
    }
    sleep(1);
    for (i=0; i<count; i++)
            delete thread[i];
}

return 0;
}

OTHER TIPS

Do not do pthread_join (or any other real work) in destructor. Add join() method to Thread and call it before delete thread[i] in main.

If you try to call pthread_join in destructor, thread may be still executing Thread::routine(). Which means it's using object that is already partially destroyed. What will happen? Who knows? Hopefully program will crash quickly.


Additionally:

  • If you wish to inherit from Thread, Thread::~Thread should be declared virtual.

  • Check for all errors and handle them properly (which BTW cannot be done inside destructor).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top