Вопросы относительно (не) летучих и оптимизирующих компиляторов - PullRequest
0 голосов
/ 03 мая 2020

У меня есть следующий C код:

/* the memory entry points to can be changed from another thread but 
 * it is not declared volatile */
struct myentry *entry;

bool isready(void)
{
    return entry->status == 1; 
}

bool isready2(int idx)
{
    struct myentry *x = entry + idx;        
    return x->status == 1; 
}

int main(void) {
    /* busy loop */
    while (!isready()) 
        ; 
    while (!isready2(5)) 
        ; 
}

Как я отмечаю в комментарии, запись не объявлена ​​как volatile, хотя массив, на который она указывает, может быть изменен из другого потока (или даже непосредственно из пространства ядра).

Является ли приведенный выше код неправильным / небезопасным? Я думаю, что оптимизация не может быть выполнена в теле isready, isready2, и, поскольку я неоднократно выполняю вызовы функций из основного, соответствующая область памяти должна читаться при каждом вызове.

С другой стороны, компилятор мог бы встроить эти функции. Возможно ли, что это происходит таким образом, что в результате происходит одно чтение (следовательно, вызывает бесконечное l oop) вместо нескольких чтений (даже если эти чтения происходят из буфера загрузки / сохранения)?

И второй вопрос. Можно ли помешать компилятору выполнять оптимизацию путем преобразования в volatile только в определенных местах, подобных этому?

void func(void)
{
    entry->status = 1;
    while (((volatile struct myentry *) entry)->status != 2)
        ;
}

Спасибо.

1 Ответ

2 голосов
/ 03 мая 2020

Если память, на которую указывает entry, может быть изменена другим потоком, то программа имеет гонку данных и, следовательно, поведение не определено. Это все еще верно, даже если используется volatile.

Чтобы иметь переменную, доступ к которой осуществляется одновременно несколькими потоками, в ISO C11 это должен быть либо atomi c тип , либо защищен правильной синхронизацией.

Если используется более старая редакция стандарта, то в Стандарте нет никаких гарантий относительно многопоточности, поэтому вы находитесь в зависимости от поведения c вашего компилятора.

При использовании потоков POSIX нет переносимых операций Atom c, но он определяет примитивы синхронизации.

См. Также:

Второй вопрос - ошибка, я бы посоветовал не делать этого, потому что разные компиляторы могут по-разному интерпретировать значение, а поведение все еще формально не определено в любом случае.

...