7.3 The ACE_Handle_Set_Iterator Class

I l @ ve RuBoard

Motivation

The preceding example showed how reactive servers demultiplex events to event handlers via the following steps:

  1. The select() function returns the number of handles whose values are active in its modified handle set(s).

  2. The server's event loop then scans through the handle sets, identifies the active handles, and runs the event handling code for each handle.

Although these steps seem simple, complications arise in practice since select() returns the total number of active handle values, but doesn't indicate in which handle set(s) they reside. Applications must therefore scan through the contents of each handle set to find the active handles. One way to scan a handle set is to call ACE_Handle_Set::is_set() on each handle that may be active, as we did in the handle_data() method on page 146. There are two problems with this approach, however:

  • The for loop technique used to iterate through the handles works only on OS platforms that represent socket handles as a contiguous range of unsigned integers. While POSIX/UNIX platforms support this technique, it doesn't work on Win32, so the code is non-portable. Moreover, the loop assumes that the acceptor socket has the lowest socket handle. In complex applications this property may not be true if handles were opened before the acceptor socket and later closed, in which case their handle values will be recycled.

  • Even on platforms that do support contiguous handle values, each is_set() operation is called sequentially on all possible handle values. In networked applications it's often the case, however, that only some of the many possible socket handles are active at any point. Searching the sparsely populated handle sets sequentially is inefficient, particularly if the potential number of handles is large; for example, many UNIX platforms default to 1,024 handles per fd_set . Although the ACE_Handle_Set max_set() and num_set() methods can be used to limit the scope of a search, there are still unnecessary inefficiencies associated with scanning handle sets sequentially.

Addressing these portability concerns and optimizing these bottlenecks in each application is tedious and error prone, which is why ACE provides the ACE_Handle_Set_Iterator class.

Class Capabilities

The ACE_Handle_Set_Iterator is based on the Iterator pattern [GHJV95] that provides ways to access elements of an aggregate object sequentially without exposing the underlying structure of the object. This class scans efficiently through the handles in an ACE_Handle_Set , returning only the active handles one at a time through its operator() method. The interface of ACE_Handle_Set_Iterator and its relationship with the ACE_Handle_Set class is shown in Figure 7.1 on page 140. Its key methods are shown in the following table:

Method Description
ACE_Handle_Set_Iterator() Initializes the handle set iterator to iterate over an ACE_Handle_Set .
operator() Returns the next unseen handle in the handle set or ACE_INVALID_HANDLE if all handles have been seen.

Since the ACE_Handle_Set_Iterator is not designed as a "robust iterator" [Kof93] it's important to not clear handles from an ACE_Handle_Set that's being iterated over.

Although ACE_Handle_Set_Iterator has a simple interface, it's implementation is more powerful than the ACE Socket wrapper facades described in Chapter 3. In particular, ACE_Handle_Set_Iterator encapsulates all the platform-specific fd_set representation details that help to iterate through all active handles in an ACE_Handle_Set portably and efficiently, as follows :

  • Winsock's fd_set has a field containing the number of handle values in the set. The handles themselves are stored in adjacent locations in an array of handle values. ACE_Handle_Set_Iterator uses this knowledge to access each handle without having to search for the next handle, and without having to search all possible handle values for active handles.

  • Most UNIX fd_set representations take advantage of the - n range of possible handle values by assigning each possible handle value to a single bit. An fd_set thus contains an array of integers that's large enough to cover the entire range of bit values. The ACE_Handle_Set_Iterator class is designed to scan past large sequences of inactive handle values rapidly by skipping integer array elements that have zero values. Nonzero array elements can be scanned quickly by shifting and masking bits, using private class members to remember the array element and shift position between operator() invocations.

  • Different sizes and bit orders of underlying fd_set are adapted to by using more appropriate shifting and array element access strategies. For example, the ACE_Handle_Set_Iterator can be configured to use a highly optimized bit manipulation algorithm based on the following two assumptions:

    - The fd_set is represented by an array of words(this is the case on most UNIX platforms).

    - If n is a word, the expression n &~( n - 1) accesses the least-significant bit (1sb) of n.

    The key optimization in this algorithm is that the number of bits tested in each word in an fd_set is exactly the number of bits active, rather than the total number of bits in the word. When there are few active handles, this algorithm can improve server performance significantly.

    In this algorithm, the ACE_Handle_Set_Iterator starts by finding the first word with an active bit in fd_set . Since the expression word &~( word 1) tests the 1sb active in a word, we must find n such that 1sb = 2 n . For each word in fd_set we have a cost of O(number of bits active in word). Thus, if a word consists of 64 bits and there's only one bit active, the run-time cost is 1, rather than 64.

  • ACE_Handle_Set can be configured to use another fast algorithm that generates a count of active handles after select() sets them. It maintains a 256 element array of characters , with each element containing the number of active bits required to produce the index value. For example, element 5 contains the value of 2 (2 bits, 0 and 2, are set for the binary number 5). By skimming through the fd_set elements and summing the corresponding array values, the number of active bits in the fd_set can be calculated quickly without scanning them all sequentially.

  • Since ACE_Handle_Set_Iterator is a friend of ACE_Handle_Set , it can use the calculated number of active handles and the maximum handle value to limit its search space. This design allows an ACE_Handle_Set_Iterator to scan the smallest necessary subset of handle values that may be active.

By hiding all these optimization details in ACE_Handle_Set_Iterator , ACE can greatly improve the performance of key synchronous event demultiplexing operations without compromising application portability. This class illustrates how the appropriate levels of abstraction can improve application performance significantly.

Example

This example illustrates how we can use the ACE_Handle_Set_Iterator to solve the drawbacks with the reactive logging server alluded to near the end of Section 7.2 on page 146. We focus our attention on the handle_data() method shown on page 146, replacing much of its code with an ACE_Handle_Set_Iterator that iterates over the active_handles_ as follows:

 virtual int handle_data (ACE_SOCK_Stream *) {   ACE_Handle_Set_Iterator peer_iterator (active_handles_);     for (ACE_HANDLE handle;          (handle = peer_iterator ()) != ACE_INVALID_HANDLE;) {       logging handler ().peer ().set_handle (handle);       if (logging_handler ().log_record () == -1) {         // Handle connection shutdown or comm failure.         master_handle_set_.clr_bit (handle);         logging_handler ().close ();       }     } } 

Not only is this code more concise and portable, but it's also more efficient due to the ACE_Handle_Set_Iterator optimizations.

I l @ ve RuBoard


C++ Network Programming
C++ Network Programming, Volume I: Mastering Complexity with ACE and Patterns
ISBN: 0201604647
EAN: 2147483647
Year: 2001
Pages: 101

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net