Сбой при использовании concurrent_unordered_map - PullRequest
1 голос
/ 26 декабря 2011

У меня есть concurrent_unordered_map. Я использую функцию insert (и никакую другую), чтобы попытаться одновременно вставить карту. Тем не менее, часто это происходит глубоко во внутренних функциях insert. Вот некоторый код:

class ModuleBase { 
public:
    virtual Wide::Parser::AST* GetAST() = 0;
    virtual ~ModuleBase() {} 
};
struct ModuleContents {
    ModuleContents() {}
    ModuleContents(ModuleContents&& other)
        : access(other.access)
        , base(std::move(other.base)) {}
    Accessibility access;
    std::unique_ptr<ModuleBase> base;
};
class Module : public ModuleBase {
public:
    // Follows Single Static Assignment form. Once it's been written, do not write again. 
    Concurrency::samples::concurrent_unordered_map<Unicode::String, ModuleContents> contents;
    Wide::Parser::AST* GetAST() { return AST; }
    Wide::Parser::NamespaceAST* AST;
};

Это функция, которую я использую для вставки на карту. Существует больше, но это не касается карты, используется только возвращаемое значение insert.

void CollateModule(Parser::NamespaceAST* module, Module& root, Accessibility access_level) {
// Build the new module, then try to insert it. If it comes back as existing, then we discard. Else, it was inserted and we can process.
Module* new_module = nullptr;
ModuleContents m;
{
    if (module->dynamic) {
        auto dyn_mod = MakeUnique<DynamicModule>();
        dyn_mod->libname = module->libname->contents;
        new_module = dyn_mod.get();
        m.base = std::move(dyn_mod);     
    } else {
        auto mod = MakeUnique<Module>();
        new_module = mod.get();
        m.base = std::move(mod);
    }
    new_module->AST = module;
    m.access = access_level;
}
auto result = root.contents.insert(std::make_pair(module->name->name, std::move(m)));

Это корневая функция. Он вызывается параллельно из множества потоков на разных входах, но с одинаковым root.

void Collater::Context::operator()(Wide::Parser::NamespaceAST* input, Module& root) {
std::for_each(input->contents.begin(), input->contents.end(), [&](Wide::Parser::AST* ptr) {
    if (auto mod_ptr = dynamic_cast<Wide::Parser::NamespaceAST*>(ptr)) {
        CollateModule(mod_ptr, root, Accessibility::Public);
    }
});
}

Я не совсем уверен, что происходит WTF. У меня есть один бит общего состояния, и я получаю к нему доступ только атомарно, так почему мой код умирает?

Редактировать: Это на самом деле полностью моя вина. Сбой произошел в строке insert, что, как я предполагал, было проблемой, но это , а не . Это не было связано с параллелизмом вообще. Я проверил возвращаемое значение result неправильно - то есть true для value existed, false для value did not exist, тогда как Стандарт определяет true для insertion succeeded - то есть value did not exist. Это значительно испортило управление памятью, вызвав сбой - хотя, как именно это привело к сбою в коде unordered_map, я не знаю. Как только я вставил правильное отрицание, оно заработало без нареканий. Это было выявлено, потому что я не проверял однопоточную версию должным образом, прежде чем перепрыгнуть через параллельный забор.

1 Ответ

1 голос
/ 27 декабря 2011

Одна из возможностей - сбой из-за проблем с семантикой перемещения.Вызван ли сбой разыменованием нулевого указателя?Это произойдет, если вы случайно получили доступ к объекту (например, ModuleContents) после его перемещения.

Также возможно, что сбой является результатом ошибки параллелизма.concurrent_unordered_map является поточно-ориентированным в том смысле, что вставка и извлечение являются атомарными.Однако то, что вы храните внутри, не защищено автоматически.Поэтому, если несколько потоков извлекают один и тот же объект ModuleContents, они будут совместно использовать дерево AST, которое находится внутри Module.Я не уверен, какие ссылки могут быть изменены, так как я не вижу никаких const указателей или ссылок.Все, что является общим и изменяемым, должно быть защищено некоторым механизмом синхронизации (например, блокировками).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...