class S
{
public:
static S& Instance()
{
static S inst;
return inst;
}
private:
....
};
---
Так вот, реализация вполне валидная, красивая, короткая - всем хороша.
Только вот иногда не работает.
Сегодня второй раз за три месяца заметил, что в объекте пропадают мемберы - был map заполнен, а на следующей строке - он уже пуст. Был мембер-указатель валидным, не трогали его, а он уже непонятно куда указывает.
Симптомы проявлялись когда:
1.Синглетон объявлен и реализован в exe. А тип какого нибудь мембера (он даже может быть указателем) объявлен и реализован в dll.
2. Оба exe (оба раза) это были сервисы.
3. Оба синглетона были еще и потоками: void Start() { new boost::thread( ref(*this)); } Кстати, черти проявляются именно после вызова Start(). Но, не факт что это причина.
На настройки проекта не грешить - выравнивания, (/MD) и пр. - все корректно и совпадает.
И это не проблема "двойного синглетона" - объект действительно один и тот же.
Порушенный стек, куча - отпадают. Код полностью safe (если, конечно, stl и boost можно считать safe). к тому же при поиске причины - код был сведен к минимуму: осталась только иницализация сервиса, инициализация синглетона, и Start.
Лечится просто: либо переносишь этот класс в dll, либо отказываешся от его синлетонности и делаешь объект, скажем, мембером основного класса приложения.
Чешу репу.
Кто нибудь с таким сталкивался?
P.S.
Чую, что дело бесовское, а обосновать не могу.
тут много много мата...
тут очень много мата...
тут столько мата, что форумная чекилка сломалась, у боцманов завяли уши, а воробьи упали мертвыми на землю.
ПИП! ПИП! ПИП! ПИП!
Весь день гонялся за багой: сначала избавился от потоков. Стабильно не работает.
Потом избавился от остальных объектов. Стабильно не работает.
Избавился от сервиса, и сделал консоль. Стабильно не работает.
В синглетоне оставил только один метод. Стабильно не работает.
Код девственно чист, есть только синглетон который выводит строчку в файл. Имя файла задается ф-ией Init(). Потоков нет, буста нет, консольное приложение - стабильно не работает. Имя файла между Init() и Write() исчезает, видимо в параллельное пространство.
ПИП!ПИП!ПИП!ПИП!
Заменил /O2 на /Od
Заработало!
ПИП!ПИП!ПИП!ПИП!
Сцука оптимизатор.
А если локальную статическую переменную объявить еще и...18.09.08 03:42 Автор: amirul <Serge> Статус: The Elderman
> Даже не вопрос... Это точно... Вопрос старый и весьма избитый. Наиболее содержательный ответ можешь най ти в очётах Мейра и Александреску - "Double-Checked Locking, Threads,Compiler Optimizations,and More Scott Meyers, Ph.D. Software Development Consultant (Based on Work with Andrei Alexandrescu.)"
Неплохое ридинг такогоже уровня - "Double-Checked Locking An Optimization Pattern for Efficiently Initializing and Accessing Thread-safe Objects Douglas C. Schmidt, Tim Harrison".
Всё остальное (статьи)- глупости и весьма пустые вариации на эту тему, ИМХО. В статьях, что выше ты найдёшь такой вывод - в VS 2005 - легко сделать (см. Мейра), поскольку volatile реализовано на memory barrier (в отличие от предыдущих версий копиллеров).
Ты гарантируешь первый вызов Instance из однопоточного [upd]17.09.08 04:16 Автор: amirul <Serge> Статус: The Elderman Отредактировано 17.09.08 04:19 Количество правок: 1
Первые вызовы идут в одном потоке.
Остальные потоки появляются только после инициализации всех синглетонов.
void тут высказывается про ровные места. Если бы они были бы ровными - поста бы не появилось.
У меня появилось другое предположение. Давно заметил, что boost::thread ведет себя не корректно в "кислотной среде".
Есть у меня код, доставшийся по наследству: он полностью unsafe, буфера, которые могут переполнится, указатели пущенные в свободное плавание, доступ к переменным из потоков не залоченые, и все остальные прелести.
Так вот в этом коде new boost::thread вываливается с эксепшеном.
Так что его можно использовать как детектор "что-то где-то плохо".
Так вот, не создал ли я где то в своем safe коде, какой нить косяк... буду разбираться. Если косяк действительно есть, то удалю этот пост. Если нарвусь на что-то более интересное - напишу.
Да, гаранитую ?17.09.08 18:09 Автор: void <Grebnev Valery> Статус: Elderman
> > Чешу репу. > > Кто нибудь с таким сталкивался? > > Ты гарантируешь первый вызов Instance из однопоточного > окружения? Если бы он мог гарантировать - то он не писал бы про "чудеса" на ровном месте.
Я наверное чего то недопонял17.09.08 11:17 Автор: amirul <Serge> Статус: The Elderman
> > Ты гарантируешь первый вызов Instance из однопоточного > > окружения? > Если бы он мог гарантировать - то он не писал бы про > "чудеса" на ровном месте.
А что мешает вызвать S::Instance из первого потока, чтобы проинициализировать статическую переменную и уж потом создавать новые? Кроме того, однопоточность функции можно гарантировать простенькой критической секцией (лучше завернутой в объект - чтоб не следить за освобождением)
Или проблема в чем то еще?
If he does exactly this way, there should be no problem.17.09.08 17:36 Автор: void <Grebnev Valery> Статус: Elderman
> Сегодня второй раз за три месяца заметил, что в объекте > пропадают мемберы - был map заполнен, а на следующей строке > - он уже пуст. Был мембер-указатель валидным, не трогали > его, а он уже непонятно куда указывает. > Симптомы проявлялись когда: > 3. Оба синглетона были еще и потоками: void Start() { new > boost::thread( ref(*this)); } Кстати, черти проявляются > именно после вызова Start(). Но, не факт что это причина. > И это не проблема "двойного синглетона" - объект > действительно один и тот же.
Если воткнуть отладочную печать/брекпойнт в конструктор S и конструктор класса мембера, лишние заходы туда не проявляются?
Точно. Только узнавал подругому: сделал S* Instance(),17.09.08 01:59 Автор: PS <PS> Статус: Elderman Отредактировано 17.09.08 02:16 Количество правок: 3
Точно. Только узнавал подругому: сделал S* Instance(),
а перд вызовами вывожу в лог указатель.
void S::operator()()
{
S* tmp = S::Instance();
Log::Instance()->Trace( (long)tmp );
vector<string> t1 = S::Instance()->t; // тут все ок.
tmp = S::Instance();
Log::Instance()->Trace( (long)tmp );
vector<string> t2 = S::Instance()->t; // тут все ок.
while( true )
{
tmp = S::Instance();
Log::Instance()->Trace( (long)tmp );
vector<string> t3 = S::Instance()->t; // а тут бред!
}
}
---
первый, второй и третий лог - выдает одно и то же.
Кстати, здесь видно что я из потока обращаюсь к мемберам на прямую. Это валидно, т.к. запись в них происходит только в Init(); которая вызывается до Start();
Больше никто не трогает эти мемберы (ни на запись, ни на чтение).
> Если воткнуть отладочную печать/брекпойнт в конструктор S и > конструктор класса мембера, лишние заходы туда не > проявляются?