Пример неочевидных граблей :)
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 06.04.07 08:25
Оценка: 5 (1)
Пару дней назад наступил на вполне понятные, но неочевидные грабли. Хочу поделиться, вдруг кому полезно будет.

В параллельных потоках используется индикатор вхождения в критический участок:

if (InterlockedExchange (InCriticalSection, true) == false) {
  ...
    InterlockedExchange (InCriticalSection, false);
}


Потоки время от времени проверяют целостность своих данных, в том числе и этого индикатора:

Assert (InCriticalSection == false && InCriticalSection == true);


Можно было написать "UINT (InCriticalSection) <= UINT (true)", но некрасиво как-то Проверка выполнялась не только внутри критического участка, но и вне его.

Вот Assert периодически и трапался, хотя при этом переменная не выходила за пределы значений true/false.

Все поняли, почему?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re: Пример неочевидных граблей :)
От: DOOM Россия  
Дата: 06.04.07 08:31
Оценка: +1
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Все поняли, почему?


Ну вообще-то пример достаточно классический.... Собственно на нем показывают необходимость атомарности операции проверки и установки семафора...
Re: Пример неочевидных граблей :)
От: Аноним  
Дата: 06.04.07 13:12
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Можно было написать "UINT (InCriticalSection) <= UINT (true)", но некрасиво как-то Проверка выполнялась не только внутри критического участка, но и вне его.

ЕМ>Вот Assert периодически и трапался, хотя при этом переменная не выходила за пределы значений true/false.


ЕМ>Все поняли, почему?


Я не понял. Можно пояснение ?
Re[2]: Пример неочевидных граблей :)
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 06.04.07 13:25
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>Я не понял. Можно пояснение ?


При указанном сравнении выполняется две выборки переменной из памяти. Может случиться так, что в момент сравнения с false она имеет значение true, а к моменту сравнения с true успевать измениться на false.

Поэтому нужно либо оборачивать подобные проверки какой-то защитой, либо (что проще и эффективнее) оформлять сравнение так, чтобы выборка была единственной.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[3]: Пример неочевидных граблей :)
От: ДимДимыч Украина http://klug.org.ua
Дата: 06.04.07 13:56
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>При указанном сравнении выполняется две выборки переменной из памяти. Может случиться так, что в момент сравнения с false она имеет значение true, а к моменту сравнения с true успевать измениться на false.


Вы открыли для себя ситуацию "гонки" (race)

ЕМ>Поэтому нужно либо оборачивать подобные проверки какой-то защитой,


Для этого и существуют средства синхронизации (мютексы etc).

ЕМ>либо (что проще и эффективнее) оформлять сравнение так, чтобы выборка была единственной.


Например?
Обязательно бахнем! И не раз. Весь мир в труху! Но потом. (ДМБ)
Re[4]: Пример неочевидных граблей :)
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 06.04.07 14:12
Оценка:
Здравствуйте, ДимДимыч, Вы писали:

ДД>Вы открыли для себя ситуацию "гонки" (race)


Вы, наверное, думали, что я не знал об этой ситуации? Я привел пример неочевидной гонки.

ЕМ>>либо (что проще и эффективнее) оформлять сравнение так, чтобы выборка была единственной.


ДД>Например?


Например, как уже писалось, "UINT (InCriticalSection) <= UINT (true)".
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[5]: Пример неочевидных граблей :)
От: ДимДимыч Украина http://klug.org.ua
Дата: 06.04.07 15:10
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Вы, наверное, думали, что я не знал об этой ситуации? Я привел пример неочевидной гонки.


Так если
ЕМ>Проверка выполнялась не только внутри критического участка, но и вне его.
то очевидно, что вычисление первой половины выражения в assert'е у первого потока может быть прервано вторым потоком, который войдет в критическую секцию, но не успеет из нее выйти, когда произойдет переключение обратно на первый поток, и он в свою очередь досчитает выражение, и таким образом попадаем в assert.

ЕМ>Например, как уже писалось, "UINT (InCriticalSection) <= UINT (true)".


Брр. Смысл этой проверки — попадает ли значение InCriticalSection в диапазон bool ?
Обязательно бахнем! И не раз. Весь мир в труху! Но потом. (ДМБ)
Re[6]: Пример неочевидных граблей :)
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 06.04.07 15:31
Оценка:
Здравствуйте, ДимДимыч, Вы писали:

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


После подсказки оно всем очевидно Когда в выражении есть модификация — тоже всем очевидно. А вот в такой форме, и без подсказки — сомневаюсь, что Вы с первого взгляда заметили бы такую возможность

ДД>Смысл этой проверки — попадает ли значение InCriticalSection в диапазон bool ?


Да.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re: Пример неочевидных граблей :)
От: Аноним  
Дата: 08.04.07 21:16
Оценка:
welcome to the mutithreading world.
Если для Вас эти грабли неочевидны — у Вас еще все впереди
Re[2]: Пример неочевидных граблей :)
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 09.04.07 02:59
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>welcome to the mutithreading world.


Спасибо, уже лет примерно тридцать, как

А>Если для Вас эти грабли неочевидны


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

Кстати, оборачивать мьютексами и прочими средствами подобные проверки — вообще дурной тон.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[3]: Пример неочевидных граблей :)
От: Аноним  
Дата: 09.04.07 06:35
Оценка:
А>>welcome to the mutithreading world.
ЕМ>Спасибо, уже лет примерно тридцать, как
Мсье программил под VMS?
Re[4]: Пример неочевидных граблей :)
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 09.04.07 06:40
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>Мсье программил под VMS?


Под VMS — руки не дошли. Из нормальных систем программил под Диспак (БЭСМ-6) и RSX.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[3]: Пример неочевидных граблей :)
От: gwg-605 Россия  
Дата: 09.04.07 12:18
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:


ЕМ>При указанном сравнении выполняется две выборки переменной из памяти. Может случиться так, что в момент сравнения с false она имеет значение true, а к моменту сравнения с true успевать измениться на false.


А где там две выборки данных?
Re[4]: Пример неочевидных граблей :)
От: gwg-605 Россия  
Дата: 09.04.07 12:22
Оценка:
Здравствуйте, gwg-605, Вы писали:

G6>Здравствуйте, Евгений Музыченко, Вы писали:



ЕМ>>При указанном сравнении выполняется две выборки переменной из памяти. Может случиться так, что в момент сравнения с false она имеет значение true, а к моменту сравнения с true успевать измениться на false.


G6>А где там две выборки данных?

Сорри вопрос снят.
Re[4]: Пример неочевидных граблей :)
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 09.04.07 12:24
Оценка:
Здравствуйте, gwg-605, Вы писали:

G6>А где там две выборки данных?


Строго говоря, оптимизирующий компилятор это дело свернет в одну выборку, но проверка-то работает как раз в отладочном варианте
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[5]: Пример неочевидных граблей :)
От: gwg-605 Россия  
Дата: 09.04.07 12:45
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Строго говоря, оптимизирующий компилятор это дело свернет в одну выборку, но проверка-то работает как раз в отладочном варианте


Да я этот assert сразу не заметил подумал что-то не так с IntelockedExchange.
Re[7]: Пример неочевидных граблей :)
От: ДимДимыч Украина http://klug.org.ua
Дата: 11.04.07 06:48
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ> А вот в такой форме, и без подсказки — сомневаюсь, что Вы с первого взгляда заметили бы такую возможность


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

ДД>>Смысл этой проверки — попадает ли значение InCriticalSection в диапазон bool ?


ЕМ>Да.


А можно поинтересоваться целью такой проверки?
Обязательно бахнем! И не раз. Весь мир в труху! Но потом. (ДМБ)
Re[3]: Пример неочевидных граблей :)
От: ДимДимыч Украина http://klug.org.ua
Дата: 11.04.07 06:52
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Кстати, оборачивать мьютексами и прочими средствами подобные проверки — вообще дурной тон.


Это где написано, что дурной тон? Вы уверены, что на любой архитектуре с любым компилятором такая проверка будет атомарной?
Обязательно бахнем! И не раз. Весь мир в труху! Но потом. (ДМБ)
Re[8]: Пример неочевидных граблей :)
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 11.04.07 09:55
Оценка:
Здравствуйте, ДимДимыч, Вы писали:

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


Да мне, как бы, тоже, но если я начну еще и дежурные проверки, вызываемые из кучи функций, в критические секции оборачивать — реалтайму вообще труба настанет

ДД>>>Смысл этой проверки — попадает ли значение InCriticalSection в диапазон bool ?


ЕМ>>Да.


ДД>А можно поинтересоваться целью такой проверки?


От нежелательной порчи дикими указателями, кривым кодом компилятора и т.п.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[4]: Пример неочевидных граблей :)
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 11.04.07 10:17
Оценка:
Здравствуйте, ДимДимыч, Вы писали:

ЕМ>>Кстати, оборачивать мьютексами и прочими средствами подобные проверки — вообще дурной тон.


ДД>Это где написано, что дурной тон?


Там же, где описаны и другие приемы сокращения издержек

ДД>Вы уверены, что на любой архитектуре с любым компилятором такая проверка будет атомарной?


Под "подобными" я как раз и подразумевал "самоатомарные", типа (x <= 1). Если на какой-то архитектуре и под каким-то компилятором переменная типа bool выбирается не атомарно — это серьезный повод для отказа от параллелизма в этих условиях.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.