Re: Новый тип синглтона! Зацените!
От: remark Россия http://www.1024cores.net/
Дата: 16.01.09 12:49
Оценка: 5 (1) +1
Здравствуйте, Аноним, Вы писали:

А>Товарищи разработчики!

А>Зацените! Новое моё изобретение!!!
А>Потоковобезопасный шаблон "Ленивого" синглтона.
А>Если будет что возразить — пишите!

Необходимо обеспечить корректное упорядочивание конструирования/считывания объекта и установки/считывания флага. В текущем варианте потоки могут работать с не полностью сконструированным объектом.


1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: Новый тип синглтона! Зацените!
От: remark Россия http://www.1024cores.net/
Дата: 16.01.09 13:02
Оценка: 3 (1) +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).
Это когда инициализация объекта идемпотентная. Т.е. все потоки, которые увидели неинициализированный объект, начинают его создавать. Далее кто-то один атомарно подменяет глобальный указатель на свой, остальные рушат свои объекты.


Данная реализация — DCSI. DCCI может быть реализован полностью неблокирующим (гарантия wait-free: http://www.rsdn.ru/Forum/?mid=2930849
Автор: remark
Дата: 27.04.08
). Соотв. не подвержен таким вещам как priority inversion.



1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Новый тип синглтона! Зацените!
От: Аноним  
Дата: 15.01.09 12:11
Оценка: -1 :)
Товарищи разработчики!

Зацените! Новое моё изобретение!!!

Потоковобезопасный шаблон "Ленивого" синглтона.

template <class T>
T* Get(bool deleteInstance=false)
{
    volatile static void* ptr = 0;
    volatile static unsigned status = 0;
    unsigned sturtup = 0, creating = 1, created = 2, deleted = 3;

    if (!InterlockedCompareExchange(&status, creating, sturtup))
    {
        ptr = new T;
        status = created;
    }

    while (status == creating)
        Sleep(0);

    if (deleteInstance && ptr && status==created)
    {
        delete (T*)ptr;
        status = deleted;
    }

    return (T*)ptr;
}


Если будет что возразить — пишите!
Re[2]: Новый тип синглтона! Зацените!
От: Quasi  
Дата: 16.01.09 17:17
Оценка: 14 (1)
Здравствуйте, remark, Вы писали:

R>Необходимо обеспечить корректное упорядочивание конструирования/считывания объекта и установки/считывания флага. В текущем варианте потоки могут работать с не полностью сконструированным объектом.


Задумался тут, достаточно легко преобразовать данный велосипед в работающий без барьеров памяти при каждом доступе к сконструированному объекту. Для этого нужно использовать само значение указателя в качестве статуса, в этом случае барьер при доступе не нужен, т.к. будет иметь место "data dependency". Только вот я думаю, перед разыменовыванием указателя нужен барьер для компилятора. И еще вместо Sleep(0), лучше как в реализации boost::call_once под Win32 создавать уникальный именованный мьютекс и ждать на нем.
Единственное преимущество такого велосипеда перед DCL Implementation
Автор: remark
Дата: 18.06.07
в корректности работы в случае гонок при инициализации приложения, что к сожалению не такая уж и редкость. Естественно речь не идет о портируемости .

R>

Re: Новый тип синглтона! Зацените!
От: рыбак  
Дата: 15.01.09 12:18
Оценка: 1 (1)
Здравствуйте, Аноним, Вы писали:


А>
А>    while (status == creating)
А>        Sleep(0);

А>


что сразу бросается в глаза — если у тебя на этом куске повиснет поток с высоким приоритетом, а синглтон будет создаваться потоком с низким — будут страшные тормоза.
Re[2]: Новый тип синглтона! Зацените!
От: Аноним  
Дата: 15.01.09 12:36
Оценка:
Цитата:
"что сразу бросается в глаза — если у тебя на этом куске повиснет поток с высоким приоритетом, а синглтон будет создаваться потоком с низким — будут страшные тормоза."

Спасибо!
Если есть у вас какиенить идеи как это можно лучше сделать/обойти — буду благодарен.
Re: Новый тип синглтона! Зацените!
От: Sergey Россия  
Дата: 15.01.09 12:54
Оценка:
" Аноним 830 " <0@users.rsdn.ru> wrote in message news:3250016@news.rsdn.ru...
> Товарищи разработчики!
>
> Зацените! Новое моё изобретение!!!
>
> Потоковобезопасный шаблон "Ленивого" синглтона.
>
>
> template <class T>
> T* Get(bool deleteInstance=false)
> {
> volatile static void* ptr = 0;
> volatile static unsigned status = 0;
> unsigned sturtup = 0, creating = 1, created = 2, deleted = 3;
> 
> if (!InterlockedCompareExchange(&status, creating, sturtup))
> {
> ptr = new T;
> status = created;
> }
> 
> while (status == creating)
> Sleep(0);
> 
> if (deleteInstance && ptr && status==created)
> {
> delete (T*)ptr;
> status = deleted;
> }
> 
> return (T*)ptr;
> }
>

>
> Если будет что возразить — пишите!

О, у меня похожий когда-то был Из ошибок — не дружит с исключениями из строки ptr = new T;
Posted via RSDN NNTP Server 2.1 beta
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re: Новый тип синглтона! Зацените!
От: Sergej Pupykin  
Дата: 15.01.09 12:57
Оценка:
Аноним 830 wrote:
> Товарищи разработчики!
>
> Зацените! Новое моё изобретение!!!
>

1. к status надо всегда обращаться interlocked функциями, иначе получишь
проблемы на SMP.
2. busywait циклы кривы и неэфективны.
Posted via RSDN NNTP Server 2.1 beta
Re: Новый тип синглтона! Зацените!
От: Аноним  
Дата: 15.01.09 13:11
Оценка:
Я проще сделал. Вот таким вот макросом:
#define MT_SAFE_STATIC(decl, name, params) \
    static volatile LONG name##_flag = 0; \
    while(::InterlockedCompareExchange(&name##_flag, 2, 0)==2)::Sleep(0); \
    static decl name params; \
    ::InterlockedCompareExchange(&name##_flag, 1, 2);


Пользоваться:

MT_SAFE_STATIC( ClassName, instance_name, (initializers) );
или
MT_SAFE_STATIC( ClassName, instance_name, ; ); 
если конструктор не принимает параметров


Кстати, VS2005 и выше это делает сама.
Re[2]: Новый тип синглтона! Зацените!
От: Аноним  
Дата: 15.01.09 13:12
Оценка:
Здравствуйте, Sergej Pupykin, Вы писали:

SP>1. к status надо всегда обращаться interlocked функциями, иначе получишь

SP>проблемы на SMP.

Ну... в том моменте кода где ньюкается — доступ к status защищён interlocked функцией и я так понимаю проблемы должны возникнуть только если начнёшь чего-то удалять (Так ли это? как вы считаете?).
Но тот участок кода где удаляется я намеренно никак не защищал, чтобы не вводить разработчиков в заблуждение что удаление может быть в принципе потоковобезопасным. Понятно дело, что если во время удаления другой поток возвратит указатель, то весь код который этот указатель использует — бажный. Поэтому следует позаботится чтобы все потоки которые используют шаблон заглохли ещё до удаления экземпляра класса через delete.

SP>2. busywait циклы кривы и неэфективны.


Согласен.
Re[3]: Новый тип синглтона! Зацените!
От: Sergej Pupykin  
Дата: 15.01.09 13:42
Оценка:
Аноним 830 wrote:
> Здравствуйте, Sergej Pupykin, Вы писали:
>
> SP>1. к status надо всегда обращаться interlocked функциями, иначе получишь
> SP>проблемы на SMP.
>
> Ну... в том моменте кода где ньюкается — доступ к status защищён interlocked функцией и я так понимаю проблемы должны возникнуть только если начнёшь чего-то удалять (Так ли это? как вы считаете?).

м быть еще
status = created;

стоит заменить на интерлокед? хотя хз...
Posted via RSDN NNTP Server 2.1 beta
Re: Новый тип синглтона! Зацените!
От: alzt  
Дата: 15.01.09 15:47
Оценка:
Здравствуйте, Аноним, Вы писали:

А для чего здесь:

volatile static void* ptr = 0;
,
а не

volatile static T* ptr = 0;

?
Re[4]: Новый тип синглтона! Зацените!
От: CreatorCray  
Дата: 16.01.09 13:33
Оценка:
Здравствуйте, remark, Вы писали:

R>Есть DCCI (double-checked concurrent initialization).

R>Это когда инициализация объекта идемпотентная. Т.е. все потоки, которые увидели неинициализированный объект, начинают его создавать. Далее кто-то один атомарно подменяет глобальный указатель на свой, остальные рушат свои объекты.
Так вот оказывается как один мой лисапед правильно называется

R>

... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Re[3]: Новый тип синглтона! Зацените!
От: remark Россия http://www.1024cores.net/
Дата: 17.01.09 08:49
Оценка:
Здравствуйте, Quasi, Вы писали:

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


R>>Необходимо обеспечить корректное упорядочивание конструирования/считывания объекта и установки/считывания флага. В текущем варианте потоки могут работать с не полностью сконструированным объектом.


Q>Задумался тут, достаточно легко преобразовать данный велосипед в работающий без барьеров памяти при каждом доступе к сконструированному объекту. Для этого нужно использовать само значение указателя в качестве статуса, в этом случае барьер при доступе не нужен, т.к. будет иметь место "data dependency". Только вот я думаю, перед разыменовыванием указателя нужен барьер для компилятора. И еще вместо Sleep(0), лучше как в реализации boost::call_once под Win32 создавать уникальный именованный мьютекс и ждать на нем.

Q>Единственное преимущество такого велосипеда перед DCL Implementation
Автор: remark
Дата: 18.06.07
в корректности работы в случае гонок при инициализации приложения, что к сожалению не такая уж и редкость. Естественно речь не идет о портируемости .



Пример реализации такого подхода — с зависимостью по данным и барьером компилятора:
http://gzip.rsdn.ru/forum/message/3252604.1.aspx
Автор: remark
Дата: 16.01.09



R>>

Q>

1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: Новый тип синглтона! Зацените!
От: Аноним  
Дата: 17.01.09 11:52
Оценка:
Здравствуйте, alzt, Вы писали:

A>А для чего здесь:


A>
A>volatile static void* ptr = 0;
A>
,

A>а не

A>
A>volatile static T* ptr = 0;
A>

A>?

Да дело в том что невстроенные типы статических членов функций статически инициализируются в момент первого вызова функции, а не заранее. И race conditions не возникает. void* — тип по любому встроенный. Поэтому он запишется в нуль ещё до первого вызова. А вот на счёт T* — тут я честно говоря не уверен. С одной стороны — указатель он и в африке указатель — изменяется ведь всего лишь мнение компилятора об объекте но не сам объект. Но с другой стороны — перестраховаться захотелось — это мне казалось проще чем тратить время на изучение стандартов. Хотя если кто знает что по этому вопросу стоящее — буду благодарен за инфу.
Re[2]: Новый тип синглтона! Зацените!
От: Аноним  
Дата: 17.01.09 12:02
Оценка:
Здравствуйте, remark, Вы писали:

R>Необходимо обеспечить корректное упорядочивание конструирования/считывания объекта и установки/считывания флага. В текущем варианте потоки могут работать с не полностью сконструированным объектом.


Если честно, то я не догоняю. Подскажите пожалуйста, каким образом поток используя этот шаблон может получить указатель на недосозданный объект?
Ведь возврат из оператора new происходит только после того как объект полностью создан включая полное выполнение его конструктора. А до того как new отработает все потоки которые захотят указатель будут висеть на Sleep(0).

Re[3]: Новый тип синглтона! Зацените!
От: Аноним  
Дата: 17.01.09 12:26
Оценка:
Здравствуйте, Quasi, Вы писали:

Q>Задумался тут, достаточно легко преобразовать данный велосипед в работающий без барьеров памяти при каждом доступе к сконструированному объекту.


Что вы имеете в виду под "барьерами памяти"? Чего-то не догнал, если честно.

Q>Для этого нужно использовать само значение указателя в качестве статуса, в этом случае барьер при доступе не нужен, т.к. будет иметь место "data dependency".


Тут вы пожалуй правы. Действительно, использование status — не так уж и необходимо.

Q>Только вот я думаю, перед разыменовыванием указателя нужен барьер для компилятора.

Зачем?

Q>И еще вместо Sleep(0), лучше как в реализации boost::call_once под Win32 создавать уникальный именованный мьютекс и ждать на нем.


Не согласен. boost::call_once содержит глобальный объект который должен быть проинициализирован в момент первого вызова. Поэтому вместо boost::call_once лучше просто создать глобальный объект класса T — проблемы те же! Ведь собственно одно из главных преимуществ моего велика — пользователь не заморачивается с порядком инициализации глобальных объектов. По крайней мере в моём текущем проекте порядок инициализации огромной массы глобальных объектов — это одна большая язва которая время от времени даёт о себе знать багами которых приходится отлавливать очень долго.

Q>Естественно речь не идет о портируемости .


По поводу портируемости — чтобы мой велик портировать — нужно Interлокед функции реализовать для UNIX. Кому надо — могу выложить их реализацию под UNIX.

По поводу подходов альтернативных моему велику. Насколько я понял, практически везде используются критические секции для синхронизации доступа. А это тянет за собой кучу проблем. По сути проблемы с корректным созданием объекта мы заменяем проблемами с созданием критической секции, которые нисколько не лучше. Шило на мыло. Или я чего-то недогоняю?
Re[3]: Новый тип синглтона! Зацените!
От: remark Россия http://www.1024cores.net/
Дата: 18.01.09 10:29
Оценка:
Здравствуйте, Аноним, Вы писали:

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


R>>Необходимо обеспечить корректное упорядочивание конструирования/считывания объекта и установки/считывания флага. В текущем варианте потоки могут работать с не полностью сконструированным объектом.


А>Если честно, то я не догоняю. Подскажите пожалуйста, каким образом поток используя этот шаблон может получить указатель на недосозданный объект?

А>Ведь возврат из оператора new происходит только после того как объект полностью создан включая полное выполнение его конструктора. А до того как new отработает все потоки которые захотят указатель будут висеть на Sleep(0).

Либо компилятор может скомпилировать, либо аппаратное обеспечение может это выполнить допустим как (псевдо-код):
    if (!InterlockedCompareExchange(&status, creating, sturtup))
    {
        ptr = (T*)malloc(sizeof(T));
        T_constructor_part1(p);
        status = created;
        T_constructor_part2(p);
    }


В текущем коде ничего не машает такому переупорядочиванию.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[4]: Новый тип синглтона! Зацените!
От: Аноним  
Дата: 18.01.09 13:13
Оценка:
R>В текущем коде ничего не машает такому переупорядочиванию.

ООчень интересно! Спасибо! То есть по сути получается что компилятор может записать статус "created" ещё до окончания работы оператора new.
Я впервые об этих граблях слышу. Про переупорядочивание. А не дадите какуюнибудь ссылочку — когда и в каких случаях такой заподлянки можно ожидать? И какие основные способы есть с таким безобразием бороться?

R>

Re[4]: Новый тип синглтона! Зацените!
От: Quasi  
Дата: 18.01.09 20:31
Оценка:
Здравствуйте, Аноним, Вы писали:

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


Q>>Задумался тут, достаточно легко преобразовать данный велосипед в работающий без барьеров памяти при каждом доступе к сконструированному объекту.


А>Что вы имеете в виду под "барьерами памяти"? Чего-то не догнал, если честно.

Q>>Только вот я думаю, перед разыменовыванием указателя нужен барьер для компилятора.
А>Зачем?

По поводу барьеров памяти, здесь
Автор: remark
Дата: 10.07.07
приведены хорошие ссылки
Неплохая статья на эту тему

А>Не согласен. boost::call_once содержит глобальный объект который должен быть проинициализирован в момент первого вызова. Поэтому вместо boost::call_once лучше просто создать глобальный объект класса T — проблемы те же! Ведь собственно одно из главных преимуществ моего велика — пользователь не заморачивается с порядком инициализации глобальных объектов. По крайней мере в моём текущем проекте порядок инициализации огромной массы глобальных объектов — это одна большая язва которая время от времени даёт о себе знать багами которых приходится отлавливать очень долго.

BOOST_ONCE_INIT — The call_once function and once_flag type (statically initialized to BOOST_ONCE_INIT) can be used to run a routine exactly once

Поэтому никаких проблем и нет, тип once_flag — POD тип (в реализации интегральный тип, статически инициализируемый 0)
Единственное, что можно сделать лучше, так это избавиться от непроизводительного барьера памяти при каждом доступе к объекту
Re[4]: Новый тип синглтона! Зацените!
От: Quasi  
Дата: 18.01.09 20:40
Оценка:
Здравствуйте, Аноним, Вы писали:

А>По поводу портируемости — чтобы мой велик портировать — нужно Interлокед функции реализовать для UNIX. Кому надо — могу выложить их реализацию под UNIX.


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