The ACE Process Wrapper Facades
Line 17 If the spawn call returned in the parent (which is always the case for Win32) the logging_peer object is no longer needed, so it's closed
The actual TCP connection is not closed because the following behavior is encapsulated in the ACE process wrapper facades:
• On POSIX, the worker process inherited a copy of the entire object, including the open handle. The OS reference counts the handles, so the connection won't actually be torn down until both the parent and worker processes close their respective copies of the handle.
• On Windows NT/2000, the handles are managed similarly to POSIX;
that is, the connection is torn down only after both processes close the handle.
• On Windows handles are not reference counted automatically.
However, :pass_handle () , called from the Logging_Process : method, duplicated the socket han-dle contained in logging_peer. The parent can therefore close its handle safely without affecting the connection.
As usual, the ACE wrapper facades shield application developers from needing to understand these subtle nuances for each OS platform!
Lines 21-22 (Steps 6 and 7) The : wait method checks to see if any worker processes have exited, reaping their status and cleaning up any handles they'd opened.
The method shown below is identical for all platforms.
It puts the client socket into blocking mode, opens the log file to hold the log records, and processes logging records until the logging client closes the socket or an error occurs. Lastly, the log file is closed.
5w on Win32 is executed by the method shown on page 176.
180 The ACE Process Wrapper Facades
virtual int handle_data (ACE_SOCK_Stream { // Ensure blocking <recv>s.
(log_file,
logging_handler
while () -1)
return
The class. To set up the new worker process properly, we define a Logging_Process class that's derived from the ACE_Process class described in Section Since setup requirements often vary be-tween platforms and applications, ACE_Process provides the prepare () and hook methods. Our Logging_Process class uses the prepare method to pass the new logging client's socket handle to the worker process. It's also the location where we localize any changes if we need to revisit the decision not to run a new program image on
class : public ACE_Process
{
Logging_Process // Force desired constructor to be used.
char +
Logging_Process (const char *prog_name, const
:
{ strcpy }
The parameters needed to set up the new worker process are passed to the Logging_Process class's constructor. These include the command name used to spawn the new process and the logging client's
to be used by the worker process. Both these parameters are used in the following prepare hook method, which is called by ACE_Process : : spawn before the new process is spawned.
Section 8.4 The Class 181
virtual int prepare (ACE_Process_Options &options) {
if ()) == -1)
ACE_ERROR_RETURN
return }
The prepare method is illustrated as Step 3 in Figures 8.6 and 8.7 on page 174. Its only argument is a reference to the
object that () is using to spawn the new process.
This gives prepare an opportunity to modify or add to the options as needed. We use prepare to set the options as follows:
• Pass the logging client's socket handle. The internals of ACE_
Process and ACE_Process_Options perform all platform-specific de-tails of getting the socket handle to the worker process correctly, in-cluding duplicating it on Windows 95/98 and remembering to close the duplicated handle when the process exits. We use the pass_
() method to pass the handle value to the worker process command line, which indicates that a logging client needs service.
• Set the program This is needed both for a call and to run the new program image when needed.
• Avoid zombie processes. This flag is needed only for POSIX and is ignored for Win32.
• Set the NO_EXEC flag so that POSIX systems simply a new worker process and do not exec a new program image. This flag is also ignored on Win32.
Although some methods have no affect on Win32, we call them anyway so the procedure for setting up the worker process is portable to all ACE platforms that support multiple processes. For a discussion of why this is the correct design (and when it isn't), see Section on page 248.
encapsulates platform-specific knowledge that determines when a process exits and calls the unmanage hook method on any process object whose underlying process has exited. The Logging_
is illustrated as Step 7 in Figures 8.6 and 8.7 on page and is shown below:
virtual void unmanage () { delete this;
182 CHAPTER 8 The ACE Process Wrapper Facades
It simply cleans up the Logging_Process object that was allocated dynam-ically when the logging client connection was accepted. The logging peer socket handle may have been duplicated, however, when passed to the worker process on Windows 95/98. By encapsulating behavior and state in the ACE_Process class, that handle will be closed in the ACE_Process destructor, so our cleanup code is also portable to all platforms that ACE supports.
Finally, we show the main program, which is a slight extension of our earlier servers.
static void (int /* */) { /* No-op. */ } int main (int argc, char
{
// Register to receive the <SIGTERM> signal.
sa
Process_Per_Connection_Logging_Server
if (argc, argv) == -1 errno !=
ACE_ERROR_RETURN 1);
// Barrier return }
The class registers the process to handle the SIGTERM signal, which administrators can use to shutdown the parent server
pro-cess. Before exiting, the parent calls to
synchronize on the exits of all worker logging processes before shutting itself down. This barrier synchronization capability could be useful if the parent process needed to write a final time stamp to the logging output device or file.
8.5 Summary
Many multiprocessing operating systems were developed when proprietary systems were in vogue, so each developed its own specific mechanisms for process management. Since maintaining version-to-version OS compat-ibility is important, many of these proprietary process mechanisms and
Section 8.5 Summary 183
APIs remain to this day. This chapter showed how to use the ACE pro-cess management capabilities, which allow multipropro-cessing servers to be programmed portably across a range of platforms. We applied the
to a new logging server implementation that grouped a set of spawned processes together. The grouping allowed the master pro-cess to wait for all the worker propro-cesses to exit before shutting itself down.
Due to inherent differences in OS process management semantics, de-velopers of networked applications must evaluate their system's concur-rency requirements carefully. Whereas previous chapters focused largely on alleviating accidental complexities introduced by APIs, this chapter expanded on the challenges of multiplatform software develop-ment. It showed how the portable ACE wrapper facades make multi-processing applications much easier to write, while also making it clear that the capabilities of an application's current and future platforms must be considered carefully when evaluating multiprocessing concurrency de-signs. For example, the ACE process wrapper facades can shield applica-tions from many OS details, but they cannot provide process abstracapplica-tions on platforms that lack them altogether.
CHAPTER 9