- 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, какое ваше мнение?
LispGovno 14.02.2014 14:57 # −1
bormand 14.02.2014 15:02 # +2
Эту ашку можно инклудить только один раз на проект? Или я отстал от жизни, и в каком-нибудь с++11 это стало работать?
Защищенный единым мутексом глобальный мап со счетчиками ссылок вообще порадовал ;) Прощайте, остатки перфоманса, нам будет вас не хватать.
В операторе присваивания вы с испытуемым забыли декрементнуть счетчик старого указателя.
P.S. А человека берите на испытательный, мозги есть, кресты на базовом уровне знает. Остальное придет с опытом.
bormand 14.02.2014 15:20 # +2
Тут главное - реакция испытуемого на ошибки, которые ты ему указал. Может ли он их понять и принять. Может ли он вести конструктивный диалог. Может ли он объяснить, почему он сделал архитектуру именно такой, и какие еще варианты он может предложить. Сможешь ли ты с ним работать в одной команде...
А знание деталей STL (например у map'а есть erase с ключем, и не надо там делать find) и типовых решений (хранение счетчика в дополнительном объекте) - это дело наживное. Если атмосфера в коллективе у вас нормальная, и его одного не будете бросать на произвол судьбы - прокачается.
TarasB 14.02.2014 16:36 # 0
Видимо ему сказали, что пиздец как важно, чтобы указатель указывал именно на T, а не на T_with_additional_info
Ограниение нелепое, нужно лишь чтобы посмотреть как будет выкручиваться.
bormand 14.02.2014 16:38 # 0
TarasB 14.02.2014 16:46 # 0
bormand 14.02.2014 16:48 # 0
Есть, правда, извращенный способ - смартпоинтеры объединить в кольцевой список. Но один такой смартпоинтер будет весить как 3 указателя, да и потокобезопасно хер запилишь эту конструкцию.
TarasB 14.02.2014 17:07 # −1
А кольцевой список по любому неизбежен для слабых указателей же
Konardo 14.02.2014 17:13 # −7
bormand 14.02.2014 17:18 # 0
Не. Они, если я правильно помню, вообще тупо сделаны. В управляющем блоке 2 счетчика - сильных и слабых. Когда сильный счетчик упал до нуля - объект удаляется, и указатель в управляющем блоке зануляется. Когда слабый счетчик упал до нуля - удаляется управляющий блок. А слабый счетчик уменьшается когда слабую ссылку переадресовывают на что-то другое, или когда в управляющем блоке уже null и ее пытаются сконвертить в сильную. Как-то так.
TarasB 14.02.2014 17:37 # 0
bormand 14.02.2014 17:41 # 0
А сильные - ну как запилишь. Если хочешь размером с указатель - будет двойное разыменование. Если размером с джва указателя - хватит одного.
TarasB 14.02.2014 17:43 # 0
В смысле, слабые нельзя разыменовывать?
bormand 14.02.2014 17:46 # 0
Именно так. Ибо нехуй. Она же не удерживает объект от смерти, и он посреди твоего алгоритма может принять ислам.
TarasB 14.02.2014 17:53 # +2
Я думал, это такие указатели, которые как сырые, только ещё и умеют предупреждать, если объект сдох, а ты пытаешься обратиться.
bormand 14.02.2014 18:01 # +2
А они умеют: > нахрен тогда они нужны
1) Для разрыва циклических зависимостей - например от владельцев к рабам юзаешь сильный указатель, а от рабов к владельцам - слабую.
2) Для всяких кешей. Но тут стоит позаботиться о сборке мусора, периодически пробегаясь по указателям, и вычищая дохлые.
TarasB 14.02.2014 18:24 # 0
И что, раб не может позвать владельца?
Почему бы тогда вообще не убрать эту ссылку, если её даже разыменовывать нельзя и на время жизни она не влияет? Какой-то Ъ-инкапсулятивный объект получается, не хуже, чем в HQ9+
bormand 14.02.2014 18:30 # +1
Может. Временно преобразовав указатель в сильный. См. пример с котом.
Если ты оставишь обе ссылки жесткими - будет утечка. (Да, ты можешь отцеплять ссылки вручную, но одну забыл - и весь кусок графа утек).
Если ты оставишь одну жесткую ссылку от владельца к рабу - раб не сможет вызывать владельца.
Если ты вместо weak_ptr от раба к владельцу поюзаешь просто указатель или простую ссылку - владелец может принять ислам в то время, пока раб с ним общался.
TarasB 14.02.2014 18:39 # +3
Аааааа, я понял! Тредоблядские проблемы!
bormand 14.02.2014 18:42 # 0
Они самые ;)
laMer007 14.02.2014 19:03 # +1
> Они самые ;)
А можно разве вик_птр локать в шаред, в то время как последний шаред_птр уходит на покой? Не будет рейс кондишена?
Stertor 14.02.2014 19:04 # −9
inkanus-gray 14.02.2014 19:05 # +3
Второе правило троллинга: никогда не употребляй слово «троллинг».
Xom94ok 14.02.2014 19:23 # +1
Konardo 14.02.2014 19:52 # −6
Лакни хуйца, зайчик-ебантяйчик.
bormand 14.02.2014 19:11 # +2
Можно. Рейс кондишена не будет. Либо ты залочишь его, и шаред_птр не успеет уйти, либо он успеет уйти, а тебе достанется нулл.
Нахер они вообще нужны без этой фичи? :)
laMer007 14.02.2014 18:57 # +1
> else
> кот мертв :(
Наблюдатель виноват.
Xom94ok 14.02.2014 18:02 # 0
Задаюсь аналогичным вопросом c того момента, как узнал про их отношения с shared_ptr. Знаю только, что weak_ptr используется внутри enable_shared_from_this, но явно он мне не пригодился ни разу.
bormand 14.02.2014 18:08 # 0
А я юзал shared_from_this на практике :) Нужно было из метода одного объекта запиливать другие, передавая им шаропоинтер на себя. Но this передать было нельзя, т.к. он уже управлялся через shared_ptr, а отдать один указатель на растерзание двум шаропоинтерам - жди беды.
Вот в таких случаях и нужен shared_from_this().
Xom94ok 14.02.2014 18:25 # +1
Ага, я тоже стал пользоваться; в некоторых классах конструкторы попрятались в фабрики, производящие shared_ptr<T>. Я просто не могу представить ситуацию, при которой нужно явно использовать weak_ptr. Пример, в котором два объекта содержат ссылки друг на друга и не могут от этого самоудалиться, выглядит несколько... м-м-м... надуманно :)
defecate-plusplus 14.02.2014 19:28 # +7
допустим, ты пишешь tcp-сервер
у тебя есть объект connection, он создается и живёт строго как shared_ptr<connection> (кстати, тут как раз везде и пригождается ему shared_from_this()), хранит в себе socket и делает всё что ему полагается как соединению - ждёт прихода данных от клиента, обрабатывает их, отвечает клиенту, ждёт следующей порции данных (ну типа как persistent).
и у тебя есть объект server, который всегда держит наизготовке свежий shared_ptr<connection>, чтобы акцептить на его объекте socket
ну так вот, коннекшен в любой момент может принять решение закончиться - ну, например, сокет уже закрылся, ну или логическая ошибка - и это его право
однако, объект server тоже могут заставить принудительно умереть - например, приложение/сервис штатно останавливают.
надо всем живым коннекшенам, которые когда-либо были созданы этим сервером сообщить, что "ребята, пора дохнуть, заканчивайте свой балаган"
т.е. приходим к тому, что серверу надо держать некий список текущих соединений, а каждому соединению, когда оно желает помереть, сообщить серверу предсмертное послание, что, дескать, меня вычеркивайте уже
вот тут-то двум объектам и приходится держать друг на друга ссылки
p.s. собственно задача элегантно и элементарно решается как раз тем, что коннекшен держит на сервер лишь weak_ptr, тогда в итоге все умирают в конце, приятного просмотра
Xom94ok 14.02.2014 20:00 # 0
Как раз сегодня именно с этим и разбирался :) Предположил, что мне понадобится 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 подключений. Тестировал поверхностно, логи пишут, что все ресурсы освобождаются корректно.
defecate-plusplus 14.02.2014 20:13 # +1
воот
очевидно, кококоллбек на объект 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 объекта
Xom94ok 14.02.2014 20:41 # 0
> 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 умирает и они друг на друга не ссылаются.
defecate-plusplus 14.02.2014 20:49 # +3
проблема не в нем, он у тебя более-менее правильно написан, там вообще сложно напортачить
проблема будет дальше, когда всё уже давно заакцепчено, работает и начинает помирать, перечитай ещё раз, что я написал
defecate-plusplus 14.02.2014 20:58 # +2
у тебя в N потоках работают коннекшены
работают асинхронно
о них знает connection_manager
connection_manager собрался помереть
командует своим коннекшенам из списка "умрите"
и как бы хочет теперь и сам помереть
теперь у тебя дилемма:
1) либо теперь запрещено всем-всем коннекшенам вызывать коллбек, и придется городить адовый ад с учетом расовых кондиционеров
2) connection_manager запрещено умереть, пока не помрут все его connection - опять же адовый ад, например, с семафором (т.е. mutex+condition_variable+counter), а поток, в котором вызвали деструктор connection_manager, должен терпеть
просто я всё это уже в старых проектах проходил
я ваши грабли замечаю ещё до того, как вы о них мне сказали
Xom94ok 14.02.2014 20:59 # +1
Дошло :) Будем думать.
defecate-plusplus 14.02.2014 21:15 # +3
иногда мне кажется, что впору сесть и писать туториал "пишем свой сервер на асио на коленях"
Konardo 14.02.2014 15:05 # −14
Xom94ok 14.02.2014 17:58 # +1
Эти все атомарные счетчики циклов для меня, например, до сих пор темный лес. Порой кажется, что авторы статей на тему копипастят друг у друга одно и то же.
А карта счетчиков ссылок имеет забавный побочный эффект: если в конце программы она не пустая, то поделенный_укзтл косячный :)
bormand 14.02.2014 18:11 # +1
Konardo 14.02.2014 18:04 # −13
TarasB 14.02.2014 20:46 # +3
defecate-plusplus 14.02.2014 20:51 # +1
что теперь то
не у всех целерон, кому-то иногда хочется выжать из железок больше, чем это было возможно в 2001 году
TarasB 14.02.2014 20:58 # +2
Вообще я тут сижу и с умным видом, как сеньёр кокойта, обсуждаю код соискателя, а на деле оказалось, что мой реальный код - нубский отстой, дающий по 100 предупреждений на всех компиляторах, кроме моего, да ещё и багующий в релизе из-за странных оптимизаций. Чувствую себя дерьмом.
TarasB 14.02.2014 21:01 # +2
А потом хранилище грохал в самом конце. Потом, правда оказалось что программа много жрёт памяти, и я немного оптимизировал (хотя мог и не делать), после загрузки каждого графа делал обход вершин и убирал недостижимые.
Да, я эмулировал сраную гэцэшечку)))
Но никакие счётчики ссылок и слабые указатели мне были нахуй не нужны.
defecate-plusplus 14.02.2014 21:06 # +1
Stertor 14.02.2014 21:09 # −5
Stertor 16.02.2014 15:08 # +2
О каком сборщике мусора может идти речь?
kipar 16.02.2014 16:14 # +3
bormand 17.02.2014 05:48 # 0
Но кресты != сишка.
А про сишку ты все правильно сказал.
kipar 17.02.2014 12:36 # 0
TarasB 15.02.2014 11:30 # +1
anonimb84a2f6fd141 15.02.2014 13:57 # −12
wvxvw 15.02.2014 18:56 # +3
Может он, узнав о печальной кончине Крея, боится? Ну вот, признает человек, что за параллельными копмутерами будущее, а тут случайно запорожец изза угла - и нет больше с нами ТарасаБ.
laMer007 15.02.2014 19:41 # 0
Вы про владельца Крей Инкопрорейтед?
wvxvw 15.02.2014 20:02 # +3
И да, там жеж какая печальная история случилась: жил человек, делал самые быстрые в мире суперкомпутеры, и не верил в распределенные системы. В один прекрасный день компутер с распределенной системой его компутер обогнал. Задумался человек, и решил тоже сделать параллельный компутер, опять же, самый быстрый, и тут внезапно его машина сбила...
laMer007 15.02.2014 21:40 # −1
defecate-plusplus 15.02.2014 21:50 # −1
Konardo 15.02.2014 22:26 # −11
Konardo 14.02.2014 21:29 # −15
laMer007 15.02.2014 19:46 # +3
stupid_ptr обернули в обертку для однородности?
TarasB 15.02.2014 19:58 # +3