Доступ к кортежу кортежей указателей и мьютексов потокобезопасный

Учитывая std::tuple,

using Tuple1 = std::tuple<Foo1*, Bar1*, std::shared_ptr<std::mutex>>;
using Tuple2 = std::tuple<Foo2*, Bar2*, std::shared_ptr<std::mutex>>;
std::tuple<Tuple1, Tuple2> tuple;

И функция,

void baz()
{
    auto tup = std::get<0>(tuple);

    std::lock_guard<std::mutex> lk(*std::get<2>(tup));

    // Do something with std::get<0>(tup) and std::get<1>(tup)
}

В соответствии с этот вопрос о доступе SO к std::tuple по своей сути не потокобезопасен, но как насчет примера кода ? Возможны ли неопределенные/странные вещи?

Это предполагает, что FooN и BarN доступны только после блокировки.


person Babar Shariff    schedule 13.06.2017    source источник
comment
std::get не изменяет состояние tuple, я не могу дать вам гарантию, но я почти уверен, что он будет работать так, как если бы вы обращались к базовому объекту напрямую, что означает, что если базовый объект является потокобезопасным, операция будет (практически) потокобезопасной.   -  person Gizmo    schedule 13.06.2017


Ответы (1)


Цитата из идеального ответа на вопрос, который вы связали:

Однако, если бы параметр был константным, то get не считался бы вызывающим гонку данных с другими константными вызовами get.

Это в основном ваш ответ. Выполняйте каждый и каждый вызов get (для любого кортежа, который не полностью защищен мьютексом) для кортежа const, и вы в безопасности.

Это означает, что опубликованный вами код небезопасен. Измените так:

void baz()
{
    //    vvvv just being explicit here
    auto const & tup = std::get<0>(static_cast<decltype(tuple) const &>(tuple));

    std::lock_guard<std::mutex> lk(*std::get<2>(tup));

    // Dereference std::get<0>(tup) and std::get<1>(tup), 
    // use the pointed to objects at will, nothing else

    // Not ok, because it could interfer with the call in initialisation of tup of another thread
    // auto non_const_tup = std::get<0>(tuple)
}

В настоящее время единственным решением, которое я вижу, является использование кортежа, например:

std::tuple<
    std::shared_pointer<std::mutex>,
    std::unique_pointer<std::tuple<Foo1*, Bar1*>>
    // Mutex and pointer to tuple for Foo2 and Bar2
    >

Требуемый const будет прилипать ко всему (кроме целей указателя).

person Daniel Jour    schedule 13.06.2017
comment
Если я правильно понимаю, указатели intup не могут быть изменены, и для их изменения мне нужно будет повторно получить их кортеж после блокировки? Например. auto tupMutable = std::get<0>(tuple). - person Babar Shariff; 13.06.2017
comment
Нет. Вы по-прежнему можете иметь одновременный доступ к tuple. Добавлю решение. - person Daniel Jour; 13.06.2017
comment
Решение, которое я имел в виду, оказалось неверным. Я думаю, вам не нужны указатели на tupleN членов? - person Daniel Jour; 13.06.2017
comment
По сути, тело функции должно иметь возможность изменять значения указателя дочерних кортежей. - person Babar Shariff; 13.06.2017