ПроблемаФункция, которая может побочно проснуться не перенесенный в цикл, происходит, когда следующие функции ожидания при условии вызваны снаружи цикла:
Функции ожидания при условии приостанавливают выполнение вызывающего потока, когда заданное условие соблюдают. Поток просыпается и возобновляется, если другой поток уведомляет его с cnd_broadcast()
или эквивалентная функция. Уведомление пробуждения может быть побочным или злонамеренным.
РискЕсли поток получает побочное уведомление пробуждения, и условие функции ожидания при условии не проверяется, поток может проснуться преждевременно. Пробуждение может вызвать неожиданный поток управления, неопределенное блокирование других потоков или отказ в обслуживании.
ФиксацияПеренесите функции ожидания при условии, которые могут проснуться побочно в цикле. Цикл проверяет условие пробуждения после возможного побочного уведомления пробуждения.
Пример - std::condition_variable::wait
Не перенесенный в цикл#include <stdio.h>
#include <stddef.h>
#include <thread>
#include <mutex>
#define THRESHOLD 100
std::mutex myMutex;
std::condition_variable cv;
void func(int input)
{
std::unique_lock<std::mutex> lk(myMutex);
// test condition to pause thread
if (input > THRESHOLD) {
//pause current thread
cv.wait(lk);//Noncompliant
}
}
В этом примере поток использует std::condition_variable::wait
приостановить выполнение когда input
больше THRESHOLD
. Приостановленный поток может возобновиться, использует ли другой поток std::condition_variable::notify_all
, который уведомляет все потоки. Это уведомление заставляет поток просыпаться, даже если условие паузы все еще верно.
Коррекция — переносит std::condition_variable::wait
в while
Циклично выполнитесь явным образомОдна возможная коррекция должна перенести вызов std::condition_variable::wait
в while
цикл. Цикл проверяет условие паузы после того, как поток получит возможное побочное уведомление пробуждения.
#include <stdio.h>
#include <stddef.h>
#include <thread>
#include <mutex>
#define THRESHOLD 100
std::mutex myMutex;
std::condition_variable cv;
void func(int input)
{
std::unique_lock<std::mutex> lk(myMutex);
// test condition to pause thread
while (input > THRESHOLD) {
//pause current thread
cv.wait(lk);
}
}
Коррекция — переносит std::condition_variable::wait
в цикле неявноstd::condition_variable::wait
функция имеет перегрузку, которая принимает функцию lambda как второй аргумент. Предикат функции Lambda указывает, когда безопасно прекратить ожидать и возобновлять выполнение кода. Эта перегрузка std::condition_variable::wait
функция ведет себя, как будто она неявно перенесена в цикл. В этом коде, functionstd::condition_variable::wait
вызывается при помощи функции Lambda. Здесь, нежелательное пробуждение потока предотвращено, потому что поток просыпается, когда предикат функции Lambda верен.
#include <stdio.h>
#include <stdio.h>
#include <stddef.h>
#include <thread>
#include <mutex>
#define THRESHOLD 100
std::mutex myMutex;
std::condition_variable cv;
void func(int input)
{
std::unique_lock<std::mutex> lk(myMutex);
cv.wait(lk,[&input]{ return !(input>THRESHOLD); });
}