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