Змінюй хід війни! Допомагай ЗСУ!

Safe singleton in C+ 03 - Разминка для мозга

🟡 11:51 Відбій тривоги в Харківська область.Зверніть увагу, тривога ще триває у:- Куп’янський район- Харківський район- Липецька територіальна громада- Вовчанська територіальна громада#Харківська_область
  • 🟡 11:51 Відбій тривоги в Харківська область.Зверніть увагу, тривога ще триває у:- Куп’янський район- Харківський район- Липецька територіальна громада- Вовчанська територіальна громада#Харківська_область
  • #41
  • 🟡 11:51 Відбій тривоги в Харківська область.Зверніть увагу, тривога ще триває у:- Куп’янський район- Харківський район- Липецька територіальна громада- Вовчанська територіальна громада#Харківська_область
  • #42
Идея с интерлоком :клас:
Но теоретически у нас может быть два static LONG volatile lock = sisNotInitialized, а следовательно два инстанса синглетона, один из которых "потеряеться", если два потока одновременно войдут в эту точку.

Конечно может (точнее конечно не два статических экземпляра, а один экземпляр, который прошли два потока). И что?)
 
  • 🟡 11:51 Відбій тривоги в Харківська область.Зверніть увагу, тривога ще триває у:- Куп’янський район- Харківський район- Липецька територіальна громада- Вовчанська територіальна громада#Харківська_область
  • #43
Шото вроде такого:

Код:
enum SingletonInitState {sisNotInitialized = -1, sisInitializing = 0, sisInitialized = 1};

        static LONG volatile lock = sisNotInitialized;

при запуске lock, в рассматриваемом случае, с наибольшей вероятностью будет равен 0 (т.е. sisInitializing), но компилятор этого не гарантирует, т.е. в зависимости от компилятора и ОС, там может быть либо ноль либо мусор. Полагаться на значение lock нельзя =)

Поэтому static переменную тут никак не получится использовать для синхронизации. Так что похоже, кроме именованного объекта синхронизации ОС, других вариантов нет :)
 
  • 🟡 11:51 Відбій тривоги в Харківська область.Зверніть увагу, тривога ще триває у:- Куп’янський район- Харківський район- Липецька територіальна громада- Вовчанська територіальна громада#Харківська_область
  • #44
при запуске lock, в рассматриваемом случае, с наибольшей вероятностью будет равен 0 (т.е. sisInitializing), но компилятор этого не гарантирует, т.е. в зависимости от компилятора и ОС, там может быть либо ноль либо мусор. Полагаться на значение lock нельзя =)

Поэтому static переменную тут никак не получится использовать для синхронизации. Так что похоже, что кроме именованного объекта синхронизации ОС, других вариантов нет :)

Как это нельзя, если стандарт х03 говорит о инициализации статических переменных области видимости метода не позднее момента первого вызова метода? )
Читайте стандарт )
Именованый объект синхронизации - вот где платформозависимость в явном виде.
 
  • 🟡 11:51 Відбій тривоги в Харківська область.Зверніть увагу, тривога ще триває у:- Куп’янський район- Харківський район- Липецька територіальна громада- Вовчанська територіальна громада#Харківська_область
  • #45
при запуске lock, в рассматриваемом случае, с наибольшей вероятностью будет равен 0 (т.е. sisInitializing), но компилятор этого не гарантирует, т.е. в зависимости от компилятора и ОС, там может быть либо ноль либо мусор. Полагаться на значение lock нельзя =)

Почему это еще? Она будет успешно проинициализирована до первого использования. Вот если бы ее вынесли в мембер класса - тогда да, возможны варианты.
 
  • 🟡 11:51 Відбій тривоги в Харківська область.Зверніть увагу, тривога ще триває у:- Куп’янський район- Харківський район- Липецька територіальна громада- Вовчанська територіальна громада#Харківська_область
  • #46
Как это нельзя, если стандарт х03 говорит о инициализации статических переменных области видимости метода не позднее момента первого вызова метода? )
Читайте стандарт )

если это происходит в одном потоке - совершенно верно, но у нас то задача для многопоточной среды. ;)

Кстати, в твоем примере сразу две проблемы - первую (со статиком) я выше описал, а вторая - твой способ с интерлоком работать в многопоточной среде не будет. Т.к. инициализация lock не атомарна, как правильно заметил lugal.

Чтобы понять проблему - достаточно рассмотреть такую ситуацию: один поток вошол в Instance, но приостановился перед инструкцией записи в ячейку lock, на этой строке:
Код:
static LONG volatile lock = sisNotInitialized;

в это время второй поток тоже вызвал Instance и выполнил инструкции вплоть до конца этого блока:
Код:
                if (InterlockedCompareExchange(&lock, sisNotInitialized, sisInitializing) == sisNotInitialized)
                {
                    _instance = new Singleton();
                    lock = sisInitialized;
                    break;
                }
т.е. второй поток успел инициализировать синглтон.
В этот момент первый поток получает управление и завершает запись в lock = sisNotInitialized, далее первый поток тоже делает InterlockedCompareExchange и входит в инициализацию повторно (ведь он перед проверкой сам записал в lock значение sisNotInitialized) :)
 
  • 🟡 11:51 Відбій тривоги в Харківська область.Зверніть увагу, тривога ще триває у:- Куп’янський район- Харківський район- Липецька територіальна громада- Вовчанська територіальна громада#Харківська_область
  • #47
Ребят, о чём вы пишете?)
Давайте асмовский разворот, ага?)
Или давайте проще: готовый пример, где этот код будет валиться)
 
  • 🟡 11:51 Відбій тривоги в Харківська область.Зверніть увагу, тривога ще триває у:- Куп’янський район- Харківський район- Липецька територіальна громада- Вовчанська територіальна громада#Харківська_область
  • #48
Почему это еще? Она будет успешно проинициализирована до первого использования. Вот если бы ее вынесли в мембер класса - тогда да, возможны варианты.

если не ошибаюсь, мы возможно в одной компании работаем, недавно был дев толк на эту тему, нужно было зайти послушать :)

Ребят, о чём вы пишете?)
Давайте асмовский разворот, ага?)
Или давайте проще: готовый пример, где этот код будет валиться)

я выше подробно разжевал где и как он будет валиться
 
  • 🟡 11:51 Відбій тривоги в Харківська область.Зверніть увагу, тривога ще триває у:- Куп’янський район- Харківський район- Липецька територіальна громада- Вовчанська територіальна громада#Харківська_область
  • #49
если это происходит в одном потоке - совершенно верно, но у нас то задача для многопоточной среды. ;)


Да какая разница, если инициализация статических членов происходит ДО.

Кстати, в твоем примере сразу две проблемы - первую (со статиком) я выше описал, а вторая - твой способ с интерлоком работать в многопоточной среде не будет. Т.к. инициализация lock не атомарна, как правильно заметил lugal.

Да, интерлок реализован просто так, для красоты или для работы исключительно в однопоточной одноядерной среде, да.

Чтобы понять проблему - достаточно рассмотреть такую ситуацию: один поток вошол в Instance, но приостановился перед инструкцией записи в ячейку lock, на этой строке:
Код:
static LONG volatile lock = sisNotInitialized;

Чтобы понят проблему, нужно открыть стандарт и понять, что инициализация происходит ДО!

я выше подробно разжевал где и как он будет валиться

Ну я вот олень, не понимаю с позиции того, что знаю. Давайте готовый пример рассмотрим с проблемами.
 
  • 🟡 11:51 Відбій тривоги в Харківська область.Зверніть увагу, тривога ще триває у:- Куп’янський район- Харківський район- Липецька територіальна громада- Вовчанська територіальна громада#Харківська_область
  • #50
если не ошибаюсь, мы возможно в одной компании работаем, недавно был дев толк на эту тему, нужно было зайти послушать :)

Врядли, я уже давно нигде не работаю :) А девтолки слушать какой смысл, там пионеры с глазами горящими начитавшиеся хабра обычно вещают, нахуй мне это надо? Надо будет - сам разберусь, зачем время на проповеди тратить.
 
  • 🟡 11:51 Відбій тривоги в Харківська область.Зверніть увагу, тривога ще триває у:- Куп’янський район- Харківський район- Липецька територіальна громада- Вовчанська територіальна громада#Харківська_область
  • #51
В этот момент первый поток получает управление и завершает запись в lock = sisNotInitialized, далее первый поток тоже делает InterlockedCompareExchange и входит в инициализацию повторно (ведь он перед проверкой сам записал в lock значение sisNotInitialized) :)

Поток НИЧЕГО НЕ ПИШЕТ ПРИ ВЫЗОВЕ МЕТОДА! Статическая переменная метода - это глобальная переменная, ограниченная областью видимости метода со специфической инициализацией. А то мы щас дойдём до того, что каждый вызов метода оказывается будет сбрасывать lock в sisNotInitialized.
 
  • 🟡 11:51 Відбій тривоги в Харківська область.Зверніть увагу, тривога ще триває у:- Куп’янський район- Харківський район- Липецька територіальна громада- Вовчанська територіальна громада#Харківська_область
  • #52
Можно еще с InterlockedCompareExchangeAcquire / InterlockedCompareExchangeRelease поиграться. Для инициализации.
 
  • 🟡 11:51 Відбій тривоги в Харківська область.Зверніть увагу, тривога ще триває у:- Куп’янський район- Харківський район- Липецька територіальна громада- Вовчанська територіальна громада#Харківська_область
  • #53
Чтобы понят проблему, нужно открыть стандарт и понять, что инициализация происходит ДО!

Нужно открыть стандарт и почитать внимательнее, если Instance вызывается ВО ВРЕМЯ инициализации, то никто не гарантирует что lock будет инициализирован ДО вызова Instance. Более того, проверка в MSVC от VS2010 показывает - Instance будет вызван ДО инициализации lock. Это одна проблема.

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

А теперь вторая проблема. Готовый пример рассмотрен выше. В твоем коде расшареный ресурс между двумя потоками - это переменная lock. В обоих потоках с этим ресурсом произовдится две операции:

1) Инициализация:
Код:
static LONG volatile lock = sisNotInitialized;

2) InterlockedCompareExchange

Первая операция не атомарна. Вторая атомарна. Для того чтобы гарантировать корректное обращение к расшаренному ресурсу, все операции к этому ресурсу должны быть атомарны. Это условие не удовлетворяется, поэтому с точки зрения многопоточности код не безопасен.
 
  • 🟡 11:51 Відбій тривоги в Харківська область.Зверніть увагу, тривога ще триває у:- Куп’янський район- Харківський район- Липецька територіальна громада- Вовчанська територіальна громада#Харківська_область
  • #54
Нужно открыть стандарт и почитать внимательнее, если Instance вызывается ВО ВРЕМЯ инициализации, то никто не гарантирует что lock будет инициализирован ДО вызова Instance. Более того, проверка в MSVC от VS2010 показывает - Instance будет вызван ДО инициализации lock. Это одна проблема.

Я всегда знал, что студийный компилер уёбищный )
Спецификацией установлено, что статические локальные переменные инициализируются при первом выполнении блока кода, в котором они объявлены. При первом - если что, это значит не после )
Другой вариант, что сама по себе инициализация многоходова и в теории действительно могут быть гонки. Но опять же, я не проверял.
 
  • 🟡 11:51 Відбій тривоги в Харківська область.Зверніть увагу, тривога ще триває у:- Куп’янський район- Харківський район- Липецька територіальна громада- Вовчанська територіальна громада#Харківська_область
  • #55
Спецификацией установлено, что статические локальные переменные инициализируются при первом выполнении блока кода, в котором они объявлены. При первом - если что, это значит не после )

процитирую автора статьи, которую ты приводил в предыдущей версии поста:
C++’s abstract machine is single-threaded, and C++ compilers may choose to generate thread-unsafe code from source

больше этот момент комментировать не буду - надоело :)

Я всегда знал, что студийный компилер уёбищный )

компиллер нормальный, просто нужно знать где искать грабли :D
 
  • 🟡 11:51 Відбій тривоги в Харківська область.Зверніть увагу, тривога ще триває у:- Куп’янський район- Харківський район- Липецька територіальна громада- Вовчанська територіальна громада#Харківська_область
  • #56
Я всегда знал, что студийный компилер уёбищный )
Спецификацией установлено, что статические локальные переменные инициализируются при первом выполнении блока кода, в котором они объявлены. При первом - если что, это значит не после )
Другой вариант, что сама по себе инициализация многоходова и в теории действительно могут быть гонки. Но опять же, я не проверял.

И таки будут. Клез прав.
Итого - осталось решить проблему безопасной инициализации.

Хочу поигаться с InterlockedCompareExchangeAcquire/Release - но сегодня лень, к тому же бесплатно.
 
  • 🟡 11:51 Відбій тривоги в Харківська область.Зверніть увагу, тривога ще триває у:- Куп’янський район- Харківський район- Липецька територіальна громада- Вовчанська територіальна громада#Харківська_область
  • #57
Вот дизасемблинг студийного кода:
Код:
static time_t Curtm = time(NULL);
001E13DE  mov         eax,dword ptr [$S1 (1E7140h)] 
001E13E3  and         eax,1 
001E13E6  jne         GetA+44h (1E1404h) 
001E13E8  mov         eax,dword ptr [$S1 (1E7140h)] 
001E13ED  or          eax,1 
001E13F0  mov         dword ptr [$S1 (1E7140h)],eax 
001E13F5  push        0    
001E13F7  call        time (1E1440h) 
001E13FC  add         esp,4 
001E13FF  mov         dword ptr [tm (1E7144h)],eax 
	return tm;
001E1404  mov         eax,dword ptr [tm (1E7144h)]

Что как бы намекате на то что несколько потоков могут одновременно пертереть друг после друга значение Curtm.
 
  • 🟡 11:51 Відбій тривоги в Харківська область.Зверніть увагу, тривога ще триває у:- Куп’янський район- Харківський район- Липецька територіальна громада- Вовчанська територіальна громада#Харківська_область
  • #58
klez, lugal - согласен с Вами)
 
  • 🟡 11:51 Відбій тривоги в Харківська область.Зверніть увагу, тривога ще триває у:- Куп’янський район- Харківський район- Липецька територіальна громада- Вовчанська територіальна громада#Харківська_область
  • #59
DCL в С++ без volatile работает херово :) В дебаге будет все ок, а в релизе заебешься искать чего там компилер наоптимизировал.
Мутех конечно хорошо, но дорого.

Немного отошел от С++..
Посему вопрос:
volatile уже гарантирует Memory Barrier?
 
  • 🟡 11:51 Відбій тривоги в Харківська область.Зверніть увагу, тривога ще триває у:- Куп’янський район- Харківський район- Липецька територіальна громада- Вовчанська територіальна громада#Харківська_область
  • #60
Немного отошел от С++..
Посему вопрос:
volatile уже гарантирует Memory Barrier?

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