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

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

  • Автор теми Автор теми lugal
  • Дата створення Дата створення
да нихера не подходит, не важно какой объект ты для синхронизации используешь, если ты его объявил статиком - это уже не рабочий вариант, т.к. статик переменная в момент обращения к ней из метода Instance может быть еще не инициализирована.

Схуяли? Если Instance - статик, а он обязан быть статик - то все будет хорошо.

При использовании именованного мьютекса, можно обойтись без статика, создавать мьютекс по имени прямо внутри метода Instance(). Таким образом мы не используем статик, а синхронизацию выполняет система.

Согласен.
 
Схуяли? Если Instance - статик, а он обязан быть статик - то все будет хорошо.

потому что кроме статика с объектом синхронизации класса MySinglton, в программе может быть еще масса других статик переменных. Инициализируются они все до main(). А теперь представь что будет, если до того как инициализируется MySingleton::s_syncRoot, произойдет инициализация другой статической переменной, которая во время инициализации обратится к MySingleton::Instance()?
А будет то что при вызове Instance() переменная MySingleton::s_syncRoot окажется еще не инициализированной, поэтому попытка использовать ее для синхронизации закончится крешем.
 
потому что кроме статика с объектом синхронизации класса MySinglton, в программе может быть еще масса других статик переменных. Инициализируются они все до main(). А теперь представь что будет, если до того как инициализируется MySingleton::s_syncRoot, произойдет инициализация другой статической переменной, которая во время инициализации обратится к MySingleton::Instance()?
А будет то что при вызове Instance() переменная MySingleton::s_syncRoot окажется еще не инициализированной, поэтому попытка использовать ее для синхронизации закончится крешем.

Дизайн значит хуевый если код полагается на порядок инициализации статиков. Не должно быть такой русской рулетки.
 
Дизайн значит хуевый если код полагается на порядок инициализации статиков. Не должно быть такой русской рулетки.

ну так в этом задача ТС-а и заключается - избавиться от этой русской рулетки. Есть идеи как сделать дизайн синглтона безопасным в этом отношении, без использования системных мьютексов?
Ведь порядок вызова статических инитиалайзеров не гарантирован компилятором.
 
ну так в этом задача ТС-а и заключается - избавиться от этой русской рулетки. Есть идеи как сделать дизайн синглтона безопасным в этом отношении, без использования системных мьютексов?
Ведь порядок вызова статических инитиалайзеров не гарантирован компилятором.

Ну там прагмы завсегда есть. Но это ****ос еще тот.

Чтоб избавиться от русской рулетки - надо дизайнить нормально, а не костыли для одноглазых потом изобретать.
 
Ну там прагмы завсегда есть. Но это ****ос еще тот.

Чтоб избавиться от русской рулетки - надо дизайнить нормально, а не костыли для одноглазых потом изобретать.

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

Кстати volatile в С++ не обспечивает атомарность, поэтому полагаться на него в многопоточной среде в корне неправильно.
 
оно-то понятно, только в жизни редко так бывает чтобы дали денег все передизайнить, поэтому приходится решать проблемы в том что есть... :)

Тогда прагмы, как уж совсем все плохо. Опять же, пофиксить статику надо один раз, чем ******* с гонками в потоках если локи не юзать.

Кстати volatile в С++ не обспечивает атомарность, поэтому полагаться на него в многопоточной среде в корне неправильно.

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



Я так понимаю, лугал написал какую-то ***ню и теперь хочет нашей помощи чтоб его зарплаты не лишили :)
Ибо вопрос реально слишком простой чтобы
а) без помощи гугла не разобраться самому
б) не сделать тупо в лоб с именоваными мутехами.
с) в стартпосле не писать код заведомо ***** вообще не синглетона, а какой-то неведомой ***ни
д) все-таки погуглить немного
 
Останнє редагування:
Я так понимаю, лугал написал какую-то ***ню и теперь хочет нашей помощи чтоб его зарплаты не лишили :)
Ты как всегда все неправильно понимешь :)
Задача к моей работе не имеет никакого отношения, мне ее рассказли недавно, и я как человек любознательный и професиональный вот все пытаюсь найти решение.
Полная постоновка задачи, если тебе интересно звучит так:
В разрабатываемой коммерческой библиотеки сделать базовый класс синглтон, требующий минимального дополнительного кода, для превращения любого класса, который его наследует в синглтон.
Синглтон должен быть потоко и контекстно безопасен.

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

с) в стартпосле не писать код заведомо ***** вообще не синглетона, а какой-то неведомой ***ни
Вообщето код из старт поста это есть искомый синглетон в С++ 11, так как стандартом языка в данном случае гарантируеться полностью потокобезопасная инициализация статистеской внутренней переменной в момент первого вызова функции.
д) все-таки погуглить немного
Так в гугли хоть много хоть немного особо данный кейс и не решен. DCL эфективен в многопоточности, но с глобал скопом проблемы, из за инициализации объекта синхронизации.
 
Полная постоновка задачи, если тебе интересно звучит так:
В разрабатываемой коммерческой библиотеки сделать базовый класс синглтон, требующий минимального дополнительного кода, для превращения любого класса, который его наследует в синглтон.
Синглтон должен быть потоко и контекстно безопасен.

Ну допустим.

Я рассматривал различные варианты, в том числе и с именованным мьютексом.
В случае с именнованным мьютексом непонятно кто должен закрывать хендлы, это раз. Два - дорого по времени.

Враппер хендла его замечательно закроет при выходе из области видимости. Что дорого - то да. Но тебе или шашечки или ехать? В SMP и твоем древнем плюсе других простых вариантов нет.

Вообщето код из старт поста это есть искомый синглетон в С++ 11, так как стандартом языка в данном случае гарантируеться полностью потокобезопасная инициализация статистеской внутренней переменной в момент первого вызова функции.

****ец, ты совсем валера что-ли. Глянь на свой код _внимательно_. Какого хера Instance не статическая? :іржач:

Так в гугли хоть много хоть немного особо данный кейс и не решен. DCL эфективен в многопоточности, но с глобал скопом проблемы, из за инициализации объекта синхронизации.

С SMP будут проблемы так или иначе. А проблемы с глобал скопом надо решать не припарками для мертвых, а глобально. Что есть означает - не инициализировать синглетоны в статике.
 
Так. Объясните мне, дураку, почему, например, такая конструкция не будет удовлетворять условиям?

Код:
class Singleton
{
    static Singleton* volatile _instance;

protected:

..........................

public:

..........................

    static Singleton* Instance()
    {
        static LONG volatile lock = 0;
        if (!_instance)
        {
            if (!InterlockedCompareExchange(&lock, 1, 0))
            {
                _instance = new Singleton();
            }
            else
            {
                while (!_instance)
                {
                    Sleep(0);
                }
            }
        }
        return _instance;
    }

..........................
};

Это просто пример реализации.
 
Останнє редагування:
Так. Объясните мне, дураку, почему, например, такая конструкция не будет удовлетворять условиям?

Код:
class Singleton
{
    static Singleton* volatile _instance;

protected:

..........................

public:

..........................

    static Singleton* Instance()
    {
        static LONG volatile lock = 0;
        if (!_instance)
        {
            if (!InterlockedCompareExchange(&lock, 1, 0))
            {
                _instance = new Singleton();
            }
            else
            {
                while (!_instance)
                {
                    Sleep(0);
                }
            }
        }
        return _instance;
    }

..........................
};

Это просто пример реализации.

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

Интерлок лочит шину памяти, так что другой поток этот путь при всём желании не успеет пройти. Плюс мемори барьер, как бонус для мультипроцессорности.

Другое дело, что значение _instance должно быть инициализировано видом:

Singleton* Singleton::_instance(NULL);

И вот тут лично у меня вопрос: эта инициализация относится к этапу инициализации константных объектов или этапу инициализации статических и глобальных переменных.
Если к этапу константных, то всё будет хорошо, а вот нет, то в теории мы можем заиметь рандомный ненулевой адрес указателя, что провалит все тесты. Но, о чудо, в такой ситуации мы можем проверять не _instance на ноль, а lock на ноль (точнее не на ноль, а на... два :), но это уже детали).
 
Интерлок лочит шину памяти, так что другой поток этот путь при всём желании не успеет пройти. Плюс мемори барьер, как бонус для мультипроцессорности.

Лочит на момент compare exchange. И все. На этом лок заканчивается.

Другое дело, что значение _instance должно быть инициализировано видом:

Singleton* Singleton::_instance(NULL);

И вот тут лично у меня вопрос: эта инициализация относится к этапу инициализации константных объектов или этапу инициализации статических и глобальных переменных.
Если к этапу константных, то всё будет хорошо, а вот нет, то в теории мы можем заиметь рандомный ненулевой адрес указателя, что провалит все тесты. Но, о чудо, в такой ситуации мы можем проверять не _instance на ноль, а lock на ноль (точнее не на ноль, а на... два :), но это уже детали).

Этап второй. Лок проверять не выйдет, он не гарантирует _instance ничего.
 
Лочит на момент compare exchange. И все. На этом лок заканчивается.

Ну так больше и не надо.
В худшем случае при переключении контекста после выполнения интерлок-инструкции первым потоком, второй поток будет стоять перед выполнением интерлок-инструкции, которую он успешно провалит и перейдёт в цикл со слипом.

Этап второй. Лок проверять не выйдет, он не гарантирует _instance ничего.

Проверка лок гарантирует, что при состоянии:

a) lock = 0, _instance не инициализирован, блокировка не выполнена.
b) lock = 1, _instance не инициализирован, блокировка выполняется.
c) lock = 2, _instance инициализирован.

Код написать?)
 
Ну так больше и не надо.
В худшем случае при переключении контекста после выполнения интерлок-инструкции первым потоком, второй поток будет стоять перед выполнением интерлок-инструкции, которую он успешно провалит и перейдёт в цикл со слипом.

Видимо ты прав.

Проверка лок гарантирует, что при состоянии:

a) lock = 0, _instance не инициализирован, блокировка не выполнена.
b) lock = 1, _instance не инициализирован, блокировка выполняется.
c) lock = 2, _instance инициализирован.

Код написать?)

А покажи, а то я что-то туплю.
 

Я на самом деле тоже теоритезирую, потомушо сам как-то с этой ситуацией на приктике не сталкивался :)

А покажи, а то я что-то туплю.

С удовольствием. Сейчас только обеденную партию в Анреал закончим и напишу :)



Шото вроде такого:

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

class Singleton
{
private:

    static Singleton* _instance;

protected:

    Singleton() {}
    Singleton(Singleton const&);
    void operator = (Singleton const&);

public:

    static Singleton* Instance()
    {
        static LONG volatile lock = sisNotInitialized;

        switch (lock)
        {
            case sisNotInitialized:
            {
                if (InterlockedCompareExchange(&lock, sisNotInitialized, sisInitializing) == sisNotInitialized)
                {
                    _instance = new Singleton();
                    lock = sisInitialized;
                    break;
                }
            }
            case sisInitializing:
            {
                while (!_instance)
                {
                    Sleep(0);
                }
                break;
            }
        }

        return _instance;
    }

    virtual ~Singleton() {}
};
 
Останнє редагування:
Я на самом деле тоже теоритезирую, потомушо сам как-то с этой ситуацией на приктике не сталкивался :)



С удовольствием. Сейчас только обеденную партию в Анреал закончим и напишу :)



Шото вроде такого:

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

class Singleton
{
private:

    static Singleton* _instance;

protected:

    Singleton() {}
    Singleton(Singleton const&);
    void operator = (Singleton const&);

public:

    static Singleton* Instance()
    {
        static LONG volatile lock = sisNotInitialized;

        switch (lock)
        {
            case sisNotInitialized:
            {
                if (InterlockedCompareExchange(&lock, sisNotInitialized, sisInitializing) == sisNotInitialized)
                {
                    _instance = new Singleton();
                    lock = sisInitialized;
                    break;
                }
            }
            case sisInitializing:
            {
                while (!_instance)
                {
                    Sleep(0);
                }
                break;
            }
        }

        return _instance;
    }

    virtual ~Singleton() {}
};

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



С удовольствием. Сейчас только обеденную партию в Анреал закончим и напишу :)



Шото вроде такого:

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

class Singleton
{
private:

    static Singleton* _instance;

protected:

    Singleton() {}
    Singleton(Singleton const&);
    void operator = (Singleton const&);

public:

    static Singleton* Instance()
    {
        static LONG volatile lock = sisNotInitialized;

        switch (lock)
        {
            case sisNotInitialized:
            {
                if (InterlockedCompareExchange(&lock, sisNotInitialized, sisInitializing) == sisNotInitialized)
                {
                    _instance = new Singleton();
                    lock = sisInitialized;
                    break;
                }
            }
            case sisInitializing:
            {
                while (!_instance)
                {
                    Sleep(0);
                }
                break;
            }
        }

        return _instance;
    }

    virtual ~Singleton() {}
};

Идея с интерлоком :клас:
Но теоретически у нас может быть два static LONG volatile lock = sisNotInitialized, а следовательно два инстанса синглетона, один из которых "потеряеться", если два потока одновременно войдут в эту точку.
 
Идея с интерлоком :клас:
Но теоретически у нас может быть два static LONG volatile lock = sisNotInitialized, а следовательно два инстанса синглетона, один из которых "потеряеться", если два потока одновременно войдут в эту точку.

Сам понял что сказал?
 
Назад
Зверху Знизу