Чтение нарушения прав доступа для отображаемого в память вектора в режиме отладки

При попытке использовать boost::interprocess для хранения std::vector в файле отображения памяти я получаю исключение Exception thrown: read access violation., когда пытаюсь вернуть загруженный вектор, но только в режиме отладки.

Этот минимальный пример кода (написанный @sehe) получен из https://stackoverflow.com/a/29602884/2741329. , а на MSVC14 происходит сбой в режиме отладки и выполняется более одного раза:

#include <boost/interprocess/managed_mapped_file.hpp>

namespace bi = boost::interprocess;

int main() {
    std::string vecFile = "vector.dat";
    bi::managed_mapped_file file_vec(bi::open_or_create,vecFile.c_str(), 1000);

    typedef bi::allocator<int, bi::managed_mapped_file::segment_manager> int_alloc;
    typedef std::vector<int, int_alloc>  MyVec;

    MyVec * vecptr = file_vec.find_or_construct<MyVec>("myvector")(file_vec.get_segment_manager());

    vecptr->push_back(rand());
}

РЕДАКТИРОВАТЬ:

Это сообщение об ошибке Visual Studio:

введите здесь описание изображения

Здесь точка, где происходит исключение:

введите здесь описание изображения

Это стек вызовов (нажмите на картинку, чтобы увеличить):

введите здесь описание изображения


person gmas80    schedule 14.07.2018    source источник
comment
Было бы полезно показать трассировку стека при сбое.   -  person John Zwinck    schedule 14.07.2018
comment
@john-zwinck добавил больше подробностей о сбое.   -  person gmas80    schedule 14.07.2018
comment
Размещение типов шаблонов C++ std в разделяемой памяти — очень рискованная затея. У вас сильная зависимость от версии компилятора/библиотеки и настроек сборки. Простой способ вызвать этот сбой — создать код с настройками отладки с одной стороны и код с настройками выпуска — с другой. Он бомбит в коде библиотеки, реализующей функцию отладки итератора, изменяет макет объекта. Sayonara, когда данные были сгенерированы кодом, созданным с отключенной отладкой итератора. Будь проще.   -  person Hans Passant    schedule 14.07.2018
comment
@HansPassant, я не использую его для межпроцессного взаимодействия. Мой вариант использования - просто сохранить огромный вектор STL в файле с отображением памяти. (И меня устраивает необходимость отлаживать и выпускать сборки, которые не работают вместе.) Кстати, как вы предлагаете справиться с моим вариантом использования?   -  person gmas80    schedule 14.07.2018
comment
open_or_create довольно сбивает с толку. Это лучше всегда создавать или у вас будет плохой день. Если IPC не является намерением, истинное намерение становится довольно загадочным. Простой std::vector уже использует файл с отображением памяти без какой-либо помощи. Файл подкачки. Не делай этого.   -  person Hans Passant    schedule 14.07.2018
comment
@HansPassant, мое намерение состояло в том, чтобы воспользоваться механизмом отображения файлов в памяти для сохранения файлов. Другими словами, мне не нужно предоставлять отдельный механизм для сериализации/десериализации вектора для будущего использования. Имеет ли это смысл?   -  person gmas80    schedule 14.07.2018
comment
Хранить необработанный std::vector в файле не очень хорошая идея. Вы просто никогда не сможете надежно прочитать его, он содержит указатели, которые действительны только один раз и становятся мусором, когда процесс завершается. Он даже не содержит данных элемента, хранящихся в куче, а не в ммф. Вы можете сохранить простой T[] и иметь некоторую надежду, пока сам T не меняется и не хранит указатели.   -  person Hans Passant    schedule 14.07.2018
comment
Разве bi::allocator не управляет размещением данных элемента в ммф?   -  person gmas80    schedule 14.07.2018
comment
@HansPassant, при всем уважении, это распространение FUD. Я понимаю, что вы просто не знаете о библиотеке Boost Interprocess, но использование здесь совершенно нормально (у вас есть действительная интуиция в отношении гонок вокруг open_or_create, но это явно не проблема и не имеет отношения к вопросу). Для OP: я посмотрю на этот слой, когда буду рядом с компьютером. (Вы случайно не запускаете несколько копий одновременно?)   -  person sehe    schedule 14.07.2018
comment
@sehe, спасибо за разъяснение. Я также открыл более общий тикет об использовании контейнеров STL с boost::interprocess здесь: github.com/boostorg/ межпроцессный/проблемы/58 . (Я не запускаю несколько копий одновременно)   -  person gmas80    schedule 14.07.2018


Ответы (1)


В качестве мозговой волны отключите итераторы отладки MSVC.

Я не уверен, как (потому что итераторы не сохраняются?), но каким-то образом отладка итератора может добавить необработанные указатели внутри макета памяти std::vector, нарушая предположения стандартной библиотеки об использовании распределителя.

Результаты теста

Создавая виртуальную машину на Azure специально для этой цели, мы протестировали следующий слегка измененный код, чтобы лучше понять причины сбоя:

#include <boost/interprocess/managed_mapped_file.hpp>
#include <iostream>

namespace bi = boost::interprocess;

int main() {
    std::string vecFile = "vector.dat";
    //std::remove(vecFile.c_str());
    std::cout << __LINE__ << "\n";
    {
        bi::managed_mapped_file file_vec(bi::open_or_create, vecFile.c_str(), 100000);

        typedef bi::allocator<int, bi::managed_mapped_file::segment_manager> int_alloc;
        typedef std::vector<int, int_alloc>  MyVec;

        MyVec * vecptr = file_vec.find_or_construct<MyVec>("myvector")(file_vec.get_segment_manager());

        vecptr->push_back(rand());
        std::cout << "size: " << vecptr->size() << "\n";
    }
    std::cout << __LINE__ << "\n";
    {
        bi::managed_mapped_file file_vec(bi::open_or_create, vecFile.c_str(), 100000);

        typedef bi::allocator<int, bi::managed_mapped_file::segment_manager> int_alloc;
        typedef std::vector<int, int_alloc>  MyVec;

        MyVec * vecptr = file_vec.find_or_construct<MyVec>("myvector")(file_vec.get_segment_manager());

        vecptr->push_back(rand());
        std::cout << "size: " << vecptr->size() << "\n";
    }
    std::cout << __LINE__ << "\n";
}

Воспроизводит проблему. Первый забег:

введите здесь описание изображения

Последующий запуск (со строкой std::remove, закомментированной, как показано):

введите здесь описание изображения

ВРЕМЕННОЕ РЕШЕНИЕ ДЕМО

После размещения

#define _ITERATOR_DEBUG_LEVEL 0

в самом верху И УДАЛЕНИЕ файла vector.dat, поскольку это изменение изменяет двоичную структуру:

Примечание: в вашем реальном проекте вам может потребоваться поместить это #define в несколько единиц перевода (особенно обратите внимание на stdafx.cpp). Вероятно, гораздо лучше включить его в листы свойств проекта, чтобы он применялся ко всем (будущим) единицам перевода!

введите здесь описание изображения

person sehe    schedule 14.07.2018
comment
Я добавил #define _ITERATOR_DEBUG_LEVEL 0, но все еще получаю то же исключение и стек вызовов. - person gmas80; 14.07.2018
comment
Я совсем забыл, как все раздражает в Windows... i.imgur.com/HEF95FD.png Это занимает много времени - person sehe; 15.07.2018
comment
Я только что проверил это сам. Это действительно отладка итератора, и действительно размещение определения (во всех подходящих местах!) Устраняет проблему. Не забудьте удалить файл vector.dat между запусками, когда вы меняете флаги/конфигурацию сборки! - person sehe; 15.07.2018
comment
Исправлен ответ с фактическим доказательством, а также различными намеками на то, что может пойти не так и почему вам нужно удалить файл vector.dat. Для downvoter, признателен, если вы передумаете. - person sehe; 15.07.2018