What to do if the object is destroyed during the call in C++

Viewed 131

Here is my test code

class bar
{
public:
    explicit bar(int x) : num(x) {}
    int get_num()
    {
        return num;
    }

private:
    int num;
};

shared_ptr<bar> ptr_store;

void get_func()
{
    while (1)
        printf("get_num:%d\n", ptr_store->get_num());
};

void set_func()
{
    while (1)
        //ptr_store = make_shared<bar>(1);
        atomic_exchange(&ptr_store, make_shared<bar>(1));

}

int main()
{
    ptr_store = make_shared<bar>(-1);
    std::thread t1(get_func);
    std::thread t2(set_func);
    t1.join();
    t2.join();
}

I wonder why this program wouldn't make a core dump?

If set_func in t2 destroy the origin ptr_store when t1 is using ptr_store->get_num(), it could cause some fault? Is it Guaranteed by shared_ptr? Or it just a coincidence.

Test environment:
OS: Ubuntu 20.04 LTS
Clang: clang version 3.9.1
G++: gcc version 7.5.0 

update: I think there is no possibility that two threads access one object. atomic_exchange or reset can be considered an atomic operation. When setting the new object, the old object will not be changed.

2 Answers

I wonder why this program wouldn't make a core dump?

There is no such thing as "core dump" in the C++ standard. These words are not in the vocabulary. There is "undefined behaviour", and your program has it. There is a data race.

If multiple threads of execution access the same std::shared_ptr object without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur unless all such access is performed through these functions source.

atomic_exchange is OK but you need atomic_load in the other thread.

Note all of this is deprecated in C++20. Use std::atomic<std::shared_ptr<bar>> instead.

And now I fix my code by @n. 'pronouns' m.'s answer. It seems like work well now

#include <bits/stdc++.h>
#include <iostream>
#include <utility>
#include <thread>
#include <chrono>
#include <functional>
#include <atomic>
#include <unistd.h>
#include <bits/shared_ptr_atomic.h>

using namespace std;

class bar
{
public:
    explicit bar(vector<int> &in) : num(-1)
    {
        for (auto iter : in)
            v.push_back(iter);
    }
    int get_num()
    {
        for (auto iter : v)
        {
            num = max(iter, num);
        }
        if (num >= 100)
        {
            printf("Fault %d\n", num);
        }
        return num;
    }

private:
    int num;
    vector<int> v;
};

shared_ptr<bar> ptr_store;

int st;

void get_func()
{
    for (int k = 0; k <= 10000; k++)
    {
        auto temp = atomic_load_explicit(&ptr_store, std::memory_order_seq_cst);
        printf("Get_num %d\n", temp->get_num());
    }
}

void set_func()
{
    for (int k = 0; k <= 10000; k++)
    {
        vector<int> v;
        int sz = rand() % 100;
        for (int i = 0; i < sz; i++)
            v.push_back(rand() % 100);
        atomic_exchange(&ptr_store, make_shared<bar>(v));
    }
}

int main()
{
    srand((unsigned)time(NULL));
    vector<int> v = {1, 2, 3};
    ptr_store = make_shared<bar>(v);
    std::thread t1(get_func);
    std::thread t2(set_func);
    t1.join();
    t2.join();
}

// g++ atomic_exchange.cpp -o atomic_exchange -pthread && ./atomic_exchange > atomic_exchange.out
Related