Beautiful Code [299]
[||||||]C++ Network Programming, Vol. 1: Mastering Complexity with ACE and Patterns, Douglas C. Schmidt and Stephen D. Huston, Addison-Wesley, 2001.
The design challenge is therefore to accommodate the fact that processes spawned after new connections are accepted will start at the beginning of our program. We certainly don't want child processes to attempt to open a new acceptor and listen for connections of their own; instead, they should listen for data events only on their assigned handle. A naïve solution to this problem would rely on applications to detect this condition and call a special entry point defined in the interface to our process-based Logging_Server class.
Figure 26-13. Portable process wrapper facades
This simple solution, however, is less than ideal. It would require us not only to change the public interface of our process-based Logging_Server, but to expose intimate implementation details to applications, violating encapsulation. A better solution is to override the run( ) template method inherited from the Logging_Server base class, which is passed a copy of the command-line argument by users, to determine whether it has been passed any I/O handles. If not, the process assumes it is a parent and delegates to the base class run( ) method. Otherwise, the process assumes it's a child, so it decodes the handle and calls handle_data( ), as shown in Figure 26-14.
Figure 26-14. Process-per-connection run( ) template method
The remainder of this server implementation is straightforward. As shown in Figure 26-15, the process wrapper facade makes the procedure for spawning our worker processes fairly simple. The implementation for handle_data( ) should be textually identical to that shown in Figure 26-12.
Figure 26-15. Connection handling for the process-per-connection server
Our reimplementation of the run( ) method from the Logging_Server base class allows us to maintain the beautifully simple, straightforward, and uniform invocation used by our other logging servers:
int main (int argc, char *argv[]) {
PPC_Logging_Server server.run ( ); return 0; } This main( ) program differs from the thread-per-connection server only in the name of the class that is instantiated and the choice of a Null_Mutex for synchronization. The dispatch of either a parent or a child process is handled transparently by the run( ) method, driven by the command-line arguments passed to the PPC_Logging_Server constructor. 26.4.3. Evaluating the Concurrent Logging Server Solutions Both concurrent logging servers described in this section significantly enhance the Reactive_Logging_Server and Iterative_Logging_Server in their ability to scale as the number of clients increases by taking leveraging hardware and OS support for multiple threads of execution. It is hard, however, to develop thread-per-connection and process-per-connection concurrency strategies in a platform-agnostic manner. We accomplished this task by using wrapper facades to hide platform differences. Our framework-based server design also provided a common external interface to the Logging_Server class,