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

Коррекция — переносит 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