简体   繁体   中英

Data race? segfault, but where is the issue?

There following simple program crashes occasionally, but I don't understand what can be worong with it?

It's compiled with '-pthread -std=c++11 -g -O2 -pie -fpie -std=c++11' valgrind drd reports a data race, but I can't see why.

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

bool running;
pthread_rwlock_t _rwlock;

class Dummy {
public:
    Dummy() : _refs(0) {
        Ref();
    }
    volatile int _refs;

    void Ref() {
        ++_refs;
    }
    void Unref() {
        --_refs;
        if (_refs <= 0) {
            delete this;
        }
    }
};

static Dummy* s_dummy;

Dummy* get_dummy() {
    pthread_rwlock_rdlock(&_rwlock);
    Dummy* ret = s_dummy;
    ret->Ref();
    pthread_rwlock_unlock(&_rwlock);
    return ret;
}

void *work1(void*) {
    while (running) {
        Dummy* new_dummy = new Dummy();
        pthread_rwlock_wrlock(&_rwlock);
        Dummy* to_del = s_dummy;
        s_dummy = new_dummy;
        pthread_rwlock_unlock(&_rwlock);
        to_del->Unref();
    }
}

void *work2(void*) {
    while (running) {
        Dummy* p = get_dummy();
        p->Unref();
    }
}

int main() {
    running = true;
    pthread_rwlock_init(&_rwlock, NULL);
    s_dummy = new Dummy();
    pthread_t threads[2];

    threads[0] = pthread_create(&threads[0], NULL, work1, NULL);
    threads[0] = pthread_create(&threads[1], NULL, work2, NULL);

    sleep(30);
    running = false;

    void* ret;
    for (int i = 0; i < 2; ++i) {
        pthread_join(threads[i], &ret);
    }

    return 0;
}

I can't speak for the exact message you're getting since you didn't add it, however you at least have a data race on _refs and it may cause a double delete .

As an example, both threads can be inside Unref on the same object at the same time with _refs initially == 2.

Let's say both threads run --_refs , the value of _refs will then be 0. Then both threads check if refs is zero, and since _refs is volatile they both read the value 0 from memory and both delete.

What you probably want for _refs is an atomic variable, not a volatile.

The two unrefs in work1 and work2 can conflict. There is nothing stopping a delete from happening in both threads simultaneously.

Also, you should make running volatile, or better yet, atomic.

Finally, it seems an aweful lot of work for something that could trivially be solved using a shared_ptr. The code below is equivalent to yours:

#include <atomic>
#include <memory>
#include <thread>

class Dummy {
};

std::atomic<bool> running = true;
static std::shared_ptr<Dummy> s_dummy = std::make_shared<Dummy> ();

void work1 () {
    while (running)
        s_dummy = std::make_shared<Dummy> ();
}

void work2 () {
    while (running) 
        s_dummy = nullptr;
}

int main() {
    std::thread t1 (work1);
    std::thread t2 (work2);

    sleep (30);
    running = false;

    t1.join ();
    t2.join ();

    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM