атомарный обмен std::unique_ptr

#include <iostream>
#include <functional>
#include <atomic>
#include <thread>

class Foo
{
public:
    void Do()
    {
        std::cout << std::hex << "Hello from: " << this << std::endl;
    } 
};

std::unique_ptr<Foo> gPtr;

std::atomic_bool flag{ true };

void Poll()
{
    while (flag)
    {
        gPtr->Do();
    }
}

int main()
{
    gPtr = std::make_unique<Foo>();

    std::thread th{ Poll };

    std::this_thread::sleep_for(std::chrono::milliseconds(1));

    std::unique_ptr<Foo> lPtr = std::make_unique<Foo>();

    // TODO: atomic swap std::unique_ptr
    gPtr.swap(lPtr);

    std::this_thread::sleep_for(std::chrono::milliseconds(1));

    flag = false;

    if (th.joinable())
    {
        th.join();
    }

    return 0;
}

Output:

Hello from: 00000252F1FFE410
...
Hello from: 00000252F1FF60F0
...

Имеем примерно такую ситуацию, есть указатель, который активно используется в нескольких потоках (в примере он глобальный, т.к. для примера сойдёт). Потом создаётся новый указатель, как безопасно зменить старое значение указателя в std::unique_ptr на новое, чтобы не получить ub?

Сам по себе указатель это число, которое можно атомарно заменить, если бы оно хранилось в std::atomic<std::uintptr_t>, но хочется сохранить удобства от std::unique_ptr


Ответы (1 шт):

Автор решения: user7860670

Никак, атомарность там не подразумевается. Более того, прежде чем что-то менять, необходимо произвести синхронизацию потоков, чтобы исключить ситуацию, когда объект разрушается в момент, когда его метод еще вызывается в одном из потоков. Т.е. атомарной замены указателей не достаточно, чтобы сделать такой код корректным.

→ Ссылка