информационная безопасность
без паники и всерьез
 подробно о проектеRambler's Top100
Сетевые кракеры и правда о деле ЛевинаSpanning Tree Protocol: недокументированное применениеПортрет посетителя
BugTraq.Ru
Русский BugTraq
 Анализ криптографических сетевых... 
 Модель надежности двухузлового... 
 Специальные марковские модели надежности... 
 Бэкдор в xz/liblzma, предназначенный... 
 Три миллиона электронных замков... 
 Doom на газонокосилках 
главная обзор RSN блог библиотека закон бред форум dnet о проекте
bugtraq.ru / форум / programming
Имя Пароль
ФОРУМ
если вы видите этот текст, отключите в настройках форума использование JavaScript
регистрация





Легенда:
  новое сообщение
  закрытая нитка
  новое сообщение
  в закрытой нитке
  старое сообщение
  • Напоминаю, что масса вопросов по функционированию форума снимается после прочтения его описания.
  • Новичкам также крайне полезно ознакомиться с данным документом.
If he does exactly this way, there should be no problem. 17.09.08 17:36  Число просмотров: 2533
Автор: void <Grebnev Valery> Статус: Elderman
<"чистая" ссылка>
<programming>
[C++] По реализации синглетона вопрос 17.09.08 00:37  
Автор: PS <PS> Статус: Elderman
Отредактировано 17.09.08 01:24  Количество правок: 1
<"чистая" ссылка>
Даже не вопрос...
Есть такая реализация:
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.
Чую, что дело бесовское, а обосновать не могу.
ПИП! ПИП! ПИП! ПИП! 17.09.08 18:53  
Автор: PS <PS> Статус: Elderman
<"чистая" ссылка>
тут много много мата...
тут очень много мата...
тут столько мата, что форумная чекилка сломалась, у боцманов завяли уши, а воробьи упали мертвыми на землю.
ПИП! ПИП! ПИП! ПИП!

Весь день гонялся за багой: сначала избавился от потоков. Стабильно не работает.
Потом избавился от остальных объектов. Стабильно не работает.
Избавился от сервиса, и сделал консоль. Стабильно не работает.
В синглетоне оставил только один метод. Стабильно не работает.

Код девственно чист, есть только синглетон который выводит строчку в файл. Имя файла задается ф-ией Init(). Потоков нет, буста нет, консольное приложение - стабильно не работает. Имя файла между Init() и Write() исчезает, видимо в параллельное пространство.

ПИП!ПИП!ПИП!ПИП!

Заменил /O2 на /Od
Заработало!

ПИП!ПИП!ПИП!ПИП!

Сцука оптимизатор.
А если локальную статическую переменную объявить еще и... 18.09.08 03:42  
Автор: amirul <Serge> Статус: The Elderman
<"чистая" ссылка>
А если локальную статическую переменную объявить еще и волатильной?
Это точно... Вопрос старый и весьма избитый. Наиболее... 17.09.08 07:35  
Автор: void <Grebnev Valery> Статус: 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
<"чистая" ссылка>
> Чешу репу.
> Кто нибудь с таким сталкивался?

Ты гарантируешь первый вызов Instance из однопоточного окружения?

> P.S.
> Чую, что дело бесовское, а обосновать не могу.

-------------
Кроме того, по хорошему стоило бы захватывать какую нибудь критическую секцию в самом начале S::Instance
Да, гаранитую 17.09.08 12:15  
Автор: PS <PS> Статус: Elderman
<"чистая" ссылка>
Да, первый вызов Instance() идет только из одного потока.
Код примерно следующий.

S1::Instance().Init()
S2::Instance().Init()
S3::Instance().Init()
...
S1::Instance().Start()

Первые вызовы идут в одном потоке.
Остальные потоки появляются только после инициализации всех синглетонов.

void тут высказывается про ровные места. Если бы они были бы ровными - поста бы не появилось.

У меня появилось другое предположение. Давно заметил, что boost::thread ведет себя не корректно в "кислотной среде".
Есть у меня код, доставшийся по наследству: он полностью unsafe, буфера, которые могут переполнится, указатели пущенные в свободное плавание, доступ к переменным из потоков не залоченые, и все остальные прелести.
Так вот в этом коде new boost::thread вываливается с эксепшеном.
Так что его можно использовать как детектор "что-то где-то плохо".

Так вот, не создал ли я где то в своем safe коде, какой нить косяк... буду разбираться. Если косяк действительно есть, то удалю этот пост. Если нарвусь на что-то более интересное - напишу.
Да, гаранитую ? 17.09.08 18:09  
Автор: void <Grebnev Valery> Статус: Elderman
<"чистая" ссылка>
> void тут высказывается про ровные места. Если бы они были
> бы ровными - поста бы не появилось.

In the simplest case you might try this. No?

////////////////////////////////////////////////////////////////////
// My_class.cpp

class My_class
{
public:
	static My_class& unsafe_instance()
	{
		static My_class inst;
		return inst;
	}
	bool initialize(void)
	{
		bool rc = false;
		// TO DO
		return rc;
	}
	void deinitialize(void)
	{
		// TO DO before dtor()
	}
private:
	My_class()
	{
	}
	~My_class()
	{
	}
	My_class& operator =(const My_class&);
};


static struct init_my_class
{
	init_my_class() : m_instance(My_class::unsafe_instance())
	{
		m_initialized = m_instance.initialize();
	}
	~init_my_class()
	{
		 m_instance.deinitialize();
	}
	bool m_initialized;
	My_class& m_instance;
	init_my_class& operator=(const init_my_class&);
} _init_my_class_;


void initialize_my_classes(void)
{
	if (false ==init_my_classm_initialized)
	{
		throw std::exception("an error at init_my_class startup");
	}
/* TO DO
	else if ()
	{
		throw std::exception("an error at ....");
	}
*/
	else
	{
	}
}

////////////////////////////////////////////////////////////////////
// main.cpp

int _tmain(int argc, _TCHAR* argv[])
{
	try
	{
		// there is only a place where you initialize/check your statics
		initialize_my_classes();
	}
	catch(std::exception& e)
	{
		printf("Failed to initialize my staff (%s)", e.what());
		return 1;
	}
	catch(...)
	{
		printf("Failed to initialize my staff (unhandled exception at startup)");
		return 1;
	}

	return 0;
}

---
Если бы он мог гарантировать - то он не писал бы про... 17.09.08 07:45  
Автор: 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
<"чистая" ссылка>
[C++] а точно один? 17.09.08 01:50  
Автор: dl <Dmitry Leonov>
<"чистая" ссылка>
> Сегодня второй раз за три месяца заметил, что в объекте
> пропадают мемберы - был 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 и
> конструктор класса мембера, лишние заходы туда не
> проявляются?
1




Rambler's Top100
Рейтинг@Mail.ru


  Copyright © 2001-2024 Dmitry Leonov   Page build time: 0 s   Design: Vadim Derkach