Motivation
Section 2.3 on page 37 described the complexities associated with direct use of I/O handles. The f d_set represents another source of accidental complexity in the following areas:
• The macros or functions that platforms supply to manipulate and scan an f d_set must be used carefully to avoid processing handles that aren't active and to avoid corrupting an f d_set inadvertently.
• The code to scan for active handles is often a hot spot because it ex-ecutes continually in a tight loop. It's therefore an obvious candidate for optimization to improve performance.
• The f d_set is defined in system-supplied header files, so its repre-sentation is exposed to programmers. Naturally, it's tempting to make use of this platform-internal knowledge in ways. However, doing so causes portability problems because representations vary widely across platforms.
• There are subtle nonportable aspects of f d_set when used in con-junction with the select () function.
To illustrate the latter point, let's revisit the function signature for the () function:
int select (int width, // Maximum handle plus 1 fd_set *read_fds, // Set of "read" handles fd_set *write_fds, // Set of "write" handles fd_set *except_fds, // Set of "exception" handles struct timeval Time to wait for events
The three f d_set arguments specify the handles to use when selecting for each event type. The timeout argument is used to specify a time limit to wait for events to occur. The particular details concerning these parame-ters vary in only minor ways across platforms. Due to the changing nature
142 7 The ACE Synchronous Event Demultiplexing Wrapper Facades
of I/O handles (and, thus, f d_set) across platforms, however, the meaning of the first argument (width) varies widely across operating systems:
• On I/O handles start at the value 0 and increase to an OS-defined maximum limit. The meaning of the width parameter on UNIX is therefore "how far into the f d_set to scan for handles"; that is, it's defined as the highest numbered handle in any of the three fd_set parameters, plus
• On socket handles are implemented as pointers rather than as low-numbered integers. The width argument to select is therefore ignored completely. Instead, each fd_set contains its own handle count.
• On other platforms, where handles may be integers that don't start at 0, the definition may differ yet again.
The size-related values in an fd_set are also computed differently de-pending on the characteristics of the platform's representation and its
semantics. Any applications written directly to native OS APIs must therefore adapt to all these subtle differences, and each project may end up redesigning and reimplementing code that manages the proper value to supply for width. Addressing these portability differences in each application is tedious and error prone, which is why ACE provides the ACE_Handle_Set class.
Class Capabilities
The ACE_Handle_Set class uses the Wrapper Facade pattern
to guide the encapsulation of f d_sets. This class provides the following capabilities:
• It enhances the portability, ease of use, and type safety of event-driven applications by simplifying the use of fd_set and select () across OS platforms
• It tracks and adjusts the f d_set size-related values automatically as handles are added and removed
Since differences in f d_set representations are hidden in the implemen-tation of ACE_Handle_Set, application code can be written once and then simply recompiled when it's ported to a new platform.
Section 7.2 The 143
The interface for ACE_Handle_Set is shown in Figure and its key methods are shown in the following table:
Method Description
Initializes the handle set to its default values.
Clears one handle in the set.
Sets one handle in the set.
Tests to see if a specified handle is active in the set.
Returns the number of active handles in the set.
Returns the value of the largest handle in the set.
Returns a pointer to the underlying f d set or a NULL pointer if there are no active handles in the set.
the handle set to find the new highest active handle and the number of active handles, which is useful after the handle set has changed as the result of an external operation, such as a call to select
Sections 4.4.3 and outlined some of the problems associated with it-erative servers. Using synchronous event demultiplexing to implement the reactive server model is one strategy to avoid these problems. We now show an example that enhances the iterative logging server implementation from Chapter 4 by using () together with the ACE_Handle_Set class.
This server demultiplexes the following two types of events:
Arrival of new connections from clients 2. Arrival of log records on client connections
We put our implementation in a header file named ive_Logging_
which starts by including ACE's Handle_Set.h file and the .h file. We derive a new class, Reactive_
Logging_Server, from the Iterative_Logging_Server class defined on page 92. We reuse its data members and define two more that are used to manage multiple clients.
ftinclude
class : public
144 CHAPTER 7 The ACE Synchronous Event Demultiplexing Wrapper Facades
// Keeps track of the acceptor socket handle and all the // connected stream socket
ACE_Handle_Set
// Keep track of handles marked as active by
// Other methods shown
To implement reactive server semantics, we override the hook methods in
Iterative_Logging_Server, starting with open :
virtual int open {
(acceptor acceptor
return }
After calling down to its parent class's open method to initialize the ac-ceptor, we call the set_bit method on the to keep track of the acceptor's socket handle. We also set the acceptor into non-blocking mode for the reasons described in Sidebar 14.
Next, we implement the method for the
reactive server. We copy into active_handles_, which will be modified by select We need to calculate the width ar-gument for select () using a cast to compile on Win32 (the actual value passed to select () is ignored on that platform). The second parameter to select () is a pointer to the underlying fd_set in active_handles_
accessed via the : f dset method.
virtual int wait_for_multiple_events () { active_handles_ =
int width = ( i n t ) () + 1;
if (select ( w i d t h ,
0, // no write_fds 0 ,
timeout return
save space, we omit more of the error handling than in our earlier iterative server
7.2 Class 145