Communicator Shutdown and Destruction

On this page:

Communicator Operations

Once you have successfully created a communicator, it is essential that you destroy this communicator before exiting your application. Destroying a communicator ensures an orderly closure of connections opened by this communicator, the clean termination of all the threads started by this communicator and more.

A communicator provides four operations related to shutdown and destruction:

Slice
["clr:implements:_System.IDisposable", "java:implements:java.lang.AutoCloseable"]
local interface Communicator
{
    ["cpp:noexcept"] void shutdown();
    ["cpp:noexcept"] void waitForShutdown();
    ["cpp:noexcept"] bool isShutdown();
    ["cpp:noexcept"] void destroy();
    // ...
} 
  • shutdown
    This operation shuts down the server side of the Ice run time:
    • Operation invocations that are in progress at the time shutdown is called are allowed to complete normally. shutdown does not wait for these operations to complete; when shutdown returns, you know that no new incoming requests will be dispatched, but operations that were already in progress at the time you called shutdown may still be running. You can wait for still-executing operations to complete by calling waitForShutdown.
    • Operation invocations that arrive after the server has called shutdown either fail with a ConnectFailedException or are transparently redirected to a new instance of the server (via IceGrid).
    • Note that shutdown initiates deactivation of all object adapters associated with the communicator, so attempts to use an adapter once shutdown has completed raise an ObjectAdapterDeactivatedException.
    shutdown does not throw any exception, and calling shutdown multiple times on the same communicator is perfectly safe and correct.
  • waitForShutdown 
    On the server side, this operation suspends the calling thread until the communicator has shut down (that is, until no more operations are executing in the server). This allows you to wait until the server is idle before you destroy the communicator. 
    On the client side, waitForShutdown simply waits until another thread has called shutdown or destroy.
  • isShutdown 
    This operation returns true if shutdown has been invoked on the communicator. A return value of true does not necessarily indicate that the shutdown process has completed, only that it has been initiated. An application that needs to know whether shutdown is complete can call waitForShutdown. If the blocking nature of waitForShutdown is undesirable, the application can invoke it from a separate thread.
  • destroy 
    This operation destroys the communicator and all its associated resources, such as threads, communication endpoints, object adapters, and memory resources. Once you have destroyed the communicator (and therefore destroyed the run time for that communicator), you must not call any other Ice operation (other than to create another communicator). destroy does not throw any exception, and calling destroy multiple times on the same communicator is perfectly safe and correct.
    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). shutdown (and, therefore, destroy) deactivates all object adapters that are associated with the communicator. Since destroy blocks until all operation invocations complete, a servant will deadlock if it invokes destroy on its own communicator while executing a dispatched operation. 

    On the client side, calling destroy while operations are still executing causes those operations to terminate with a CommunicatorDestroyedException.

Most language mappings provide constructs to destroy the communicator automatically when leaving a scope:

  • C++: the Ice::CommunicatorHolder RAII helper class
  • C#: Communicator implements the IDisposable interface, which allows you to initialize a communicator in a using statement
  • Java: Communicator implements java.lang.AutoCloseable, which allows you to initialize a communicator in a try-with-resources statement
  • PythonCommunicator implements the Python context manager protocol, which allows you to call initialize in a with statement
  • RubyIce::initialize accepts an optional block, which destroys the communicator automatically at the end of the block

Common Patterns

Pure Client

In a pure client - a client that does not create any object adapter - there is no need to shutdown your communicator so you typically destroy your communicator without an intermediate explicit shutdown.

Server

In a server, you can destroy your communicator or first shut it down and only later destroy it.

  • Plain Destroy
    In a server, a communicator destroy with no prior shutdown is correct provided this destroy is not called from an operation dispatch, and you don't need the communicator afterwards. For example, many C# and Java servers in ice-demos destroy their communicators in response to a CTRL-C or similar signal. This is the simplest signal handling strategy since C# (by default) and Java (always) terminate the application once the signal handlers have finished execution.
  • Shutdown with WaitForShutdown and Destroy
    You can initiate the shutdown of your communicator in one thread (with shutdown) while another thread is blocked on waitForShutdown. When the thread blocked on waitForShutdown is freed, it has a shutdown but not destroyed communicator, still usable for remote invocations. This thread is then typically responsible to destroy the communicator.
    For example, all the ice-demos applications use this pattern to implement remote shutdown: the shutdown operation implementation calls shutdown on the communicator, while the main thread waits on waitForShutdown and later destroys the communicator. C++, Python and some C# demo servers also shutdown their communicator in response to a CTRL-C signal or similar signal. With C++, Python and optionally in C#, the application keeps running after a signal is handled, so we can have the main thread perform post-shutdown cleanups (if needed) and then destroy the communicator before completing cleanly.