Предотвращение взаимоблокировки в реентерабельном коде C++11

Я работаю над рефакторингом некоторого устаревшего кода, который страдает от взаимоблокировок. Есть две основные первопричины:

1) один и тот же поток несколько раз блокирует один и тот же мьютекс, что нетрудно разрешить, и 2) код время от времени вызывает определенные пользователем функции, которые могут вводить один и тот же код на верхнем уровне. Мне нужно заблокировать мьютекс перед вызовом пользовательских функций, но я могу снова выполнить тот же код, что приведет к тупиковой ситуации. Итак, мне нужен какой-то механизм, чтобы сказать мне, что мьютекс уже заблокирован, и я не должен блокировать его снова. Какие-либо предложения?

Вот (очень) краткое изложение того, что делает код:

class TreeNode {
public:
    // Assign a new value to this tree node
    void set(const boost::any& value, boost::function<void, const TreeNode&> validator) {
      boost::upgrade_lock<boost::shared_mutex> lock(mutexToTree_);

      //  call validator here       

      boost::upgrade_to_unique_lock<boost::shared_mutex> ulock(lock);

      // set this TreeNode to value
    }

    // Retrieve the value of this tree node
    boost::any get() {
      boost::shared_lock<boost::shared_mutex> lock(mutexToTree_);

      // get value for this tree node
    }
private:
    static boost::shared_mutex mutexToRoot_;
};

Проблема в том, что функция валидатора может вызывать get(), что блокирует mutexToRoot_ в том же потоке. Я мог бы изменить mutexToRoot_, чтобы он был рекурсивным мьютексом, но это помешало бы другим потокам читать дерево во время операции get(), что является нежелательным поведением.


person user3464675    schedule 09.08.2017    source источник
comment
Измените свое сообщение, включив в него минимально воспроизводимый пример соответствующего кода.   -  person perigon    schedule 09.08.2017
comment
Нам нужен код, чтобы понять последовательность этих потоков и мьютексов. Этот случай о том, как другой человек запрограммировал этот код и использовал мьютекс для синхронизации потоков. Мьютекс полезен, но мы должны быть осторожны, чтобы не создать смертельную блокировку.   -  person Jorge Omar Medra    schedule 10.08.2017


Ответы (1)


Начиная с C++11 вы можете использовать std::recursive_mutex, что позволяет потоку-владельцу вызовите lock или try_lock без блокировки/сообщения об ошибке, тогда как другие потоки будут блокироваться в lock/получать false в try_lock до тех пор, пока поток-владелец не вызовет unlock столько раз, сколько он вызывал lock/try_lock до этого.

person Dev Null    schedule 09.08.2017
comment
Я думал об использовании рекурсивного мьютекса, но это означало бы, что мне нужно было бы использовать его везде, даже в тех местах, где это неуместно. Я добавил код, который, надеюсь, поможет проиллюстрировать ситуацию. - person user3464675; 11.08.2017