- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
template <typename T>
class MySharedPtr{
public:
explicit MySharedPtr(T* obj) : _obj(obj){}
// --> я дописал
MySharedPtr(const MySharedPtr& other) : _obj(other._obj){ inc_ref_count(_obj);}
// <-- я дописал
MySharedPtr& operator=(const MySharedPtr& other) {
// --> я дописал
if (this == &other)
return *this;
// <-- я дописал
_obj = other._obj;
inc_ref_count(_obj);
}
~MySharedPtr(){
dec_ref_count(_obj);
}
private:
static void inc_ref_count(T* obj){
std::lock_guard<std::mutex> lock(_mutex);
_ref_count[obj] ++ ;
}
static void dec_ref_count(T* obj){
std::lock_guard<std::mutex> lock(_mutex);
if (--_ref_count[obj]){
delete obj;
_ref_count.erase(_ref_count.find(obj));
}
}
T* _obj;
static std::mutex MySharedPtr<T>::_mutex;
static std::map<T*,int> MySharedPtr<T>::_ref_count;
};
template <typename T>
std::map<T*,int> MySharedPtr<T>::_ref_count;
template <typename T>
std::mutex MySharedPtr<T>::_mutex;
сегодня приходил чел-выпускник, написал на листочке shared_ptr, какое ваше мнение?
Эту ашку можно инклудить только один раз на проект? Или я отстал от жизни, и в каком-нибудь с++11 это стало работать?
Защищенный единым мутексом глобальный мап со счетчиками ссылок вообще порадовал ;) Прощайте, остатки перфоманса, нам будет вас не хватать.
В операторе присваивания вы с испытуемым забыли декрементнуть счетчик старого указателя.
P.S. А человека берите на испытательный, мозги есть, кресты на базовом уровне знает. Остальное придет с опытом.
Тут главное - реакция испытуемого на ошибки, которые ты ему указал. Может ли он их понять и принять. Может ли он вести конструктивный диалог. Может ли он объяснить, почему он сделал архитектуру именно такой, и какие еще варианты он может предложить. Сможешь ли ты с ним работать в одной команде...
А знание деталей STL (например у map'а есть erase с ключем, и не надо там делать find) и типовых решений (хранение счетчика в дополнительном объекте) - это дело наживное. Если атмосфера в коллективе у вас нормальная, и его одного не будете бросать на произвол судьбы - прокачается.
Видимо ему сказали, что пиздец как важно, чтобы указатель указывал именно на T, а не на T_with_additional_info
Ограниение нелепое, нужно лишь чтобы посмотреть как будет выкручиваться.
Есть, правда, извращенный способ - смартпоинтеры объединить в кольцевой список. Но один такой смартпоинтер будет весить как 3 указателя, да и потокобезопасно хер запилишь эту конструкцию.
А кольцевой список по любому неизбежен для слабых указателей же
Не. Они, если я правильно помню, вообще тупо сделаны. В управляющем блоке 2 счетчика - сильных и слабых. Когда сильный счетчик упал до нуля - объект удаляется, и указатель в управляющем блоке зануляется. Когда слабый счетчик упал до нуля - удаляется управляющий блок. А слабый счетчик уменьшается когда слабую ссылку переадресовывают на что-то другое, или когда в управляющем блоке уже null и ее пытаются сконвертить в сильную. Как-то так.
А сильные - ну как запилишь. Если хочешь размером с указатель - будет двойное разыменование. Если размером с джва указателя - хватит одного.
В смысле, слабые нельзя разыменовывать?
Именно так. Ибо нехуй. Она же не удерживает объект от смерти, и он посреди твоего алгоритма может принять ислам.
Я думал, это такие указатели, которые как сырые, только ещё и умеют предупреждать, если объект сдох, а ты пытаешься обратиться.
А они умеют: > нахрен тогда они нужны
1) Для разрыва циклических зависимостей - например от владельцев к рабам юзаешь сильный указатель, а от рабов к владельцам - слабую.
2) Для всяких кешей. Но тут стоит позаботиться о сборке мусора, периодически пробегаясь по указателям, и вычищая дохлые.
И что, раб не может позвать владельца?
Почему бы тогда вообще не убрать эту ссылку, если её даже разыменовывать нельзя и на время жизни она не влияет? Какой-то Ъ-инкапсулятивный объект получается, не хуже, чем в HQ9+
Может. Временно преобразовав указатель в сильный. См. пример с котом.
Если ты оставишь обе ссылки жесткими - будет утечка. (Да, ты можешь отцеплять ссылки вручную, но одну забыл - и весь кусок графа утек).
Если ты оставишь одну жесткую ссылку от владельца к рабу - раб не сможет вызывать владельца.
Если ты вместо weak_ptr от раба к владельцу поюзаешь просто указатель или простую ссылку - владелец может принять ислам в то время, пока раб с ним общался.
Аааааа, я понял! Тредоблядские проблемы!
Они самые ;)
> Они самые ;)
А можно разве вик_птр локать в шаред, в то время как последний шаред_птр уходит на покой? Не будет рейс кондишена?
Второе правило троллинга: никогда не употребляй слово «троллинг».
Лакни хуйца, зайчик-ебантяйчик.
Можно. Рейс кондишена не будет. Либо ты залочишь его, и шаред_птр не успеет уйти, либо он успеет уйти, а тебе достанется нулл.
Нахер они вообще нужны без этой фичи? :)
> else
> кот мертв :(
Наблюдатель виноват.
Задаюсь аналогичным вопросом c того момента, как узнал про их отношения с shared_ptr. Знаю только, что weak_ptr используется внутри enable_shared_from_this, но явно он мне не пригодился ни разу.
А я юзал shared_from_this на практике :) Нужно было из метода одного объекта запиливать другие, передавая им шаропоинтер на себя. Но this передать было нельзя, т.к. он уже управлялся через shared_ptr, а отдать один указатель на растерзание двум шаропоинтерам - жди беды.
Вот в таких случаях и нужен shared_from_this().
Ага, я тоже стал пользоваться; в некоторых классах конструкторы попрятались в фабрики, производящие shared_ptr<T>. Я просто не могу представить ситуацию, при которой нужно явно использовать weak_ptr. Пример, в котором два объекта содержат ссылки друг на друга и не могут от этого самоудалиться, выглядит несколько... м-м-м... надуманно :)
допустим, ты пишешь tcp-сервер
у тебя есть объект connection, он создается и живёт строго как shared_ptr<connection> (кстати, тут как раз везде и пригождается ему shared_from_this()), хранит в себе socket и делает всё что ему полагается как соединению - ждёт прихода данных от клиента, обрабатывает их, отвечает клиенту, ждёт следующей порции данных (ну типа как persistent).
и у тебя есть объект server, который всегда держит наизготовке свежий shared_ptr<connection>, чтобы акцептить на его объекте socket
ну так вот, коннекшен в любой момент может принять решение закончиться - ну, например, сокет уже закрылся, ну или логическая ошибка - и это его право
однако, объект server тоже могут заставить принудительно умереть - например, приложение/сервис штатно останавливают.
надо всем живым коннекшенам, которые когда-либо были созданы этим сервером сообщить, что "ребята, пора дохнуть, заканчивайте свой балаган"
т.е. приходим к тому, что серверу надо держать некий список текущих соединений, а каждому соединению, когда оно желает помереть, сообщить серверу предсмертное послание, что, дескать, меня вычеркивайте уже
вот тут-то двум объектам и приходится держать друг на друга ссылки
p.s. собственно задача элегантно и элементарно решается как раз тем, что коннекшен держит на сервер лишь weak_ptr, тогда в итоге все умирают в конце, приятного просмотра
Как раз сегодня именно с этим и разбирался :) Предположил, что мне понадобится weak_ptr, но обошелся без него. Вот сумбурное описание моего решения:
Есть connection, который содержит в себе сокет и создается только в shared_ptr.
Есть connection_manager, который хранит в себе живые подключения: vector<shared_ptr<connection> >
Также connection_manager содержит acceptor и метод begin_accept(), который выглядит примерно так:
Благодаря boost::bind, объект conn, в который нужно принять клиента, "доживает" до вызова accept_handler().
Если клиент принят без проблем, то он добавляется в список живых клиентов и продолжает жить вместе с ним.
Если клиента принять не удалось - объект самоуничтожится по выходу из begin_accept().
connection содержит коллбек, который вызывается при разрыве соединения (зафейленный read_handler или write_handler, закрытие соединения со стороны клиента). Этот коллбек настраивается на удаление клиента из списка connection_manager.
Сервер сделан несколько примитивно и вряд ли масштабируется хотя бы до 1000 подключений. Тестировал поверхностно, логи пишут, что все ресурсы освобождаются корректно.
воот
очевидно, кококоллбек на объект connection_manager
ну типа boost::bind(&connection_manager::on_dead_connection, this, _1, ...), верно? (сужу по твоему обращению с accept_handler)
теперь классическая ситуация
очевидно, у тебя asio, и объект connection_manager драматично умирает в одном потоке (т.е. опять тот же пример, что приложение банально штатно закрывается), а чуть погодя всё ещё живой connection (хотя бы тоже умирающий в другом потоке, хотя это и не столь важно) пытается вызвать у этого connection_manager злосчастный коллбек - по мёртвому объекту
и в итоге вместо приятно умершего приложения, пользователь видит противное "БАМЦ, приложение обратилось к необратимому и допустило недопустимое!"
как решаем
ага! connection_manager надо тоже пошарить, и в коллбек коннекшену скормить boost::bind(&..., shared_from_this(), ...)
вуаля - получили два взаимосвязанных по shared_ptr объекта
> connection_manager драматично умирает в одном потоке
> всё ещё живой connection <...> пытается вызвать у этого connection_manager
Коллбеку on_dead_connection биндится самоудаление из сервера в случае успешного акцепта.
А перед смертью акцептор закрывает себя, поэтому handle_accept не должен завершиться успешно после смерти сервера. Напоминает говнокод if(this != NULL) { /*...*/ }
> всё ещё живой connection <...> пытается вызвать у этого connection_manager злосчастный коллбек - по мёртвому объекту
Можно обойти - сделать обработчик handle_accept статической функцией и привязывать к нему shared_ptr<connection_manager> и shared_ptr<connection>.
Перед вызовом async_accept они друг с другом никак не связаны.
Успешный accept - и живой connection становится рабом живого connection_manager.
Фейл - connection умирает и они друг на друга не ссылаются.
проблема не в нем, он у тебя более-менее правильно написан, там вообще сложно напортачить
проблема будет дальше, когда всё уже давно заакцепчено, работает и начинает помирать, перечитай ещё раз, что я написал
у тебя в N потоках работают коннекшены
работают асинхронно
о них знает connection_manager
connection_manager собрался помереть
командует своим коннекшенам из списка "умрите"
и как бы хочет теперь и сам помереть
теперь у тебя дилемма:
1) либо теперь запрещено всем-всем коннекшенам вызывать коллбек, и придется городить адовый ад с учетом расовых кондиционеров
2) connection_manager запрещено умереть, пока не помрут все его connection - опять же адовый ад, например, с семафором (т.е. mutex+condition_variable+counter), а поток, в котором вызвали деструктор connection_manager, должен терпеть
просто я всё это уже в старых проектах проходил
я ваши грабли замечаю ещё до того, как вы о них мне сказали
Дошло :) Будем думать.
иногда мне кажется, что впору сесть и писать туториал "пишем свой сервер на асио на коленях"
Эти все атомарные счетчики циклов для меня, например, до сих пор темный лес. Порой кажется, что авторы статей на тему копипастят друг у друга одно и то же.
А карта счетчиков ссылок имеет забавный побочный эффект: если в конце программы она не пустая, то поделенный_укзтл косячный :)
что теперь то
не у всех целерон, кому-то иногда хочется выжать из железок больше, чем это было возможно в 2001 году
Вообще я тут сижу и с умным видом, как сеньёр кокойта, обсуждаю код соискателя, а на деле оказалось, что мой реальный код - нубский отстой, дающий по 100 предупреждений на всех компиляторах, кроме моего, да ещё и багующий в релизе из-за странных оптимизаций. Чувствую себя дерьмом.
А потом хранилище грохал в самом конце. Потом, правда оказалось что программа много жрёт памяти, и я немного оптимизировал (хотя мог и не делать), после загрузки каждого графа делал обход вершин и убирал недостижимые.
Да, я эмулировал сраную гэцэшечку)))
Но никакие счётчики ссылок и слабые указатели мне были нахуй не нужны.
О каком сборщике мусора может идти речь?
Но кресты != сишка.
А про сишку ты все правильно сказал.
Может он, узнав о печальной кончине Крея, боится? Ну вот, признает человек, что за параллельными копмутерами будущее, а тут случайно запорожец изза угла - и нет больше с нами ТарасаБ.
Вы про владельца Крей Инкопрорейтед?
И да, там жеж какая печальная история случилась: жил человек, делал самые быстрые в мире суперкомпутеры, и не верил в распределенные системы. В один прекрасный день компутер с распределенной системой его компутер обогнал. Задумался человек, и решил тоже сделать параллельный компутер, опять же, самый быстрый, и тут внезапно его машина сбила...
stupid_ptr обернули в обертку для однородности?