On this page:
main Function in C++
The main entry point to the Ice run time is represented by the local Slice interface
Ice::Communicator. As for the client side, you must initialize the Ice run time by calling
Ice::initialize before you can do anything else in your server.
Ice::initialize returns a smart pointer to an instance of an
Ice::initialize accepts a C++ reference to
argv. The function scans the argument vector for any command-line options that are relevant to the Ice run time; any such options are removed from the argument vector 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.
Ice::initialize has additional overloads to permit other information to be passed to the Ice run time.
Before leaving your
main function, you must call
destroy operation is responsible for finalizing the Ice run time. In particular,
destroy waits for any operation implementations that are still executing in the server 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
main function to terminate without calling
destroy first; doing so has undefined behavior.
The general shape of our server-side
main function is therefore as follows:
Note that the code places the call to
Ice::initialize in to a
try block and takes care to return the correct exit status to the operating system. Also note that an attempt to destroy the communicator is made only if the initialization succeeded.
catch handlers for
const std::string & and
const char * are in place as a convenience feature: if we encounter a fatal error condition anywhere in the server code, we can simply throw a string or a string literal containing an error message; this causes the stack to be unwound back to
main, at which point the error message is printed and, after destroying the communicator,
main terminates with non-zero exit status.
The preceding structure for the
main function is so common that Ice offers a class,
Ice::Application, that encapsulates all the correct initialization and finalization activities. The definition of the class is as follows (with some detail omitted for now):
The intent of this class is that you specialize
Ice::Application and implement the pure virtual
run method in your derived class. Whatever code you would normally place in
main goes into the
run method instead. Using
Ice::Application, our program looks as follows:
Application::main is overloaded: you can pass a string sequence instead of an
argv pair. This is useful if you need to parse application-specific property settings on the command line. You also can call
main with an optional file name or an
If you pass a configuration file name to
main, the property settings in this file are overridden by settings in a file identified by the
ICE_CONFIG environment variable (if defined). Property settings supplied on the command line take precedence over all other settings.
Application::main function does the following:
- It installs an exception handler for
Ice::Exception. If your code fails to handle an Ice exception,
Application::mainprints the exception details on
stderrbefore returning with a non-zero return value.
- It installs exception handlers for
const std::string &and
const char*. This allows you to terminate your server in response to a fatal error condition by throwing a
std::stringor a string literal.
Application::mainprints the string on
stderrbefore returning a non-zero return value.
- It initializes (by calling
Ice::initialize) and finalizes (by calling
Communicator::destroy) a communicator. You can get access to the communicator for your server by calling the static
- It scans the argument vector for options that are relevant to the Ice run time and removes any such options. The argument vector that is passed to your
runmethod therefore is free of Ice-related options and only contains options and arguments that are specific to your application.
- It provides the name of your application via the static
appNamemember function. The return value from this call is
argv, so you can get at
argvfrom anywhere in your code by calling
Ice::Application::appName(which is often necessary for error messages).
- It installs a signal handler that properly destroys the communicator.
- It installs a per-process logger if the application has not already configured one. The per-process logger uses the value of the
Ice.ProgramNameproperty as a prefix for its messages and sends its output to the standard error channel. An application can also specify an alternate logger.
Ice::Application ensures that your program properly finalizes the Ice run time, whether your server terminates normally or in response to an exception or signal. We recommend that all your programs use this class; doing so makes your life easier. In addition,
Ice::Application also provides features for signal handling and configuration that you do not have to implement yourself when you use this class.
Ice::Application on the Client Side
You can use
Ice::Application for your clients as well: simply implement a class that derives from
Ice::Application and place the client code into its
run method. The advantage of this approach is the same as for the server side:
Ice::Application ensures that the communicator is destroyed correctly even in the presence of exceptions.
Catching Signals in C++
The simple server we developed in Hello World Application had no way to shut down cleanly: we simply interrupted the server from the command line to force it to exit. Terminating a server in this fashion is unacceptable for many real-life server applications: typically, the server has to perform some cleanup work before terminating, such as flushing database buffers or closing network connections. This is particularly important on receipt of a signal or keyboard interrupt to prevent possible corruption of database files or other persistent data.
To make it easier to deal with signals,
Ice::Application encapsulates the platform-independent signal handling capabilities provided by the class
IceUtil::CtrlCHandler. This allows you to cleanly shut down on receipt of a signal and to use the same source code regardless of the underlying operating system and threading package:
You can use
Ice::Application under both Windows and Unix: for Unix, the member functions control the behavior of your application for
SIGTERM; for Windows, the member functions control the behavior of your application for
The functions behave as follows:
This function creates an
IceUtil::CtrlCHandlerthat destroys the communicator when one of the monitored signals is raised. This is the default behavior.
This function creates an
IceUtil::CtrlCHandlerthat shuts down the communicator when one of the monitored signals is raised.
This function causes signals to be ignored.
This function configures
interruptCallbackwhen a signal occurs, thereby giving the subclass responsibility for handling the signal. Note that if the signal handler needs to terminate the program, you must call
exit). This prevents global destructors from running which, depending on the activities of other threads in the program, could cause deadlock or assertion failures.
This function causes signals to be held.
This function restores signal delivery to the previous disposition. Any signal that arrives after
holdInterruptwas called is delivered when you call
This function returns
trueif a signal caused the communicator to shut down,
falseotherwise. This allows us to distinguish intentional shutdown from a forced shutdown that was caused by a signal. This is useful, for example, for logging purposes.
A subclass overrides this function to respond to signals. The Ice run time may call this function concurrently with any other thread. If the function raises an exception, the Ice run time prints a warning on
cerrand ignores the exception.
Ice::Application behaves as if
destroyOnInterrupt was invoked, therefore our server
main function requires no change to ensure that the program terminates cleanly on receipt of a signal. (You can disable the signal-handling functionality of
Ice::Application by passing the enumerator
NoSignalHandling to the constructor. In that case, signals retain their default behavior, that is, terminate the process.) However, we add a diagnostic to report the occurrence of a signal, so our
main function now looks like:
Note that, if your server is interrupted by a signal, the Ice run time waits for all currently executing operations to finish. This means that an operation that updates persistent state cannot be interrupted in the middle of what it was doing and cause partial update problems.
Under Unix, if you handle signals with your own handler (by deriving a subclass from
Ice::Application and calling
callbackOnInterrupt), the handler is invoked synchronously from a separate thread. This means that the handler can safely call into the Ice run time or make system calls that are not async-signal-safe without fear of deadlock or data corruption. Note that
Ice::Application blocks delivery of
SIGTERM. If your application calls
exec, this means that the child process will also ignore these signals; if you need the default behavior of these signals in the
exec'd process, you must explicitly reset them to
SIG_DFL before calling
Ice::Application and Properties
Apart from the functionality shown in this section,
Ice::Application also takes care of initializing the Ice run time with property values. Properties allow you to configure the run time in various ways. For example, you can use properties to control things such as the thread pool size or port number for a server.
Ice::Application is a singleton class that creates a single communicator. If you are using multiple communicators, you cannot use
Ice::Application. Instead, you must structure your code as we saw in Hello World Application (taking care to always destroy the communicators).
Ice::Application class is very convenient for general use by Ice client and server applications. In some cases, however, an application may need to run at the system level as a Unix daemon or Windows service. For these situations, Ice includes
Ice::Service, a singleton class that is comparable to
Ice::Application but also encapsulates the low-level, platform-specific initialization and shutdown procedures common to system services. The
Ice::Service class is defined as follows:
At a minimum, an Ice application that uses the
Ice::Service class must define a subclass and override the
start member function, which is where the service must perform its startup activities, such as processing command-line arguments, creating an object adapter, and registering servants. The application's
main function must instantiate the subclass and typically invokes its
main member function, passing the program's argument vector as parameters. The example below illustrates a minimal
Service::main member function performs the following sequence of tasks:
- Scans the argument vector for reserved options that indicate whether the program should run as a system service and removes these options from the argument vector (
argcis adjusted accordingly). Additional reserved options are supported for administrative tasks.
- Configures the program for running as a system service (if necessary) by invoking
configureDaemon, as appropriate for the platform.
- Invokes the
runmember function and returns its result.
Note that, as for
Service::main is overloaded to accept a string sequence instead of an
argv pair. This is useful if you need to parse application-specific property settings on the command line.
For maximum portability, we strongly recommend that all initialization tasks be performed in the
start member function and not in the global
main function. For example, allocating resources in
main can cause program instability for Unix daemons.
Service::run member function executes the service in the steps shown below:
- Installs a signal handler.
- Invokes the
initializeCommunicatormember function to obtain a communicator. The communicator instance can be accessed using the
- Invokes the
startmember function. If
falseto indicate failure,
rundestroys the communicator and returns immediately using the exit status provided in
- Invokes the
waitForShutdownmember function, which should block until
- Invokes the
stopmember function. If
runconsiders the application to have terminated successfully.
- Destroys the communicator.
- Gracefully terminates the system service (if necessary).
If an unhandled exception is caught by
Service::run, a descriptive message is logged, the communicator is destroyed and the service is terminated.
Ice::Service Member Functions
The virtual member functions in
Ice::Service represent the points at which a subclass can intercept the service activities. All of the virtual member functions (except
start) have default implementations.
void handleInterrupt(int sig)
Invoked by the
CtrlCHandlerwhen a signal occurs. The default implementation ignores the signal if it represents a logoff event and the
Ice.Nohupproperty is set to a value larger than zero, otherwise it invokes the
Ice::CommunicatorPtr initializeCommunicator(int & argc, char * argv,
const Ice::InitializationData & data)
Initializes a communicator. The default implementation invokes
Ice::initializeand passes the given arguments.
Invoked by the signal handler to indicate a signal was received. The default implementation invokes the
Causes the service to begin the shutdown process. The default implementation invokes
shutdownon the communicator. The subclass must return
trueif shutdown was started successfully, and
bool start(int argc, char * argv, int & status)
Allows the subclass to perform its startup activities, such as scanning the provided argument vector for recognized command-line options, creating an object adapter, and registering servants. The subclass must return
trueif startup was successful, and
falseotherwise. The subclass can set an exit status via the
statusparameter. This status is returned by
Allows the subclass to clean up prior to termination. The default implementation does nothing but return
true. The subclass must return
trueif the service has stopped successfully, and
void syserror(const std::string & msg)
void error(const std::string & msg)
void warning(const std::string & msg)
void trace(const std::string & msg)
void print(const std::string & msg)
Convenience functions for logging messages to the communicator's logger. The
syserrormember function includes a description of the system's current error code. You can also log messages to these functions using utility classes similar to the C++ Logger Utility Classes: these classes are
ServicePrint, all nested in the Service class.
Waits indefinitely for the service to shut down. The default implementation invokes
waitForShutdownon the communicator.
The non-virtual member functions shown in the class definition are described below:
bool checkSystem() const
Returns true if the operating system supports Windows services or Unix daemons.
Ice::CommunicatorPtr communicator() const
Returns the communicator used by the service, as created by
void configureDaemon(bool chdir, bool close, const std::string & pidFile)
Configures the program to run as a Unix daemon. The
chdirparameter determines whether the daemon changes its working directory to the root directory. The
closeparameter determines whether the daemon closes unnecessary file descriptors (i.e., stdin, stdout, etc.). If a non-empty string is provided in the
pidFileparameter, the daemon writes its process ID to the given file.
void configureService(const std::string & name)
Configures the program to run as a Windows service with the given name.
Disables the signal handling behavior in
Ice::Service. When disabled, signals are ignored.
Enables the signal handling behavior in
Ice::Service. When enabled, the occurrence of a signal causes the
handleInterruptmember function to be invoked.
static Service * instance()
Returns the singleton
int main(int & argc, char * argv,
const Ice::InitializationData & data = Ice::InitializationData())
int main(Ice::StringSeq& args,
const Ice::InitializationData& = Ice::InitializationData());
int main(int & argc, wchar_t * argv,
const Ice::InitializationData & data = Ice::InitializationData())
The primary entry point of the
Ice::Serviceclass. The tasks performed by this function are described earlier in this section. The function returns
EXIT_FAILUREfor failure. For Windows, this function is overloaded to allow you to pass a
std::string name() const
Returns the name of the service. If the program is running as a Windows service, the return value is the Windows service name, otherwise it returns the value of
int run(int & argc, char * argv, const Ice::InitializationData & data)
Alternative entry point for applications that prefer a different style of service configuration. The program must invoke
configureDaemon(Unix) in order to run as a service. The tasks performed by this function were described earlier. The function normally returns
EXIT_FAILURE, but the
startmethod can also supply a different value via its
bool service() const
Returns true if the program is running as a Windows service or Unix daemon, or false otherwise.
On Unix platforms, passing
--daemon causes your program to run as a daemon. When this option is present,
Ice::Service performs the following additional actions:
- Creates a background child process in which
Service::mainperforms its tasks. The foreground process does not terminate until the child process has successfully invoked the
startmember function. This behavior avoids the uncertainty often associated with starting a daemon from a shell script by ensuring that the command invocation does not complete until the daemon is ready to receive requests.
- Changes the current working directory of the child process to the root directory, unless
Closes all file descriptors, unless
--nocloseis specified. The standard input (stdin) channel is closed and reopened to
/dev/null. Likewise, the standard output (stdout) and standard error (stderr) channels are also closed and reopened to
Ice.StdErrare defined, respectively, in which case those channels use the designated log files.
The file descriptors are not closed until after the communicator is initialized, meaning standard input, standard output, and standard error are available for use during this time. For example, the IceSSL plug-in may need to prompt for a passphrase on standard input, or Ice may print the child's process id on standard output if the property
The following additional command-line options can be specified in conjunction with the
This option writes the process ID of the service into the specified
Ice::Servicefrom closing unnecessary file descriptors. This can be useful during debugging and diagnosis because it provides access to the output from the daemon's standard output and standard error.
Ice::Servicefrom changing the current working directory.
All of these options are removed from the argument vector that is passed to the
start member function.
We strongly recommend that you perform all initialization tasks in your service's
start member function, and not in the global
main function. This is especially important for process-specific resources such as file descriptors, threads, and mutexes, which can be affected by the use of the
fork system call in
Ice::Service. For example, any files opened in
main are automatically closed by
Ice::Service and therefore unusable in your service, unless the daemon is started with the
Ice::Service recognizes the following command-line options:
Run as a Windows service named
NAME, which must already be installed. This option is removed from the argument vector that is passed to the
Installing and configuring a Windows service is outside the scope of the
Ice::Service class. Ice includes a utility for installing its services which you can use as a model for your own applications.
Ice::Service class supports the Windows service control codes
SERVICE_CONTROL_STOP. Upon receipt of
Ice::Service invokes the
shutdown member function.
Ice::Service Logging Considerations
A service that uses a custom logger has several ways of configuring it:
- as a process-wide logger,
- in the
InitializationDataargument that is passed to
- by overriding the
Ice::Service installs its own logger that uses the Windows
Application event log if no custom logger is defined. The source name for the event log is the service's name unless a different value is specified using the property
On Unix, the default Ice logger (which logs to the standard error output) is used when no other logger is configured. For daemons, this is not appropriate because the output will be lost. To change this, you can either implement a custom logger or set the
Ice.UseSyslog property, which selects a logger implementation that logs to the
syslog facility. Alternatively, you can set the
Ice.LogFile property to write log messages to a file.
Ice::Service may encounter errors before the communicator is initialized. In this situation,
Ice::Service uses its default logger unless a process-wide logger is configured. Therefore, even if a failing service is configured to use a different logger implementation, you may find useful diagnostic information in the
Application event log (on Windows) or sent to standard error (on Unix).