LINUX.ORG.RU

Способ на vptr data race

 bad design, ,


0

5

Есть data race прям как в примере тут: https://github.com/google/sanitizers/wiki/ThreadSanitizerPopularDataRaces#dat...

<Для Ъ>:

struct Base {
    virtual ~Base() { 
        unregister(this); // внутри сложный код с синхронизацией
    }

    virtual void someMethod() = 0;
};

struct Derived : public Base {
    ~Derived() { simpleDestruction(); }
    void someMethod() override;
};

Проблема такова, что деструктор и someMethod() могут быть вызваны с разных потоков. А используемые в проекте реализации GCC/Clang меняют vptr при вызове родительских деструкторов. Соотв. если во время синхронизации в ~Base() будет вызван someMethod() получится pure virtual method call. (так как используемые компиляторы GCC).

</Для Ъ>

Было бы дешево и сердито решить проблему переносом синхронизации в деструктор дочернего класса, однако в реальности ситуация такова, что существует дерево из 6 потомков класса Base:

Base_______________
|        \         \
Derived1  Derived2  Derived3
|         |
Derived11 Derived21
|
Derived111
И каждый из потомков может использоваться самостоятельно. Т.е. если реальный тип обьекта Derived111, то синхронизация должна быть в ~Derived111 (и не происходить ни в ~Derived11, ни в Derived1, ни в ~Base). Однако если реальный тип — Derived11, то синхронизация должна происходить только в ~Derived11.

Подскажите пожалуйста как поправить проблему? Может существует какой-нибудь паттерн для такого?

Ответ на: комментарий от lovesan

у меня опыта в C++ побольше

Опыт опыту рознь. Можно 10 лет на С++ программить и не знать про shared_ptr.

rupert ★★★★★
()
Ответ на: комментарий от Iron_Bug

не лучше ли для начала попробовать поизучать плюсы, прежде чем что-то на них писать?

Не, и так норм ;)

А если серьезно, «зубрить» стандарт — так себе затея. Изучение на практике куда интереснее (и, как следствие, быстрее).

KennyMinigun ★★★★★
() автор топика
Последнее исправление: KennyMinigun (всего исправлений: 1)
Ответ на: комментарий от anonymous

Короче, у тебя не должен вызываться деструктор, пока есть ссылки на этот объект, которыми кто-то в дальнейшем воспользуется (например, для вызова someMethod()). Если не можешь продумать архитектуру, попробуй shared_ptr.

Ну формально я только исправляю баг, а код не мой. Написан местным сеньйором. А еще там страшная лапша в регистрации обьектов. Короче, пока что (наверняка перманентно) я подпер полу-костылем из моего коментария выше

KennyMinigun ★★★★★
() автор топика
Ответ на: комментарий от lovesan

У C++ нет области применения.

Да. Потому что это язык общего назначения.

utf8nowhere ★★★
()
Ответ на: комментарий от KennyMinigun

Изучение на практике куда интереснее (и, как следствие, быстрее).

Изучение плюсов на практике — это, как правило, укоренение в своих фантазиях о нём. Которые далеки от действительности.

anonymous
()
Ответ на: комментарий от lovesan

Я рад за тебя. Более чем уверен, что мой опыт C++ явно меньше, чем твой. Но это отнюдь не говорит о том, что я его хуже знаю.

Ну так вместо голословных утверждений «не нужен», «использовать кресты - ошибка», выдавайте, пожалуйста, аргументы и скажите, что стОит вместо крестов использовать.

zamazan4ik ★★
()
Ответ на: комментарий от anonymous

Если не можешь продумать архитектуру, попробуй shared_ptr.

Не могу придумать лучшего способа описать, зачем нужны shared_ptr-ы.

Kuzy ★★★
()
Ответ на: комментарий от zamazan4ik

что стОит вместо крестов использовать

Раст, конечно, даже спрашивать не надо. Кстати сегодня новая версия вышла, 1.20

pftBest ★★★★
()
Ответ на: комментарий от pftBest

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

zamazan4ik ★★
()
Ответ на: комментарий от pftBest

Кстати сегодня новая версия вышла, 1.20

И до сих пор нет новости на главной? Да как же так? Попкорн уже остывает... :)

eao197 ★★★★★
()

По хорошему ты должен обеспечить следующий последовательный порядок выполнения.

1) Создание объекта
2...N-1) Создание, выполнение и полное завершение тредов использующих объект
N) удаление объектов

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

Dudraug ★★★★★
()
Ответ на: комментарий от Dudraug

Просто ситуации когда у тебя вызывается деструктор, а тред юзающий этот объект еще не завершен не должно быть в принципе.

Это в идеале. А так даже у того же Williams-а в коде тредпулов из деструктора выставляется флаг done, а треды из пула крутятся в цикле в методе этого же объекта-пула и читают флаг. Формально — UB.

utf8nowhere ★★★
()
Ответ на: комментарий от KennyMinigun

Невладеющий поток должен использовать weak_ptr и его метод lock для получения shared_ptr, на котором он сможет спокойно вызывать методы, если lock не вернёт nullptr.

Хороший пример использования weak_ptr - идиома weak this.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.