Communicator Initialization and Destruction
4 min read
2 min read
2 min read
2 min read
2 min read
2 min read
2 min read
Creating a Communicator
In C++, you create a communicator by calling the C++ function Ice::initialize, for example:
intmain(int argc, char* argv[]){ Ice::CommunicatorPtr communicator = Ice::initialize(argc, argv); // ...}initialize accepts a C++ reference to argc and an argument vector argv. The function scans the argument vector for any command-line options that are relevant to the Ice runtime; any such options are removed from the argument vector so, when initialize returns, the only options and arguments remaining are those that concern your application. If anything goes wrong during initialization, initialize throws an exception.
You need to call destroy on the returned object when you're done with this communicator, typically just before returning from main. The destroy member function is responsible for cleaning-up the communicator. In particular, in a server, destroy waits for any operation dispatch that are still executing to complete. In addition, destroy ensures that any outstanding threads are joined with and reclaims a number of operating system resources, such as file descriptors and memory.
The general shape of the main function of an Ice-based application is therefore:
#include <Ice/Ice.h>
intmain(int argc, char* argv[]){ int status = 0; try { // CommunicatorPtr is an alias for std::shared_ptr<Ice::Communicator> Ice::CommunicatorPtr communicator = Ice::initialize(argc, argv);
try { ... application code ...
communicator->destroy(); // destroy is noexcept } catch (const std::exception&) { ... // make sure communicator is destroyed if an exception is thrown communicator->destroy(); throw; } } catch (const std::exception& e) { cerr << e.what() << endl; status = 1; } return status;}This code is a little bit clunky, as we need to make sure the communicator gets destroyed in all paths, including when an exception is thrown. As a result, most of the time, you should use a helper class to call destroy on your communicator.
Ice::CommunicatorHolder Helper Class
Ice::CommunicatorHolder Helper ClassA CommunicatorHolder is a small helper class that you construct with a communicator. It’s then responsible for destroying it.
With a CommunicatorHolder, our typical main function becomes much simpler:
#include <Ice/Ice.h>
intmain(int argc, char* argv[]){ int status = 0; try { Ice::CommunicatorPtr communicator = Ice::initialize(argc, argv);
// Schedule destruction of communicator. Ice::CommunicatorHolder communicatorHolder{communicator};
... application code ...
// CommunicatorHolder's destructor calls destroy on the communicator // whether or not an exception is thrown. } catch (const std::exception& e) { cerr << e.what() << endl; status = 1; } return status;}You create a communicator by using its constructor, for example:
await using var communicator = new Ice.Communicator(ref args);Ice.Communicator constructor accepts the argument vector that is passed to Main by the operating system. The method scans the argument vector for any command-line options that are relevant to the Ice runtime; any such options are removed from the argument vector so, when Ice.Communicator constructor returns, the only options and arguments remaining are those that concern your application. If anything goes wrong during initialization, it throws an exception.
Ice.Communicator implements both IDisposable and IAsyncDisposable. This allows you to create and cleanup your communicator with await using in async applications (as shown above). In synchronous code, you can use instead:
// In synchronous code.using var communicator = new Ice.Communicator(ref args);You create a communicator by using its constructor, for example:
class Client { public static void main(String[] args) { try (Communicator communicator = new Communicator(args)) { ... } }}the constructor accepts the argument vector that is passed to main by the operating system. The constructor scans the argument vector for any command-line options that are relevant to the Ice runtime; if anything goes wrong during initialization, it throws an exception.
Communicator implements AutoCloseable. This allows you to create and cleanup your communicator in a try-with-resources statement as shown above.
You create a communicator by using its constructor for example:
import { Ice } from "@zeroc/ice";
await using communicator = new Ice.Communicator(process.argv);This constructor accepts the argument vector. It scans the argument vector for any command-line options that are relevant to the Ice runtime; any such options are removed from the argument vector so, when the constructor returns, the only options and arguments remaining are those that concern your application. If anything goes wrong during initialization, it throws an exception.
In a browser application, you should call the constructor without the argument vector.
Communicator implements the asyncDispose method. This allows you to create and cleanup your communicator with await using as shown above.
You create a communicator by calling Ice\initialize, for example:
<?phprequire_once 'Ice.php';
$communicator = Ice\initialize();...?>In PHP, unlike other languages, you do not need to destroy the communicator: the Ice PHP extension automatically destroys the communicator created during a request.
You create a communicator by using its constructor, for example:
import Iceimport sys
def main(): with Ice.Communicator(sys.argv) as communicator: ...Ice.Communicator constructor accepts the argument list that is passed to the program by the operating system. The constructor scans the argument list for any command-line options that are relevant to the Ice runtime; any such options are removed from the argument list so, when Ice.Communicator constructor returns, the only options and arguments remaining are those that concern your application. If anything goes wrong during initialization, it throws an exception.
Communicator implements the Python context manager protocol, with cleans up the communicator automatically at the end of the with block.
async with is preferred in an async context. For example:
import Iceimport asyncioimport sys
async def main(): async with Ice.Communicator( sys.argv, eventLoop=asyncio.get_running_loop()) as communicator: ...You create a communicator by calling Ice::initialize, for example:
require 'Ice'
Ice.initialize(ARGV) do |communicator| ...endIce,initialize accepts the argument list that is passed to the program by the operating system. The function scans the argument list for any command-line options that are relevant to the Ice runtime; any such options are removed from the argument list so, when Ice.initialize returns, the only options and arguments remaining are those that concern your application. If anything goes wrong during initialization, initialize throws an exception.
This syntax ensures the communicator is destroyed when the block completes. The destroy method is responsible for cleaning up the communicator. In particular, destroy ensures that any outstanding threads started by the underlying Ice C++ communicator are joined with and reclaims a number of operating system resources, such as file descriptors and memory. Never allow your program to terminate without calling destroy first.
The initialize block accepts a single argument, the communicator.
You create a communicator by calling Ice.initialize, for example:
let communicator = try Ice.initialize(CommandLine.arguments)initialize scans the argument array for any command-line options that are relevant to the Ice runtime. If anything goes wrong during initialization, initialize throws an exception.
Once you no longer need a Communicator, you must call destroy on this Communicator. The destroy method is responsible for cleaning up the communicator. In particular, in an Ice server, destroy waits for operation implementations that are still executing to complete. In addition, destroy ensures that any outstanding threads are joined with and reclaims a number of operating system resources, such as file descriptors and memory. Never allow your application to terminate without calling destroy first.
The general shape of a simple command-line Swift application becomes:
let communicator = try Ice.initialize(CommandLine.arguments)defer { communicator.destroy()}Initialization Data
When a communicator is created, its constructor or the initialize method configures several features that control its behavior. Once set, these features remain in effect for the lifetime of the communicator and cannot be changed afterward. Therefore, any customization of these features must be done at communicator creation time.
The InitializationDataInitializationDataInitializationDataInitializationDataInitializationDataInitializationDataInitializationDataInitializationDataInitializationData class or struct holds all the features (or options) that you can customize when you create a communicator.