Уничтожение общего std :: vector в другом процессе

Я пытаюсь передать std::vector<std::string> новому разветвленному процессу через Boost.Interprocess, чтобы дочерний процесс взял его на себя и уничтожил. Получение и чтение вектора работает, однако при его удалении возникает нарушение прав доступа. Я так понимаю, что распределитель запускает какое-то состояние с указателями, которые указывают на адресное пространство родителя и не имеют смысла в дочернем.

Как мне создать этот вектор в родительском элементе и правильно уничтожить в дочернем?

Код родительского процесса:

namespace ip = boost::interprocess;

template <class T>
using ip_allocator = ip::allocator<T, ip::managed_shared_memory::segment_manager>;

template <class T>
using ip_vector = std::vector<T, ip_allocator<T>>;

int CALLBACK WinMain(
    _In_ HINSTANCE,
    _In_ HINSTANCE,
    _In_ LPSTR,
    _In_ int
) {
    ip::shared_memory_object::remove("MyTestShm");
    ip::managed_shared_memory mshm{ ip::open_or_create, "MyTestShm", 1024 * 1024 };

    ip_allocator<int> intAlloc{ mshm.get_segment_manager() };

    mshm.construct<ip_vector<int>>("ForkData")(
        ip_vector<int>{ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, intAlloc}
    );

    boost::process::spawn(L"Executor.exe", L"ForkData");
}

Код дочернего процесса (Executor.exe):

int wmain(int argc, wchar_t **argv) try {
    namespace ip = boost::interprocess;

    // Kludge to attach the debugger and force continuation
    bool volatile coin = true;
    while (coin)
        ::Sleep(1000);

    ip::managed_shared_memory mshm{ ip::open_only, "MyTestShm" };
    auto const * const fd = mshm.find<ip_vector<int>>("ForkData").first;

    if (!fd)
        return 1;

    for (int i : *fd)
        std::cout << i << ' ';
    std::cout << '\n';

    mshm.destroy_ptr(fd); // Crashing line
}
catch (std::exception const &exc) {
    std::cerr
        << "Unhandled " << boost::typeindex::type_id_runtime(exc).pretty_name() << ":\n"
        << exc.what() << '\n';
    return 1;
}

Ошибка и стек вызовов:

Exception thrown: read access violation. _Pnext was 0x21FE21200B0. occurred

Executor.exe!std::_Container_base12::_Orphan_all() Line 221
Executor.exe!std::_Vector_alloc<std::_Vec_base_types<int,boost::interprocess::allocator<int,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index> > > >::_Orphan_all() Line 536
Executor.exe!std::vector<int,boost::interprocess::allocator<int,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index> > >::_Tidy() Line 1913
Executor.exe!std::vector<int,boost::interprocess::allocator<int,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index> > >::~vector<int,boost::interprocess::allocator<int,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index> > >() Line 894
Executor.exe!std::vector<int,boost::interprocess::allocator<int,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index> > >::`scalar deleting destructor'(unsigned int)   Executor.exe!boost::interprocess::ipcdetail::placement_destroy<std::vector<int,boost::interprocess::allocator<int,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index> > > >::destroy_n(void * mem, unsigned __int64 num, unsigned __int64 & destroyed) Line 61
Executor.exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>::priv_generic_named_destroy<char>(const char * name, boost::interprocess::iset_index<boost::interprocess::ipcdetail::index_config<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0> > > & index, boost::interprocess::ipcdetail::in_place_interface & table, boost::interprocess::ipcdetail::bool_<1> is_intrusive_index) Line 976
Executor.exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>::priv_generic_named_destroy<char>(boost::interprocess::ipcdetail::block_header<unsigned __int64> * block_header, boost::interprocess::iset_index<boost::interprocess::ipcdetail::index_config<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0> > > & index, boost::interprocess::ipcdetail::in_place_interface & table, boost::interprocess::ipcdetail::bool_<0> is_node_index) Line 929
Executor.exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>::priv_destroy_ptr(const void * ptr, boost::interprocess::ipcdetail::in_place_interface & dtor) Line 777
Executor.exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>::destroy_ptr<std::vector<int,boost::interprocess::allocator<int,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index> > > >(const std::vector<int,boost::interprocess::allocator<int,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index> > > * p) Line 550
Executor.exe!boost::interprocess::ipcdetail::basic_managed_memory_impl<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index,8>::destroy_ptr<std::vector<int,boost::interprocess::allocator<int,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index> > > >(const std::vector<int,boost::interprocess::allocator<int,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index> > > * ptr) Line 608
Executor.exe!wmain(int argc, wchar_t * * argv) Line 31
Executor.exe!invoke_main() Line 91
Executor.exe!__scrt_common_main_seh() Line 288
Executor.exe!__scrt_common_main() Line 331
Executor.exe!wmainCRTStartup() Line 17
kernel32.dll!BaseThreadInitThunk()
ntdll.dll!RtlUserThreadStart()

Можно ли использовать какой-нибудь обратный вызов, например: «Вы дали мне этот вектор? Отлично. Теперь удалите его для меня!»

TobiMcNamobi 08.11.2018 17:10

@TobiMcNamobi Нет, родительский процесс может завершиться еще до того, как дочерний процесс попадет в общую память. Мой фактический вариант использования - это расширение оболочки, поэтому я не могу контролировать время жизни родительского процесса.

Quentin 08.11.2018 17:14

Странно, но я бы предположил, что это точный вариант использования так называемого необычные указатели.

Passer By 08.11.2018 19:31

@PasserBy, следуя по следу, я оказался на этот пример, который в значительной степени является моим кодом (включая segment.destroy<MyVector>, который у меня был до destroy_ptr и тоже разбился). Думаю, завтра мне придется попробовать скомпилировать этот пример, чтобы посмотреть, что происходит ...

Quentin 08.11.2018 20:44
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
8
4
349
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Благодаря Прохожий, который побудил меня снова просмотреть документацию Boost.Interprocess, я наткнулся на этот пример, что, к счастью, именно то, что я пытаюсь сделать. Убедившись, что он работает с моей стороны (а это действительно так), я начал постепенно преобразовывать его в копию моей собственной программы и обнаружил проблему.

Разница заключалась в выборе std::vector или boost::interprocess::vector (также известного как boost::container::vector). std::vector должен нормально работать с настраиваемым распределителем, но проблема заключается в реализации Microsoft: _Orphan_all, функция, при которой происходит нарушение прав доступа, является частью функциональности Итераторы отладки. Эта система отслеживает, какой итератор принадлежит какому контейнеру, но делает это, сохраняя необработанные указатели, которые устаревают, когда вектор меняет адресные пространства и в конечном итоге вызывает сбой, когда вектор пытается обновить данные бухгалтерского учета после уничтожения.

Действительно, компиляция в режиме Release (где _ITERATOR_DEBUG_LEVEL == 0 и итераторы отладки отключены) или использование реализации Boost vector работают безупречно.

Другие вопросы по теме