Archived

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

AMD - AMI interactions

Hi,

I have been playing around with Ice - mainly its AMD and AMI functionality.
I am having some issues with AMI. Initially, I had written some test code to use AMI and AMD in the dynamic invocation world (invoke_sync and invoke_async). They worked well - all the 4 modes of operation since AMD and AMI are decoupled. But in my tests, when using an AMD server, i was calling ice_response on the call back object with in the ice_invoke_async function itself (not a proper test for async).

Now, as far as I understand, I should be able to hold a reference to the callback object and call ice_invoke on it from another thread; otherwise it is not async! This is what I am doing in a framework that I am writing that uses Ice as the middleware. I hold a reference to the callback object and return from the ice_invoke_async function. And later I try to call ice_response on the call back object from another thread. However, this fails. The client in AMI mode, throws a CommunicatorDestroyedException. But, in synchronous mode the client blocks waiting for a response.

As a simple test, in my test code I simply commented out the call to ice_response on the callback object from the ice_invoke_async function. This again causes the same problem as above. The client in AMI mode, throws a CommunicatorDestroyedException. I am not sure why this is happening.

Does the communicator->destroy() wait for ice_response's to arrive for calls that have been made on the proxy? I assumed that it might because, in my test code when I have an AMD server (where I make the call to ice_response() in the ice_invoke_async() itself) and AMI client, it works well when I call proxy->ice_invoke_async() and then call communicator->destroy() without creating any other waiting thread in the client.

I am still debugging to figure out if I am missing something. Any insights would be helpful.

Thanks
Gesly

Comments

  • matthew
    matthew NL, Canada
    As mentioned on page 794 of the Ice manual calling communicator->destroy() causes all pending client side invocations to terminate with a CommunicatorDestroyedException. You can try this yourself with the async demo (demo/Ice/async).
  • " If you call destroy without calling shutdown, the call waits for all executing
    operation invocations to complete before it returns (that is, the implementation of destroy implicitly calls shutdown followed by waitForShutdown). "

    From what I understand, if I make an AMI request and immediately call communicator->destroy (no thread waiting to get a response from the server), then I will get a CommunicatorDestroyed exception. Is this correct?
    And why does it call waitForShutdown after the call to shutdown?

    If so, then why does the destroy() function wait for a reply from the server in the following case:
    I do a sleep for sometime in the ice_invoke_async() on the server and then call ice_response from the same function i.e. the same thread, the call to destroy() does not throw an exception, rather it waits till it gets a reply from the server.

    On the client in main():

    proxy->ice_invoke_async(cb, "add", Ice::Nonmutating, inP);
    ....
    if(ic){
    std::cout << "Calling destroy " << std::endl;
    ic->destroy();

    }


    On the server in ice_invoke_async:

    ::Sleep(10000);
    cb->ice_response(true, outP);


    Am I misunderstanding something here?
  • benoit
    benoit Rennes, France
    The shutdown() method initiates the communicator shutdown whereas the waitForShutdown() waits for the communicator shutdown to complete. Shutting down the communicator doesn't affect pending outgoing requests to servers. Shutting down the communicator only affects the server side part of the communicator (it deactivates each object adapter and waits for the invocations being dispatched to complete.)

    You're correct, calling destroy() on the communicator will cause pending outgoing client requests (AMI or not) to raise Ice::CommunicatorDestroyedException.

    As for why the client waits for the server when you call the destroy() method if the server is sleeping... this is because the communicator gracefully shuts down the connection to the server when you call destroy(). It sends a close connection message to the server and waits for the server to close the connection. By default, the server has only thread in its thread pool. In your case, this thread is "busy" sleeping in the implementation of your ice_invoke_async method so it can't read the close connection message until you actually return from ice_invoke_async...

    That's why it looks like destroy() returns when you call ice_response() on the AMD callback object but it's not the case. Just try it out: if you don't call ice_response on the AMD callback object after the sleep, you should still see that the client call to destroy() returns after the sleep. You can also try to increase the number of threads in the server thread pool, the call to Communicator::destroy() should return immediately (set the property Ice.ThreadPool.Server.Size to 5 for example).

    Let us know if this is still not clear!

    Cheers,
    Benoit.
  • That makes a lot of sense. Yes, I tested with more than one thread in the thread pool and destroy returns immediately. Didnt realise that the client might be sending a message to the server.

    Another question, can I call ice_response on the call back object more than once on the server? I have been trying it and looks like I cant. Does the client expect to receive only one ice_response on its call back object?

    -Gesly
  • benoit
    benoit Rennes, France
    Yes, you should only call ice_response once. An AMI/AMD request is not different from a regular request -- you can only have one response for the request.

    Cheers,
    Benoit.
  • So the only way to get a single request multiple response is Ice Storm I assume?
    I have not looked at the Ice source code much, but I would assume that I am doing multiple-request responses over the same connection in Ice, correct?
  • benoit
    benoit Rennes, France
    What do you mean by "multiple-request responses"? You can only have one response per request. Requests/responses are sent/receveid over the same connection. IceStorm is a publish/subscribe messaging service which allows to dispatch a request published on a topic to multiple subscribers.

    Cheers,
    Benoit.
  • Sorry, meant "multiple request-response ".
  • In the invoke functions what does the OperationMode mean? How is Ice interpreting them? I did not come across anything in the manual.
  • benoit
    benoit Rennes, France
    See 35.3.1 in the Ice manual and the "Idempotent operations" paragraph in 4.10.1 for more information about this operation mode parameter.

    Cheers,
    Benoit.
  • Hey thanks for the info.

    Had a question on the call back objects that are passed to the ice_invoke_async() function. How does Ice do cleanup related to the callback?

    If I keep a copy of the callback Handle that is passed to the ice_invoke_async() (which I understand will increment the ref count) in some shared data structure, then can I assume that I can safely call ice_response on the handle from some other thread at a later time?
  • benoit
    benoit Rennes, France
    Hi,

    Yes, it's fine to call on the handle at a later time from another thread. See "6.14.6 Smart Pointers for Classes" in the Ice manual for more information on smart pointers.

    Cheers,
    Benoit.
  • Can a similar assumption be made about Ice::Current? The ice_invoke_async()
    function passes a reference to Ice::Current? Can I carry around a pointer to the Ice::Current object without making a copy?

    I guess I cannot, otherwise the interface would be passing a smart pointer?
  • benoit
    benoit Rennes, France
    Yes, you shouldn't reference the Ice::Current structure outside the method being dispatched. You should copy it instead.

    Cheers,
    Benoit.