Function that can spuriously wake up not wrapped in loop

Цикл проверяет условие пробуждения после возможного ложного пробуждения

Описание

Этот дефект возникает, когда следующие функции ожидания вызываются из-за цикла:

  • Функции C:

    • cnd_wait()

    • cnd_timedwait()

  • Функции POSIX:

    • pthread_cond_wait()

    • pthread_cond_timedwait()

  • C++ std::condition_variable и std::condition_variable_any функции члена класса:

    • wait()

    • wait_until()

    • wait_for()

Функции ожидания зависят от выполнения вызывающего потока при выполнении заданного условия. Поток просыпается и возобновляется, когда другой поток уведомляет его с cnd_broadcast() или эквивалентную функцию. Уведомление о пробуждении может быть ложным или вредоносным.

Риск

Если поток получает ложное уведомление о пробуждении и условие функции ожидания не проверяется, поток может проснуться преждевременно. Пробуждение может вызвать неожиданный поток управления, неопределенную блокировку других потоков или отказ в обслуживании.

Зафиксировать

Оберните функции ожидания при условии, которые могут ложно проснуться в цикле. Цикл проверяет условие пробуждения после возможного ложного уведомления о пробуждении.

Примеры

расширить все

#include <stdio.h>
#include <stddef.h>
#include <threads.h>

#define THRESHOLD 100

static mtx_t lock;
static cnd_t cond;

void func(int input)
{
    if (thrd_success != mtx_lock(&lock)) {
        /* Handle error */
    }
    /* test condition to pause thread */
    if (input > THRESHOLD) {
        if (thrd_success != cnd_wait(&cond, &lock)) {
            /* Handle error */
        }
    }
    /* Proceed if condition to pause does not hold */


    if (thrd_success != mtx_unlock(&lock)) {
        /* Handle error */
    }
}

В этом примере поток использует cnd_wait() для паузы выполнения при input больше THRESHOLD. Приостановленный поток может возобновиться, если другой поток использует cnd_broadcast(), который уведомляет все потоки. Это уведомление заставляет поток просыпаться, даже если условие паузы все еще соответствует true.

Коррекция - Обернуть cnd_wait() в while Цикл

Одной из возможных коррекций является обертывание cnd_wait() в while цикл. Цикл проверяет условие паузы после получения потоком возможного ложного уведомления о пробуждении.

#include <stdio.h>
#include <stddef.h>
#include <threads.h>

#define THRESHOLD 100

static mtx_t lock;
static cnd_t cond;

void func(int input)
{
    if (thrd_success != mtx_lock(&lock)) {
        /* Handle error */
    }
    /* test condition to pause thread */
    while (input > THRESHOLD) {
        if (thrd_success != cnd_wait(&cond, &lock)) {
            /* Handle error */
        }
    }
    /* Proceed if condition to pause does not hold */


    if (thrd_success != mtx_unlock(&lock)) {
        /* Handle error */
    }
}
 

Информация о результатах

Группа: Параллелизм
Язык: C | C++
По умолчанию: Off
Синтаксис командной строки : SPURIOUS_WAKEUP_NOT_WRAPPED_IN_LOOP
Влияние: Низкое
Введенный в R2018b