Beautiful Code [295]
handle_connections ();
// Step 2c: process received log record (if available)
handle_data ();
}
} catch (...) { /* ... Handle the exception ... */ }
}
The beauty of this code is that:
Its pattern-based design makes it easy to handle variation in concurrency models, such as by varying the behavior of the run( ) template method by providing specific implementations of the hook methods in the implementation of subclasses.
Its template-based design makes it easy to handle variation in IPC and synchronization mechanisms, such as by plugging different types into the ACCEPTOR and MUTEX template parameters.
Labor-Saving Architecture: An Object-Oriented Framework for Networked Software > Implementing Sequential Logging Servers
26.3. Implementing Sequential Logging Servers
This section demonstrates the implementation of logging servers that feature sequential concurrency models—i.e., all processing is performed in a single thread. We cover both iterative and reactive implementations of sequential logging servers.
26.3.1. An Iterative Logging Server
Iterative servers process all log records from each client before handling any log records from the next client. Since there is no need to spawn or synchronize threads, we use the Null_Mutex facade to parameterize the Iterative_Logging_Server subclass template, as follows:
template class Iterative_Logging_Server : virtual Logging_Server public: typedef Logging_Server Iterative_Logging_Server (int argc, char *argv[]); protected: virtual void open (void); virtual void wait_for_multiple_events (void) {}; virtual void handle_connections (void); virtual void handle_data (typename ACCEPTOR::PEER_STREAM *stream = 0); HANDLER log_handler_; // One log file shared by all clients. std::ofstream logfile_; }; Implementing this version of our server is straightforward. The open( ) method decorates the behavior of the method from the Logging_Server base class by opening an output file before delegating to the parent's open( ), as follows: template Interative_Logging_Server logfile_.open (filename_.c_str ()); if (!logfile_.good ()) throw std::runtime_error; // Delegate to the parent's open() method. Logging_Server } The wait_for_multiple_events( ) method is a no-op. It is not needed because we just handle a single connection at any one time. The handle_connections( ) method therefore simply blocks until a new connection is established, as follows: template Iterative_Logging_Server { acceptor_.accept (log_handler_.peer ()); } Finally, handle_data( ) simply reads log records from the client and writes them to the log-file until the client closes the connection or an error occurs: template Iterative_Logging_Server while (log_handler_.log_record (logfile _)) count_request (); } While the iterative server is straightforward to implement, it suffers from the drawback of being able to service only one client at a time. A second client that attempts to connect may time out while waiting for the first to finish its request. 26.3.2. A Reactive Logging Server The reactive logging server alleviates one of the primary drawbacks with the iterative logging server in the previous section by processing multiple client connections and log record requests via operating system synchronous event demultiplexing APIs provided by the OS, such as select( ) and WaitForMultipleObjects( ). These APIs can monitor multiple clients by waiting in a single thread of control for I/O-related events to occur on a group of I/O handles, and then interleave the processing of log records. Since a reactive logging server is still fundamentally sequential, however, it inherits from the iterative logging server