Спецификация std :: shared_ptr гарантирует, что только один поток вызовет удаление внутреннего указателя.Этот ответ содержит действительно приятное объяснение о порядке упорядочения памяти при манипулировании счетчиком refrence для shared_ptr, чтобы гарантировать, что удаление будет вызвано в синхронизированной памяти.
Что я не делаюпонимать следующее:
- Если shared_ptr инициализируется конструктором копирования, гарантируется ли, что он будет пустым или действительным shared_ptr?
Я ищув MVCC реализация конструктора копирования shared_ptr.и я думаю, что смогу идентифицировать хотя бы одно состояние гонки.
template<class _Ty2>
void _Copy_construct_from(const shared_ptr<_Ty2>& _Other)
{ // implement shared_ptr's (converting) copy ctor
if (_Other._Rep)
{
_Other._Rep->_Incref();
}
_Ptr = _Other._Ptr;
_Rep = _Other._Rep;
}
Реализация проверяет, что блок управления является действительным, затем увеличивает его счетчик ссылок и копирует, присваивает внутренние поля.
Предполагая, что _Other
принадлежит другому потоку, отличному от того, который вызывает конструктор копирования.Если между строками if (_Other._Rep)
и _Other._Rep->_Incref();
этот поток вызывает деструктор, который удаляет блок управления и указатель, то _Other._Rep->_Incref()
разыменует удаленный указатель.
Дальнейшее уточнение
Вот код, который иллюстрирует угловой случай, о котором я говорю.Я настрою реализацию конструктора копирования share_ptr для имитации переключения контекста:
template<class _Ty2>
void _Copy_construct_from(const shared_ptr<_Ty2>& _Other)
{ // implement shared_ptr's (converting) copy ctor
if (_Other._Rep)
{
// now lets put here a really long loop or sleep to simulate a context switch
int count = 0;
for (int i = 0; i < 99999999; ++i)
{
for (int j = 0; j < 99999999; ++j)
{
count++;
}
}
// by the time we get here, the owning thread may already destroy the shared_ptr that was passed to this constructor
_Other._Rep->_Incref();
}
_Ptr = _Other._Ptr;
_Rep = _Other._Rep;
}
А вот код, который, вероятно, покажет проблему:
int main()
{
{
std::shared_ptr<int> sh1 = std::make_shared<int>(123);
auto lambda = [&]()
{
auto sh2 = sh1;
std::cout << sh2.use_count(); // this prints garbage, -572662306 in my case
};
std::thread t1(lambda);
t1.detach();
// main thread destroys the shared_ptr
// background thread probably did not yet finished executing the copy constructor
}
Sleep(10000);
}