Re[8]: Как создать объект раньше остальных?
От: slavo  
Дата: 29.08.07 12:54
Оценка:
Здравствуйте, Кодт, Вы писали:

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


К>Можно записать проще:

К>Тем более, что в исходном коде у тебя
К>- гонки при инициализации критической секции

В этом коде при инициализации критической секции гонки? Статическая переменная ведь один раз создается. Или гонка происходит во время выполнения InitializeStaticCriticalSection?
bool InitializeStaticCriticalSection(CCriticalSection * i_pCS)
{
    i_pCS->Initialize();

    return false;
}

bool CCore::DoCreate()
{
    static CCriticalSection CS;
    static bool fCreated = InitializeStaticCriticalSection(&CS);
...


К>- нет кода деинициализации этой секции

Она статическая, ее не нужно деинициализировать.

К>А вот интересный вопрос: это предполагалось использовать в таком контексте?

К>
К>class CCore
К>{
К>public:
К>    CCore()
К>    {
К>        DoCreate();
К>    }
К>};

К>CCore& core()
К>{
К>    static CCore obj;
К>    return obj;
К>}
К>

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

Это используется так. Существует набор некоторых функций Foo, в которых делается следующее:
if(!CCore::DoCreate())
{
    return false;
}

// здесь обращаемся к членам CCore, которые создаются и инициализируются в CCore::DoCreate.


Функции эти могут вызываться когда угодно и где угодно, в том числе в конструкторах разных статических объектов.
Re[8]: Как создать объект раньше остальных?
От: slavo  
Дата: 29.08.07 13:19
Оценка:
Здравствуйте, Кодт, Вы писали:

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


К>Можно записать проще:

S>>
S>>bool CCore::DoCreate()
S>>{
К>    static long fCreated = 0;
К>    if( !fCreated ) // первая проверка - дешёвая (и плевать на гонки)
К>        if( !atomic_test_and_set(&fCreate,0,1) ) // вторая - строгая
К>            Create();
S>>}
S>>


Если первый поток пройдет atomic_test_and_set и войдет в Create(), то fCreated будет равна 1. Если в этот момент второй поток войдет в CCore::DoCreate, то проверит, что fCreated = 1 и вернет true, говоря дальнейшему коду в своем потоке, что CCore создан. А на самом деле он еще только создается в первом потоке.
Re[8]: Как создать объект раньше остальных?
От: Кодт Россия  
Дата: 29.08.07 13:24
Оценка:
Достаточно вообще InterlockedExchange.
static long volatile flag = 0;
if(!flag && !InterlockedExchange(&flag,1))
{
    // flag был 0, стал 1
    // начинаем инициализацию
}
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[6]: #pragma init_seg
От: Erop Россия  
Дата: 29.08.07 13:38
Оценка:
Здравствуйте, slavo, Вы писали:

S>Информация полезная, но не хотелось бы привязывать проект (и без того мутный) к таким тонкостям .

А почему тебе кажется, что описанное в документации поведение компилятора -- это более мутное решение, чем пляски с бубнами вокруг многопоточности и синглетонов.

Ещё раз обращаю твоё внимание, что Может быть и так (рассмотрим для ясности однопоточный случай):
class CSingleton {
public:
    static CSingleton& Get()
    {
        static CSingleton seed;
        return seed;
    }
    
    void Method() { assert( is_created ); }

private:
    bool is_created;

    CSingleton() : is_created( true ) {}
    ~CSingleton() { is_created = false; }
};

static class CA {
public:
    ~CA() { CSingleton::Get().Method(); }  // Тут-то всё и гавкнется... :(
} a;

static class CB {
public:
    CB() {  CSingleton::Get().Method(); }
} b;


Таки прямое решение намного лучше...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[9]: Как создать объект раньше остальных?
От: Кодт Россия  
Дата: 29.08.07 14:01
Оценка:
Здравствуйте, slavo, Вы писали:

К>>- гонки при инициализации критической секции

S>В этом коде при инициализации критической секции гонки? Статическая переменная ведь один раз создается. Или гонка происходит во время выполнения InitializeStaticCriticalSection?

В обоих местах гонка происходит.
Если CCriticalSection имеет нетривиальный конструктор, то он вызывается при первом прохождении через точку определения. Собственно, в этом и состоит блеск и нищета синглетона Майерса.
То же относится и к static bool fCreated = (нечто не являющееся константой времени компиляции)

К>>- нет кода деинициализации этой секции

S>Она статическая, ее не нужно деинициализировать.

Фигасе!
Очень смелое допущение. Хорошо, если твой код запускается в изолированном процессе — тогда все занятые ресурсы будут отпущены по завершении процесса.
А если это DLL — плагин или COM-сервер какой-нибудь?

S>Это используется так. Существует набор некоторых функций Foo, в которых делается следующее:

S>
S>if(!CCore::DoCreate())
S>{
S>    return false;
S>}

S>// здесь обращаемся к членам CCore, которые создаются и инициализируются в CCore::DoCreate.
S>

То есть, DoCreate и Create — это статические члены-функции? И они служат настройке статических переменных?
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[9]: Как создать объект раньше остальных?
От: Кодт Россия  
Дата: 29.08.07 14:01
Оценка:
Здравствуйте, slavo, Вы писали:

S>Если первый поток пройдет atomic_test_and_set и войдет в Create(), то fCreated будет равна 1. Если в этот момент второй поток войдет в CCore::DoCreate, то проверит, что fCreated = 1 и вернет true, говоря дальнейшему коду в своем потоке, что CCore создан. А на самом деле он еще только создается в первом потоке.


Да, согласен, погорячился.
Значит, нужно сделать мьютекс (критическую секцию) на технологии "синглетон ремарка".
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[9]: Как создать объект раньше остальных?
От: slavo  
Дата: 29.08.07 14:04
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Достаточно вообще InterlockedExchange.

К>
К>static long volatile flag = 0;
К>if(!flag && !InterlockedExchange(&flag,1))
К>{
К>    // flag был 0, стал 1
К>    // начинаем инициализацию
К>}
К>


В конце концов получилось вот что:

bool CCore::DoCreate()
{
    static long volatile fCreating = 0;

    if(!fCreating && !InterlockedExchange(&fCreating, 1))
    {
        if(!Create()) // Construct all core objects.
        {
            fCreating = 3; // To set free pending threads (3 - return false).
            return false;
        }

        fCreating = 2; // To set free pending threads (2 - return true).
    }

    while(1 == fCreating) // Wait until Create() has finished (pending).
    {
        Sleep(200);
    }

    if(3 == fCreating)
    {
        return false;
    }

    return true;
}


Только InterlockedExchange надо будет заменить на ассемблерный код, т.к. в этом месте WinAPI не доступны.
Re[9]: Как создать объект раньше остальных?
От: remark Россия http://www.1024cores.net/
Дата: 29.08.07 14:44
Оценка: 36 (3)
Здравствуйте, Кодт, Вы писали:

К>Достаточно вообще InterlockedExchange.

К>
К>static long volatile flag = 0;
К>if(!flag && !InterlockedExchange(&flag,1))
К>{
К>    // flag был 0, стал 1
К>    // начинаем инициализацию
К>}
К> (*)
К>


Проблема только в том, что в (*) мы можем оказаться с неинициализированным объектом

Есть DCSI — double-checked serialized initialization, он же DCL (double-check locking) и DCI (double-check initialization).
Это когда, пока один инициализирует объект, остальные ждут на мьютексе.

Есть DCSI-TSD — DCSI with thread-specific data
То же, но с копией указателя на объект в TSD.

Есть DCCI (double-checked concurrent initialization).
Это когда инициализация объекта идемпотентная. Т.е. все потоки, которые увидели неинициализированный объект, начинают его создавать. Далее кто-то один атомарно подменяет глобальный указатель на свой, остальные рушат свои объекты.

Эти методы гарантируют, что мы не окажемся после проверки созданности объекта, но с не созданным объектом.

Термины by Alexander Terekhov и David Butenhof. Подробнее здесь:
http://groups.google.com/group/comp.programming.threads/msg/a93eb83270621f60?hl=en&amp;
и далее по ветке


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[10]: Как создать объект раньше остальных?
От: slavo  
Дата: 29.08.07 15:45
Оценка:
Здравствуйте, Кодт, Вы писали:

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


К>>>- нет кода деинициализации этой секции

S>>Она статическая, ее не нужно деинициализировать.

К>Фигасе!

К>Очень смелое допущение. Хорошо, если твой код запускается в изолированном процессе — тогда все занятые ресурсы будут отпущены по завершении процесса.
К>А если это DLL — плагин или COM-сервер какой-нибудь?

Нет, это процесс.

К>То есть, DoCreate и Create — это статические члены-функции? И они служат настройке статических переменных?


Да, они статические и инициализируют статические члены.
Re[5]: Как создать объект раньше остальных?
От: Аноним  
Дата: 29.08.07 20:01
Оценка:
Здравствуйте, Jonathan, Вы писали:

J>Здравствуйте.

J>Я не понимаю одну вещь
J>Как эта идиома позволяет обеспечить гарантированный порядок инициализации объектов со static storage duration в таком случае:

J>Почему я могу быть уверен, что объект 'a' будет создан до Stream и разрушен после?


J>Спс.


Нифига не понятно: с одной стороны ты хочешь, чтобы время жизни a было больше времени жизни stream, а с другой стороны вызываешь ф-и stream в деструкторе a. Скажи нормально что ты хочень сделать.
Re[5]: Как создать объект раньше остальных?
От: Jonathan  
Дата: 29.08.07 21:20
Оценка: +1
Изв,
хотел спросить:
Почему я могу быть уверен, что объект Stream будет создан до 'a' и разрушен после?
Спс.
"If everything seems under control, you're just not going fast enough"
Re[7]: Так чем же UB лучше? :)
От: Erop Россия  
Дата: 30.08.07 06:35
Оценка:
Здравствуйте, rg45, Вы писали:

R>Время жизни статической переменной, объявленной внутри функции начинается при первом вызове функции — это по стандарту. Поэтому порядок инициализации, все-таки, детерминированный.

По стандарту там не по стандарту. Всё детерминировано только в случае, если у тебя в детерминированном порядке зовутся функции-клиенты этого синглетона. А если нет?
Вот тебе три простых источника недетерминизма
1) Файл конфигурации
2) Гонки, в случае многопоточного окружения
3) Провал инициализации какого-нибудь статического объекта

Мало того, даже если у тебя всё детерминировано (в том смысле, что от сборки к сборке не меняется порядок), ты часто не можешь его восстановить, а тем более повлиять на него. В этом смысле и просто статические объекты в детерминированном порядке создаются на большинстве реализаций, если не на всех.
Что тут плохо? А то, что любая, связанная с инициализацией правка может неожиданно и некотролируемо, да ещё и трудноотлаживаемым способом нагнуть систему синглетонов. Так что "детерминизм" он весьма условный. Ну а если вспомнить, что есть ещё и источники (1, 2, 3, ...) просто недетерминизма, то эти два фактора перемножаются и мы получаем, что любая правка, связанная с прямым или косвенным использованием синглетонов на этапе загрузки приводит к необходимости всё перетестировать по полной программе. Включая стресс-тестирование на отказы сервисов на старте.
Кому такой "детерминизм" нужен?

R>Если к этим переменным нет обращения в период разрушения статики...\

К сожалению, люди часто и сами не знают, что у них это может быть. Особенно в случае провала инициализации статических переменных...

E>>Почему бы действительно не управлять этим делом явно? Ведь в компиляторах обычно есть для этого средства!!!


R>Ну это у автора вопроса надо спрашивать, согласен ли он на зависимость от фич отдельных компиляторов.


Ну, когда ты решаешься использовать синглетон Майерса, ты сразу же рискуешь UB, по всей видимости. Если это не зависимость от "фич отдельных компиляторов", к тому же и недокументированных, то я испанский лётчик
static const std::string& GetName()
{
    static std::string name = "Йо-Мойо!!!";
    return name;
}
static class CA {
public:
    ~CA() { GetName(); }
} ca;

Порядок разрушения name и ca -- UB, AFAIK
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: Как создать объект раньше остальных?
От: Kubroid  
Дата: 30.08.07 07:30
Оценка:
R>Это называется "что будет, если всёпробивающее ядро попадёт в непробиваемую стену?"
записал!

R>А что будет если появится ещё один такой объект? Произойдёт коллапс проекта?

этот проект находится в такой стадии перманентно.
Re[6]: Как создать объект раньше остальных?
От: Аноним  
Дата: 30.08.07 08:04
Оценка:
Здравствуйте, Jonathan, Вы писали:

J>Изв,

J>хотел спросить:
J>Почему я могу быть уверен, что объект Stream будет создан до 'a' и разрушен после?
J>Спс.

#include "StreamInitializer.h"
#include "A.h"

// stream уже инициализирован потому как StreamInitializer.h включен выше.
// класс A может создавать объекты Stream
A a;

int main()
{
return 0;
}


Или вопрос был в другом?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.