exponenta event banner

Функция, которая может ложно проснуться, но не обернута в цикл

Шлейф проверяет состояние пробуждения после возможного ложного пробуждения

Описание

Этот дефект возникает при вызове следующих функций ожидания состояния вне цикла:

  • Функции 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(), который уведомляет все потоки. Это уведомление вызывает пробуждение потока даже в том случае, если условие паузы остается верным.

Коррекция - перенос 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++
По умолчанию: Откл.
Синтаксис командной строки: SPURIOUS_WAKEUP_NOT_WRAPPED_IN_LOOP
Воздействие: Низкий
Представлен в R2018b