| Race condition | |
| От: | Alexander G | ||
| Дата: | 02.01.09 09:02 |
Есть ли тут гонка ?
|
| Re: Race condition | |
| От: | sc | ||
| Дата: | 02.01.09 09:32 |
| Здравствуйте, Alexander G, Вы писали: AG>Есть ли тут гонка ? ... Может тогда лучше так?
Мне кажется, с учетом условий в комментах, гонки нет. |
| Re: Race condition | |
| От: | Аноним 820 | ||
| Дата: | 02.01.09 11:30 |
| Здравствуйте, Alexander G, Вы писали: AG>Есть ли тут гонка ? Да, есть. |
| Re[2]: Race condition | |
| От: | sc | ||
| Дата: | 02.01.09 13:15 |
| Здравствуйте, Аноним, Вы писали: А>Здравствуйте, Alexander G, Вы писали: AG>>Есть ли тут гонка ? А>Да, есть. А почему? Вот одно из определений race condition. Мой, краткий перевод: Race condition — ошибка из-за зависимости результата от последовательности событий. Значение i везде одинаково и не меняется, значит нет. Конечно, если топикстартер под словом используется понимает и изменяется, то тогда да, есть. |
| Re[2]: Race condition | |
| От: | byleas | ||
| Дата: | 02.01.09 16:19 | ||
| Оценка: | -1 | ||
| Здравствуйте, sc, Вы писали: sc>Может тогда лучше так? Это ничего не меняет, в контексте темы. |
| Re[3]: Race condition | |
| От: | Аноним 820 | ||
| Дата: | 03.01.09 07:19 |
| Здравствуйте, byleas, Вы писали: B>Здравствуйте, sc, Вы писали: sc>>Может тогда лучше так? B>Это ничего не меняет, в контексте темы. Ну почему же? Гонка становится более очевидной, если прикинуть, как инициируются подобные статические переменные |
| Re[4]: Race condition | |
| От: | sc | ||
| Дата: | 03.01.09 07:57 |
| Здравствуйте, Аноним, Вы писали: А>Здравствуйте, byleas, Вы писали: B>>Здравствуйте, sc, Вы писали: sc>>>Может тогда лучше так? B>>Это ничего не меняет, в контексте темы. А>Ну почему же? А>Гонка становится более очевидной, А>если прикинуть, как инициируются подобные статические переменные И как они инициируются? Хотелось бы на примере увидеть. |
| Re[2]: Race condition | |
| От: | Sergey Chadov | ||
| Дата: | 03.01.09 09:07 | ||
| Оценка: | +1 | ||
| Здравствуйте, sc, Вы писали: sc>Здравствуйте, Alexander G, Вы писали: AG>>Есть ли тут гонка ? sc>... sc>Может тогда лучше так? sc>
sc>Мне кажется, с учетом условий в комментах, гонки нет. Вот теперь точно есть.
-- Sergey Chadov ... << RSDN@Home 1.2.0 alpha rev. 685>> |
| Re[2]: Race condition | |
| От: | Alexander G | ||
| Дата: | 03.01.09 09:16 |
| Здравствуйте, Аноним, Вы писали: AG>>Есть ли тут гонка ? А>Да, есть. Можете обосновать, показать ? |
| Re[3]: Race condition | |
| От: | sc | ||
| Дата: | 03.01.09 09:47 |
| Здравствуйте, Sergey Chadov, Вы писали: SC>Вот теперь точно есть. SC>
Спасибо, не знал, что со статиком такая засада может быть. Век живи — век учись |
| Re[3]: Race condition | |
| От: | Аноним 820 | ||
| Дата: | 03.01.09 19:14 | ||
| Оценка: | +1 | ||
| Здравствуйте, Alexander G, Вы писали: AG>Можете обосновать, показать ? Зависит от платформы. Какая тебя интересует? А вообще, если хочешь жить спокойно, то тупо сделай одну функцию, которая гарантированно инициализирует все твои глобальные переменные (которых много быть не должно), до того, как запустится 2-й поток. Ты же надеюсь не создаешь потоки при инициализации глобальных данных. Один из положительных эффектов такого упражнения — ты будешь лучше контролировать глобальные данные, которые всегда требуют особого внимания. |
| Re[5]: Race condition | |
| От: | Аноним 820 | ||
| Дата: | 03.01.09 19:19 |
| Здравствуйте, sc, Вы писали: sc>И как они инициируются? Хотелось бы на примере увидеть. Тебе уже ответили. Статические переменные по типу, как ты предложил, в С++ в принципе создают кучу неприятностей в многопоточном приложении. Глобальные данные в этом смысле даже безопаснее, поскольку их можно попытаться инициировать до запуска дополнительных потоков. |
| Re[4]: Race condition | |
| От: | Alexander G | ||
| Дата: | 03.01.09 19:38 |
| Здравствуйте, Аноним, Вы писали: А>Зависит от платформы. Какая тебя интересует? MSVC8 (2005) Windows XP Vista x86 x64 А>А вообще, если хочешь жить спокойно, то тупо сделай одну функцию, А>которая гарантированно инициализирует все твои глобальные переменные А>(которых много быть не должно), до того, как запустится 2-й поток. А>Ты же надеюсь не создаешь потоки при инициализации глобальных данных. А>Один из положительных эффектов такого упражнения — ты будешь лучше контролировать глобальные данные, А>которые всегда требуют особого внимания. Так и сделаю. Просто, если бы не гонки, обычная глобальная переменная удобнее в моём случае. Я вот что не понимаю. Допустим первый поток изменил переменную с 0 до 6. Далее он видит только 6. Последующие потоки будут менять с 6 до 6, т.е. изменений не будет. Где ошибки ? |
| Re[5]: Race condition | |
| От: | Аноним 820 | ||
| Дата: | 03.01.09 19:57 |
| Здравствуйте, Alexander G, Вы писали: AG>Я вот что не понимаю. Допустим первый поток изменил переменную с 0 до 6. Далее он видит только 6. Последующие потоки будут менять с 6 до 6, т.е. изменений не будет. Где ошибки ? Ну например ошибка в том, что ты думаешь, что операция "первый поток изменил переменную с 0 до 6" — атомарна. Кстати, меняешь ты не с 0 а со случайной величины. |
| Re[6]: Race condition | |
| От: | Alexander G | ||
| Дата: | 03.01.09 20:14 |
| Здравствуйте, Аноним, Вы писали: AG>>Я вот что не понимаю. Допустим первый поток изменил переменную с 0 до 6. Далее он видит только 6. Последующие потоки будут менять с 6 до 6, т.е. изменений не будет. Где ошибки ? А>Ну например ошибка в том, что ты думаешь, что операция "первый поток изменил переменную с 0 до 6" — атомарна. И что из не-атомарности следует ? А>Кстати, меняешь ты не с 0 а со случайной величины. Точно ? Вроде как:
|
| Re: Race condition | |
| От: | Кодт модератор | ||
| Дата: | 04.01.09 21: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 22:11 |
| Здравствуйте, Кодт, Вы писали: К>Если sizeof(long)>sizeof(int), то, скорее всего, запись будет неатомарна — в том смысле, что другой поток, не позаботившийся о вызове f(), может прочесть комбинацию из половинки OSVersion и нуля. К>Если же каждый поток обязательно вызывает f(), то нужно иметь особо извращённую платформу, на которой перезапись того же самого значения проходит через промежуточную порчу переменной. Вот. Хотелось бы подробнее про эту платформу Также интересно, как в ней повлияет замена присвоения на memcpy. И замена long на long volatile. И замена long на bool. К>Но кстати, раз f() вызывается в каждом потоке — так, может быть, стоит использовать TLS? На самом деле задача ставится так. Есть глобальная структура, инициализировванная статичeски:
Есть функция для инициализации, которая может быть в редких случаях вызвана из нескольки потоков одновременно:
Т.е никакая не init-once, скорее init-Nce Задача в том можно ли это сделать работоспособным без блокировок. |
| Re[3]: Race condition | |
| От: | Аноним 820 | ||
| Дата: | 05.01.09 04:23 | ||
| Оценка: | -1 | ||
| Здравствуйте, Alexander G, Вы писали: AG>Задача в том можно ли это сделать работоспособным без блокировок. Без блокировки никак. Воткни в main вызов init() до того, как запускаешь потоки и все будет нормально. |
| Re[3]: Race condition | |
| От: | Кодт модератор | ||
| Дата: | 05.01.09 10:45 |
| Здравствуйте, Alexander G, Вы писали: AG>Т.е никакая не init-once, скорее init-Nce AG>Задача в том можно ли это сделать работоспособным без блокировок. Если ты мамой клянёшься, что пишешь туда всегда одно и то же, то гонки здесь не возникают. Разве что для надёжности можешь заменить неатомарные присваивания атомарными — InterlockedExchange / InterlockedCompareExchange. Ещё можно подумать о проверках, скажем, таких
А заморачиваться в сторону универсальных lock-free алгоритмов... Ну, можно, конечно, но в твоей постановке задачи не нужно. ... << RSDN@Home 1.2.0 alpha 4 rev. 1111>> Перекуём баги на фичи! |
| Re[4]: Race condition | |
| От: | bkat | ||
| Дата: | 05.01.09 10:59 |
| Здравствуйте, Кодт, Вы писали: К>Если ты мамой клянёшься, что пишешь туда всегда одно и то же, то гонки здесь не возникают. А какая разница, что он туда пишет? Достаточно уже этого:
чтобы подумать о нормальной инициализации. А мамой можно клянуться сколько угодно, но этих клятв хватает максимум до следующего релиза |
| Re[4]: Race condition | |
| От: | Alexander G | ||
| Дата: | 05.01.09 11:00 |
| Здравствуйте, Кодт, Вы писали: К>Разве что для надёжности можешь заменить неатомарные присваивания атомарными — InterlockedExchange / InterlockedCompareExchange. InterlockedExchange тоже не хочу, прийдётся писать volatile и терять возможную оптимизацию при дальнейшем использовании. А что будет значить "для надёжности" ? |
| Re[5]: Race condition | |
| От: | Alexander G | ||
| Дата: | 05.01.09 11:42 |
| Здравствуйте, bkat, Вы писали: B>Достаточно уже этого: B>
B>чтобы подумать о нормальной инициализации. Давай подумаем о нормальной инициализации. Требования: 1. Возможность вызова до main. Желательно чтобы была такая защищённость от дурака, чтобы работало даже если дополнительные потоки созданы до main. 2. Максимальная простота. 3. Максимальная переносимость. 4. Расположение в статической памяти. Поэтому, откинем все эти boost::call_once или InitOnceXxx, синглтон Мейерса, другие известные реализации синглтона... и вроде ничего "нормального" не остаётся. И я не против если не нормальное оказывается лучше. B>А мамой можно клянуться сколько угодно, но этих клятв хватает максимум до следующего релиза Как бы эта штука вообще меняется в первый раз. И там нет ничего, что может нарушать клятву о полной повторимости каждой инициализации. |
| Re[6]: Race condition | |
| От: | bkat | ||
| Дата: | 05.01.09 12:30 |
| Здравствуйте, Alexander G, Вы писали: AG>Здравствуйте, bkat, Вы писали: B>>Достаточно уже этого: B>>
B>>чтобы подумать о нормальной инициализации. AG>Давай подумаем о нормальной инициализации. AG>Требования: AG>1. Возможность вызова до main. Желательно чтобы была такая защищённость от дурака, чтобы работало даже если дополнительные потоки созданы до main. AG>2. Максимальная простота. AG>3. Максимальная переносимость. AG>4. Расположение в статической памяти. AG>Поэтому, откинем все эти boost::call_once или InitOnceXxx, синглтон Мейерса, другие известные реализации синглтона... и вроде ничего "нормального" не остаётся. Как раз банальный синглтон, в котором бы честно синхронизировалось создание экземпляра, было бы, на мой взгляд, простым решением. Пусть твой синглтон и отвечает за такие вещи, как версия OS и прочие подобные вещи. Проблема то стандартная. Чего тут то на блокировках экономить? B>>И я не против если не нормальное оказывается лучше. Лучше чего? Твое решение — это просто инициализация с полным игнорированием проблем многопоточных приложений. Ну блокировки нет, зато гонки есть, и есть большая вероятность начать пользоваться данными до того, как кто-то их проинициировал. B>>А мамой можно клянуться сколько угодно, но этих клятв хватает максимум до следующего релиза AG>Как бы эта штука вообще меняется в первый раз. И там нет ничего, что может нарушать клятву о полной повторимости каждой инициализации. Ну тебе еще придется давать клятву о том, что каждый поток (включая главный поток) гарантированно вызывает функцию init() до того, как он попытается использовать результаты инициализации. Кстати, это может запросто случится даже без дополнительных потоков. С учетом того, что глобальные данные в различных модулях инициализируются в непредсказуемом порядке, ты можешь поиметь очень забавные побочные эффекты. |
| Re[5]: Race condition | |
| От: | bkat | ||
| Дата: | 05.01.09 12:31 |
| Здравствуйте, Alexander G, Вы писали: AG>InterlockedExchange тоже не хочу, прийдётся писать volatile и терять возможную оптимизацию при дальнейшем использовании. А что будет значить "для надёжности" ? Похоже, что ты экономишь даже не на спичках, а огрызках от спичек. |
| Re[5]: Race condition | |
| От: | Кодт модератор | ||
| Дата: | 05.01.09 12:35 | ||
| Оценка: | 6 (1) | ||
| Здравствуйте, Alexander G, Вы писали: К>>Разве что для надёжности можешь заменить неатомарные присваивания атомарными — InterlockedExchange / InterlockedCompareExchange. AG>InterlockedExchange тоже не хочу, прийдётся писать volatile и терять возможную оптимизацию при дальнейшем использовании. volatile можешь присобачить по месту, в const_cast. Всё равно твой сценарий не предполагает, что там будет нечто постоянно меняющееся, так что пусть компилятор сколько угодно оптимизирует чтение после инициализации. AG>А что будет значить "для надёжности" ? На случай той самой экзотической платформы (или взбесившегося компилятора), который в процессе перезаписи повреждает переменную. ... << RSDN@Home 1.2.0 alpha 4 rev. 1111>> Перекуём баги на фичи! |
| Re[7]: Race condition | |
| От: | Alexander G | ||
| Дата: | 05.01.09 12:59 |
| Здравствуйте, bkat, Вы писали: B>Как раз банальный синглтон, в котором бы честно синхронизировалось создание экземпляра, B>было бы, на мой взгляд, простым решением. С помощью какого примитива синхронизации ? B>Пусть твой синглтон и отвечает за такие вещи, как версия OS и прочие подобные вещи. B>Проблема то стандартная. Чего тут то на блокировках экономить? Тут такое дело, что желательно избавиться от каждого вызова апи иоли библиотеки. Вроде придумал так:
B>Ну тебе еще придется давать клятву о том, что каждый поток (включая главный поток) гарантированно вызывает B>функцию init() до того, как он попытается использовать результаты инициализации. B>Кстати, это может запросто случится даже без дополнительных потоков. Это детали реализации, наружу будет немного методов, init в каждом из которых. Или наружу можно даже типа синглтона, с методом Instance. |
| Re[6]: Race condition | |
| От: | Alexander G | ||
| Дата: | 05.01.09 13:05 |
| Здравствуйте, bkat, Вы писали: B>Похоже, что ты экономишь даже не на спичках, а огрызках от спичек. Я знаю на что это похоже. volatile был бы добавлен если было бы известно что без него не работает, а с ним работает. |
| Re[8]: Race condition | |
| От: | bkat | ||
| Дата: | 05.01.09 13:31 |
| Здравствуйте, Alexander G, Вы писали: AG>Здравствуйте, bkat, Вы писали: B>>Как раз банальный синглтон, в котором бы честно синхронизировалось создание экземпляра, B>>было бы, на мой взгляд, простым решением. AG>С помощью какого примитива синхронизации ? Да хоть с критической секцией. B>>Пусть твой синглтон и отвечает за такие вещи, как версия OS и прочие подобные вещи. B>>Проблема то стандартная. Чего тут то на блокировках экономить? AG>Тут такое дело, что желательно избавиться от каждого вызова апи иоли библиотеки. AG>Вроде придумал так: AG>
Плохо, очень плохо. Ты можешь с таким же успехом выкинуть InterlockedExchange и это будет так же "надежно", как и твой вариант. В момент, когда работает memcpy, для других потоков данные уже инициированы, хотя это не так. Сделай лучше для начала типа этого:
Если getInstance() безбожно будет тормозить твою систему (в чем я сильно сомневаюсь), то просто вызывай getInstance один раз на поток. Будь проще, и не пытайся оптимизировать то, что скорей всего никогда не будет тормозить. |
| Re[9]: Race condition | |
| От: | Alexander G | ||
| Дата: | 05.01.09 14:45 |
| Здравствуйте, bkat, Вы писали: AG>>С помощью какого примитива синхронизации ? B>Да хоть с критической секцией. Покажи работающий синглтон с критичесской секцией Это нетривиально. B>>>Пусть твой синглтон и отвечает за такие вещи, как версия OS и прочие подобные вещи. B>>>Проблема то стандартная. Чего тут то на блокировках экономить? AG>>Тут такое дело, что желательно избавиться от каждого вызова апи иоли библиотеки. AG>> if (::InterlockedExchange(&initialized, TRUE)) AG>> memcpy(&data, local_data, sizeof(data_t); AG>>} AG>>[/ccode] B>Плохо, очень плохо. Понял, хуже чем было |
| Re[10]: Race condition | |
| От: | bkat | ||
| Дата: | 05.01.09 14:50 |
| Здравствуйте, Alexander G, Вы писали: AG>Здравствуйте, bkat, Вы писали: AG>>>С помощью какого примитива синхронизации ? B>>Да хоть с критической секцией. AG>Покажи работающий синглтон с критичесской секцией AG>Это нетривиально. Дак я тебе же показал. Нетривиально — это для тех, кто не хотят пользоваться критическими секциями. Ты наверное имеешь ввиду дискуссии на тему типа этой: http://ru.wikipedia.org/wiki/Double_checked_locking А то, что я тебе показал — это примитивное решение "в лоб", которое работает |
| Re[11]: Race condition | |
| От: | Alexander G | ||
| Дата: | 05.01.09 14:56 |
| Здравствуйте, bkat, Вы писали: B>Дак я тебе же показал. B>Нетривиально — это для тех, кто не хотят пользоваться критическими секциями. нет, покажи полностью, с самой КС, чтобы было cs.Lock() или EnterCriticalSection(&cs); . А то непонятно как то что было показано может работать. |
| Re[12]: Race condition | |
| От: | bkat | ||
| Дата: | 05.01.09 15:12 | ||
| Оценка: | ![]() | ||
| Здравствуйте, Alexander G, Вы писали: AG>нет, покажи полностью, с самой КС, чтобы было cs.Lock() или EnterCriticalSection(&cs); . AG>А то непонятно как то что было показано может работать. Ну блин... Народ с основами не разобрался, а уже многопоточные приложения пишет Вот, как бы это выглядело бы на Qt
Считай, QMutex в конструкторе инициализирует CRITICAL_SECTION QMutexLocker в конструкторе вызывает EnterCriticalSection, а в деструкторе — LeaveCriticalSection. |
| Re[13]: Race condition | |
| От: | Alexander G | ||
| Дата: | 05.01.09 15:16 |
| Здравствуйте, bkat, Вы писали: B>Ну блин... B>Народ с основами не разобрался, а уже многопоточные приложения пишет B>Вот, как бы это выглядело бы на Qt B>
B>Считай, QMutex в конструкторе инициализирует CRITICAL_SECTION B>QMutexLocker в конструкторе вызывает EnterCriticalSection, B>а в деструкторе — LeaveCriticalSection. Этот код не рабочий, т.к. getInstance может быть вызван до конструктора QMutex. |
| Re[14]: Race condition | |
| От: | Alexander G | ||
| Дата: | 05.01.09 15:19 |
| Здравствуйте, Alexander G, Вы писали: AG>Этот код не рабочий, т.к. getInstance может быть вызван до конструктора QMutex. Да, возможно QMutex не имеет конструктора, тогда код рабочий. Но — увы — виндовая КС должна инициализироваться InitializeCriticalSection |
| Re[14]: Race condition | |
| От: | bkat | ||
| Дата: | 05.01.09 15:29 | ||
| Оценка: | -2 ![]() | ||
| Здравствуйте, Alexander G, Вы писали: AG>Этот код не рабочий, т.к. getInstance может быть вызван до конструктора QMutex. Смотри внимателей. mutex — статический, а иначе ты бы его не смог пользовать в статическом методе getInstance. Я понимаю, что ты просто не знаешь, как пользоваться CRITICAL_SECTION. Почитай тут: http://www.rsdn.ru/article/baseserv/critsec.xml Автор(ы): Павел Блудов Дата: 14.03.2005 В статье рассматриваются аспекты работы с критическими секциями, их внутреннее устройство и способы отладки Заодно там же и найдешь демо, как можно сделать обертки виндовых критических секций, чтобы ими можно было бы пользоваться по типу Qt: http://www.rsdn.ru/article/baseserv/critsec/cswrap.zip Да и вообще тут много чего полезного: http://www.rsdn.ru/summary/1390.xml P.S. Конструкторы у объектов есть всегда. |
| Re[15]: Race condition | |
| От: | Alexander G | ||
| Дата: | 05.01.09 15:49 | ||
| Оценка: | 1 (1) -1 | ||
| Здравствуйте, bkat, Вы писали: B>Здравствуйте, Alexander G, Вы писали: AG>>Этот код не рабочий, т.к. getInstance может быть вызван до конструктора QMutex. B>Смотри внимателей. B>mutex — статический, а иначе ты бы его не смог пользовать в статическом методе getInstance. Да. но порядок инициализации статических объектов не определён. Конструкторы статических объектов ничем не лучше конструкторов глобальных объектов, и так же вызываются в неопределённом порядке. т.е. в этом коде ничего нельзя сказать про порядок S1, S2, S3:
B>Я понимаю, что ты просто не знаешь, как пользоваться CRITICAL_SECTION. B>Почитай тут: B>http://www.rsdn.ru/article/baseserv/critsec.xml Автор(ы): Павел Блудов Дата: 14.03.2005 В статье рассматриваются аспекты работы с критическими секциями, их внутреннее устройство и способы отладки Я знаю как ими пользоваться. Это ты похоже не понял проблему Если бы всё было так просто, я бы действительно взял КС. B>P.S. Конструкторы у объектов есть всегда. Да, но может не быть пользовательских конструкторов. Тогда возможна статическая инициализаций, как с S1 ниже. Такая инициализация гарантирована до инициализации S2.
|
| Re[16]: Race condition | |
| От: | bkat | ||
| Дата: | 05.01.09 16:15 |
| Здравствуйте, Alexander G, Вы писали: AG>Если бы всё было так просто, я бы действительно взял КС. Все время забываю, что тебе нужна гарантия в случае если кто-то пожелает вызвать getInstance при создании глобальных объектов. Тут извини. Я пас... Чем-то придется пожертвовать. |
| Re[12]: Race condition | |
| От: | Кодт модератор | ||
| Дата: | 05.01.09 17:02 | ||
| Оценка: | 6 (1) | ||
| Здравствуйте, Alexander G, Вы писали: AG>нет, покажи полностью, с самой КС, чтобы было cs.Lock() или EnterCriticalSection(&cs); . AG>А то непонятно как то что было показано может работать. Щас попробую на коленке родить. Сам мьютекс (и все остальные примитивы lock-based синхронизации) нужно инициализировать в рантайме. Следовательно, мы всё равно хоть что-то, да должны сделать на lock-free. Поскольку это делается единожды, то нет нужды требовать, чтобы это было ещё и wait-free, так что мы вольны использовать спинлок.
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>> Перекуём баги на фичи! |
| Re[13]: Race condition | |
| От: | Alexander G | ||
| Дата: | 05.01.09 18:00 |
| Здравствуйте, Кодт, Вы писали: К>Щас попробую на коленке родить. К>Сам мьютекс (и все остальные примитивы lock-based синхронизации) нужно инициализировать в рантайме. К>Следовательно, мы всё равно хоть что-то, да должны сделать на lock-free. К>Поскольку это делается единожды, то нет нужды требовать, чтобы это было ещё и wait-free, так что мы вольны использовать спинлок. ... Так, это уже лучше чем КС с конструктором пара вопросов: 1. Нужна ли мне КС или инициализировать через do_once саму data_t — какие будут практические рекомендации для выбора ? 2. А вот ты бы оставил "опасный" вариант или делал бы настоящий do_once вроде этого ? |
| Re[17]: Race condition | |
| От: | Alexander G | ||
| Дата: | 05.01.09 18:22 |
| Здравствуйте, bkat, Вы писали: B>Здравствуйте, Alexander G, Вы писали: AG>>Если бы всё было так просто, я бы действительно взял КС. B>Все время забываю, что тебе нужна гарантия в случае если B>кто-то пожелает вызвать getInstance при создании глобальных объектов. Я действительно хочу много странного, но именно это странным не является. Требование чтобы синглтон работал при конструировании глобальных объектов, в том числе и других синглтонов — стандартное требование. Фактически, самый простой известный синглтон — синглтон Мейерса — решает в первую очередь именно эту задачу — при этом создавая взамен проблемы с многопоточностью и с exception safety. Если бы существование при инициализации не было бы нужно, я бы просто сделал инициализацию в конструкторе глобального объекта. Можно конечно сделать что-то вроде
, но у меня в требованиях Автор: Alexander G есть "желательно".Дата: 05.01.09 |
| Re[14]: Race condition | |
| От: | Кодт модератор | ||
| Дата: | 05.01.09 18:32 |
| Здравствуйте, Alexander G, Вы писали: AG>пара вопросов: AG>1. Нужна ли мне КС или инициализировать через do_once саму data_t — какие будут практические рекомендации для выбора ? AG>2. А вот ты бы оставил "опасный" вариант или делал бы настоящий do_once вроде этого ? Лично я бы постарался всячески избавиться от той ситуации, когда недетерминизм многопоточности накладывается на недетерминизм инициализации статических объектов. Раз у тебя загрузка модуля отложенная, то, наверно, я бы сделал так: 1) Процедуру загрузки — атомарной 2) Атомарность — lock-based (просто потому, что это проще для понимания и раздачи всяких гарантий) 3) Инфраструктуру для атомарности — т.е. мьютекс aka CRITICAL_SECTION — инициализировал бы отдельно (или заранее, в конструкторе статического объекта, или на механизме do_once) 4) Процедуру чтения — тут есть два варианта. — Либо потребовать, чтобы загрузка была выполнена обязательно до использования, и тогда плевать на атомарность (мы же только читаем) — Либо загрузка по требованию, каждый раз, и тогда для ускорения прикрутил бы внутрь процедуры загрузки ещё и двойную проверку. Тут главное, чтобы каждая подзадача была прозрачна. Ещё я бы постарался сгруппировать все эти загрузки деталек из модуля в одно целое, чтобы не городить по синглетону на каждую импортируемую функцию. ... << RSDN@Home 1.2.0 alpha 4 rev. 1111>> Перекуём баги на фичи! |
| Re[18]: Race condition | |
| От: | bkat | ||
| Дата: | 05.01.09 18:39 |
| Здравствуйте, Alexander G, Вы писали: AG>Я действительно хочу много странного, но именно это странным не является. Требование чтобы синглтон работал при конструировании глобальных объектов, в том числе и других синглтонов — стандартное требование. Фактически, самый простой известный синглтон — синглтон Мейерса — решает в первую очередь именно эту задачу — при этом создавая взамен проблемы с многопоточностью и с exception safety. Понятно. У меня просто другая философия. Подобную глобальную инициализацию/деинициализацию предпочитаю делать явной, например, создав специальный объект в main. На зависимостях между глобальными данными уже достаточно много шишек набил. |
| Re: Что собственно надо... | |
| От: | Alexander G | ||
| Дата: | 05.01.09 21:14 | ||
| Оценка: | +1 | ||
| Наверное таки сформулирую исходную задачу. Чтобы было понятно, почему у меня нет систематического подхода к инициализации объектов. Я иногда анализирую креш-дампы. Как и автоматически создаваемые и отправляемые пользователями на https://winqual.microsoft.com/ , так и вручную создаваемые через MiniDumpWriteDump. Различные версии Windows дают различные возможности для этого: Windows 98, ME Нет ничего. Да и вряд ли стоит стараться для нескольки динозавров. Windows 2000 Есть DbgHelp.dll . Возможно, той версии, в которой уже есть MiniDumpWriteDump. Так, можно в случае исключения, перехваченного через __except или SetUnhandledExceptionFilter записать креш-дамп. Возможностей по управлению этим крешдампом не очень много, и его стоит делать только в случае продвинутого пользователя, который сам потом его отправит нам и расскажет что за проблема. Windows XP, 2003 В DbgHelp.dll точно есть MiniDumpWriteDump. В случае необработанного исключения или зависания пользователь может оправить отчёт в майкрософт. Через тот самый диалог где "Send report" и "Do not send" (жаль что дефолтна последняя). API для управления этим отчётом не предоставляет ничего. Встречаются два вида дампов. Один содержит только эксепшн инфо, стек бектрейс, сегмент данных, инфу по модулям и тредам. Другой — полный дамп кучи, он реже бывает, обычно при зависаниях или по явному требованию после анализа микро-дампов. Windows Vista, 2008 Имеется полный набор Wer* функций. Можно гра^H^H^H создавать и оправлять свои креш-репорты в winqual, как хочется, включять в них самостоятельно сгенерированные креш-дампы и другие файлы. А ещё можно через функции WerRegisterMemoryBlock, WerRegisterFile, WerSetFlags управлять генерацией автоматически создаваемых креш-дампов. А ещё можно сделать дамп прямо из такск менеджера. Теперь, как я желаю этим воспользоваться. Во-первых, если надо поддерживать 2000, то всё прийдётся грузить динамически. Далее, дампы, которые делаются вручную, наверное стоит включать только через command-line option, т.к. они не попадут в Winqual и не будут систематизированы, пусть тогда эти дампы отправляются только продвинутыми пользователями по их желанию вместе с описанием проблемы. Чтобы воспользоваться автоматическими дампами в ХР, неплохо иметь полезную информацию в data segment, т.е. в глобальной переменной. Полезна например будет достаточно свежая MEMORYSTATUS (это API функция вернула нулевой указатель, бо память закончилась — или таки логическая ошибка?). Отсюда требование располагать отладочную структуру в static storage. В Vista большой интерес представляют WerRegisterMemoryBlock/WerUnregisterMemoryBlock. Можно попробовать зарегистрировать каждый синглтон. Отсюда требование — умение инициализироваться первым. Пока нет полного перехода всех пользователей на Висту, прийдётся находить их динамически из kernel32 (да, самое полезное не в Wer.dll, а в kernel32.dll) через GetProcAddress и иметь стабы для более раних систем. Это — одно из того, что предполагалось инициализировать без синхронизации. Крешдамп может делаться в условиях испорченной кучи, в условиях нехватки памяти, в условиях переполнения стека. Поэтому сам обработчик UnhandledExceptionFilter пусть вообще использует только глобальные переменные. Такие дела. |
| Re[15]: Race condition | |
| От: | Alexander G | ||
| Дата: | 05.01.09 21:28 |
| Здравствуйте, Кодт, Вы писали: К>Лично я бы постарался всячески избавиться от той ситуации, когда недетерминизм многопоточности накладывается на недетерминизм инициализации статических объектов. Не, оно именно работает без детерминизма, вне зависимости от остальной программы, и фича — пытаться работать даже если у меня ошибка синхронизации. Я детализировал Автор: Alexander G .Дата: 06.01.09 |
| Re: Race condition | |
| От: | remark | ||
| Дата: | 16.01.09 16:19 | ||
| Оценка: | 26 (5) | ||
| Здравствуйте, Alexander G, Вы писали: AG>Есть ли тут гонка ? AG>
Зависит от того, что назвать гонкой. Если взять формальное определение POSIX (более одного потока одновременно обращаются к ячейке памяти, причём хотя бы один поток на запись), то гонка здесь, очевидно, есть. Со всеми вытекающими — UB. Microsoft явно не документирует модель памяти Windows, хотя, видимо, подоплёка такая же как и в POSIX. Поэтому тоже формально гонка и UB. C++09 — гонка и UB. С++03/С99 — формально не документировано. Java/C# — нет гонок (в том смысле, что считываемое значение детерминировано). Если смотреть с практической стороны — т.е. гонка есть "нехорошее" поведение, то, в принципе, должно работать на всех распространенных аппаратных платформах и компиляторах. На всех распространенных аппаратных платформах сохранения/загрузки размером со слово по выровненным адресам будут вообще атомарны (на X86/Itanium, на которых работает Windows, даже и 16-ти байтные обращения будут атомарны). А что бы компилятор это попортил,.. ну он должен быть сильно невменяемым (хотя опять же — это не гарантируется). Но самое интересное начинается дальше. Твой пример — это т.н. "синхронизация на флаге" (немного модифицированная). Она работает. Но если мы возьмём т.н. "синхронизацию на флаге с зависимым состоянием", то что ты приводил в одном из постов, то она уже не будет работать:
Если тебя интересует MSVC+Windows, то это аппаратные платформы IA-32, Intel 64 И IA-64. На IA-32/Intel 64 это не будет работать из-за возможных переупорядочиваний компилятором, т.е. компилятор волен сгенерировать, допустим, такой код:
На IA-64 это не будет работать из-за возможных переупорядочиваний компилятором + из-за возможных переупорядочиваний аппаратным обеспечением. Т.е. если даже компилятор сгенерирует "правильный" код, т.е. аппаратное обеспечение всё-равно вольно это выполнить так, как показано в примере с переупорядочиванием компилятором. Для MSVC самый просто способ решить это — это добавить volatile к определению initialized. MSVC фактически расширяет значение volatile с примитива для работы с оборудованием до примитива синхронизации потоков. Т.о. volatile и подавит переупорядочивания компилятором, и заставит компилятор вставить необходимые инструкции (барьеры памяти) для подавления переупорядочивания аппаратным обеспечением (для IA-64). Однако это будет иметь некоторую ран-тайм стоимость на IA-64. Если барьер при сохранении в initialized не интересен, т.к. будет выполнен только один раз за время работы программы, то барьер при считывании может быть неприятен, т.к. будет добавлять порядка пары десятков тактов. Хорошие новости то, что его можно элиминировать следующим образом:
Ключевой момент здесь — это замена флага initialized на указатель p_data. Это — искуственная инджекция зависимости по данным между загрузкой флага и загрузкой самого объекта. Все современные процессоры автоматически соблюдают упорядочивание при зависимости по данным, в данном случае — между загрузкой указателя и загрузкой данных через этот указатель. Реализация сохранения во флаг тут реализуется так же — т.е. через volatile. Хотя, в принципе, конечно, желательно кодировать не против конкретной платформы, а против портабельного стандарта; и спрашивать не "почему это может не работать?", а "кем гарантировано, что это должно работать?" (я имею в виду использование мьютексов и т.д). Однако курьёз в том, что Windows (я так понимаю речь идёт о нём) не предоставляет средств для корректной инициализации объектов (по крайней мере до Vista). В POSIX threads (pthread) всё проще — он из покон веков предоставляет макрос PTHREAD_MUTEX_INITIALIZER для *статической* инициализации глобальных мьютексов. boost.thread с (по-моему) 1.35 предоставляет аналогичную функциональность и под Windows (кстати, это была одна из основных причин для переписывания мьютекса с CRITICAL_SECTION на собственный велосипед на Interlocked операциях). А тащить в проект thread-win32 или boost только из-за этого видится не меньшим злом... Есть ещё вариант с т.н. Lakos Singleton (nifty counting trick). Он инициализируется при инициализации глобальных объектов, однако старается это делать до любого его использования:
В принципе, его можно обмануть при желании, поэтому желательно этот заголовочный файл включить во все файлы проекта, stdafx.h подойдёт. Тогда объект будет создан во время инициализации глобальных объектов, однако гарантированно до любого его использования. Плюс не вносит никаких издержек во время выполнения, т.е. никаких дополнительных проверок и индирекций при доступе к объекту. |
| Re[13]: Race condition | |
| От: | remark | ||
| Дата: | 16.01.09 16:36 |
| Здравствуйте, Кодт, Вы писали: К>Сам мьютекс (и все остальные примитивы lock-based синхронизации) нужно инициализировать в рантайме. К>Следовательно, мы всё равно хоть что-то, да должны сделать на lock-free. Это только в мире Windows, в мире POSIX (или pthread-win32) мы можем просто написать:
в мире boost:
|
| Re[14]: Race condition | |
| От: | alsemm | ||
| Дата: | 16.01.09 18:08 | ||
| Оценка: | 10 (1) | ||
| Здравствуйте, remark, Вы писали: R>Здравствуйте, Кодт, Вы писали: К>>Сам мьютекс (и все остальные примитивы lock-based синхронизации) нужно инициализировать в рантайме. К>>Следовательно, мы всё равно хоть что-то, да должны сделать на lock-free. R>Это только в мире Windows, в мире POSIX (или pthread-win32) мы можем просто написать: R>
R>в мире boost: R>
Это все работать будет, если только все эти красивые макросы разворачиваются в константы. В MS Windows никаких констант нет, значит инициализация g_guard будет не на этапе загрузки секций кода из exe в память, а позже, когда возможно уже какие-то глобальные функции вызываются. to Alexander G: вас спасут именованные мьютексы. Если из нескольких потоков запрашивать создать мьютекс с одним и тем же именем, то создан будет он только в одном потоке, а в остальные будет возвращен handle созданного мьютекса. Как-то так:
По хорошему надо еще где-то
делать, т.к. если память выделенную для globals_ система может и почистит сама, после завершения приложения, то именованный мьютекс — вряд-ли. Алексей |
| Re[2]: Race condition | |
| От: | Alexander G | ||
| Дата: | 16.01.09 19:51 |
| Здравствуйте, remark, Вы писали: R>Microsoft явно не документирует модель памяти Windows, хотя, видимо, подоплёка такая же как и в POSIX. Поэтому тоже формально гонка и UB. В MSDN Library нашел, как мне кажется, достаточную для моего случая гарантию: http://msdn.microsoft.com/en-us/library/ms684122.aspx
R>
R>Ключевой момент здесь — это замена флага initialized на указатель p_data. Это — искуственная инджекция зависимости по данным между загрузкой флага и загрузкой самого объекта. Все современные процессоры автоматически соблюдают упорядочивание при зависимости по данным, в данном случае — между загрузкой указателя и загрузкой данных через этот указатель. Реализация сохранения во флаг тут реализуется так же — т.е. через volatile. Супер. Только непонятно почему _ReadWriteBarrier(); Ещё, можно ли убрать volatile и заменить его на _WriteBarrier(); перед сохранением указателя ? R>Хотя, в принципе, конечно, желательно кодировать не против конкретной платформы, а против портабельного стандарта; и спрашивать не "почему это может не работать?", а "кем гарантировано, что это должно работать?" (я имею в виду использование мьютексов и т.д). Вообще да, но данный случай именно против платформы. И желательно без мьютексов. Выше я описал причину странных требований. R>Есть ещё вариант с т.н. Lakos Singleton (nifty counting trick). Он инициализируется при инициализации глобальных объектов, однако старается это делать до любого его использования: ... R>В принципе, его можно обмануть при желании, поэтому желательно этот заголовочный файл включить во все файлы проекта, stdafx.h подойдёт. Тогда объект будет создан во время инициализации глобальных объектов, однако гарантированно до любого его использования. Плюс не вносит никаких издержек во время выполнения, т.е. никаких дополнительных проверок и индирекций при доступе к объекту. Если файл включается во все единицы, то всё равно, почему при использование этого из другого глобального объекта (возможно, тоже с внутренним связыванием) не может произойти до создания этого объекта ? R> |
| Re[15]: Race condition | |
| От: | Alexander G | ||
| Дата: | 16.01.09 20:47 |
| Здравствуйте, alsemm, Вы писали: A>to Alexander G: вас спасут именованные мьютексы. Если из нескольких потоков запрашивать создать мьютекс с одним и тем же именем, то создан будет он только в одном потоке, а в остальные будет возвращен handle созданного мьютекса. Интересно. Для моего случая видимо стоит делать мьютекс так, чтобы он не был один на все копии процесса:
Но мне больше нравится ответ remark, что можно и без мьютекса, если обеспечить правильный запрет переупорядочивания. A>т.к. если память выделенную для globals_ система может и почистит сама, после завершения приложения, то именованный мьютекс — вряд-ли. Очистит, и, если им больше никто не владеет, удалит. Можно попробовать в одном процессе создать, занять и не вернуть, а в другом создать и ждать, при завершении первого во втором д.б. WAIT_ABANDONED. |
| Re[15]: Race condition | |
| От: | remark | ||
| Дата: | 17.01.09 08:38 |
| Здравствуйте, alsemm, Вы писали: R>>Это только в мире Windows, в мире POSIX (или pthread-win32) мы можем просто написать: R>>
R>>в мире boost: R>>
A>Это все работать будет, если только все эти красивые макросы разворачиваются в константы. Именно. Они в константы и будут всегда разворачиваться. A>В MS Windows никаких констант нет, значит инициализация g_guard будет не на этапе загрузки секций кода из exe в память, а позже, когда возможно уже какие-то глобальные функции вызываются. В Windows можно использовать boost или pthread-win32. А нужно именно Win32 API, то InitOnce. Если до Vista, то ничего хорошего для этой цели нет — либо самому морочиться с Interlocked операциями, либо premature pessimization в виде именованных объектов ядра для межпроцессного взаимодействия. |
| Re[15]: Race condition | |
| От: | remark | ||
| Дата: | 17.01.09 09:05 |
| Здравствуйте, alsemm, Вы писали: A>to Alexander G: вас спасут именованные мьютексы. Если из нескольких потоков запрашивать создать мьютекс с одним и тем же именем, то создан будет он только в одном потоке, а в остальные будет возвращен handle созданного мьютекса. A>Как-то так: A>
A>По хорошему надо еще где-то A>
A>делать, т.к. если память выделенную для globals_ система может и почистит сама, после завершения приложения, то именованный мьютекс — вряд-ли. Не считая того, что получаем 3 системных вызова вместо + блокировку вместо пары инструкций процессора... Если при каждом получении синглтона создавать по хендлу, то через пару минут лимит хендлов будет достигнут и новые хендлы на event не будут создаваться — будем получать 0 вместо синглтона. Плюс new (std::nothrow) не работает так, как ты думаешь, соотв. будем получать полное зависание процесса при исключении из new (std::nothrow). Плюс CloseHandle() надо делать на каждый CreateMutex(), а не один раз. Один CloseHandle() ничего не закроет, т.к. хэндлов насоздавалась уже целая туча. Плюс ОС сама разрушит event при завершении процесса. |
| Re[16]: Race condition | |
| От: | alsemm | ||
| Дата: | 17.01.09 11:18 |
| Здравствуйте, remark, Вы писали: A>>В MS Windows никаких констант нет, значит инициализация g_guard будет не на этапе загрузки секций кода из exe в память, а позже, когда возможно уже какие-то глобальные функции вызываются. R>В Windows можно использовать boost или pthread-win32. Там полностью свои сампописные муьютексы/крит. секции? R>А нужно именно Win32 API, то InitOnce. Если до Vista, то ничего хорошего для этой цели нет — либо самому морочиться с Interlocked операциями, А чего с ними мучаться — взять просто boost::detail::spinlock и все дела. R>либо premature pessimization в виде именованных объектов ядра для межпроцессного взаимодействия. Ну getInstance() — это не тот случай, тобы такты экономить, не часто она и вызываться-то будет. Алексей R> |
| Re[16]: Race condition | |
| От: | alsemm | ||
| Дата: | 17.01.09 11:28 |
| Здравствуйте, remark, Вы писали: R>Не считая того, что получаем 3 системных вызова вместо + блокировку вместо пары инструкций процессора... R>Если при каждом получении синглтона создавать по хендлу, то через пару минут лимит хендлов будет достигнут и новые хендлы на event не будут создаваться — будем получать 0 вместо синглтона. Мой косяк, согласен. См. ниже. R>Плюс new (std::nothrow) не работает так, как ты думаешь, соотв. будем получать полное зависание процесса при исключении из new (std::nothrow). Замечание по существу, но ыглядит как придирка, т.к. ничего не мешает или 'new Globals();' в try/catch обернуть, или просто принять, что конструктор Globals не должен кидать исключений. R>Плюс CloseHandle() надо делать на каждый CreateMutex(), а не один раз. Один CloseHandle() ничего не закроет, т.к. хэндлов насоздавалась уже целая туча. Да нет проблем:
R>Плюс ОС сама разрушит event при завершении процесса. Так это ж хорошо. Алексей |
| Re[16]: Race condition | |
| От: | alsemm | ||
| Дата: | 17.01.09 11:39 |
| Здравствуйте, Alexander G, Вы писали: AG>Здравствуйте, alsemm, Вы писали: A>>to Alexander G: вас спасут именованные мьютексы. Если из нескольких потоков запрашивать создать мьютекс с одним и тем же именем, то создан будет он только в одном потоке, а в остальные будет возвращен handle созданного мьютекса. AG>Интересно. AG>Для моего случая видимо стоит делать мьютекс так, чтобы он не был один на все копии процесса: Ну не знаю, Globals::globals_-то у вас одна на все процессы будет, если она в dll. А если в exe, то да, нужно имя мьютекса уникальное для каждого процесса заводить. AG>Но мне больше нравится ответ remark, что можно и без мьютекса, если обеспечить правильный запрет переупорядочивания. "Скользкое решение" imho, хотя и довольно элегантное. С именованным мьтексом выглядит брутально, но зато все очевидно. Кстати в pthread-ах именованные мьютексы тоже есть, это к вопросу о переносимости. AG>Можно попробовать в одном процессе создать, занять и не вернуть, а в другом создать и ждать, при завершении первого во втором д.б. WAIT_ABANDONED. А это для чего? Алексей |
| Re[17]: Race condition | |
| От: | Alexander G | ||
| Дата: | 17.01.09 11:43 |
| Здравствуйте, alsemm, Вы писали: AG>>Можно попробовать в одном процессе создать, занять и не вернуть, а в другом создать и ждать, при завершении первого во втором д.б. WAIT_ABANDONED. A>А это для чего? Это просто к тому, что при аварийном завершении процесса ничего страшного не произойдёт. |
| Re[17]: Race condition | |
| От: | Alexander G | ||
| Дата: | 17.01.09 11:51 |
| Здравствуйте, alsemm, Вы писали: AG>>Интересно. AG>>Для моего случая видимо стоит делать мьютекс так, чтобы он не был один на все копии процесса: A>Ну не знаю, Globals::globals_-то у вас одна на все процессы будет, если она в dll. А если в exe, то да, нужно имя мьютекса уникальное для каждого процесса заводить. не уловил, какая разница dll/exe, если синглтон один штук на одно адресное пространство в обоих случаях. В случае dll вообще прийдётся забыть про Win 2000, которая интересует многих Автор: Alexander G .Дата: 30.12.08 Вопрос: Для какой наиболее ранней версии Windows 9x/NT должно работать разрабатываемое вами ПО ? http://msdn.microsoft.com/en-us/library/ms682583(VS.85).aspx
|
| Re[18]: Race condition | |
| От: | alsemm | ||
| Дата: | 17.01.09 12:30 |
| Здравствуйте, Alexander G, Вы писали: AG>Здравствуйте, alsemm, Вы писали: AG>>>Интересно. AG>>>Для моего случая видимо стоит делать мьютекс так, чтобы он не был один на все копии процесса: A>>Ну не знаю, Globals::globals_-то у вас одна на все процессы будет, если она в dll. А если в exe, то да, нужно имя мьютекса уникальное для каждого процесса заводить. AG>не уловил, какая разница dll/exe, если синглтон один штук на одно адресное пространство в обоих случаях. Разница такая, что если Globals::globals_ определена в dll, то она будет одинаковая во всех процессах, которые эту dll используют. Следовательно имя мьютекса должно быть одинаковым для все процессов из которых Globals::getInstance() может быть вызвано. Если Globals::globals_ определена в exe, то на каждый экземпляр запущенного приложения будет своя копия Globals::globals_. Следовательно нужно имя мьютекса выбирать для каждого процесса свое, чтобы оно не конфликтовало с "соседями". AG>http://msdn.microsoft.com/en-us/library/ms682583(VS.85).aspx AG> Досадно. Возможно решить проблему можно статически слинковав свою dll с той DLL, которую дополнительно грузит система. Надо только узнать, чего она там грузит. Алексей |
| Re[19]: Race condition | |
| От: | Alexander G | ||
| Дата: | 17.01.09 12:52 |
| Re[2]: Lakos Singleton (nifty counting trick). | |
| От: | Alexander G | ||
| Дата: | 17.01.09 13:36 |
| Здравствуйте, remark, Вы писали: R>Есть ещё вариант с т.н. Lakos Singleton (nifty counting trick). Он инициализируется при инициализации глобальных объектов, однако старается это делать до любого его использования:
R>В принципе, его можно обмануть при желании, поэтому желательно этот заголовочный файл включить во все файлы проекта, stdafx.h подойдёт. Тогда объект будет создан во время инициализации глобальных объектов, однако гарантированно до любого его использования. Плюс не вносит никаких издержек во время выполнения, т.е. никаких дополнительных проверок и индирекций при доступе к объекту. Чем это лучше/хуже синглтона Мейерса, форсированного через конструктор глобального объекта с внутренним связыванием ? R> |
| Re[20]: Race condition | |
| От: | alsemm | ||
| Дата: | 17.01.09 13:50 |
| Здравствуйте, Alexander G, Вы писали: AG>Здравствуйте, alsemm, Вы писали: A>>Разница такая, что если Globals::globals_ определена в dll, то она будет одинаковая во всех процессах, которые эту dll используют. AG>С чего бы это ? она одна на каждый процесс. AG>Разве что если она в Shared section — так зачем её туда пихать? Согласен, я ошибся. A>>Досадно. Возможно решить проблему можно статически слинковав свою dll с той DLL, которую дополнительно грузит система. Надо только узнать, чего она там грузит. AG>Нет, это не решит проблему. Почему? AG>Советую сходить по моей ссылке, найти процитированный текст, и почитать что там ещё рядом написано Да вроде почитал. С другой стороны, почему бы просто явно не инициализировать глобальные данные в DllMain? Алексей |
| Re[21]: Race condition | |
| От: | Alexander G | ||
| Дата: | 17.01.09 14:19 |
| Здравствуйте, alsemm, Вы писали: A>Да вроде почитал. Тогда д.б. понятно, что ничего из дополнительных dll оттуда вызвать не получится. A>С другой стороны, почему бы просто явно не инициализировать глобальные данные в DllMain? Да. Это действительно правильное решение для dll. Крит.секции, анонимные мьютексы и события, слоты TLS можно проинициализировать из DllMain. Или, то же самое, но лучше — через конструкторы глобальных объектов. У меня не dll. В ехе допускается "безответственный" подход, при котором многое происходит до main и неизвестно что именно. Поэтому в ехе и нужны хитрые инициализации. |
| Re[3]: Lakos Singleton (nifty counting trick). | |
| От: | remark | ||
| Дата: | 18.01.09 10:31 | ||
| Оценка: | 1 (1) | ||
| Здравствуйте, Alexander G, Вы писали: R>>В принципе, его можно обмануть при желании, поэтому желательно этот заголовочный файл включить во все файлы проекта, stdafx.h подойдёт. Тогда объект будет создан во время инициализации глобальных объектов, однако гарантированно до любого его использования. Плюс не вносит никаких издержек во время выполнения, т.е. никаких дополнительных проверок и индирекций при доступе к объекту. AG>Чем это лучше/хуже синглтона Мейерса, форсированного через конструктор глобального объекта с внутренним связыванием ? Если глобальный объект с внутренним связыванием описать в заголовочном файле, т.е. он попадёт во все единицы трансляции, то на первый взгляд получается примерно то же самое... Хммм... хотя не совсем — в минглтоне Майерса у нас статический объект в функции, соотв. доступ к нему всегда будет иметь некоторый оверхед, как минимум проверку, как максимум какую-то синхронизацию. В синглтоне Лакос мы вольны размещать объект как хочется. R>> AG> |
| Re[3]: Race condition | |
| От: | remark | ||
| Дата: | 18.01.09 10:48 | ||
| Оценка: | 5 (1) | ||
| Здравствуйте, Alexander G, Вы писали: R>>Ключевой момент здесь — это замена флага initialized на указатель p_data. Это — искуственная инджекция зависимости по данным между загрузкой флага и загрузкой самого объекта. Все современные процессоры автоматически соблюдают упорядочивание при зависимости по данным, в данном случае — между загрузкой указателя и загрузкой данных через этот указатель. Реализация сохранения во флаг тут реализуется так же — т.е. через volatile. AG>Супер. Только непонятно почему _ReadWriteBarrier(); "Для порядка". Должен был быть и барьер компилятора и аппаратный. Аппаратный убрали, компилятора должен остаться. Хотя, возможно, из-за зависимости по-управлению, он тут и не обязателен, но это такая область, где лучше переборщить, чем недоборщить. AG>Ещё, можно ли убрать volatile и заменить его на _WriteBarrier(); перед сохранением указателя ? Нет. Ну точнее так — на x86 должно работать. А на IA-64 обязателен аппаратный барьер. R>>Есть ещё вариант с т.н. Lakos Singleton (nifty counting trick). Он инициализируется при инициализации глобальных объектов, однако старается это делать до любого его использования: AG>... R>>В принципе, его можно обмануть при желании, поэтому желательно этот заголовочный файл включить во все файлы проекта, stdafx.h подойдёт. Тогда объект будет создан во время инициализации глобальных объектов, однако гарантированно до любого его использования. Плюс не вносит никаких издержек во время выполнения, т.е. никаких дополнительных проверок и индирекций при доступе к объекту. AG>Если файл включается во все единицы, то всё равно, почему при использование этого из другого глобального объекта (возможно, тоже с внутренним связыванием) не может произойти до создания этого объекта ? Идея такая, что если какой-то глобальный объект использует наш синглтон, то data.h должен быть подключен в этот файл, и data_initializer_ будет определен в этом файле раньше того глобального объекта. Хотя, если включаемые файлы подключать не "сверху", а и в середине исходных файлов, то можно его обмануть... |
| Re[17]: Race condition | |
| От: | remark | ||
| Дата: | 18.01.09 10:51 |
| Здравствуйте, alsemm, Вы писали: A>Здравствуйте, remark, Вы писали: A>>>В MS Windows никаких констант нет, значит инициализация g_guard будет не на этапе загрузки секций кода из exe в память, а позже, когда возможно уже какие-то глобальные функции вызываются. R>>В Windows можно использовать boost или pthread-win32. A>Там полностью свои сампописные муьютексы/крит. секции? Да. R>>А нужно именно Win32 API, то InitOnce. Если до Vista, то ничего хорошего для этой цели нет — либо самому морочиться с Interlocked операциями, A>А чего с ними мучаться — взять просто boost::detail::spinlock и все дела. Ну boost::detail::spinlock наврядли можно назвать Win32 API. R>>либо premature pessimization в виде именованных объектов ядра для межпроцессного взаимодействия. A>Ну getInstance() — это не тот случай, тобы такты экономить, не часто она и вызываться-то будет. Откуда такая уверенность? Alexander G не хочет volatile и Interlocked добавлять, а тут уже 4 системных вызова получается. Я вполне могу представить себе ситуации, когда обращение к синглтону вынужденно получается очень частым... |
| Re[17]: Race condition | |
| От: | remark | ||
| Дата: | 18.01.09 11:04 |
| Здравствуйте, alsemm, Вы писали: R>>Плюс new (std::nothrow) не работает так, как ты думаешь, соотв. будем получать полное зависание процесса при исключении из new (std::nothrow). A>Замечание по существу, но ыглядит как придирка, т.к. ничего не мешает или 'new Globals();' в try/catch обернуть, или просто принять, что конструктор Globals не должен кидать исключений. Меня просто смутило (std::nothrow). Обычно, если пишут псевдо-код, то — просто new. R>>Плюс CloseHandle() надо делать на каждый CreateMutex(), а не один раз. Один CloseHandle() ничего не закроет, т.к. хэндлов насоздавалась уже целая туча. A>Да нет проблем: Это уже 4 системных вызова получается... Нет, как теоретическо-абстрактное упражнение, это конечно интересно, но с практической т.з... |
| Re[17]: Race condition | |
| От: | remark | ||
| Дата: | 18.01.09 11:10 |
| Здравствуйте, bkat, Вы писали: B>Здравствуйте, Alexander G, Вы писали: AG>>Если бы всё было так просто, я бы действительно взял КС. B>Все время забываю, что тебе нужна гарантия в случае если B>кто-то пожелает вызвать getInstance при создании глобальных объектов. B>Тут извини. Я пас... B>Чем-то придется пожертвовать. Жертвовать не обязательно — достаточно мьютекс с возможностью статической инициализации (boost, pthread). |
| Re[19]: Race condition | |
| От: | remark | ||
| Дата: | 18.01.09 11:16 | ||
| Оценка: | 1 (1) | ||
| Здравствуйте, bkat, Вы писали: AG>>Я действительно хочу много странного, но именно это странным не является. Требование чтобы синглтон работал при конструировании глобальных объектов, в том числе и других синглтонов — стандартное требование. Фактически, самый простой известный синглтон — синглтон Мейерса — решает в первую очередь именно эту задачу — при этом создавая взамен проблемы с многопоточностью и с exception safety. B>Понятно. B>У меня просто другая философия. B>Подобную глобальную инициализацию/деинициализацию предпочитаю делать явной, B>например, создав специальный объект в main. B>На зависимостях между глобальными данными уже достаточно много шишек набил. Если речь идёт о приложении — то полностью согласен. Однако, если речь о библиотечном коде, то возможность вызова до main из нескольких потоков полезна. Понятно, что такое не очень частая ситуация, и скорее может рассматриваться как паталогия. Однако всё равно придётся добавлять в документацию дополнительные предупреждения "Вы не должны...", и совесть всё равно не будет спокойна, т.к. документацию всё равно никто не читает, поэтому это будет решаться только шишками пользователей... Если сделать полностью пуле-непробиваемое решение, то и предупреждений никаких не надо и на душе спокойно — "если хотите, используйте самым извращенными способами, со стороны библиотеки никаких препятствий нет". |
| Re[18]: Race condition | |
| От: | alsemm | ||
| Дата: | 18.01.09 11:23 |
| Здравствуйте, remark, Вы писали: R>Ну boost::detail::spinlock наврядли можно назвать Win32 API. Я этого и не утверждал. Посмотрел на boost-овые мьютексы. boost::mutex нельзя использовать для защиты доступа к глобальным данным: у него дефолтовый конструктор определен. А вот boost::detail::basic_timed_mutex вполне подойдет. R>Откуда такая уверенность? Alexander G не хочет volatile и Interlocked добавлять, а тут уже 4 системных вызова получается. Я вполне могу представить себе ситуации, когда обращение к синглтону вынужденно получается очень частым... Например? Алексей |
| Re[22]: Race condition | |
| От: | alsemm | ||
| Дата: | 18.01.09 11:28 |
| AG>Тогда д.б. понятно, что ничего из дополнительных dll оттуда вызвать не получится. Я ж написал про статическую линковку, а не про LoadLibrary. AG>У меня не dll. В ехе допускается "безответственный" подход, при котором многое происходит до main и неизвестно что именно. Поэтому в ехе и нужны хитрые инициализации. Тогда или именованный мьютекс, или boost::detail::basic_timed_mutex вполне подойдут. Алексей |
| Re[18]: Race condition | |
| От: | Alexander G | ||
| Дата: | 18.01.09 13:20 |
| Здравствуйте, remark, Вы писали: R>>>либо premature pessimization в виде именованных объектов ядра для межпроцессного взаимодействия. A>>Ну getInstance() — это не тот случай, тобы такты экономить, не часто она и вызываться-то будет. R>Откуда такая уверенность? Alexander G не хочет volatile и Interlocked добавлять, а тут уже 4 системных вызова получается. Я вполне могу представить себе ситуации, когда обращение к синглтону вынужденно получается очень частым... Именно. Мой синглтон, как я уже писал, — набор жучков для отладки, такой чтобы в креш-дамп писалась нужная информация. На данный момент это, допустим, получение некоторой системной информации, плюс регистрация блоков памяти некоторых объектов. В будущем, по мере анализа крэшдампов новых версий, туда будет что-то добавляться, что-то убираться, и не исключён вызов getInstance() из более узких мест, чем можно предположить сейчас. "Затащить" в глубину цикла ссылку на синглтон плюс убрать потом, когда больше та информация не нужна — много лишней возни. Особенно в случае регистрации блоков памяти, когда синглтон нужен из своих операторов new/delete — это ж нужно настраивать сматрпоинтеры, и из них вызвать явно деструктор плюс delete-с-контекстом. В общем, мне нужен синглтон, желательно без блокировок, и без преждевременной пессимизации — что бы не говорили теоретики |
| Re[4]: Race condition | |
| От: | Аноним 155 | ||
| Дата: | 18.01.09 13:21 |
| AG>>Ещё, можно ли убрать volatile и заменить его на _WriteBarrier(); перед сохранением указателя ? R>Нет. R>Ну точнее так — на x86 должно работать. А на IA-64 обязателен аппаратный барьер. а разве _WriteBarrier не является и аппаратным барьером тоже? т.е. как может перегруппироваться на процессоре IA-64 код:
, чтобы начать неправильно работать? |
| Re[23]: Race condition | |
| От: | Alexander G | ||
| Дата: | 18.01.09 13:27 |
| Здравствуйте, alsemm, Вы писали: AG>>Тогда д.б. понятно, что ничего из дополнительных dll оттуда вызвать не получится. A>Я ж написал про статическую линковку, а не про LoadLibrary. Статическая линковка — это, я понимаю, добавление соотв функций в таблицу импорта ? Если так, то не поможет. Возможно сменит проявление с дедлока на AV или ещё что-то — но не более того. Там по ссылкам всё написано. |
| Re[19]: Race condition | |
| От: | alsemm | ||
| Дата: | 18.01.09 13:32 |
| Здравствуйте, Alexander G, Вы писали: AG>Мой синглтон, как я уже писал, — набор жучков для отладки, такой чтобы в креш-дамп писалась нужная информация. На данный момент это, допустим, получение некоторой системной информации, плюс регистрация блоков памяти некоторых объектов. В будущем, по мере анализа крэшдампов новых версий, туда будет что-то добавляться, что-то убираться, и не исключён вызов getInstance() из более узких мест, чем можно предположить сейчас. "Затащить" в глубину цикла ссылку на синглтон плюс убрать потом, когда больше та информация не нужна — много лишней возни. Особенно в случае регистрации блоков памяти, когда синглтон нужен из своих операторов new/delete — это ж нужно настраивать сматрпоинтеры, и из них вызвать явно деструктор плюс delete-с-контекстом. AG>В общем, мне нужен синглтон, желательно без блокировок, и без преждевременной пессимизации — что бы не говорили теоретики Тогда не удобнее-ли оформить своих жучков в виде отдельной dll? Инициализацию глобальных данных делать в DllMain без всяких синхронизаций лишних. Алексей |
| Re[20]: Race condition | |
| От: | Alexander G | ||
| Дата: | 18.01.09 13:45 |
| Здравствуйте, alsemm, Вы писали: A>Тогда не удобнее-ли оформить своих жучков в виде отдельной dll? Инициализацию глобальных данных делать в DllMain без всяких синхронизаций лишних. Нет. dll принесёт свои неудобства. На самом деле сейчас жучки вполне работоспособны с инициализацией в конструкторе глобальной переменной. Но хотелось бы увеличить уровень защищённости от дурня (меня же |
| Re[24]: Race condition | |
| От: | alsemm | ||
| Дата: | 18.01.09 13:46 |
| Здравствуйте, Alexander G, Вы писали: AG>Здравствуйте, alsemm, Вы писали: AG>>>Тогда д.б. понятно, что ничего из дополнительных dll оттуда вызвать не получится. A>>Я ж написал про статическую линковку, а не про LoadLibrary. AG>Статическая линковка — это, я понимаю, добавление соотв функций в таблицу импорта ? AG>Если так, то не поможет. Возможно сменит проявление с дедлока на AV или ещё что-то — но не более того. Там по ссылкам всё написано. Статическая линковка с библиотекой A.dll приведет к тому что, LoadLibrary(A.dll) будет сделано до вызова вашей DllMain. Соответ. когда надо будет создавать именованный мьютекс и неявно будет сделан LoadLibrary(A.dll), то он ничего по факту делать не будет (счетчик ссылок на A.dll увеличит просто). http://msdn.microsoft.com/en-us/library/ms682583.aspx: Я это так понимаю, что если в адресное пространство заранее загрузить все dll, в которых определены функции, которые вы собрались вызывать из своей DllMain то никаких проблем не будет. Алексей |
| Re[21]: Race condition | |
| От: | alsemm | ||
| Дата: | 18.01.09 13:48 |
| Здравствуйте, Alexander G, Вы писали: AG>Нет. dll принесёт свои неудобства. Дело ваше конечно, но в чем неудобства? Алексей |
| Re[22]: Race condition | |
| От: | Alexander G | ||
| Дата: | 18.01.09 15:29 |
| Здравствуйте, alsemm, Вы писали: A>Дело ваше конечно, но в чем неудобства? Ну что проще — вставить инициализацию отсюда Автор: remark вместо существующей, или:Дата: 16.01.09 — придумать бинарный интерфейс между dll и exe — добавить файл в build и deployment |
| Re[4]: Race condition | |
| От: | Alexander G | ||
| Дата: | 18.01.09 15:32 |
| Здравствуйте, remark, Вы писали: R>Идея такая, что если какой-то глобальный объект использует наш синглтон, то data.h должен быть подключен в этот файл, и data_initializer_ будет определен в этом файле раньше того глобального объекта. Раньше определён — раньше создан ? Почему ? |
| Re[4]: Race condition | |
| От: | Alexander G | ||
| Дата: | 18.01.09 16:07 |
| Здравствуйте, remark, Вы писали: R>"Для порядка". Должен был быть и барьер компилятора и аппаратный. Аппаратный убрали, компилятора должен остаться. Хотя, возможно, из-за зависимости по-управлению, он тут и не обязателен, но это такая область, где лучше переборщить, чем недоборщить. Может у меня не хватает фантазии, но я не редставляю, как компилятор может испортить. Мне кажется, что такое
можно заменить на такое
при этом барьер на патологический случай, если копилятор вздумает делать что-то до проверки указателя, а потом, при успешной проверке, отменять что наделал. AG>>Ещё, можно ли убрать volatile и заменить его на _WriteBarrier(); перед сохранением указателя ? R>Нет. R>Ну точнее так — на x86 должно работать. А на IA-64 обязателен аппаратный барьер. http://msdn.microsoft.com/en-us/library/f20w0x5e(VS.80).aspx ? |
| Re[5]: Race condition | |
| От: | remark | ||
| Дата: | 19.01.09 08:50 |
| Здравствуйте, Аноним, Вы писали: AG>>>Ещё, можно ли убрать volatile и заменить его на _WriteBarrier(); перед сохранением указателя ? R>>Нет. R>>Ну точнее так — на x86 должно работать. А на IA-64 обязателен аппаратный барьер. А>а разве _WriteBarrier не является и аппаратным барьером тоже? Документация оставляет желать лучшего. Но по факту на x86 _ReadWriteBarrier() не выливается в mfence. Хотя с другой стороны в документации написано, что _ReadWriteBarrier() это "как volatile"... А>т.е. как может перегруппироваться на процессоре IA-64 код: , чтобы начать неправильно работать? Я имел в виду вот такой вариант (соотв. исходя из того, что _WriteBarrier — это НЕ аппаратный барьер):
|
| Re[5]: Race condition | |
| От: | remark | ||
| Дата: | 19.01.09 09:04 | ||
| Оценка: | 1 (1) | ||
| Здравствуйте, Alexander G, Вы писали: AG>Здравствуйте, remark, Вы писали: R>>Идея такая, что если какой-то глобальный объект использует наш синглтон, то data.h должен быть подключен в этот файл, и data_initializer_ будет определен в этом файле раньше того глобального объекта. AG>Раньше определён — раньше создан ? Почему ? Это всегда гарантировалось — если глобальный объект определен раньше другово в единице трансляции, то он и создастся раньше. Это только между разными единицами трансляции порядок не определен. |
| Re[5]: Race condition | |
| От: | remark | ||
| Дата: | 19.01.09 09:09 | ||
| Оценка: | +1 | ||
| Здравствуйте, Alexander G, Вы писали: AG>Здравствуйте, remark, Вы писали: R>>"Для порядка". Должен был быть и барьер компилятора и аппаратный. Аппаратный убрали, компилятора должен остаться. Хотя, возможно, из-за зависимости по-управлению, он тут и не обязателен, но это такая область, где лучше переборщить, чем недоборщить. AG>Может у меня не хватает фантазии, но я не редставляю, как компилятор может испортить. Мне кажется, что такое AG>при этом барьер на патологический случай, если копилятор вздумает делать что-то до проверки указателя, а потом, при успешной проверке, отменять что наделал. А смысл, если _ReadWriteBarrier() ничего не стОит в ран-тайм? AG>>>Ещё, можно ли убрать volatile и заменить его на _WriteBarrier(); перед сохранением указателя ? R>>Нет. R>>Ну точнее так — на x86 должно работать. А на IA-64 обязателен аппаратный барьер. AG>http://msdn.microsoft.com/en-us/library/f20w0x5e(VS.80).aspx AG> AG>? У них, честно говоря, такая дерьмовая документация, что уже отчаялся в ней что-либо понять. Нет, что б явно и чётко всё написать, всё какими-то намёками выражаются... По моим представлениям, если бы эти _ReadWriteBarrier/_WriteBarrier/_ReadBarrier были бы и аппаратными барьерами, то на x86 _ReadWriteBarrier должен был бы выливаться в инструкцию mfence. Этого не происходит. Поэтому я считаю, что это — только барьеры компилятора... Хотя интересно было бы поглядеть как _WriteBarrier компилируется на IA-64 — я не знаю... |
| Re[6]: Race condition | |
| От: | Alexander G | ||
| Дата: | 19.01.09 12:10 |
| Здравствуйте, remark, Вы писали: R>У них, честно говоря, такая дерьмовая документация, что уже отчаялся в ней что-либо понять. Нет, что б явно и чётко всё написать, всё какими-то намёками выражаются... R>По моим представлениям, если бы эти _ReadWriteBarrier/_WriteBarrier/_ReadBarrier были бы и аппаратными барьерами, то на x86 _ReadWriteBarrier должен был бы выливаться в инструкцию mfence. Этого не происходит. Поэтому я считаю, что это — только барьеры компилятора... Хотя интересно было бы поглядеть как _WriteBarrier компилируется на IA-64 — я не знаю... x86
x64
никакой mfence на х86 и х64 не нужно ? |
| Re[6]: Race condition | |
| От: | Аноним 401 | ||
| Дата: | 19.01.09 12:27 |
| Здравствуйте, remark, Вы писали: ... А какой практический смысл есть в чисто "НЕ аппаратных" барьерах, если, полюбому, на процессоре всё может перемешаться, как ему угодно? |
| Re[7]: Race condition | |
| От: | remark | ||
| Дата: | 19.01.09 19:14 |
| Здравствуйте, Аноним, Вы писали: А>Здравствуйте, remark, Вы писали: А>... А>А какой практический смысл есть в чисто "НЕ аппаратных" барьерах, если, полюбому, на процессоре всё может перемешаться, как ему угодно? Есть. С их помощью можно (1) упорядочивать между потоком и обработчиком сигнала для этого же потока, (2) упорядочивать между 2 потоками, которые выполняются на одном процессоре, (3) использовать, если упорядочивание на аппаратном уровне выполняется другими средствами (обычно в таком случае барьер компилятора стоит при всех обращениях, т.к. он бесплатный; а цена аппаратного барьера амортизируется на множество обращений; хороший пример RCU в ядре Linux). Так же стоит отметить, что барьеры компилятора включены в C++0x — там смотри std::atomic_signal_fence(std::memory_order). |
| Re[7]: Race condition | |
| От: | remark | ||
| Дата: | 19.01.09 19:16 |
| Здравствуйте, Alexander G, Вы писали: AG>Здравствуйте, remark, Вы писали: R>>У них, честно говоря, такая дерьмовая документация, что уже отчаялся в ней что-либо понять. Нет, что б явно и чётко всё написать, всё какими-то намёками выражаются... R>>По моим представлениям, если бы эти _ReadWriteBarrier/_WriteBarrier/_ReadBarrier были бы и аппаратными барьерами, то на x86 _ReadWriteBarrier должен был бы выливаться в инструкцию mfence. Этого не происходит. Поэтому я считаю, что это — только барьеры компилятора... Хотя интересно было бы поглядеть как _WriteBarrier компилируется на IA-64 — я не знаю... AG>x86 AG>x64 AG>никакой mfence на х86 и х64 не нужно ? На IA-32 и Intel 64 (x86-32/64) барьеры не нужны. А на IA-64 (Itanium) при volatile обращениях ты должен увидеть инструкции ld.acq/st.rel (load-acquire/store-release) вместо обычных ld/st (load/store). |
| Re[8]: Race condition | |
| От: | Alexander G | ||
| Дата: | 19.01.09 20:14 |
| Здравствуйте, remark, Вы писали: R>На IA-32 и Intel 64 (x86-32/64) барьеры не нужны. А на IA-64 (Itanium) при volatile обращениях ты должен увидеть инструкции ld.acq/st.rel (load-acquire/store-release) вместо обычных ld/st (load/store). Ну т.е. нет никаких свидетельств что барьеры "хуже" volatile ? (ассемблера IA64 не знаю, для IA64 не собираю) R> |
| Re[9]: Race condition | |
| От: | remark | ||
| Дата: | 20.01.09 06:02 |
| Здравствуйте, Alexander G, Вы писали: AG>Здравствуйте, remark, Вы писали: R>>На IA-32 и Intel 64 (x86-32/64) барьеры не нужны. А на IA-64 (Itanium) при volatile обращениях ты должен увидеть инструкции ld.acq/st.rel (load-acquire/store-release) вместо обычных ld/st (load/store). AG>Ну т.е. нет никаких свидетельств что барьеры "хуже" volatile ? В смысле хуже? |
| Re[10]: Race condition | |
| От: | Alexander G | ||
| Дата: | 20.01.09 06:58 |
| Здравствуйте, remark, Вы писали: R>В смысле хуже? Я имею ввиду, что можно заменить voiatile на барьеры, как пишут в MSDN, т.к. машииный код такой же |
| Re[11]: Race condition | |
| От: | remark | ||
| Дата: | 20.01.09 14:29 |
| Здравствуйте, Alexander G, Вы писали: AG>Здравствуйте, remark, Вы писали: R>>В смысле хуже? AG>Я имею ввиду, что можно заменить voiatile на барьеры, как пишут в MSDN, т.к. машииный код такой же Мммм... ну у меня всё ещё остаётся подозрение, что volatile в MSVC — это и барьер компилятора и аппаратный, в барьеры — это только компилятора. Т.к. иначе не понятно почему _ReadWriteBarrier() не эмитит mfence на x86. Плюс они, так сказать, ортогональны друг другу. В том смысле, что например при захвате мьютекса нужен барьер типа load-acquire (или #LoadLoad|#LoadStore membar в терминологии SPARC), и volatile load даёт именно это; точно же смоделировать это с помощью барьеров не получается, т.к. _ReadBarrier() и _WriteBarrier() каждый по отдельности недостаточны, а _ReadWriteBarrier() слишком тяжелый (хммм... точнее он бы слишком тяжелым, если бы эмитил mfence, а так вообще не понятно, что это такое). Честно говоря, я чем больше читаю их документацию, тем больше запутываюсь |
| Re[12]: Race condition | |
| От: | Alexander G | ||
| Дата: | 20.01.09 20:54 |
| Здравствуйте, remark, Вы писали: R>Т.к. иначе не понятно почему _ReadWriteBarrier() не эмитит mfence на x86. Зачем там mfence ? Кстати, почему-то __mf intrinsic только для Itanium. Видимо, кроме инлайн ассемблера никак этот самый _mfence на х86 не сделать. нужен ли он ? |
| Re[13]: Race condition | |
| От: | remark | ||
| Дата: | 21.01.09 10:19 |
| Здравствуйте, Alexander G, Вы писали: AG>Здравствуйте, remark, Вы писали: R>>Т.к. иначе не понятно почему _ReadWriteBarrier() не эмитит mfence на x86. AG>Зачем там mfence ? Полный барьер на x86 должен отображаться на mfence. AG>Кстати, почему-то __mf intrinsic только для Itanium. Видимо, кроме инлайн ассемблера никак этот самый _mfence на х86 не сделать. нужен ли он ? _mm_mfence() в заголовке <intrin.h>. Да, нужен для многих алгоритмов синхронизации. |
| Re[9]: Race condition | |
| От: | Quasi | ||
| Дата: | 30.01.09 08:08 | ||
| Оценка: | 30 (1) | ||
| Здравствуйте, Alexander G, Вы писали: AG>Ну т.е. нет никаких свидетельств что барьеры "хуже" volatile ? AG>(ассемблера IA64 не знаю, для IA64 не собираю) R>> Полностью согласен с remark, однако хотелось бы прояснить некоторые моменты: Для процессоров IA-32 и Intel 64 (Intel Core 2 Duo, Intel Atom, Intel Core Duo, Pentium M, Intel Xeon, P6 family, Pentium 4 и более ранних), AMD64 и более ранних операции чтения из памяти и запись всегда имеют семантику Acquire и Release соответственно (если не считать "string operations" и "streaming stores"). Однако это не мешает и вышеописанные процессоры (кроме совсем ранних) переупорядочивают чтение и предшествующую запись, если они не относятся к одному адресу памяти.
Из-за возможного переупорядочивания в результате выполнения возможна ситуация, когда r1 = r2 = 0 Некоторые алгоритмы могут полагаться на строгую последовательность операций чтения и предшествующей записи, поэтому реализация барьера для процессора должна содержать либо инструкцию mfence, либо инструкцию с префиксом "LOCK" в случае вышеописанных процессоров. Соответсвенно, по факту функция _ReadWriteBarrier не реализует барьер памяти для процессора, а лишь барьер памяти для компилятора:
Единственное, что вводит в ложное заблуждение в документации Microsoft, так это:
По упорядочиванию процессоров: Intel® 64 and IA-32 Architectures Software Developer’s Manual (7.2 Memory ordering) AMD64 Architecture Programmer’s Manual (7.2 Multiprocessor Memory Access Ordering) Intel® Itanium® Architecture Software Developer’s Manual (Part II, 2.2 Memory Ordering) |
| Re[10]: Race condition | |
| От: | remark | ||
| Дата: | 30.01.09 08:51 | ||
| Оценка: | 4 (1) +1 | ||
| Здравствуйте, Quasi, Вы писали: Q>Для процессоров IA-32 и Intel 64 (Intel Core 2 Duo, Intel Atom, Intel Core Duo, Pentium M, Intel Xeon, P6 family, Pentium 4 и более ранних), Q>AMD64 и более ранних операции чтения из памяти и запись всегда имеют семантику Acquire и Release соответственно (если не считать "string operations" и "streaming stores"). Однако это не мешает и вышеописанные процессоры (кроме совсем ранних) переупорядочивают чтение и предшествующую запись, если они не относятся к одному адресу памяти. АФАИК, они переупорядочивают даже обращения к одному адресу памяти. Свои обращения процессор-то, конечно, увидит в программном порядке, а вот другие процессоры могут увидеть обращения к одному адресу памяти в отличном от программного. Единственное, что сейчас видимо не переупорядочивается (хотя АФАИК это не документировано) — это запись и чтение по одному адресу, но с разными размерами (т.н. collocation trick):
Q>Из-за возможного переупорядочивания в результате выполнения возможна ситуация, когда r1 = r2 = 0 Q>Некоторые алгоритмы могут полагаться на строгую последовательность операций чтения и предшествующей записи, поэтому реализация барьера для процессора должна содержать либо инструкцию mfence, либо инструкцию с префиксом "LOCK" в случае вышеописанных процессоров. Соответсвенно, по факту функция _ReadWriteBarrier не реализует барьер памяти для процессора, а лишь барьер памяти для компилятора: Q>
Q>Единственное, что вводит в ложное заблуждение в документации Microsoft, так это: Q>
А почему это вводит в заблуждение? Ведь volatile в данном случае тоже не приведёт к добавлению mfence/lock. |
| Re[11]: Race condition | |
| От: | Quasi | ||
| Дата: | 30.01.09 09:18 |
| Здравствуйте, remark, Вы писали: R>Здравствуйте, Quasi, Вы писали: Q>>Для процессоров IA-32 и Intel 64 (Intel Core 2 Duo, Intel Atom, Intel Core Duo, Pentium M, Intel Xeon, P6 family, Pentium 4 и более ранних), Q>>AMD64 и более ранних операции чтения из памяти и запись всегда имеют семантику Acquire и Release соответственно (если не считать "string operations" и "streaming stores"). Однако это не мешает и вышеописанные процессоры (кроме совсем ранних) переупорядочивают чтение и предшествующую запись, если они не относятся к одному адресу памяти. R>АФАИК, они переупорядочивают даже обращения к одному адресу памяти. Свои обращения процессор-то, конечно, увидит в программном порядке, а вот другие процессоры могут увидеть обращения к одному адресу памяти в отличном от программного. В общем, с этой точки зрения да, такое поведение документированно. R>А почему это вводит в заблуждение? Ведь volatile в данном случае тоже не приведёт к добавлению mfence/lock. В то время как, _ReadWriteBarrier ( ) никакого отношения к упорядочиванию операций в памяти процессором не имеет, по факту. R> |
| Re[12]: Race condition | |
| От: | Alexander G | ||
| Дата: | 02.02.09 12:14 |
| Здравствуйте, Quasi, Вы писали: R>>А почему это вводит в заблуждение? Ведь volatile в данном случае тоже не приведёт к добавлению mfence/lock. Q> Q>В то время как, _ReadWriteBarrier ( ) никакого отношения к упорядочиванию операций в памяти процессором не имеет, по факту. Т.е. надо вставлять _mm_fence вручную ?
|
| Re[13]: Race condition | |
| От: | Quasi | ||
| Дата: | 02.02.09 15:54 | ||
| Оценка: | 30 (1) | ||
| Здравствуйте, Alexander G, Вы писали: AG>Т.е. надо вставлять _mm_fence вручную ? AG>
Принципиальным здесь является наличие зависимости по данным, которая гарантирует, что на всех современных процессорах чтения указателя p_data и данных по указателю не будут переупорядочены. Однако, это не налагает ограничений на переупорядочивание записи p_data и данных. В данном случае для предотвращения переупорядочивания достаточно записи указателя p_data c release семантикой (на x86, Intel 64 и AMD64 для этого ничего дополнительно не требуется). Если верить документации Microsoft по квалификатору volatile, то для переносимости на другие аппаратные платформы можно воспользоваться трюком, который описал remark:
На других компиляторах без соответствующих acquire, release семантик для чтения, записи volatile данных можно использовать атомарную операцию, аналогичную ::InterlockedExchange в WINAPI, имеющую либо release, либо fence семантику (full memory barrier, как в случае с ::InterlockedExchange). |
| Re[14]: Race condition | |
| От: | Alexander G | ||
| Дата: | 02.02.09 16:18 |
| Здравствуйте, Quasi, Вы писали: Q>Здравствуйте, Alexander G, Вы писали: AG>>Т.е. надо вставлять _mm_fence вручную ? AG>>
Q> В данном случае для предотвращения переупорядочивания достаточно записи указателя p_data c release семантикой (на x86, Intel 64 и AMD64 для этого ничего дополнительно не требуется). Я не зочу volatile т.к. его смысл зависит от компилятора. Может тогда так ?
|
| Re[15]: Race condition | |
| От: | Quasi | ||
| Дата: | 02.02.09 16:57 |
| Здравствуйте, Alexander G, Вы писали: AG>Я не зочу volatile т.к. его смысл зависит от компилятора. AG>Может тогда так ? AG>
Как раз для семейства Intel Itanium (_M_IA64) барьера компилятора не достаточно, под Windows проще всего:
|
| Re[16]: Race condition | |
| От: | Quasi | ||
| Дата: | 02.02.09 17:04 |
| Здравствуйте, Quasi, Вы писали: Q>Здравствуйте, Alexander G, Вы писали: Q>Как раз для семейства Intel Itanium (_M_IA64) барьера компилятора не достаточно, под Windows проще всего: Q>
MSDN, InterlockedExchangePointer:
|
| Re[15]: Race condition | |
| От: | remark | ||
| Дата: | 02.02.09 17:14 | ||
| Оценка: | 11 (2) +1 | ||
| Здравствуйте, Alexander G, Вы писали: AG>Я не зочу volatile т.к. его смысл зависит от компилятора. AG>Может тогда так ? _WriteBarrier() ещё больше зависит от компилятора (intrinsic MSVC). А если код только под MSVC, то проще и правильнее использовать volatile. |
| Re[16]: Race condition | |
| От: | Alexander G | ||
| Дата: | 02.02.09 17:57 |
| Здравствуйте, Quasi, Вы писали: Q>Как раз для семейства Intel Itanium (_M_IA64) барьера компилятора не достаточно, под Windows проще всего: Q>
Так можно ?
|