C99: ограниченные указатели для документирования безопасности потоков? - PullRequest
5 голосов
/ 16 июля 2011

Этот вопрос не о техническом использовании с ограничениями, а о субъективном использовании.Хотя я могу ошибаться в отношении того, как технически работает ограничение, в этом случае вы можете смело критиковать меня за то, что я основал вопрос на ложной предпосылке.

Вот два примера того, как я пока использую ограничение:

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

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

Я не много кодирую C,Я легко могу ошибаться в том, что я здесь предполагаю.Это правильное использование restrict?Стоит ли делать в этом сценарии?

Я также предполагаю, что когда ограниченный указатель выталкивается из стека при возврате функции, что к данным можно затем снова получить свободный доступ через любой другой указатель, и чтоограничение действует только до тех пор, пока ограниченный указатель.Я знаю, что это зависит от кодера, следующего правилам, поскольку доступ к ограниченным данным через «неофициальный» указатель является UB.

Я все понял правильно?

РЕДАКТИРОВАТЬ:

Я просто хотел бы прояснить, что я уже знаю, что он абсолютно ничего не препятствует доступу пользователей к данным через несколько потоков, и я также знаю, что C89 не знает, что такое потоки'even are.

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

Даже если многопоточность полностью исключена из уравнения, вы все равно допускаете дальнейшую оптимизацию в сценарии, где он мне кажется правильным.

Несмотря на это, спасибо за все ваши авторитетные ответы до сих пор.Подчеркну ли я все ответы, которые мне понравились, или только тот, который я принимаю?Что делать, если принято более одного?Извините, я новичок здесь, теперь я буду более подробно просматривать FAQ ...

Ответы [ 4 ]

5 голосов
/ 16 июля 2011

restrict не имеет никакого отношения к безопасности потока. Фактически, существующим стандартам C вообще нечего сказать по теме потоков; с точки зрения спецификации, нет такой вещи как «нить».

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

void foo(int *x, int *y) {
    *x = 5;
    *y = 7;
    printf("%d\n", *x);
}

Когда компилятор обрабатывает эту функцию, он не знает, ссылаются ли x и y на одну и ту же ячейку памяти. Поэтому он не знает, будет ли он печатать 5 или 7, и он должен выдать код, чтобы фактически прочитать *x перед вызовом printf.

Но если вы объявите x как int *restrict x, компилятор может доказать, что эта функция печатает 5, поэтому она может передать константу времени компиляции в вызов printf.

Многие такие оптимизации становятся возможными с restrict, особенно когда речь идет об операциях с массивами.

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

[править]

Чтобы ответить на ваш вопрос об использовании restrict в качестве формы документации, я бы тоже сказал «нет».

Вы, кажется, думаете, что вам следует документировать , когда переменная может или не может быть доступна одновременно. Но для общих данных надлежащая дисциплина (почти всегда) заключается в обеспечении одновременного доступа к .

Соответствующая документация для переменной: , является ли общей для всех и , какой мьютекс защищает ее. Любой код, обращающийся к этой переменной по любой причине, даже для чтения, должен содержать мьютекс. Автору не следует ни знать, ни заботиться о том, может ли какой-либо другой поток получать доступ к переменной одновременно или нет, потому что другой поток будет подчиняться той же дисциплине, и мьютекс гарантирует, что одновременного доступа нет.

Эта дисциплина проста, она работает и масштабируется, поэтому она является одной из доминирующих парадигм для обмена данными между потоками. (Другой - это передача сообщений.) Если вы когда-нибудь пытаетесь понять, «действительно ли мне нужно заблокировать мьютекс на этот раз?», Вы почти наверняка делаете что-то не так. Было бы трудно переоценить этот момент.

3 голосов
/ 16 июля 2011

Нет, я не думаю, что это хороший диалект для предоставления какой-либо информации о доступе из разных потоков.Это подразумевается как утверждение об указателях, которое получает определенный код для разных указателей, которые он обрабатывает.Темы еще не являются частью языка.Потокобезопасный доступ к данным требует гораздо большего, restrict - не правильный инструмент.volatile не является гарантией, которую вы иногда предлагаете.

  • мьютексы или другие структуры блокировки
  • ограждения памяти, обеспечивающие целостность данных
  • атомарныеоперации и типы

Предстоящий стандарт C1x должен обеспечивать такие конструкции.C99 нет.

1 голос
/ 16 июля 2011

restrict является подсказкой компилятору, что буфер, к которому осуществляется доступ через указатель, не является псевдонимом через другой указатель в области видимости.Поэтому, если у вас есть такая функция:

foo(char *restrict datai, char *restrict dataj)
{
    // we've "promised" the compiler that it should not worry about overlap between 
    // datai and dataj buffers, this gives the compiler the opportunity to generate 
    // "better" code, potentially mitigating load-store issues, amongst other things
}

Чтобы не использовать restrict, недостаточно обеспечить защищенный доступ в многопоточном приложении.Если у вас есть общий буфер, доступ к которому одновременно осуществляется несколькими потоками через параметры char * в режиме чтения / записи, вам, возможно, потребуется использовать какой-либо тип блокировки / мьютекса и т. Д. - отсутствие restrict не означает безопасность потока.

Надеюсь, это поможет.

1 голос
/ 16 июля 2011

Каноническим примером restrict является memcpy() - который man-страница в OS X 10.6 дает прототипу как:

void* memcpy(void *restrict s1, const void *restrict s2, size_t n);

Области источника и назначения в memcpy не могут перекрываться;поэтому, пометив их как restrict, это ограничение применяется принудительно - компилятор может узнать, что ни одна часть исходного массива не имеет псевдонима с адресатом;может ли он выполнять такие вещи, как чтение большого фрагмента исходного кода и затем записать его в место назначения.

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

См. Как использовать ограничение, указанное в C и статья в Википедии об ограничении для более продолжительного обсуждения.

...