Re[4]: Race condition
От: Alexander G Украина  
Дата: 05.01.09 12:00
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Разве что для надёжности можешь заменить неатомарные присваивания атомарными — InterlockedExchange / InterlockedCompareExchange.


InterlockedExchange тоже не хочу, прийдётся писать volatile и терять возможную оптимизацию при дальнейшем использовании. А что будет значить "для надёжности" ?
Русский военный корабль идёт ко дну!
Re[5]: Race condition
От: Alexander G Украина  
Дата: 05.01.09 12:42
Оценка:
Здравствуйте, bkat, Вы писали:

B>Достаточно уже этого:

B>
B>  if (initialized)
B>    return;
B>  // тут даже не важно, что вообще инициируется
B>  initialized = true;
B>

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

Давай подумаем о нормальной инициализации.
Требования:
1. Возможность вызова до main. Желательно чтобы была такая защищённость от дурака, чтобы работало даже если дополнительные потоки созданы до main.
2. Максимальная простота.
3. Максимальная переносимость.
4. Расположение в статической памяти.

Поэтому, откинем все эти boost::call_once или InitOnceXxx, синглтон Мейерса, другие известные реализации синглтона... и вроде ничего "нормального" не остаётся. И я не против если не нормальное оказывается лучше.

B>А мамой можно клянуться сколько угодно, но этих клятв хватает максимум до следующего релиза


Как бы эта штука вообще меняется в первый раз. И там нет ничего, что может нарушать клятву о полной повторимости каждой инициализации.
Русский военный корабль идёт ко дну!
Re[6]: Race condition
От: bkat  
Дата: 05.01.09 13:30
Оценка:
Здравствуйте, Alexander G, Вы писали:

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


B>>Достаточно уже этого:

B>>
B>>  if (initialized)
B>>    return;
B>>  // тут даже не важно, что вообще инициируется
B>>  initialized = true;
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 13:31
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>InterlockedExchange тоже не хочу, прийдётся писать volatile и терять возможную оптимизацию при дальнейшем использовании. А что будет значить "для надёжности" ?


Похоже, что ты экономишь даже не на спичках, а огрызках от спичек.
Re[5]: Race condition
От: Кодт Россия  
Дата: 05.01.09 13: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 13:59
Оценка:
Здравствуйте, bkat, Вы писали:


B>Как раз банальный синглтон, в котором бы честно синхронизировалось создание экземпляра,

B>было бы, на мой взгляд, простым решением.

С помощью какого примитива синхронизации ?

B>Пусть твой синглтон и отвечает за такие вещи, как версия OS и прочие подобные вещи.

B>Проблема то стандартная. Чего тут то на блокировках экономить?

Тут такое дело, что желательно избавиться от каждого вызова апи иоли библиотеки.

Вроде придумал так:

struct data_t
{
  ...
} data = {0};

long volatile initialized = FALSE;

void init()
{
  if (!initialized)
    return;
  data_t local_data;
  /* инициализация local_data */
  ...
  if (::InterlockedExchange(&initialized, TRUE))
    memcpy(&data, local_data, sizeof(data_t);
}


B>Ну тебе еще придется давать клятву о том, что каждый поток (включая главный поток) гарантированно вызывает

B>функцию init() до того, как он попытается использовать результаты инициализации.
B>Кстати, это может запросто случится даже без дополнительных потоков.

Это детали реализации, наружу будет немного методов, init в каждом из которых. Или наружу можно даже типа синглтона, с методом Instance.
Русский военный корабль идёт ко дну!
Re[6]: Race condition
От: Alexander G Украина  
Дата: 05.01.09 14:05
Оценка:
Здравствуйте, bkat, Вы писали:

B>Похоже, что ты экономишь даже не на спичках, а огрызках от спичек.


Я знаю на что это похоже. volatile был бы добавлен если было бы известно что без него не работает, а с ним работает.
Русский военный корабль идёт ко дну!
Re[8]: Race condition
От: bkat  
Дата: 05.01.09 14:31
Оценка:
Здравствуйте, Alexander G, Вы писали:

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



B>>Как раз банальный синглтон, в котором бы честно синхронизировалось создание экземпляра,

B>>было бы, на мой взгляд, простым решением.

AG>С помощью какого примитива синхронизации ?

Да хоть с критической секцией.

B>>Пусть твой синглтон и отвечает за такие вещи, как версия OS и прочие подобные вещи.

B>>Проблема то стандартная. Чего тут то на блокировках экономить?

AG>Тут такое дело, что желательно избавиться от каждого вызова апи иоли библиотеки.


AG>Вроде придумал так:


AG>
AG>struct data_t
AG>{
AG>  ...
AG>} data = {0};

AG>long volatile initialized = FALSE;

AG>void init()
AG>{
AG>  if (!initialized)
AG>    return;
AG>  data_t local_data;
AG>  /* инициализация local_data */
AG>  ...
AG>  if (::InterlockedExchange(&initialized, TRUE))
AG>    memcpy(&data, local_data, sizeof(data_t);
AG>}
AG>


Плохо, очень плохо.
Ты можешь с таким же успехом выкинуть InterlockedExchange и это будет так же "надежно", как и твой вариант.
В момент, когда работает memcpy, для других потоков данные уже инициированы, хотя это не так.

Сделай лучше для начала типа этого:
struct data_t
{
  static data_t* getInstance()
  {
    lock(); // например с помощью мьютекса
    static data_t* instance = new data_t;
    unlock();
    return instance;
  }
};


Если getInstance() безбожно будет тормозить твою систему (в чем я сильно сомневаюсь),
то просто вызывай getInstance один раз на поток.
Будь проще, и не пытайся оптимизировать то, что скорей всего никогда не будет тормозить.
Re[9]: Race condition
От: Alexander G Украина  
Дата: 05.01.09 15: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 15: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 15:56
Оценка:
Здравствуйте, bkat, Вы писали:

B>Дак я тебе же показал.

B>Нетривиально — это для тех, кто не хотят пользоваться критическими секциями.

нет, покажи полностью, с самой КС, чтобы было cs.Lock() или EnterCriticalSection(&cs); .
А то непонятно как то что было показано может работать.
Русский военный корабль идёт ко дну!
Re[12]: Race condition
От: bkat  
Дата: 05.01.09 16:12
Оценка: :))
Здравствуйте, Alexander G, Вы писали:

AG>нет, покажи полностью, с самой КС, чтобы было cs.Lock() или EnterCriticalSection(&cs); .

AG>А то непонятно как то что было показано может работать.

Ну блин...
Народ с основами не разобрался, а уже многопоточные приложения пишет

Вот, как бы это выглядело бы на Qt

struct data_t
{
  static data_t* getInstance()
  {
    QMutexLocker locker(&mutex);
    static data_t* instance = new data_t;
    return instance;
  }
  static QMutex mutex;
};


Считай, QMutex в конструкторе инициализирует CRITICAL_SECTION
QMutexLocker в конструкторе вызывает EnterCriticalSection,
а в деструкторе — LeaveCriticalSection.
Re[13]: Race condition
От: Alexander G Украина  
Дата: 05.01.09 16:16
Оценка:
Здравствуйте, bkat, Вы писали:

B>Ну блин...

B>Народ с основами не разобрался, а уже многопоточные приложения пишет

B>Вот, как бы это выглядело бы на Qt


B>
B>struct data_t
B>{
B>  static data_t* getInstance()
B>  {
B>    QMutexLocker locker(&mutex);
B>    static data_t* instance = new data_t;
B>    return instance;
B>  }
B>  static QMutex mutex;
B>};
B>


B>Считай, QMutex в конструкторе инициализирует CRITICAL_SECTION

B>QMutexLocker в конструкторе вызывает EnterCriticalSection,
B>а в деструкторе — LeaveCriticalSection.

Этот код не рабочий, т.к. getInstance может быть вызван до конструктора QMutex.
Русский военный корабль идёт ко дну!
Re[14]: Race condition
От: Alexander G Украина  
Дата: 05.01.09 16:19
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Этот код не рабочий, т.к. getInstance может быть вызван до конструктора QMutex.


Да, возможно QMutex не имеет конструктора, тогда код рабочий.
Но — увы — виндовая КС должна инициализироваться InitializeCriticalSection
Русский военный корабль идёт ко дну!
Re[14]: Race condition
От: bkat  
Дата: 05.01.09 16: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 16:49
Оценка: 1 (1) -1
Здравствуйте, bkat, Вы писали:

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


AG>>Этот код не рабочий, т.к. getInstance может быть вызван до конструктора QMutex.


B>Смотри внимателей.

B>mutex — статический, а иначе ты бы его не смог пользовать в статическом методе getInstance.

Да. но порядок инициализации статических объектов не определён.
Конструкторы статических объектов ничем не лучше конструкторов глобальных объектов, и так же вызываются в неопределённом порядке.

т.е. в этом коде ничего нельзя сказать про порядок S1, S2, S3:

struct S { S() }

S s1;

struct X
{
  static S s2;
}

S X::s2;

S s3;


B>Я понимаю, что ты просто не знаешь, как пользоваться CRITICAL_SECTION.

B>Почитай тут:
B>http://www.rsdn.ru/article/baseserv/critsec.xml
Автор(ы): Павел Блудов
Дата: 14.03.2005
В статье рассматриваются аспекты работы с критическими секциями, их внутреннее устройство и способы отладки


Я знаю как ими пользоваться. Это ты похоже не понял проблему
Если бы всё было так просто, я бы действительно взял КС.

B>P.S. Конструкторы у объектов есть всегда.


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

struct S1
{
  int i;
} s2 = {1};

struct S2
{
  int i;
  S2() : i(1) {}
} s2 = 1;
Русский военный корабль идёт ко дну!
Re[16]: Race condition
От: bkat  
Дата: 05.01.09 17:15
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Если бы всё было так просто, я бы действительно взял КС.


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

Тут извини. Я пас...
Чем-то придется пожертвовать.
Re[12]: Race condition
От: Кодт Россия  
Дата: 05.01.09 18:02
Оценка: 6 (1)
Здравствуйте, Alexander G, Вы писали:

AG>нет, покажи полностью, с самой КС, чтобы было cs.Lock() или EnterCriticalSection(&cs); .

AG>А то непонятно как то что было показано может работать.

Щас попробую на коленке родить.

Сам мьютекс (и все остальные примитивы lock-based синхронизации) нужно инициализировать в рантайме.
Следовательно, мы всё равно хоть что-то, да должны сделать на lock-free.
Поскольку это делается единожды, то нет нужды требовать, чтобы это было ещё и wait-free, так что мы вольны использовать спинлок.

enum InitStatus { NotSet, InProgress, Complete };

template<class Fun>
void do_once(long volatile* status, Fun fun)
{
    switch( InterlockedCompareExchange(status, InProgress, NotSet) )
    {
    case NotSet:
        fun();
        InterlockedExchange(status, Complete);
        return;
    case InProgress:
        while( InterlockedCompareExchange(status, InProgress, InProgress) != Complete )
            Sleep();
        return;
    case Complete:
        return;
    default:
        assert(false);
    }
}

struct StaticCS // POD
{
    long volatile status;
    CRITICAL_SECTION cs;
    
    void init()
    {
        do_once(&status, boost::bind(InitializeCriticalSection, &cs));
    }
    
    void enter()
    {
        init();
        EnterCriticalSection(&cs);
    }
    void leave()
    {
        assert(status==Complete);
        LeaveCriticalSection(&cs);
    }
};
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Перекуём баги на фичи!
Re[13]: Race condition
От: Alexander G Украина  
Дата: 05.01.09 19:00
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Щас попробую на коленке родить.


К>Сам мьютекс (и все остальные примитивы lock-based синхронизации) нужно инициализировать в рантайме.

К>Следовательно, мы всё равно хоть что-то, да должны сделать на lock-free.
К>Поскольку это делается единожды, то нет нужды требовать, чтобы это было ещё и wait-free, так что мы вольны использовать спинлок.

...

Так, это уже лучше чем КС с конструктором

пара вопросов:
1. Нужна ли мне КС или инициализировать через do_once саму data_t — какие будут практические рекомендации для выбора ?
2. А вот ты бы оставил "опасный" вариант или делал бы настоящий do_once вроде этого ?
Русский военный корабль идёт ко дну!
Re[17]: Race condition
От: Alexander G Украина  
Дата: 05.01.09 19:22
Оценка:
Здравствуйте, bkat, Вы писали:

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


AG>>Если бы всё было так просто, я бы действительно взял КС.


B>Все время забываю, что тебе нужна гарантия в случае если

B>кто-то пожелает вызвать getInstance при создании глобальных объектов.

Я действительно хочу много странного, но именно это странным не является. Требование чтобы синглтон работал при конструировании глобальных объектов, в том числе и других синглтонов — стандартное требование. Фактически, самый простой известный синглтон — синглтон Мейерса — решает в первую очередь именно эту задачу — при этом создавая взамен проблемы с многопоточностью и с exception safety.

Если бы существование при инициализации не было бы нужно, я бы просто сделал инициализацию в конструкторе глобального объекта. Можно конечно сделать что-то вроде
struct T{
  static T& instance() {
    static T t;
    return t;
  }
};

struct T_init {
  T_init() { T::instance(); }
} const t_init;

, но у меня в требованиях
Автор: Alexander G
Дата: 05.01.09
 есть "желательно".
Русский военный корабль идёт ко дну!
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.