Время разрушения статических объектов
От: Went  
Дата: 17.09.09 18:50
Оценка:
Здрасте. Имеется некий менеджер, который живет на статике в одном из cpp-файлов программы, и некие объекты, которые тоже иногда живут на статике, и в момент конструкции регистрятся в этом менеджере, а в момент разрушения — удаляются из него. Когда оказывается, что менеджер разрушился раньше объекта, то при попытке удалиться из него, объект бьет мимо памяти! Какие есть хорошие решения проблемы?
Re: Время разрушения статических объектов
От: Сергей Мухин Россия  
Дата: 17.09.09 19:05
Оценка:
Здравствуйте, Went, Вы писали:

W>Здрасте. Имеется некий менеджер, который живет на статике в одном из cpp-файлов программы, и некие объекты, которые тоже иногда живут на статике, и в момент конструкции регистрятся в этом менеджере, а в момент разрушения — удаляются из него. Когда оказывается, что менеджер разрушился раньше объекта, то при попытке удалиться из него, объект бьет мимо памяти! Какие есть хорошие решения проблемы?


может объект не должен знать, откуда ему надо удалиться, ведь он, в принципе, может быть зарегистрирован в разных менеджерах?
---
С уважением,
Сергей Мухин
Re: Время разрушения статических объектов
От: Erop Россия  
Дата: 17.09.09 19:20
Оценка:
Здравствуйте, Went, Вы писали:

W>Какие есть хорошие решения проблемы?


Ну не совсем понятно, например, зачем вообще вычёркивать объекты из менеджера. Ещё не понятно как они регятся в ещё не созданном менеджере...

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

Ну а если надо ещё и переносимо, то приходится химичить...
template<typename TSeed> class SelfRegistered {
public:
    class Accessor {
        friend TSeed;
        static SelfRegistered* First() { return first; }
        static SelfRegistered* Next( SelfRegistered* t ) { return t->next; }
        static SelfRegistered* Prev( SelfRegistered* t ) { return t->prev; }
    };

protected:
    SelfRegistered() { reg( this ); }
    ~SelfRegistered() { ureg( this ); }
    SelfRegistered( const SelfRegistered& ) { reg( this ); }
    void operator = ( const SelfRegistered& ) {}

private:
    static SelfRegistered* first;
    SelfRegistered* prev;
    SelfRegistered* next;

    static void reg( SelfRegistered* toReg ) 
    {
        toReg->prev = toReg;
        toReg->next = toReg;
        std::swap( first, toReg->next );  
        std::swap( toReg->next->prev, toReg->prev );
    }
    static void unreg( SelfRegistered* toUnreg )
    {
        if( toUnreg->prev == 0 ) {
            assert( first == toUnreg );
            std::swap( toUnreg->prev, toUnreg->next->prev );
            std::swap( first, toUnreg->next );
        } else {
            std::swap( toUnreg->prev, toUnreg->next->prev );
            std::swap( toUnreg->prev->next, toUnreg->next );
        }
    }
};

template<typename TSeed> SelfRegistered<TSeed>* SelfRegistered<TSeed>::fisrt = 0;


Ну и типа выводишь из SelfRegistered<Manager> то, что регишь, а в Manager имеешь доступ к списку зарегиных на текущий момент наследников...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Время разрушения статических объектов
От: nen777w  
Дата: 17.09.09 19:48
Оценка:
W>>Какие есть хорошие решения проблемы?
E>Ну не совсем понятно, например, зачем вообще вычёркивать объекты из менеджера. Ещё не понятно как они регятся в ещё не созданном менеджере...

Ну тут как раз все понятно. Вероятно когда объект хочет себя зарегестрировать он вызывает какой то метод у менеджера который и создает в этот момент менеджер. Вот и получается что яйцо раньше курицы появляется
Во общем знакомая ситуация, у меня была.
Решением было создавать менеджер при старте программы. Я так понимаю менеджер это синглтон Мейерса, вот и сделай где то в начале main фиктивный вызов.
MyManager::get();
Re: Время разрушения статических объектов
От: vadimcher  
Дата: 17.09.09 22:14
Оценка: +1
Здравствуйте, Went, Вы писали:

W>Здрасте. Имеется некий менеджер, который живет на статике в одном из cpp-файлов программы, и некие объекты, которые тоже иногда живут на статике, и в момент конструкции регистрятся в этом менеджере, а в момент разрушения — удаляются из него. Когда оказывается, что менеджер разрушился раньше объекта, то при попытке удалиться из него, объект бьет мимо памяти! Какие есть хорошие решения проблемы?


Самое лучшее решение -- избавиться от статики. Это зло: 10.12 &mdash; 10.16.

Если сам менеджер не твой или трогать не хочется, можно, как вариант, оставить только его статическим, а все остальное создавать внутри main.

Если менеджер ничего полезного при удалении не делает, то можно его выделять динамически по требованию и просто "забыть" удалить в конце. Память освободится, если это конечно не ДОС. Если же он делает что-то полезное при удалении, то можно все полезное занести в отдельный метод, сделать статическую обертку со статическими методами, которая создает менеджер и пересылает все "команды" ему, но в конце при удалении его не трогает, а только вызывает тот самый метод.

Еще вопрос: я так понимаю, что вычеркивание в конце по завершению работы программы -- дело бесполезное (оно может быть нужно в процессе работы программы, но в конце -- незачем). Если это так, то можно опять-таки создать свой класс-посредник, который будет работать с менеджером, пока тот жив, а если нет, то просто игнорировать запросы (ну или игнорировать запросы на вычеркивание, а, например, на выделение -- посылать exception). И т.д.

Но самый верный способ -- избавиться от статики, если это возможно.

А вот зайца кому, зайца-выбегайца?!
Re: Время разрушения статических объектов
От: Владислав Курмаз Украина http://tis-method.org/
Дата: 18.09.09 06:52
Оценка: 2 (2)
Здравствуйте, Went, Вы писали:

W>Здрасте. Имеется некий менеджер, который живет на статике в одном из cpp-файлов программы, и некие объекты, которые тоже иногда живут на статике, и в момент конструкции регистрятся в этом менеджере, а в момент разрушения — удаляются из него. Когда оказывается, что менеджер разрушился раньше объекта, то при попытке удалиться из него, объект бьет мимо памяти! Какие есть хорошие решения проблемы?


Здравствуйте.
Решение этой задачи описано в книге Александреску А. "Современное проектирование на С++", "Глава 6. Реализация шаблона Singleton".
Re: Время разрушения статических объектов
От: Vlad_SP  
Дата: 18.09.09 07:05
Оценка:
Здравствуйте, Went, Вы писали:

В книге: Джефф Элджер, "С++ Библиотека программиста", стр.38-40 описан еще один возможный подход к решению проблемы.
(сама книга существует в электронном виде, гуглится)
Re: Время разрушения статических объектов
От: Went  
Дата: 18.09.09 10:09
Оценка:
Здравствуйте, все, вы писали полезные советы Спасибо. Отвечу в одном посте, чтобы не повторяться.

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

2. Как я добиваюсь своевременного создания менеджера. Через функцию со статическим членом. Всегда создастся по первому запросу.

3. Что это вообще такое и зачем понадобилось. Для счетчиков ссылок в умном указателе (многочисленных мелких объектов одинаковой длинны) использую boost::singleton_pool. Перегружаю операторы new и delete для этого счетчика и перенаправляю их на этот pool. Получаю неплохой прирост производительности и экономию памяти, но втыкаюсь в указанную проблему, когда счетчики разрушаются на статике после того, как самовыпилился pool.

> может объект не должен знать, откуда ему надо удалиться, ведь он, в принципе, может быть зарегистрирован в разных менеджерах?

Не может, пул один на всех

> Ну а если надо ещё и переносимо, то приходится химичить...

Фактически, глобальный лист объектов, в который регаются автоматом. Мне это не нужно

> Сделать синглтон.

Название boost::singleton_pool кагбе намекает

4. Как я думаю это решить. Сделать шаблон "умного синглтона" со счетчиком ссылок и семафором саморазрушения. Сам объект будет выделяться на куче и уничтожаться, когда нет ссылок и разрушение статики уже началось. Потом приведу код на проку
Re: Время разрушения статических объектов
От: MShura  
Дата: 18.09.09 10:18
Оценка:
обрати внимание на функцию onexit/atexit
Re[2]: Время разрушения статических объектов
От: Erop Россия  
Дата: 18.09.09 10:31
Оценка:
Здравствуйте, Went, Вы писали:

>> Ну а если надо ещё и переносимо, то приходится химичить...

W>Фактически, глобальный лист объектов, в который регаются автоматом. Мне это не нужно
Так там можно сделать всё, что угодно. Фишка в том, что мы явно считаем созданные/разрушенные объекты и при этом ещё и не имеем явного конструктора/деструктора.
Конструировать всё можно при регистрации первого объекта, разрушать при вычёркивании последнего...
Кстати, да. Если я понял тебя верно, то регить надо фабрики...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: Время разрушения статических объектов
От: Went  
Дата: 18.09.09 11:23
Оценка:
Здравствуйте, Erop, Вы писали:

E>Кстати, да. Если я понял тебя верно, то регить надо фабрики...


Да нет. "Менеджером" я назвал пул счетчиков умных указателей. То есть нужно не просто "регить", а "размещать" там объекты, причем точкой входа будет ref_counter::operator new. Ну, а так мы все ближе сходимся в решении
Re[2]: Время разрушения статических объектов
От: Went  
Дата: 18.09.09 11:26
Оценка:
Здравствуйте, MShura, Вы писали:

MS>обрати внимание на функцию onexit/atexit


А что раньше происходит, очистка статики или вызов этих функций?
Re[3]: Время разрушения статических объектов
От: MShura  
Дата: 18.09.09 12:01
Оценка:
MS>>обрати внимание на функцию onexit/atexit

W>А что раньше происходит, очистка статики или вызов этих функций?


очистка статики также происходит с помощью этих функций:
Компилятор строит таблицу вызовов функций внутри единицы трансляции для инициализации глобальных переменных
Внутри каждой такой функции помимо вызова инициализации есть вызов onexit/atexit с указанием функции разрушения.

Используя onexit/atexit можно получить чуть бОльший контроль над разрушением.
Re: Время разрушения статических объектов
От: Кодт Россия  
Дата: 18.09.09 13:26
Оценка: 3 (2)
Здравствуйте, Went, Вы писали:

W>Здрасте. Имеется некий менеджер, который живет на статике в одном из cpp-файлов программы, и некие объекты, которые тоже иногда живут на статике, и в момент конструкции регистрятся в этом менеджере, а в момент разрушения — удаляются из него. Когда оказывается, что менеджер разрушился раньше объекта, то при попытке удалиться из него, объект бьет мимо памяти! Какие есть хорошие решения проблемы?


Очевидно, что статический объект конструируется до первого обращения к менеджеру, и следовательно, до его конструирования.
Поэтому при размотке atexit менеджер стоит в очереди на разрушение первым.

Так что нужно
— или форсировать создание менеджера до объекта
— или химичить со счётчиком ссылок
Последнее несложно, даже можно с помощью shared_ptr замутить.

Одна +1 глобальная, от статического указателя на экземпляр менеджера (размещённого в куче), остальные +1 — от элементов реестра.
Единственно, нужно быть аккуратным при удалении элементов реестра, чтобы не выбить табуретку из-под ног.

Нам потребуются два статических указателя:
— Умный, обеспечивающий владение (и -1 при разрушении)
— Глупый, обеспечивающий доступ даже после того, как умный разрушен.
static Manager* instance()
{
    static Manager* just = 0;
    if(!just)
    {
        // внимание! здесь ещё нет потокобезопасности!
        static intrusive_ptr<Manager> root(just = new Manager());
    }
    return just;
}

void register_object(Object* obj)
{
    instance()->register_object(obj); // влечёт инкремент внутреннего счётчика
}
void unregister_object(Object* obj)
{
    instance()->unregister_object(obj); // влечёт декремент внутреннего счётчика
}

Чтобы обеспечить потокобезопасность у синглетона Мейерса, нужно
— или спровоцировать обращение к нему в однопоточной фазе
— или сделать там синхронизацию (можно даже wait-free, если создание лишних экземпляров менеджера не слишком обременительно)
static Manager* instance()
{
    static Manager* just = 0;
    if(!just)
    {
        Manager* temp = new Manager();
        if( InterlockedCompareExchangePointer(&just, temp, 0) ) // (just==0) ? (just=temp, 0) : (just)
            delete temp;
        else
        {
            static intrusive_ptr<Manager> root(just);
        }
    }
    return just;
}
... << RSDN@Home 1.2.0 alpha 4 rev. 1237>>
Перекуём баги на фичи!
Re[4]: Время разрушения статических объектов
От: Erop Россия  
Дата: 18.09.09 14:24
Оценка:
Здравствуйте, Went, Вы писали:

W>Ну, а так мы все ближе сходимся в решении


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

только я всё равно не понимаю на кой вообще пул счётчиков умных указателей грохать?
Пусть себе теряется...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: Время разрушения статических объектов
От: Went  
Дата: 18.09.09 14:42
Оценка:
Здравствуйте, Erop, Вы писали:

E>только я всё равно не понимаю на кой вообще пул счётчиков умных указателей грохать?

E>Пусть себе теряется...

То есть заньюкать его себе и хранить по указателю обычному и пусть теряется? CRT при выходе материть не будет?
Re[6]: Время разрушения статических объектов
От: Erop Россия  
Дата: 18.09.09 15:20
Оценка:
Здравствуйте, Went, Вы писали:

W>То есть заньюкать его себе и хранить по указателю обычному и пусть теряется? CRT при выходе материть не будет?


Ну, как вариант, можно память под пул выделять как-нибудь мимо CRT... Например на своей куче, или ещё как.
Кроме того, можно аллокировать сразу большими кусками, так что если CRT и будет ругаться, то немного...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Время разрушения статических объектов
От: Went  
Дата: 18.09.09 16:13
Оценка:
Здравствуйте. Итак, всем спасибо за подсказки, особенно Кодт-у. Вот что получилось в конце:
class RefPool
  : public IntrusivePolymorph //класс, считающий ссылки и убивающий себя по уничтожению последней
  , public boost::pool<>
{
public:
  RefPool() : boost::pool<>(sizeof(RefCounter), 16384) {}
};

RefPool& ref_pool()
{
  static RefPool* p_pool;
  static IntrusivePtr<RefPool> s_pool(p_pool = new RefPool);
  return *p_pool;
}

void* RefCounter::operator new(Size size)
{
  ASSERT(size == sizeof(RefCounter));
  ref_pool().add_ref();
  return ref_pool().malloc();
}

void RefCounter::operator delete(void* ptr)
{
  ref_pool().free(reinterpret_cast<RefCounter*>(ptr));
  ref_pool().remove_ref();
}

Единственная дырка, я думаю, — компилер, формально, имеет право записать мусор в p_pool во время удаления статики. Но, на практике, я думаю, он таким заниматься не станет
Re[2]: Время разрушения статических объектов
От: Erop Россия  
Дата: 18.09.09 20:17
Оценка:
Здравствуйте, Went, Вы писали:

W>Единственная дырка, я думаю, — компилер, формально, имеет право записать мусор в p_pool во время удаления статики. Но, на практике, я думаю, он таким заниматься не станет


А зачем p_pool делать static in function? Почему бы его не сделать просто глобальной POD-переменной?
При запросе пула, если p_pool ещё 0, то инициализировать.
А s_pool сделать просто статической переменной и инициализировать её примерно так:
static IntrusivePtr<RefPool> s_pool( &ref_pool());

Казалось бы, s_pool обеспечит существование пула в течении всего времени работы программы, а указатель на пул точно не затрётся...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: Время разрушения статических объектов
От: Went  
Дата: 19.09.09 07:25
Оценка:
Здравствуйте, Erop, Вы писали:

E>Казалось бы, s_pool обеспечит существование пула в течении всего времени работы программы, а указатель на пул точно не затрётся...


Ну, в принципе, да. Но есть ли разница (с точки зрения вероятности порчи памяти) в обычной статике и статике-в-функции?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.