Archived

This forum has been archived. Please start a new discussion on GitHub.

ObjectAdapter deactivation question/problem

Hello again,

First a basic question: do individual servants need to be removed from an ObjectAdapter object before it is deactivated or does deactivate() do this automatically?

Next, a problem: when we call waitForDeactivate() after deactivating an adapter, our thread just hangs. In this particular case, we are running v1.4 on Solaris.

Here is how we handle destruction of the adapter:

for each servant
ObjectAdapter::remove( servant identity )

ObjectAdapter::deactivate();

ObjectAdapter::waitForDeactivate // hangs forever

If we attach to the icebox process in our debugger at this point the stack trace looks like this:

(/packages/sunpro/bin/../WS6U2/bin/sparcv9/dbx) where
current thread: t@10
[1] _lwp_sema_wait(0xf22ffe60, 0x0, 0x0, 0xfecec000, 0x2d634, 0x2), at 0xfec1f208
[2] _park(0xf22ffe60, 0xfecec000, 0x0, 0xf22ffd98, 0x6, 0xfe509d98), at 0xfecc96dc
[3] _swtch(0xf22ffd98, 0x1079a90, 0xfecec000, 0x5, 0x1000, 0x2), at 0xfecc93a4
[4] cond_wait(0xf22ffd98, 0x4, 0x6, 0xfecec000, 0x0, 0x0), at 0xfecc81ac
[5] _ti_pthread_cond_wait(0x1079a80, 0x1079a90, 0x1079a90, 0x39520, 0x109528, 0x24c28), at 0xfecc8070
[6] IceUtil::Monitor<IceUtil::RecMutex>::wait(0x1079a80, 0x1, 0xfeea4578, 0xfee18ea4, 0x8, 0x0), at 0xfefe6c18
[7] Ice::ObjectAdapterI::waitForDeactivate(0x1079a78, 0xfeea4578, 0x1, 0x1082, 0xfeea4578, 0x2000), at 0xfefdf294
=>[8] icy::robotrade::mb::RoboProxyFacade::~RoboProxyFacade(0xd6a988, 0x39640, 0x0, 0x400, 0x53c, 0xff357754), at 0xf4218ef8
[9] __SLIP.DELETER__G(0xd6a988, 0x1, 0xd6a9dc, 0xfffffff4, 0xf45cd6e0, 0x389c0), at 0xf422d94c
[10] __rwstd::__rb_tree<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::pair<const std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object> >,__rwstd::__select1st<std::pair<const std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object> >,std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<std::pair<const std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object> > > >::__put_node(0x23027e0, 0x2302ed0, 0x1, 0x0, 0x0, 0x0), at 0xfef7e794
[11] __rwstd::__rb_tree<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::pair<const std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object> >,__rwstd::__select1st<std::pair<const std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object> >,std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<std::pair<const std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object> > > >::__erase(0x23027e0, 0x2302ed0, 0x0, 0xfecec000, 0x2d634, 0x0), at 0xfef7e74c
[12] __rwstd::__rb_tree<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::pair<const std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object> >,__rwstd::__select1st<std::pair<const std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object> >,std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<std::pair<const std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object> > > >::erase(0xf22fdbc4, 0x23027e0, 0xf22fdbbc, 0xf22fdbb4, 0x2d5e4, 0xff3c7258), at 0xfef7dab8
[13] __rwstd::__rb_tree<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::pair<const std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object> >,__rwstd::__select1st<std::pair<const std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object> >,std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<std::pair<const std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object> > > >::~__rb_tree(0x23027e0, 0x0, 0xff3605b4, 0xff3e7760, 0x0, 0x2), at 0xfef81560
[14] __rwstd::__rb_tree<Ice::Identity,std::pair<const Ice::Identity,std::map<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object>,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<std::pair<const std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object> > > > >,__rwstd::__select1st<std::pair<const Ice::Identity,std::map<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object>,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<std::pair<const std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object> > > > >,Ice::Identity>,std::less<Ice::Identity>,std::allocator<std::pair<const Ice::Identity,std::map<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object>,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<std::pair<const std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object> > > > > > >::__put_node(0xf22fe034, 0x23027c8, 0x1, 0xff3e7760, 0x0, 0x2), at 0xfef81480
[15] __rwstd::__rb_tree<Ice::Identity,std::pair<const Ice::Identity,std::map<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object>,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<std::pair<const std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object> > > > >,__rwstd::__select1st<std::pair<const Ice::Identity,std::map<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object>,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<std::pair<const std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object> > > > >,Ice::Identity>,std::less<Ice::Identity>,std::allocator<std::pair<const Ice::Identity,std::map<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object>,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<std::pair<const std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object> > > > > > >::__erase(0xf22fe034, 0x23027c8, 0x3fff0000, 0x10, 0x0, 0x0), at 0xfef81444
[16] __rwstd::__rb_tree<Ice::Identity,std::pair<const Ice::Identity,std::map<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object>,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<std::pair<const std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object> > > > >,__rwstd::__select1st<std::pair<const Ice::Identity,std::map<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object>,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<std::pair<const std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object> > > > >,Ice::Identity>,std::less<Ice::Identity>,std::allocator<std::pair<const Ice::Identity,std::map<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object>,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<std::pair<const std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object> > > > > > >::erase(0xf22fdd74, 0xf22fe034, 0xf22fdd70, 0xf22fdd6c, 0xd8b1d8, 0x0), at 0xfef812d4
[17] std::map<Ice::Identity,std::map<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object>,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<std::pair<const std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object> > > >,std::less<Ice::Identity>,std::allocator<std::pair<const Ice::Identity,std::map<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object>,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<std::pair<const std::basic_string<char,std::char_traits<char>,std::allocator<char> >,IceInternal::Handle<Ice::Object> > > > > > >::clear(0xf22fe034, 0x23024d0, 0x61ac8, 0x1588, 0x1400, 0x15c8), at 0xfefed764
[18] IceInternal::ServantManager::destroy(0x23024a8, 0xff0ccff3, 0x1, 0xff0ccf61, 0xf22fdffc, 0x23447e0), at 0xfefed390
[19] Ice::ObjectAdapterI::waitForDeactivate(0x1079a78, 0x1400, 0x0, 0xff020918, 0x1079ad8, 0x1400), at 0xfefdf3a8
[20] icy::robotrade::mb::RoboProxyFacade::~RoboProxyFacade(0xd6a988, 0xf25c0, 0x0, 0x0, 0x0, 0x0), at 0xf4218ef8



If we do not use waitForDeactivate(), everything works fine, but thought we "should" be using it for "correctness".

Any ideas?

Thanks again,

Brian

Comments

  • There is no need to explicitly remove all the servants in the ASM. When you call deactivate(), it takes care of that for you.

    Your code is suffering a deadlock. Just from looking at the stack trace, it's not clear to me exactly what is going on, but two observations:

    1) If you call waitForDeactivate() from within an operation implementation on a servant using the same object adapter as the one being deactivated, the code will deadlock. That is because waitForDeactivate() waits for all operation invocations that are in progress to finish but, of course, if waitForDeactivate() is called from within an operation on the deactivated adapter, that operation cannot finish and, therefore, waitForDeactivate() cannot finish.

    You didn't show the complete stack trace, so I cannot tell whether that is what is happening in your particular case, but it is something you must avoid.

    2) Your stack trace contains a reentrant call to waitForDeactivate(). waitForDeactivate() is called in frame 19 and again in frame 7.

    It looks like the destructor for RoboProxyFacade() calls waitForDeactivate(). WaitForDeactivate() calls ServantManager::destroy() which, in turn tries to drop the servant manager's references to all its servants. That results in another call to the destructor of RoboPRoxyFacade(). At that point, the code deadlocks.

    So, calling waitForDeactivate() from within the destructor of a servant is bad idea.

    Cheers,

    Michi.
  • Thanks for the quick response, Michi.

    This all makes sense. I should have looked at the stack trace harder to find that recursion myself.

    We sometimes have been creating creating adapters within servants and adding "this" to the adapter.

    Is this bad design?

    Should be always at least creating an adapter from an encapsulating object
    of the actual servant and then do the creation in the container object, pass that adapter to the contained servant, and do the deactivation in the containing object as well?

    I think you are saying that calling deactivate on an adapter has the side effect of calling all the destructors of the servants in the adapter. Is that correct?

    Thanks again,

    Brian
  • Originally posted by brian
    We sometimes have been creating creating adapters within servants and adding "this" to the adapter.

    Is this bad design?

    Not necessarily. (I'm assuming that the servant that adds itself to the adapter it has just created isn't registered with some other adapter already? If so, no problem. If you register the same servant with two adapters, still no problem as far as Ice is concerned, but your design becomes more complex (and it's difficult to come up with a use case that would make sense for doing this).
    Should be always at least creating an adapter from an encapsulating object
    of the actual servant and then do the creation in the container object, pass that adapter to the contained servant, and do the deactivation in the containing object as well?

    It really depends on your design and how you want to do things. Typically, servants will be created by some piece of code and that same piece of code will also register the servant with the adapter. Alternatively, you can have a servant's constructor add the servant (this) to an adapter that's passed as a constructor argument. Note though that, to remove the servant from the adapter, you can't do that in the servant's destructor (because the servant's destructor can't possibly run while the adapter stores a smart pointer to the servant).
    I think you are saying that calling deactivate on an adapter has the side effect of calling all the destructors of the servants in the adapter. Is that correct?

    Not quite. All the adapter does during deactivation is the smart pointer it holds for each servant. If the adapter is the only thing that still has a smart pointer to a particular servant, then, yes, the servant's destructor will run while deactivate() is in progress. However, if your code holds a smart pointer to the same servant elsewhere, deactivate() won't result in a destructor call. In other words, the destructor of a servant runs when the last smart pointer to that servant is dropped. If the adapter is the only part of the code that holds a smart pointer to a servant, deactivate() will destroy the servant; otherwise, it won't.

    Cheers,

    Michi.