Легенда:
   новое сообщение
    закрытая нитка
    новое сообщение
    в закрытой нитке
    старое сообщение
         
		 | 
- Напоминаю, что масса вопросов по функционированию форума снимается после прочтения его описания.
 - Новичкам также крайне полезно ознакомиться с данным документом.
   
  | 
Ты гарантируешь первый вызов Instance из однопоточного [upd]  17.09.08 04:16  Число просмотров: 2899
 Автор: amirul <Serge> Статус: The Elderman Отредактировано 17.09.08 04:19  Количество правок: 1
 | 
 
> Чешу репу. > Кто нибудь с таким сталкивался? 
 Ты гарантируешь первый вызов Instance из однопоточного окружения?
 
 > P.S. > Чую, что дело бесовское, а обосновать не могу. 
 -------------
 Кроме того, по хорошему стоило бы захватывать какую нибудь критическую секцию в самом начале S::Instance
 | 
 
| 
<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 и > конструктор класса мембера, лишние заходы туда не > проявляются?
 | 
 
 
  
 
 | 
 |