Race condition
От: Alexander G Украина  
Дата: 02.01.09 10:02
Оценка:
Есть ли тут гонка ?

long i; // глобальная переменная

void f() // вызывается из нескольких потоков выполнения одновременно
{
  i = OSVersion(); // возврашает каждый раз одно и то же
  /* дальше используется i. больше нигде она не используется */
}
Русский военный корабль идёт ко дну!
Re: Race condition
От: sc Россия  
Дата: 02.01.09 10:32
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Есть ли тут гонка ?

...
Может тогда лучше так?
void f() // вызывается из нескольких потоков выполнения одновременно
{
    static long const i = OSVersion(); // возврашает каждый раз одно и то же
  /* дальше используется i. больше нигде она не используется */
}

Мне кажется, с учетом условий в комментах, гонки нет.
Re: Race condition
От: Аноним  
Дата: 02.01.09 12:30
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Есть ли тут гонка ?


Да, есть.
Re[2]: Race condition
От: sc Россия  
Дата: 02.01.09 14:15
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, Alexander G, Вы писали:


AG>>Есть ли тут гонка ?


А>Да, есть.


А почему? Вот одно из определений race condition.

A race condition is a flaw in a system or process whereby the output and/or result of the process is unexpectedly and critically dependent on the sequence or timing of other events.

Мой, краткий перевод: Race condition — ошибка из-за зависимости результата от последовательности событий.

Значение i везде одинаково и не меняется, значит нет. Конечно, если топикстартер под словом используется понимает и изменяется, то тогда да, есть.
Re[2]: Race condition
От: byleas  
Дата: 02.01.09 17:19
Оценка: -1
Здравствуйте, sc, Вы писали:

sc>Может тогда лучше так?

Это ничего не меняет, в контексте темы.
Re[3]: Race condition
От: Аноним  
Дата: 03.01.09 08:19
Оценка:
Здравствуйте, byleas, Вы писали:

B>Здравствуйте, sc, Вы писали:


sc>>Может тогда лучше так?

B>Это ничего не меняет, в контексте темы.

Ну почему же?
Гонка становится более очевидной,
если прикинуть, как инициируются подобные статические переменные
Re[4]: Race condition
От: sc Россия  
Дата: 03.01.09 08:57
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, byleas, Вы писали:


B>>Здравствуйте, sc, Вы писали:


sc>>>Может тогда лучше так?

B>>Это ничего не меняет, в контексте темы.

А>Ну почему же?

А>Гонка становится более очевидной,
А>если прикинуть, как инициируются подобные статические переменные

И как они инициируются? Хотелось бы на примере увидеть.
Re[2]: Race condition
От: Sergey Chadov Россия  
Дата: 03.01.09 10:07
Оценка: +1
Здравствуйте, sc, Вы писали:

sc>Здравствуйте, Alexander G, Вы писали:


AG>>Есть ли тут гонка ?

sc>...
sc>Может тогда лучше так?
sc>
sc>void f() // вызывается из нескольких потоков выполнения одновременно
sc>{
sc>    static long const i = OSVersion(); // возврашает каждый раз одно и то же
sc>  /* дальше используется i. больше нигде она не используется */
sc>}
sc>

sc>Мне кажется, с учетом условий в комментах, гонки нет.

Вот теперь точно есть.
    static long i = OSVersion(); // возврашает каждый раз одно и то же
0041142D  mov         eax,dword ptr [$S1 (41817Ch)] 
00411432  and         eax,1 
00411435  jne         f+6Ch (41145Ch) 
00411437  mov         eax,dword ptr [$S1 (41817Ch)] 
0041143C  or          eax,1 
0041143F  mov         dword ptr [$S1 (41817Ch)],eax 
//тыдымс, переменная "мы проинициализировались" уже true, но сама переменная i еще не проинициализирована
//если сейчас управление получит другой поток - будет ой
00411444  mov         dword ptr [ebp-4],0 
0041144B  call        OSVersion (411104h)
--
Sergey Chadov

... << RSDN@Home 1.2.0 alpha rev. 685>>
Re[2]: Race condition
От: Alexander G Украина  
Дата: 03.01.09 10:16
Оценка:
Здравствуйте, Аноним, Вы писали:

AG>>Есть ли тут гонка ?


А>Да, есть.


Можете обосновать, показать ?
Русский военный корабль идёт ко дну!
Re[3]: Race condition
От: sc Россия  
Дата: 03.01.09 10:47
Оценка:
Здравствуйте, Sergey Chadov, Вы писали:

SC>Вот теперь точно есть.

SC>
SC>    static long i = OSVersion(); // возврашает каждый раз одно и то же
SC>0041142D  mov         eax,dword ptr [$S1 (41817Ch)] 
SC>00411432  and         eax,1 
SC>00411435  jne         f+6Ch (41145Ch) 
SC>00411437  mov         eax,dword ptr [$S1 (41817Ch)] 
SC>0041143C  or          eax,1 
SC>0041143F  mov         dword ptr [$S1 (41817Ch)],eax 
SC>//тыдымс, переменная "мы проинициализировались" уже true, но сама переменная i еще не проинициализирована
SC>//если сейчас управление получит другой поток - будет ой
SC>00411444  mov         dword ptr [ebp-4],0 
SC>0041144B  call        OSVersion (411104h) 
SC>


Спасибо, не знал, что со статиком такая засада может быть. Век живи — век учись
Re[3]: Race condition
От: Аноним  
Дата: 03.01.09 20:14
Оценка: +1
Здравствуйте, Alexander G, Вы писали:

AG>Можете обосновать, показать ?


Зависит от платформы. Какая тебя интересует?

А вообще, если хочешь жить спокойно, то тупо сделай одну функцию,
которая гарантированно инициализирует все твои глобальные переменные
(которых много быть не должно), до того, как запустится 2-й поток.
Ты же надеюсь не создаешь потоки при инициализации глобальных данных.
Один из положительных эффектов такого упражнения — ты будешь лучше контролировать глобальные данные,
которые всегда требуют особого внимания.
Re[5]: Race condition
От: Аноним  
Дата: 03.01.09 20:19
Оценка:
Здравствуйте, sc, Вы писали:

sc>И как они инициируются? Хотелось бы на примере увидеть.


Тебе уже ответили.
Статические переменные по типу, как ты предложил, в С++ в принципе создают кучу неприятностей
в многопоточном приложении.
Глобальные данные в этом смысле даже безопаснее, поскольку их можно
попытаться инициировать до запуска дополнительных потоков.
Re[4]: Race condition
От: Alexander G Украина  
Дата: 03.01.09 20:38
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Зависит от платформы. Какая тебя интересует?


MSVC8 (2005) Windows XP Vista x86 x64

А>А вообще, если хочешь жить спокойно, то тупо сделай одну функцию,

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

Так и сделаю. Просто, если бы не гонки, обычная глобальная переменная удобнее в моём случае.

Я вот что не понимаю. Допустим первый поток изменил переменную с 0 до 6. Далее он видит только 6. Последующие потоки будут менять с 6 до 6, т.е. изменений не будет. Где ошибки ?
Русский военный корабль идёт ко дну!
Re[5]: Race condition
От: Аноним  
Дата: 03.01.09 20:57
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Я вот что не понимаю. Допустим первый поток изменил переменную с 0 до 6. Далее он видит только 6. Последующие потоки будут менять с 6 до 6, т.е. изменений не будет. Где ошибки ?


Ну например ошибка в том, что ты думаешь, что операция "первый поток изменил переменную с 0 до 6" — атомарна.
Кстати, меняешь ты не с 0 а со случайной величины.
Re[6]: Race condition
От: Alexander G Украина  
Дата: 03.01.09 21:14
Оценка:
Здравствуйте, Аноним, Вы писали:

AG>>Я вот что не понимаю. Допустим первый поток изменил переменную с 0 до 6. Далее он видит только 6. Последующие потоки будут менять с 6 до 6, т.е. изменений не будет. Где ошибки ?


А>Ну например ошибка в том, что ты думаешь, что операция "первый поток изменил переменную с 0 до 6" — атомарна.


И что из не-атомарности следует ?

А>Кстати, меняешь ты не с 0 а со случайной величины.


Точно ? Вроде как:

3.8/6
The memory occupied by any object of static storage duration shall be
zero-initialized at program startup before any other initialization
takes place. [Note: in some cases, additional initialization is done
later. ]

Русский военный корабль идёт ко дну!
Re: Race condition
От: Кодт Россия  
Дата: 04.01.09 22:29
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Есть ли тут гонка ?


Если sizeof(long)>sizeof(int), то, скорее всего, запись будет неатомарна — в том смысле, что другой поток, не позаботившийся о вызове f(), может прочесть комбинацию из половинки OSVersion и нуля.

Если же каждый поток обязательно вызывает f(), то нужно иметь особо извращённую платформу, на которой перезапись того же самого значения проходит через промежуточную порчу переменной.

Но кстати, раз f() вызывается в каждом потоке — так, может быть, стоит использовать TLS?
Для MS-specific есть __declspec(thread), для других компиляторов надо читать документацию. Или вызывать соответствующие функции ОС.
Перекуём баги на фичи!
Re[2]: Race condition
От: Alexander G Украина  
Дата: 04.01.09 23:11
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Если sizeof(long)>sizeof(int), то, скорее всего, запись будет неатомарна — в том смысле, что другой поток, не позаботившийся о вызове f(), может прочесть комбинацию из половинки OSVersion и нуля.

К>Если же каждый поток обязательно вызывает f(), то нужно иметь особо извращённую платформу, на которой перезапись того же самого значения проходит через промежуточную порчу переменной.

Вот. Хотелось бы подробнее про эту платформу
Также интересно, как в ней повлияет замена присвоения на memcpy. И замена long на long volatile. И замена long на bool.

К>Но кстати, раз f() вызывается в каждом потоке — так, может быть, стоит использовать TLS?


На самом деле задача ставится так. Есть глобальная структура, инициализировванная статичeски:
struct data_t
{
  void * p1;
  void * p2;
  int  i3;
  ...
  bool initialized;
} data = {0};


Есть функция для инициализации, которая может быть в редких случаях вызвана из нескольки потоков одновременно:
void init()
{
  if (initialized)
    return;
  data.p1 = ::GetModuleHandle(...);
  data.p2 = ::GetProcAddress(...);
  data.i3 = (int)sqrt(42*_WIN_VER);
  initialized = true;
}

Т.е никакая не init-once, скорее init-Nce

Задача в том можно ли это сделать работоспособным без блокировок.
Русский военный корабль идёт ко дну!
Re[3]: Race condition
От: Аноним  
Дата: 05.01.09 05:23
Оценка: -1
Здравствуйте, Alexander G, Вы писали:

AG>Задача в том можно ли это сделать работоспособным без блокировок.


Без блокировки никак.
Воткни в main вызов init() до того, как запускаешь потоки и все будет нормально.
Re[3]: Race condition
От: Кодт Россия  
Дата: 05.01.09 11:45
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Т.е никакая не init-once, скорее init-Nce

AG>Задача в том можно ли это сделать работоспособным без блокировок.

Если ты мамой клянёшься, что пишешь туда всегда одно и то же, то гонки здесь не возникают.
Разве что для надёжности можешь заменить неатомарные присваивания атомарными — InterlockedExchange / InterlockedCompareExchange.

Ещё можно подумать о проверках, скажем, таких
void perfect_assign(long& var, long value)
{
    long old = InterlockedCompareExchange(&var, value, 0); // перезаписываем, только если там 0
    assert(old == 0 || old == value);
}

long perfect_read(long& var) // для проверки initialized
{
    return InterlockedCompareExchange(&var, 0, 0); // единственная засада - это создаётся барьер памяти
}


А заморачиваться в сторону универсальных lock-free алгоритмов... Ну, можно, конечно, но в твоей постановке задачи не нужно.
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Перекуём баги на фичи!
Re[4]: Race condition
От: bkat  
Дата: 05.01.09 11:59
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Если ты мамой клянёшься, что пишешь туда всегда одно и то же, то гонки здесь не возникают.


А какая разница, что он туда пишет?
Достаточно уже этого:
  if (initialized)
    return;
  // тут даже не важно, что вообще инициируется
  initialized = true;

чтобы подумать о нормальной инициализации.

А мамой можно клянуться сколько угодно, но этих клятв хватает максимум до следующего релиза
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.