удаление вызывающего объекта, и непонятка с размоткой стека
От: niXman Ниоткуда https://github.com/niXman
Дата: 13.03.13 13:27
Оценка:
приветствую!

имеется такой код:
#include <iostream>
#include <memory>
#include <map>

#include <boost/format.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/deadline_timer.hpp>

/****************************************************************************/

// forward
struct item;

/****************************************************************************/

struct container {
   container(boost::asio::io_service &ios);

   void delete_me(int id) {
      auto it = map.find(id);
      BOOST_ASSERT(it != map.end());
      std::cerr << boost::format("before erase item %1% (%2%)")
         % id % it->second.get()
      << std::endl;
      
      map.erase(it);

      std::cerr << boost::format("after erase item %1% (%2%)")
         % id % it->second.get()
      << std::endl;
   }
   
   boost::asio::io_service &_ios;
   std::map<int, std::shared_ptr<item>> map;
};

/****************************************************************************/

struct item {
   item(int id, boost::asio::io_service &ios, container& cont)
      :_id(id)
      ,_timer(ios)
   {
      const int time = (id==0 ? 2 : 1);
      
      std::cerr << boost::format("item %1% created. time=%2% (%3%)") % id % time % this << std::endl;
      
      _timer.expires_from_now(boost::posix_time::seconds(time));
      _timer.async_wait(
         [this, &cont, id](const boost::system::error_code&) {
            std::cerr << boost::format("time-out for item %1% expired, request for delete it (%2%)")
               % id % this
            << std::endl;
            
            cont.delete_me(id);
         }
      );
   }
   
   ~item() {
      std::cerr << boost::format("dtor item %1% (%2%)")
         % _id % this
      << std::endl;
   }
   
   int _id;
   boost::asio::deadline_timer _timer;
};

/****************************************************************************/

container::container(boost::asio::io_service &ios)
   :_ios(ios)
{
   auto p1 = new item(0, _ios, *this);
   map[0].reset(p1);
   auto p2 = new item(1, _ios, *this);
   map[1].reset(p2);
}

/****************************************************************************/

int main() {
   boost::asio::io_service ios;
   container cont(ios);
   ios.run();
   std::cerr << std::endl << "finished!" << std::endl;
}

/****************************************************************************/

http://liveworkspace.org/code/2brGJU$0

что выполняет код?: container создает некоторое кол-во объектов типа item. item`ы в свою очередь, удаляются по событиям производимым в ответ на сетевое взаимодействие, т.е. асинхронно. (тут в примере — по таймеру)
в чем вопрос?: вопрос в том, что как вы можете видеть в выводе(ниже), когда item по адресу 0xda6370 зовет 'container::delete_me(id)' для себя же, что происходит со стеком? стек ведь должен размотаться до уровня, из которого вызвали 'container::delete_me(id)', но объект же удалится раньше чем стек размотается %)

item 0 created. time=2 (0xda6110)
item 1 created. time=1 (0xda6370)
time-out for item 1 expired, request for delete it (0xda6370)
before erase item 1 (0xda6370)
dtor item 1 (0xda6370)
after erase item 1 (0xda6370)
time-out for item 0 expired, request for delete it (0xda6110)
before erase item 0 (0xda6110)
dtor item 0 (0xda6110)
after erase item 0 (0xda6110)

finished!


спасибо.


зы
я плохо знаю ассемблер .и наверное по этому и не понимаю, валиден ли код или нет...
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re: удаление вызывающего объекта, и непонятка с размоткой стека
От: Evgeny.Panasyuk Россия  
Дата: 13.03.13 14:01
Оценка:
Здравствуйте, niXman, Вы писали:

X>в чем вопрос?: вопрос в том, что как вы можете видеть в выводе(ниже), когда item по адресу 0xda6370 зовет 'container::delete_me(id)' для себя же, что происходит со стеком? стек ведь должен размотаться до уровня, из которого вызвали 'container::delete_me(id)', но объект же удалится раньше чем стек размотается %)


В твоём случае он не удаляет "себя же", это делает "ios.run();" :
http://liveworkspace.org/code/2brGJU$8

stderr:
item 0 created. time=2 (0xbdc110)
item 1 created. time=1 (0xbdc370)
started!
time-out for item 1 expired, request for delete it (0xbdc370)
before erase item 1 (0xbdc370)
dtor item 1 (0xbdc370)
after erase item 1 (0xbdc370)
time-out for item 0 expired, request for delete it (0xbdc110)
before erase item 0 (0xbdc110)
dtor item 0 (0xbdc110)
after erase item 0 (0xbdc110)

finished!

P.S. не надо лезть в итератор после erase.
Re[2]: удаление вызывающего объекта, и непонятка с размоткой стека
От: niXman Ниоткуда https://github.com/niXman
Дата: 13.03.13 14:06
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>В твоём случае он не удаляет "себя же", это делает "ios.run();" :

почему же?
нет, я понимаю как работает io_service::run(), но цепь вызовов у нас такая: timeout_handler()->container::delete_me()->map::erase()->delete <item address>

EP>P.S. не надо лезть в итератор после erase.

я сначала убедился в том, что адрес выводится правильный, и только потом решил почистить код от дополнительной переменной.
и суть не в этом
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[3]: удаление вызывающего объекта, и непонятка с размоткой стека
От: Evgeny.Panasyuk Россия  
Дата: 13.03.13 14:12
Оценка:
Здравствуйте, niXman, Вы писали:

EP>>В твоём случае он не удаляет "себя же", это делает "ios.run();" :

X>почему же?
X>нет, я понимаю как работает io_service::run(), но цепь вызовов у нас такая: timeout_handler()->container::delete_me()->map::erase()->delete <item address>

Ок, давай рассмотрим вот такой вариант:
int main() {
   boost::asio::io_service ios;
   container cont(ios);
   std::cerr << "started!" << std::endl;
   cont.delete_me(0);
   cont.delete_me(1);
   //ios.run();
   std::cerr << std::endl << "finished!" << std::endl;
}


X>и суть не в этом


согласен
Re[4]: удаление вызывающего объекта, и непонятка с размоткой стека
От: niXman Ниоткуда https://github.com/niXman
Дата: 13.03.13 14:15
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Ок, давай рассмотрим вот такой вариант:

т.е. тут ты акцентируешь внимание на том, что io_service::run() замыкает хендлер на основной поток выполнения, так?
но все равно не понимаю, что общего у твоего примера, и моего %)
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[5]: удаление вызывающего объекта, и непонятка с размоткой стека
От: Evgeny.Panasyuk Россия  
Дата: 13.03.13 14:22
Оценка:
Здравствуйте, niXman, Вы писали:

EP>>Ок, давай рассмотрим вот такой вариант:

X>т.е. тут ты акцентируешь внимание на том, что io_service::run() замыкает хендлер на основной поток выполнения, так?
X>но все равно не понимаю, что общего у твоего примера, и моего %)

Вот call stack для твоего примера (MSVC2010SP1 Debug):
item::~item()  Строка 64    C++
item::`scalar deleting destructor'()  + 0x2c байт    C++
std::tr1::_Ref_count<item>::_Destroy()  Строка 1107 + 0x2f байт    C++
std::tr1::_Ref_count_base::_Decref()  Строка 1067    C++
std::tr1::_Ptr_base<item>::_Decref()  Строка 1291    C++
std::tr1::shared_ptr<item>::~shared_ptr<item>()  Строка 1577    C++
std::_Pair_base<int const ,std::tr1::shared_ptr<item> >::~_Pair_base<int const ,std::tr1::shared_ptr<item> >()  + 0x2f байт    C++
std::pair<int const ,std::tr1::shared_ptr<item> >::~pair<int const ,std::tr1::shared_ptr<item> >()  + 0x28 байт    C++
std::pair<int const ,std::tr1::shared_ptr<item> >::`scalar deleting destructor'()  + 0x2c байт    C++
std::_Destroy<std::pair<int const ,std::tr1::shared_ptr<item> > >(std::pair<int const ,std::tr1::shared_ptr<item> > * _Ptr=0x0000000000df9278 (0x00000001, shared_ptr {_id=0x00000001 _timer={...} } [0x00000000 strong refs] [default]))  Строка 64    C++
std::allocator<std::pair<int const ,std::tr1::shared_ptr<item> > >::destroy(std::pair<int const ,std::tr1::shared_ptr<item> > * _Ptr=0x0000000000df9278 (0x00000001, shared_ptr {_id=0x00000001 _timer={...} } [0x00000000 strong refs] [default]))  Строка 214    C++
std::_Dest_val<std::allocator<std::pair<int const ,std::tr1::shared_ptr<item> > >,std::pair<int const ,std::tr1::shared_ptr<item> > >(std::allocator<std::pair<int const ,std::tr1::shared_ptr<item> > > & _Alval={...}, std::pair<int const ,std::tr1::shared_ptr<item> > * _Pdest=0x0000000000df9278 (0x00000001, shared_ptr {_id=0x00000001 _timer={...} } [0x00000000 strong refs] [default]))  Строка 288    C++
std::_Tree<std::_Tmap_traits<int,std::tr1::shared_ptr<item>,std::less<int>,std::allocator<std::pair<int const ,std::tr1::shared_ptr<item> > >,0> >::erase(std::_Tree_const_iterator<std::_Tree_val<std::_Tmap_traits<int,std::tr1::shared_ptr<item>,std::less<int>,std::allocator<std::pair<int const ,std::tr1::shared_ptr<item> > >,0> > > * _Where=0x0000000000a1f338 (0xcdcdcdcd, shared_ptr {_id=??? _timer={...} } [... strong refs] [{_Uses=??? _Weaks=??? }]))  Строка 1370    C++
container::delete_me(int id=0x00000001)  Строка 27 + 0x4e байт    C++
`anonymous namespace'::<lambda0>::operator()(const boost::system::error_code & __formal={...})  Строка 57    C++
boost::asio::detail::binder1<`anonymous namespace'::<lambda0>,boost::system::error_code>::operator()()  Строка 47    C++
boost::asio::asio_handler_invoke<boost::asio::detail::binder1<`anonymous namespace'::<lambda0>,boost::system::error_code> >(boost::asio::detail::binder1<`anonymous namespace'::<lambda0>,boost::system::error_code> function={...}, ...)  Строка 65    C++
boost_asio_handler_invoke_helpers::invoke<boost::asio::detail::binder1<`anonymous namespace'::<lambda0>,boost::system::error_code>,`anonymous namespace'::<lambda0> >(boost::asio::detail::binder1<`anonymous namespace'::<lambda0>,boost::system::error_code> & function={...}, `anonymous-namespace'::<lambda0> & context={...})  Строка 41    C++
boost::asio::detail::wait_handler<`anonymous namespace'::<lambda0> >::do_complete(boost::asio::detail::win_iocp_io_service * owner=0x0000000000df8c30, boost::asio::detail::win_iocp_operation * base=0x0000000000df9190, const boost::system::error_code & __formal={...}, const boost::system::error_code & __formal={...})  Строка 69 + 0xf байт    C++
boost::asio::detail::win_iocp_operation::complete(boost::asio::detail::win_iocp_io_service & owner={...}, const boost::system::error_code & ec={...}, unsigned __int64 bytes_transferred=0x0000000000000000)  Строка 46    C++
boost::asio::detail::win_iocp_io_service::do_one(bool block=true, boost::system::error_code & ec={...})  Строка 405    C++
boost::asio::detail::win_iocp_io_service::run(boost::system::error_code & ec={...})  Строка 161 + 0x17 байт    C++
boost::asio::io_service::run()  Строка 59 + 0x13 байт    C++
main()  Строка 91    C++
__tmainCRTStartup()  Строка 555 + 0x19 байт    C
mainCRTStartup()  Строка 371    C

Что конкретно смущает?
Re[6]: удаление вызывающего объекта, и непонятка с размоткой стека
От: niXman Ниоткуда https://github.com/niXman
Дата: 13.03.13 14:27
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Что конкретно смущает?

не понимаю, что ты хочешь сказать. вроде как, твой ответ не отвечает на мой вопрос. или я не "туда смотрю"...
возможно мы говорим о разном.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re: удаление вызывающего объекта, и непонятка с размоткой стека
От: zaufi Земля  
Дата: 13.03.13 14:28
Оценка: 18 (1) +1
Здравствуйте, niXman, Вы писали:

X>приветствую!



X>что выполняет код?: container создает некоторое кол-во объектов типа item. item`ы в свою очередь, удаляются по событиям производимым в ответ на сетевое взаимодействие, т.е. асинхронно. (тут в примере — по таймеру)


X>в чем вопрос?: вопрос в том, что как вы можете видеть в выводе(ниже), когда item по адресу 0xda6370 зовет 'container::delete_me(id)' для себя же, что происходит со стеком? стек ведь должен размотаться до уровня, из которого вызвали 'container::delete_me(id)', но объект же удалится раньше чем стек размотается %)


чтобы понимать, что происходит лучше конечно знать в какой машинный код транслируется твой пример. и да, это понимание требует знания машинных команд и практики работы с ними (реверсинг С++ного кода, а также желательно написание своего кода на "голом" asm, чтобы прочуствовать все на свое шкуре )

возвращаясь к твоему вопросу: что ты позразумеваешь тут под "размоткой стека" ? ) когда по твоему и самое главное почему это должно происходить? -- вообщето данный терм относится к теме исключений, но речь ведь сейчас не о них!

когда ты в С++ пишешь вызов функции, в машшинных командах это означает следующие шаги:
0) засунуть параметры функции в регистры/стэк (что и куда, в общем случае, зависит от calling convention)
1) выполнить инструкцию call, которая положит в стек (возможно в добавок в параметрам) адрес возврата, и передаст управление по адресу функции/метода
2) далее выполняется пролог вызванной функции (организация stack frame: push %ebp; mov %esp, %ebp)
3) выполняется собственно функция
4) выполняем эпилог функции: возвращаем ebp в состояние которое он имел до вызова (pop %ebp)
5) выполняем инструкцию ret, которая возвращает нас по адресу сохраненному в вершине стека (вытаскивая его оттуда)
6) после возвращения из подпрограммы, согласно Сишному CC, вызывающая сторона, убирает из стека, засунутые туда параметры (ибо, в общем случае, только она и знает сколько их было и какого размера)... благодаря этому кстати в С возможны функции с неопределенным числом параметров (ellipses), и не возможны в pascal CC, где подпрограмма сама убирает из стека переданные ей параметры перед возвратом (ну или во время его, для чего инструкция ret, позднее была сделана с параметром: на сколько подвинуть стэк перед возвратом).

т.е. с точки зрения машинных команд нет ни каких объектов с методами -- есть просто куча "подпрограмм" которые принимают какие-то параметры и работают с какими-то данными, и методы от свободных функций на этом уровне не отличаются ничем! (просто первый параметр у них (неявный с точки зрения С++) это this указывающий на экземпляр объекта).

таким образом, после того как ты опосредованно удалил сам себя (не будем пока говорить о прямоте данного дизайна), важно соблюдать "гигиену" в коде, и не пытаться обращаться к своим дата-мемберам и виртуальным функциям (это ессно бует UB)... хотя в некоторых случаях и это сканает, потому что удаленный объект не сразу может превратиться в мусор, нужно чтобы кто-нить (из вызываемых далее функций) аллоцировал что-нить, "порушив" освобожденную только что память) -- но в любом случае это UB, и поиск подобного бага то еще развлечение

теперь о главном: практически всегда извраты подобные этому -- это проблемы в понимании и сопровождении такого кода (даже в таком тупом случае как 'delete this;'! нужно четко себе представлять life time всех участвующих объектов, что в мясном коде (boost::asio + куча асинхнронных callbackов) однозначно вызовет проблемы если не с написанием , то при сопровождении (даже если это будешь ты сам, но после нескольких месяцев а может и лет... не говоря уже о других людях

старайся избегать подобных наворотов -- будь проще! сделай так, чтобы удалением занимался сам контейнер, а не "тупой" item, который ничего не знает (да и не должен) о том где он хранится, сколько ему отведено жить, и т.д. -- это все находится в ведении контейнера: он единоличный хозяин всех itemов! он может кому-то дать "попользоваться" своей "собственностью", отдав weak_ptr "наружу", или может "подарить" кого-то из itemов, явно (!) передав владение... но давать itemам возможность самоубиться, это глупо и неправильно!
Re[2]: удаление вызывающего объекта, и непонятка с размоткой стека
От: niXman Ниоткуда https://github.com/niXman
Дата: 13.03.13 14:40
Оценка:
Здравствуйте, zaufi, Вы писали:

о!, ты вроде понял о чем я


Z>возвращаясь к твоему вопросу: что ты позразумеваешь тут под "размоткой стека" ? ) когда по твоему и самое главное почему это должно происходить? -- вообщето данный терм относится к теме исключений, но речь ведь сейчас не о них!


Z>когда ты в С++ пишешь вызов функции, в машшинных командах это означает следующие шаги:

Z>0) засунуть параметры функции в регистры/стэк (что и куда, в общем случае, зависит от calling convention)
Z>1) выполнить инструкцию call, которая положит в стек (возможно в добавок в параметрам) адрес возврата, и передаст управление по адресу функции/метода
Z>2) далее выполняется пролог вызванной функции (организация stack frame: push %ebp; mov %esp, %ebp)
Z>3) выполняется собственно функция
Z>4) выполняем эпилог функции: возвращаем ebp в состояние которое он имел до вызова (pop %ebp)
Z>5) выполняем инструкцию ret, которая возвращает нас по адресу сохраненному в вершине стека (вытаскивая его оттуда)
Z>6) после возвращения из подпрограммы, согласно Сишному CC, вызывающая сторона, убирает из стека, засунутые туда параметры (ибо, в общем случае, только она и знает сколько их было и какого размера)... благодаря этому кстати в С возможны функции с неопределенным числом параметров (ellipses), и не возможны в pascal CC, где подпрограмма сама убирает из стека переданные ей параметры перед возвратом (ну или во время его, для чего инструкция ret, позднее была сделана с параметром: на сколько подвинуть стэк перед возвратом).

Z>т.е. с точки зрения машинных команд нет ни каких объектов с методами -- есть просто куча "подпрограмм" которые принимают какие-то параметры и работают с какими-то данными, и методы от свободных функций на этом уровне не отличаются ничем! (просто первый параметр у них (неявный с точки зрения С++) это this указывающий на экземпляр объекта).

это все я понимаю, в теории. но сам никогда не писал на асме, и многих деталей не знал. но, в общем, так я и представлял все эти внутренности.


Z>таким образом, после того как ты опосредованно удалил сам себя (не будем пока говорить о прямоте данного дизайна), важно соблюдать "гигиену" в коде, и не пытаться обращаться к своим дата-мемберам и виртуальным функциям (это ессно бует UB)... хотя в некоторых случаях и это сканает, потому что удаленный объект не сразу может превратиться в мусор, нужно чтобы кто-нить (из вызываемых далее функций) аллоцировал что-нить, "порушив" освобожденную только что память) -- но в любом случае это UB, и поиск подобного бага то еще развлечение


Z>теперь о главном: практически всегда извраты подобные этому -- это проблемы в понимании и сопровождении такого кода (даже в таком тупом случае как 'delete this;'! нужно четко себе представлять life time всех участвующих объектов, что в мясном коде (boost::asio + куча асинхнронных callbackов) однозначно вызовет проблемы если не с написанием , то при сопровождении (даже если это будешь ты сам, но после нескольких месяцев а может и лет... не говоря уже о других людях


Z>старайся избегать подобных наворотов -- будь проще! сделай так, чтобы удалением занимался сам контейнер, а не "тупой" item, который ничего не знает (да и не должен) о том где он хранится, сколько ему отведено жить, и т.д. -- это все находится в ведении контейнера: он единоличный хозяин всех itemов! он может кому-то дать "попользоваться" своей "собственностью", отдав weak_ptr "наружу", или может "подарить" кого-то из itemов, явно (!) передав владение... но давать itemам возможность самоубиться, это глупо и неправильно!


нет, я не собирался такой код написать и пустить в продакшн, нет
просто несколько раз натыкаюсь на то, что существующий дизайн пытается меня заставить сделать именно это.
это не страшно, ибо придерживаюсь правила: "кто ресурс выделил, тот его и освобождать должен" или "ресурс должен освобождаться на том же уровне, где был захвачен". и в этом духе.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[2]: удаление вызывающего объекта, и непонятка с размоткой стека
От: Evgeny.Panasyuk Россия  
Дата: 13.03.13 14:40
Оценка:
Здравствуйте, zaufi, Вы писали:

Z>) чтобы понимать, что происходит лучше конечно знать в какой машинный код транслируется твой пример. и да, это понимание требует знания машинных команд и практики работы с ними (реверсинг С++ного кода, а также желательно написание своего кода на "голом" asm, чтобы прочуствовать все на свое шкуре )


Полностью согласен. Опыт реверсинга мало того что крайне полезен, так ещё и жутко интересен — bruteforce решения во многих случаях неприемлемы (требуют много времени), приходится постоянно "соображать".

Z>таким образом, после того как ты опосредованно удалил сам себя


Я выше привёл call stack, в этом примере нет "удаления самого себя". Есть просьба объекта, убить его через секунду, которая выполняется третьим лицом.
Re[3]: удаление вызывающего объекта, и непонятка с размоткой стека
От: niXman Ниоткуда https://github.com/niXman
Дата: 13.03.13 14:43
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Есть просьба объекта, убить его через секунду, которая выполняется третьим лицом.

снова не понимаю %)
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[4]: удаление вызывающего объекта, и непонятка с размоткой стека
От: zaufi Земля  
Дата: 13.03.13 14:52
Оценка:
Здравствуйте, niXman, Вы писали:

X>Здравствуйте, Evgeny.Panasyuk, Вы писали:


EP>>Есть просьба объекта, убить его через секунду, которая выполняется третьим лицом.

X>снова не понимаю %)

фактически это не твой item удаляет сам себя (вызывая delete_me у контейнера)... item всего лишь просит таймер, когда у того случится событие, вызвать лямбду, в которой заcaptureн cont, выполнить delete_me (в контексте неопределенного потока выполняющего io_service::run())... и да, сам таймер принадлежит item'у -- т.е. он будет убит в то время, пока его асинхронный handler все еще выполняется... т.е. если после этого вызова, таймер попробует чонить сделать с собой -- будет UB.

поэтому на всякий случай в деструкторе item'a я бы сделать ему cancel()... ну тоесть если бы у меня не было выбора (под дулом пистолета к примеру) и нельзя было бы передизайнить тут все к фигам...
Re[4]: удаление вызывающего объекта, и непонятка с размоткой стека
От: Evgeny.Panasyuk Россия  
Дата: 13.03.13 15:09
Оценка: 18 (1) +1
Здравствуйте, niXman, Вы писали:

EP>>Есть просьба объекта, убить его через секунду, которая выполняется третьим лицом.

X>снова не понимаю %)

Как-то так:
#include <iostream>
#include <ostream>
#include <memory>
#include <vector>
#include <map>
using namespace std;

struct Money{} $$$;

class Killer;
class Item
{
    int id;
public:
    Item(int id_,Killer &killer);
    ~Item()
    {
        cout << "I am dying! id=" << id << endl;
    }
};
struct Container
{
    map<int,unique_ptr<Item>> elements;
    void fill(Killer &killer)
    {
        elements[0] = unique_ptr<Item>(new Item(0,killer));
        elements[1] = unique_ptr<Item>(new Item(1,killer));
    }
};
class Killer
{
    vector<int> victims;
    Container &container;
public:
    Killer(Container &container_)
        : container(container_)
    {}
    void kill_me_later(int id,Money)
    {
        victims.push_back(id);
    }
    void do_job()
    {
        for(auto victim : victims)
        {
            cout << "Killer: shooting id=" << victim << endl;
            container.elements.erase(container.elements.find(victim));
        }
    }
};
Item::Item(int id_,Killer &killer)
    : id(id_)
{
    cout << "id=" << id << " was born" << endl;
    killer.kill_me_later(id,$$$);
    cout << "asking Killer to kill id=" << id << " later" << endl;
}

int main()
{
    Container container;
    Killer killer(container);
    container.fill(killer);
    killer.do_job();
    cout << "THE END" << endl;
}

id=0 was born
asking Killer to kill id=0 later
id=1 was born
asking Killer to kill id=1 later
Killer: shooting id=0
I am dying! id=0
Killer: shooting id=1
I am dying! id=1
THE END

Re[5]: удаление вызывающего объекта, и непонятка с размоткой стека
От: Кодт Россия  
Дата: 13.03.13 15:17
Оценка:
Здравствуйте, zaufi, Вы писали:

<>

Не надо дула пистолета. Нужно сделать у item'а валидные предсмертные состояния:
— "умираю" — перед входом в delete_me; объект ещё нельзя удалять, но уже нельзя использовать
— "умер" — по выходу из delete_me; теперь его и удалять можно (когда на него закончатся ссылки)
В предсмертных состояниях объект перестаёт содержательно реагировать на вызовы своих методов и, возможно, даже кидает исключения.
В состоянии "умер" объект отчислен из контейнера и более недоступен, кроме как по старым ссылкам.
Перекуём баги на фичи!
Re[6]: удаление вызывающего объекта, и непонятка с размоткой стека
От: zaufi Земля  
Дата: 13.03.13 15:30
Оценка:
дык никто и не спорит что можно "выкрутиться" и попытаться придать этому коду большую maintainability, введя состояние, проверку этого состояния во всех методах объекта item, выкидывание исключений (кстати куда? -- все выполняется в контексте одного из "тупых" thread'ов-worker'ов, который маскимум что сможет сделать это catch(...) и отспамить в лог... чтобы сделать на этом месте что-то вменяемое, нужно чтобы этот worker как-то передал пойманное исключение кому-то (кому?) кто сможет его обработать... нужен видимо какой-то диспетчер, который по типу исключения, возможно, сможет понять кому оно предназначалось, и каким-то волшебным образом "засигналить" тому объекту типа "эгей! тут тебе прилетело!" -- в целом все оч быстро превращается в нагромождение кучи кода "на пустом месте" (в зависимости от обстоятельств и желания реагировать на ошибки, меняется только размер "кучи")... это вместо того, чтобы следовать простому правилу "кто девушку ужинает (alloчит), тот ее и танцует (delete)" ))
Re[5]: удаление вызывающего объекта, и непонятка с размоткой стека
От: niXman Ниоткуда https://github.com/niXman
Дата: 13.03.13 17:01
Оценка:
Здравствуйте, zaufi, Вы писали:

Z>фактически это не твой item удаляет сам себя (вызывая delete_me у контейнера)... item всего лишь просит таймер, когда у того случится событие, вызвать лямбду, в которой заcaptureн cont, выполнить delete_me (в контексте неопределенного потока выполняющего io_service::run())... и да, сам таймер принадлежит item'у -- т.е. он будет убит в то время, пока его асинхронный handler все еще выполняется... т.е. если после этого вызова, таймер попробует чонить сделать с собой -- будет UB.


а ведь да. понял.
всем спасибо, вопрос закрыт.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.