Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Advanced CORBA Programming wit C++ - M. Henning, S. Vinoski.pdf
Скачиваний:
63
Добавлен:
24.05.2014
Размер:
5 Mб
Скачать

IT-SC book: Advanced CORBA® Programming with C++

connections. POAManager objects logically represent communications endpoints where objects listen for requests. In some ORB implementations, POAManager objects encapsulate connections and perform network connection management. When created, these POAManager implementations start listening for incoming requests, and when they are deactivated, they stop listening and close their connections.

11.11 ORB Event Handling

Any CORBA application that acts as a server must listen for and handle events such as incoming connections from clients and their subsequent requests. With respect to event handling, server applications fall into one of two categories.

In some applications, only the ORB has the need to listen for and handle such events. These applications can simply turn the main thread of control over to the ORB so that it can handle requests and dispatch them to its object adapters and servants. Such applications are said to perform blocking event handling because the application main blocks until the ORB shuts down its event handling and returns control to main.

In other applications, the ORB is only one of several components that must perform event handling. For example, a CORBA application with a graphical user interface (GUI) must allow the GUI to handle windowing events in addition to allowing the ORB to handle incoming requests. These types of applications therefore perform non-blocking event handling. They turn the main thread of control over to each of the various event-handling subsystems while not allowing any of them to block for significant periods of time.

Just as POAManager objects give you control over the request flow for your POAs, the ORB provides operations that allow you to control request flow and event handling for your whole application, including all object adapters.[1] Following are the definitions of these operations.

[1] Although they are not strictly POA-specific, we cover ORB event-handling issues in this chapter because they, like the POA, relate to server applications.

#pragma prefix "omg.org"

module CORBA { interface ORB {

void

run();

void

shutdown(in boolean wait_for_completion);

boolean

work_pending();

void

perform_work();

// ...

 

};

 

// ...

 

};

 

445

IT-SC book: Advanced CORBA® Programming with C++

These operations support both the blocking and the non-blocking varieties of eventhandling applications. The run operation, which is blocking, causes the application to start listening for requests. After an application is listening for requests, you can invoke shutdown to make it stop listening. For non-blocking event handling, you use work_pending and perform_work. We supply details of how each of these operations works in the following sections.

If the ORB did not provide such operations, applications would have to individually tell each POA or POAManager to listen for requests. This in turn would mean that your application main would have to know about all POAs in your application and deal with all of them directly. Instead, applications initiate event handling at the ORB level, and it delegates to each of its object adapters.

11.11.1 Blocking Event Handling

The ORB::run operation blocks until the ORB has shut down. By invoking run from the thread executing your application main, you permit the ORB to take over the main thread to perform its work. The ORB keeps control of the main thread and does not return until after you invoke ORB::shutdown and the ORB completely shuts itself down. Invoking run from any other thread merely blocks that thread by making it wait for ORB shutdown.

11.11.2 Non-Blocking Event Handling

The ORB::run operation suffices for applications that operate correctly when the ORB takes over the main thread. For applications that share the main thread with other event loops, however, yielding control of the main thread to the ORB is unacceptable. Instead, such applications need a way to determine when the ORB requires the main thread to perform some work and then to temporarily hand over control of the main thread to the ORB to complete that work.

To determine whether the ORB has any work items pending, you call work_pending. It returns true if the ORB needs the main thread to perform some work; otherwise, it returns false. If work_pending returns true, you can temporarily give control of the main thread to the ORB by invoking perform_work.

//The handle_gui_events function allows the user

//interface to refresh itself and handle its events.

//It returns true if the user has clicked the

//"exit" button.

extern bool handle_gui_events();

int

main(int argc, char * argv[])

{

CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);

// Initialize POAs and POAManagers and then activate

446

IT-SC book: Advanced CORBA® Programming with C++

//objects (not shown).

//Enter event loop. bool done = false; while (!done) {

if (orb->work_pending()) orb->perform_work();

done = handle_gui_events();

}

orb->shutdown(1);

return 0;

}

In this example we use the non-blocking ORB event-handling functions to allow our GUI to handle its events as well. After initializing the ORB and POAs and then activating our objects, we enter an event loop. In the loop we first call work_pending to see whether the ORB has any work items it needs to complete. If work_pending returns true, we invoke perform_work to let the ORB do its work. We then call our hypothetical handle_gui_events function to let our GUI handle input from the user. If the user clicks the GUI's exit button, handle_gui_events returns true, so we exit our event loop and shut down our application.

The "size" of the unit of work that the ORB performs is implementationdependent, but it could involve activities such as reading an incoming message from a socket or dispatching a request to an object adapter. This means that the amount of time that perform_work blocks varies from ORB to ORB and potentially from one invocation to the next.

Single-threaded CORBA server applications that also have other event loops must use work_pending and perform_work as shown in the preceding example. For multithreaded applications, however, an alternative to an event loop like this is to invoke ORB::run in the main thread and invoke any other event loops for other parts of the application in their own separate threads. This approach will not work if the other event loops also require the main thread to get their work done, but it is a viable alternative for most multithreaded applications. We discuss this issue in more detail in Chapter 21.

11.11.3 Application Shutdown

When you want to shut down your application, you invoke ORB::shutdown. It takes a single boolean argument that tells it whether to block waiting for all shutdown activities to finish or whether it can return before all shutdown work has completed.

Server applications generally shut down in one of three ways.

447

IT-SC book: Advanced CORBA® Programming with C++

The application can use a time-out approach. If it does not receive any requests within a certain amount of time, the application initiates its own shutdown.

The user can force a shutdown by sending a signal to the running application. For example, on UNIX the user might generate an interrupt for the server by typing the interrupt character (usually Ctrl-C). If the application has a GUI, the user might click a button to inform the application to exit.

Another application might invoke some sort of shutdown operation provided by one of the application's CORBA objects.

We discuss these approaches in more detail in the following sections.

Shutdown via Time-Out

Most ORBs supply a proprietary operation that acts like ORB::run but takes a time-out parameter. This parameter typically specifies a time-out period as a number of seconds. If the specified amount of time elapses without the application handling any CORBA requests, the ORB run time initiates application shutdown.

Because ORB systems are capable of activating server processes when needed (see Chapter 21), having a server shut itself down after a time-out has elapsed is quite practical. It prevents idle servers from running, thereby needlessly using machine and operating system resources, and it helps garbagecollect transient objects.

The time-out approach, however, is not without drawbacks. CORBA does not provide a standard time-out-based shutdown operation (and it is not clear that anything like it should be standardized), so if you want to shut down your applications based on timeouts, you must use whatever proprietary functions your ORB vendor gives you. Servers that shut down based on time-outs can also cause clients who infrequently issue requests to transient objects to occasionally find that their transient objects have unexpectedly disappeared. Section 12.7.4 discusses details of how a time-out-based shutdown approach affects object life cycles.

Shutdown via Signals

If you start your servers from a command line or from a control script, you might want to use UNIX signals, Windows console events, or GUI controls to shut them down. Shutting down due to a GUI button click is easy because your application receives the shutdown notification synchronously, but shutting down due to an asynchronous signal or console event is more difficult.

If your application's GUI provides a button for you to click to initiate shutdown, for example, the code that runs when you click the button eventually calls ORB::shutdown. This approach is easy and straightforward, as we demonstrate with the example in Section 11.11.2.

448

IT-SC book: Advanced CORBA® Programming with C++

The main difficulty with shutting your application down correctly and cleanly when it receives a signal lies mostly in non-portability issues with signal handling. This is especially true for multithreaded applications. Some operating systems require that you establish a single signal-handling thread, whereas others can deliver an asynchronous signal to whatever thread is running when the signal arrives. Windows console events are somewhat (but not quite) like UNIX signals, making portable signal handling more difficult.

Fortunately, some ORBs supply portable signal-handling abstractions that hide the details and idiosyncrasies of each platform's signal-handling mechanisms. These services generally require you to supply a callback handler function that is invoked when a signal typically used to kill a process (such as SIGINT and SIGTERM) or a console event arrives. You can write your handler function to initiate ORB shutdown, as shown here:

//File-static ORB reference. static CORBA::ORB_var orb;

//Signal-handling function. static void

async_handler()

{

if (!CORBA::is_nil(orb)) orb->shutdown(0);

}

int

main(int argc, char * argv[])

{

//First set up our asynchronous signal handler. TerminationHandler::set_handler(async_handler);

//Initialize the ORB.

orb = CORBA::ORB_init(argc, argv);

// ...

}

We pass a pointer to the async_handler function to our proprietary

TerminationHandler::set_handler function to register async_handler as the callback to handle signal events. We then code the rest of the main as usual.

Note that in the async_handler function we call ORB::shutdown with a false (0) argument. This is to avoid blocking the handler function by making it wait for all requests currently in progress to finish, then for all object adapters to shut down, and finally for the ORB itself to shut down. For portability reasons, your handler functions should perform as little work as possible because some operating systems limit the types of activities that signal handlers are allowed to perform.

We recommend using these abstractions if the ORB you use provides them. However, beware that if code in your application already makes extensive use of signals, these

449

IT-SC book: Advanced CORBA® Programming with C++

proprietary signal-handling abstractions may not work for you. Even worse, your ORB may not supply such an abstraction. If it does not, you should ask your ORB vendor how it recommends that you deal with portable signal-initiated ORB shutdown.

Shutdown via CORBA Requests

The third approach to shutting down server applications involves sending a shutdown request to an object in the server. Such an object might have an interface like the following:

interface ProcessTerminator { void shutdown();

};

Only one such object is needed per process, so you might add a shutdown operation to some other object's interface rather than create a whole new interface for it. For example, for our CCS server, we could make shutdown part of the Controller interface. The body of the shutdown method would look much like that of our signalhandling code from the preceding section:

void MyProcessTerminator::

shutdown() throw(CORBA::SystemException)

{

orb->shutdown(0);

}

In this case, we are required to pass a false (0) value to ORB::shutdown to avoid deadlock. If we passed a true value, ORB::shutdown would try to wait for all requests to complete before returning, but because we are calling it from within a request, we would be blocking it from returning.

This approach looks simple enough, but it has several drawbacks.

Initiating ORB shutdown causes the ORB to shut down all its object adapters. This means that all client connections will be closed, including the one to the client that invoked this request. This in turn might mean that the connection will be closed before the ORB sends the response, and that will cause the client ORB to raise a CORBA::COMM_FAILURE exception.

You can alleviate this problem in some cases by declaring the ProcessTerminator::shutdown operation as oneway to let the client ORB run time know that it should not expect a response (see Section 4.12). However, oneway is highly dependent on the underlying transport and protocol, and, in some cases, a full round-trip request-response cannot be avoided. For example, in the OMG standard Distributed Computing Environment Common Inter-ORB Protocol (DCE-CIOP), a

450