Select_Reactor_Base.h

Go to the documentation of this file.
00001 // -*- C++ -*-
00002 
00003 //=============================================================================
00004 /**
00005  *  @file    Select_Reactor_Base.h
00006  *
00007  *  $Id: Select_Reactor_Base.h 81138 2008-03-28 09:18:15Z johnnyw $
00008  *
00009  *  @author Douglas C. Schmidt <schmidt@cs.wustl.edu>
00010  */
00011 //=============================================================================
00012 
00013 #ifndef ACE_SELECT_REACTOR_BASE_H
00014 #define ACE_SELECT_REACTOR_BASE_H
00015 
00016 #include /**/ "ace/pre.h"
00017 
00018 #include "ace/Timer_Queuefwd.h"
00019 
00020 #if !defined (ACE_LACKS_PRAGMA_ONCE)
00021 # pragma once
00022 #endif /* ACE_LACKS_PRAGMA_ONCE */
00023 
00024 #include "ace/Event_Handler.h"
00025 #include "ace/Handle_Set.h"
00026 #include "ace/Pipe.h"
00027 #include "ace/Reactor_Impl.h"
00028 
00029 #if defined (ACE_HAS_REACTOR_NOTIFICATION_QUEUE)
00030 # include "ace/Notification_Queue.h"
00031 #endif /* ACE_HAS_REACTOR_NOTIFICATION_QUEUE */
00032 
00033 #ifdef ACE_WIN32
00034 # include "ace/Null_Mutex.h"
00035 # include "ace/Hash_Map_Manager_T.h"
00036 # include "ace/Functor.h"  /* For ACE_Hash<void *> */
00037 # include <functional>      /* For std::equal_to<>  */
00038 #else
00039 # include "ace/Array_Base.h"
00040 #endif  /* ACE_WIN32 */
00041 
00042 #if !defined (ACE_DISABLE_NOTIFY_PIPE_DEFAULT)
00043 # define ACE_DISABLE_NOTIFY_PIPE_DEFAULT 0
00044 #endif /* ACE_DISABLE_NOTIFY_PIPE_DEFAULT */
00045 
00046 ACE_BEGIN_VERSIONED_NAMESPACE_DECL
00047 
00048 // Add useful typedefs to simplify the following code.
00049 typedef void (ACE_Handle_Set::*ACE_FDS_PTMF) (ACE_HANDLE);
00050 typedef int (ACE_Event_Handler::*ACE_EH_PTMF) (ACE_HANDLE);
00051 
00052 // Forward declaration.
00053 class ACE_Select_Reactor_Impl;
00054 class ACE_Sig_Handler;
00055 
00056 /*
00057  * Hook to specialize the Select_Reactor_Base implementation
00058  * with the concrete reactor, e.g., select or tp reactor
00059  * specified at build/compilation time.
00060  */
00061 //@@ REACTOR_SPL_INCLUDE_FORWARD_DECL_ADD_HOOK
00062 
00063 /**
00064  * @class ACE_Select_Reactor_Handle_Set
00065  *
00066  * @brief Track handles we are interested for various events.
00067  */
00068 class ACE_Export ACE_Select_Reactor_Handle_Set
00069 {
00070 public:
00071   /// Read events (e.g., input pending, accept pending).
00072   ACE_Handle_Set rd_mask_;
00073 
00074   /// Write events (e.g., flow control abated, non-blocking connection
00075   /// complete).
00076   ACE_Handle_Set wr_mask_;
00077 
00078   /// Exception events (e.g., SIG_URG).
00079   ACE_Handle_Set ex_mask_;
00080 };
00081 
00082 /**
00083  * @class ACE_Event_Tuple
00084  *
00085  * @brief An ACE_Event_Handler and its associated ACE_HANDLE.
00086  *
00087  * One ACE_Event_Handler is registered for one or more
00088  * ACE_HANDLE.  At various points, this information must be
00089  * stored explicitly.  This class provides a lightweight
00090  * mechanism to do so.
00091  */
00092 class ACE_Event_Tuple
00093 {
00094 public:
00095 
00096   /// Default constructor.
00097   ACE_Event_Tuple (void);
00098 
00099   /// Constructor.
00100   ACE_Event_Tuple (ACE_Event_Handler *eh, ACE_HANDLE h);
00101 
00102   /// Equality operator.
00103   bool operator== (const ACE_Event_Tuple &rhs) const;
00104 
00105   /// Inequality operator.
00106   bool operator!= (const ACE_Event_Tuple &rhs) const;
00107 
00108 public:
00109 
00110   /// Handle.
00111   ACE_HANDLE handle_;
00112 
00113   /// ACE_Event_Handler associated with the ACE_HANDLE.
00114   ACE_Event_Handler *event_handler_;
00115 
00116 };
00117 
00118 /**
00119  * @class ACE_Select_Reactor_Notify
00120  *
00121  * @brief Unblock the ACE_Select_Reactor from its event loop.
00122  *
00123  * This implementation is necessary for cases where the
00124  * ACE_Select_Reactor is run in a multi-threaded program.  In
00125  * this case, we need to be able to unblock @c select or @c poll
00126  * when updates occur other than in the main
00127  * ACE_Select_Reactor thread.  To do this, we signal an
00128  * auto-reset event the ACE_Select_Reactor is listening on.
00129  * If an ACE_Event_Handler and ACE_Select_Reactor_Mask is
00130  * passed to @c notify, the appropriate @c handle_* method is
00131  * dispatched in the context of the ACE_Select_Reactor thread.
00132  */
00133 class ACE_Export ACE_Select_Reactor_Notify : public ACE_Reactor_Notify
00134 {
00135 public:
00136   /// Constructor.
00137   ACE_Select_Reactor_Notify (void);
00138 
00139   /// Destructor.
00140   virtual ~ACE_Select_Reactor_Notify (void);
00141 
00142   // = Initialization and termination methods.
00143   /// Initialize.
00144   virtual int open (ACE_Reactor_Impl *,
00145                     ACE_Timer_Queue * = 0,
00146                     int disable_notify_pipe = ACE_DISABLE_NOTIFY_PIPE_DEFAULT);
00147 
00148   /// Destroy.
00149   virtual int close (void);
00150 
00151   /**
00152    * Called by a thread when it wants to unblock the
00153    * ACE_Select_Reactor.  This wakeups the ACE_Select_Reactor if
00154    * currently blocked in @c select/poll.  Pass over both the
00155    * @c Event_Handler *and* the @c mask to allow the caller to dictate
00156    * which @c Event_Handler method the ACE_Select_Reactor will
00157    * invoke.  The ACE_Time_Value indicates how long to blocking
00158    * trying to notify the ACE_Select_Reactor.  If @a timeout == 0,
00159    * the caller will block until action is possible, else will wait
00160    * until the relative time specified in @c *timeout elapses).
00161    */
00162   virtual int notify (ACE_Event_Handler * = 0,
00163                       ACE_Reactor_Mask = ACE_Event_Handler::EXCEPT_MASK,
00164                       ACE_Time_Value * timeout = 0);
00165 
00166   /// Handles pending threads (if any) that are waiting to unblock the
00167   /// ACE_Select_Reactor.
00168   virtual int dispatch_notifications (int &number_of_active_handles,
00169                                       ACE_Handle_Set &rd_mask);
00170 
00171   /// Returns the ACE_HANDLE of the notify pipe on which the reactor
00172   /// is listening for notifications so that other threads can unblock
00173   /// the Select_Reactor
00174   virtual ACE_HANDLE notify_handle (void);
00175 
00176   /// Handle one of the notify call on the @c handle. This could be
00177   /// because of a thread trying to unblock the Reactor_Impl
00178   virtual int dispatch_notify (ACE_Notification_Buffer &buffer);
00179 
00180   /// Read one of the notify call on the @a handle into the
00181   /// @a buffer. This could be because of a thread trying to unblock
00182   /// the Reactor_Impl
00183   virtual int read_notify_pipe (ACE_HANDLE handle,
00184                                 ACE_Notification_Buffer &buffer);
00185 
00186   /// Verify whether the buffer has dispatchable info or not.
00187   virtual int is_dispatchable (ACE_Notification_Buffer &buffer);
00188 
00189   /// Called back by the ACE_Select_Reactor when a thread wants to
00190   /// unblock us.
00191   virtual int handle_input (ACE_HANDLE handle);
00192 
00193   /**
00194    * Set the maximum number of times that the
00195    * ACE_Select_Reactor_Notify::handle_input() method will iterate and
00196    * dispatch the ACE_Event_Handlers that are passed in via the
00197    * notify pipe before breaking out of its @c recv loop.  By default,
00198    * this is set to -1, which means "iterate until the pipe is empty."
00199    * Setting this to a value like "1 or 2" will increase "fairness"
00200    * (and thus prevent starvation) at the expense of slightly higher
00201    * dispatching overhead.
00202    */
00203   virtual void max_notify_iterations (int);
00204 
00205   /**
00206    * Get the maximum number of times that the
00207    * ACE_Select_Reactor_Notify::handle_input() method will iterate and
00208    * dispatch the ACE_Event_Handlers that are passed in via the
00209    * notify pipe before breaking out of its recv loop.
00210    */
00211   virtual int max_notify_iterations (void);
00212 
00213   /**
00214    * Purge any notifications pending in this reactor for the specified
00215    * ACE_Event_Handler object. If @a eh == 0, all notifications for all
00216    * handlers are removed (but not any notifications posted just to wake up
00217    * the reactor itself). Returns the number of notifications purged.
00218    * Returns -1 on error.
00219    */
00220   virtual int purge_pending_notifications (
00221       ACE_Event_Handler *sh,
00222       ACE_Reactor_Mask mask = ACE_Event_Handler::ALL_EVENTS_MASK);
00223 
00224   /// Dump the state of an object.
00225   virtual void dump (void) const;
00226 
00227   /// Declare the dynamic allocation hooks.
00228   ACE_ALLOC_HOOK_DECLARE;
00229 
00230 protected:
00231   /**
00232    * Keep a back pointer to the ACE_Select_Reactor.  If this value
00233    * if NULL then the ACE_Select_Reactor has been initialized with
00234    * disable_notify_pipe.
00235    */
00236   ACE_Select_Reactor_Impl *select_reactor_;
00237 
00238   /**
00239    * Contains the ACE_HANDLE the ACE_Select_Reactor is listening
00240    * on, as well as the ACE_HANDLE that threads wanting the
00241    * attention of the ACE_Select_Reactor will write to.
00242    */
00243   ACE_Pipe notification_pipe_;
00244 
00245   /**
00246    * Keeps track of the maximum number of times that the
00247    * ACE_Select_Reactor_Notify::handle_input() method will iterate and
00248    * dispatch the ACE_Event_Handlers that are passed in via the
00249    * notify pipe before breaking out of its recv loop.  By default,
00250    * this is set to -1, which means "iterate until the pipe is empty."
00251    */
00252   int max_notify_iterations_;
00253 
00254 #if defined (ACE_HAS_REACTOR_NOTIFICATION_QUEUE)
00255   /**
00256    * @brief A user-space queue to store the notifications.
00257    *
00258    * The notification pipe has OS-specific size restrictions.  That
00259    * is, no more than a certain number of bytes may be stored in the
00260    * pipe without blocking.  This limit may be too small for certain
00261    * applications.  In this case, ACE can be configured to store all
00262    * the events in user-space.  The pipe is still needed to wake up
00263    * the reactor thread, but only one event is sent through the pipe
00264    * at a time.
00265    */
00266   ACE_Notification_Queue notification_queue_;
00267 #endif /* ACE_HAS_REACTOR_NOTIFICATION_QUEUE */
00268 };
00269 
00270 /**
00271  * @class ACE_Select_Reactor_Handler_Repository
00272  *
00273  * @brief Used to map ACE_HANDLEs onto the appropriate
00274  * ACE_Event_Handler *.
00275  *
00276  * This class is necessary to shield differences between UNIX
00277  * and Win32.  In UNIX, ACE_HANDLE is an int, whereas in Win32
00278  * it's a void *.  This class hides all these details from the
00279  * bulk of the ACE_Select_Reactor code.  All of these methods
00280  * are called with the main <Select_Reactor> token lock held.
00281  */
00282 class ACE_Export ACE_Select_Reactor_Handler_Repository
00283 {
00284 public:
00285   friend class ACE_Select_Reactor_Handler_Repository_Iterator;
00286 
00287   typedef ACE_HANDLE          key_type;
00288   typedef ACE_Event_Handler * value_type;
00289 
00290   // = The mapping from <HANDLES> to <Event_Handlers>.
00291 #ifdef ACE_WIN32
00292   /**
00293    * The NT version implements this via a hash map
00294    * @c ACE_Event_Handler*.  Since NT implements @c ACE_HANDLE
00295    * as a void * we can't directly index into this array.  Therefore,
00296    * we must explicitly map @c ACE_HANDLE to @c ACE_Event_Handler.
00297    */
00298   typedef ACE_Hash_Map_Manager_Ex<key_type,
00299                                   value_type,
00300                                   ACE_Hash<key_type>,
00301                                   std::equal_to<key_type>,
00302                                   ACE_Null_Mutex> map_type;
00303 
00304   typedef map_type::size_type max_handlep1_type;
00305 #else
00306   /**
00307    * The UNIX version implements this via a dynamically allocated
00308    * array of @c ACE_Event_Handler* that is indexed directly using
00309    * the @c ACE_HANDLE value.
00310    */
00311   typedef ACE_Array_Base<value_type> map_type;
00312   typedef ACE_HANDLE max_handlep1_type;
00313 #endif  /* ACE_WIN32 */
00314 
00315   typedef map_type::size_type size_type;
00316 
00317   // = Initialization and termination methods.
00318   /// Default "do-nothing" constructor.
00319   ACE_Select_Reactor_Handler_Repository (ACE_Select_Reactor_Impl &);
00320 
00321   /// Initialize a repository of the appropriate @a size.
00322   /**
00323    * On Unix platforms, the size parameter should be as large as the
00324    * maximum number of file descriptors allowed for a given process.
00325    * This is necessary since a file descriptor is used to directly
00326    * index the array of event handlers maintained by the Reactor's
00327    * handler repository.  Direct indexing is used for efficiency
00328    * reasons.
00329    */
00330   int open (size_type size);
00331 
00332   /// Close down the repository.
00333   int close (void);
00334 
00335   // = Search structure operations.
00336 
00337   /**
00338    * Return the @c ACE_Event_Handler* associated with @c ACE_HANDLE.
00339    */
00340   ACE_Event_Handler * find (ACE_HANDLE handle);
00341 
00342   /// Bind the ACE_Event_Handler * to the ACE_HANDLE with the
00343   /// appropriate ACE_Reactor_Mask settings.
00344   int bind (ACE_HANDLE,
00345             ACE_Event_Handler *,
00346             ACE_Reactor_Mask);
00347 
00348   /// Remove the binding of ACE_HANDLE in accordance with the @a mask.
00349   int unbind (ACE_HANDLE,
00350               ACE_Reactor_Mask mask);
00351 
00352   /// Remove all the <ACE_HANDLE, ACE_Event_Handler> tuples.
00353   int unbind_all (void);
00354 
00355   // = Sanity checking.
00356 
00357   // Check the @a handle to make sure it's a valid @c ACE_HANDLE that
00358   // is within the range of legal handles (i.e., >= 0 && < max_size_).
00359   bool invalid_handle (ACE_HANDLE handle);
00360 
00361   // Check the @a handle to make sure it's a valid @c ACE_HANDLE that
00362   // within the range of currently registered handles (i.e., >= 0 && <
00363   // @c max_handlep1_).
00364   bool handle_in_range (ACE_HANDLE handle);
00365 
00366   // = Accessors.
00367   /// Returns the current table size.
00368   size_type size (void) const;
00369 
00370   /// Maximum ACE_HANDLE value, plus 1.
00371   max_handlep1_type max_handlep1 (void) const;
00372 
00373   /// Dump the state of an object.
00374   void dump (void) const;
00375 
00376   /// Declare the dynamic allocation hooks.
00377   ACE_ALLOC_HOOK_DECLARE;
00378 
00379 private:
00380 
00381   /// Remove the binding of @a handle corresponding to position @a pos
00382   /// in accordance with the @a mask.
00383   int unbind (ACE_HANDLE handle,
00384               map_type::iterator pos,
00385               ACE_Reactor_Mask mask);
00386 
00387   /**
00388    * @return @c iterator corresponding @c ACE_Event_Handler*
00389    *         associated with @c ACE_HANDLE.
00390    */
00391   map_type::iterator find_eh (ACE_HANDLE handle);
00392 
00393 private:
00394   /// Reference to our @c Select_Reactor.
00395   ACE_Select_Reactor_Impl &select_reactor_;
00396 
00397 #ifndef ACE_WIN32
00398   /// The highest currently active handle, plus 1 (ranges between 0 and
00399   /// @c max_size_.
00400   max_handlep1_type max_handlep1_;
00401 #endif  /* !ACE_WIN32 */
00402 
00403   /// Underlying table of event handlers.
00404   map_type event_handlers_;
00405 };
00406 
00407 /**
00408  * @class ACE_Select_Reactor_Handler_Repository_Iterator
00409  *
00410  * @brief Iterate through the ACE_Select_Reactor_Handler_Repository.
00411  */
00412 class ACE_Export ACE_Select_Reactor_Handler_Repository_Iterator
00413 {
00414 public:
00415 
00416   typedef
00417     ACE_Select_Reactor_Handler_Repository::map_type::const_iterator const_base_iterator;
00418 
00419   // = Initialization method.
00420   explicit ACE_Select_Reactor_Handler_Repository_Iterator (
00421     ACE_Select_Reactor_Handler_Repository const * s);
00422 
00423   // = Iteration methods.
00424 
00425   /// Pass back the @a next_item that hasn't been seen in the Set.
00426   /// Returns @c false when all items have been seen, else @c true.
00427   bool next (ACE_Event_Handler* & next_item);
00428 
00429   /// Returns @c true when all items have been seen, else @c false.
00430   bool done (void) const;
00431 
00432   /// Move forward by one element in the set.  Returns @c false when
00433   /// all the items in the set have been seen, else @c true.
00434   bool advance (void);
00435 
00436   /// Dump the state of an object.
00437   void dump (void) const;
00438 
00439   /// Declare the dynamic allocation hooks.
00440   ACE_ALLOC_HOOK_DECLARE;
00441 
00442 private:
00443 
00444   /// Reference to the Handler_Repository we are iterating over.
00445   ACE_Select_Reactor_Handler_Repository const * const rep_;
00446 
00447   /// Pointer to the current iteration level.
00448   const_base_iterator current_;
00449 };
00450 
00451 /**
00452  * @class ACE_Select_Reactor_Impl
00453  *
00454  * @brief This class simply defines how Select_Reactor's basic interface
00455  * functions should look like and provides a common base class for
00456  * @c Select_Reactor using various locking mechanism.
00457  */
00458 class ACE_Export ACE_Select_Reactor_Impl : public ACE_Reactor_Impl
00459 {
00460 public:
00461   enum
00462   {
00463     /// Default size of the Select_Reactor's handle table.
00464     DEFAULT_SIZE = ACE_DEFAULT_SELECT_REACTOR_SIZE
00465   };
00466 
00467   /// Constructor.
00468   ACE_Select_Reactor_Impl (bool mask_signals = true);
00469 
00470   friend class ACE_Select_Reactor_Notify;
00471   friend class ACE_Select_Reactor_Handler_Repository;
00472 
00473   /**
00474    * Purge any notifications pending in this reactor for the specified
00475    * ACE_Event_Handler object. Returns the number of notifications
00476    * purged. Returns -1 on error.
00477    */
00478   virtual int purge_pending_notifications (ACE_Event_Handler * = 0,
00479                                            ACE_Reactor_Mask    = ACE_Event_Handler::ALL_EVENTS_MASK);
00480 
00481   /// Does the reactor allow the application to resume the handle on
00482   /// its own ie. can it pass on the control of handle resumption to
00483   /// the application.  The select reactor has no handlers that can be
00484   /// resumed by the  application. So return 0;
00485   virtual int resumable_handler (void);
00486 
00487   /*
00488    * Hook to add concrete methods required to specialize the
00489    * implementation with concrete methods required for the concrete
00490    * reactor implementation, for example, select, tp reactors.
00491    */
00492   //@@ REACTOR_SPL_PUBLIC_METHODS_ADD_HOOK
00493 
00494 protected:
00495   /// Allow manipulation of the <wait_set_> mask and <ready_set_> mask.
00496   virtual int bit_ops (ACE_HANDLE handle,
00497                        ACE_Reactor_Mask mask,
00498                        ACE_Select_Reactor_Handle_Set &handle_set,
00499                        int ops);
00500 
00501   /// Enqueue ourselves into the list of waiting threads at the
00502   /// appropriate point specified by <requeue_position_>.
00503   virtual void renew (void) = 0;
00504 
00505   /// Check to see if the <Event_Handler> associated with @a handle is
00506   /// suspended. Returns 0 if not, 1 if so.
00507   virtual int is_suspended_i (ACE_HANDLE handle) = 0;
00508 
00509   /// When register/unregister occur, then we need to re-eval our
00510   /// wait/suspend/dispatch set.
00511   virtual void clear_dispatch_mask (ACE_HANDLE handle,
00512                                     ACE_Reactor_Mask mask);
00513 
00514   /// Table that maps <ACE_HANDLEs> to <ACE_Event_Handler *>'s.
00515   ACE_Select_Reactor_Handler_Repository handler_rep_;
00516 
00517   /// Tracks handles that are ready for dispatch from <select>
00518   ACE_Select_Reactor_Handle_Set dispatch_set_;
00519 
00520   /// Tracks handles that are waited for by <select>.
00521   ACE_Select_Reactor_Handle_Set wait_set_;
00522 
00523   /// Tracks handles that are currently suspended.
00524   ACE_Select_Reactor_Handle_Set suspend_set_;
00525 
00526   /// Track HANDLES we are interested in for various events that must
00527   /// be dispatched *without* going through <select>.
00528   ACE_Select_Reactor_Handle_Set ready_set_;
00529 
00530   /// Defined as a pointer to allow overriding by derived classes...
00531   ACE_Timer_Queue *timer_queue_;
00532 
00533   /// Handle signals without requiring global/static variables.
00534   ACE_Sig_Handler *signal_handler_;
00535 
00536   /// Callback object that unblocks the ACE_Select_Reactor if it's
00537   /// sleeping.
00538   ACE_Reactor_Notify *notify_handler_;
00539 
00540   /// Keeps track of whether we should delete the timer queue (if we
00541   /// didn't create it, then we don't delete it).
00542   bool delete_timer_queue_;
00543 
00544   /// Keeps track of whether we should delete the signal handler (if we
00545   /// didn't create it, then we don't delete it).
00546   bool delete_signal_handler_;
00547 
00548   /// Keeps track of whether we need to delete the notify handler (if
00549   /// we didn't create it, then we don't delete it).
00550   bool delete_notify_handler_;
00551 
00552   /// True if we've been initialized yet...
00553   bool initialized_;
00554 
00555   /// Restart the <handle_events> event-loop method automatically when
00556   /// <select> is interrupted via <EINTR>.
00557   int restart_;
00558 
00559   /**
00560    * Position that the main ACE_Select_Reactor thread is requeued in
00561    * the list of waiters during a <notify> callback.  If this value ==
00562    * -1 we are requeued at the end of the list.  Else if it's 0 then
00563    * we are requeued at the front of the list.  Else if it's > 1 then
00564    * that indicates the number of waiters to skip over.
00565    */
00566   int requeue_position_;
00567 
00568   /// The original thread that created this Select_Reactor.
00569   ACE_thread_t owner_;
00570 
00571   /**
00572    * True if state has changed during dispatching of
00573    * <ACE_Event_Handlers>, else false.  This is used to determine
00574    * whether we need to make another trip through the
00575    * <Select_Reactor>'s <wait_for_multiple_events> loop.
00576    */
00577   bool state_changed_;
00578 
00579   /**
00580    * If 0 then the Reactor will not mask the signals during the event
00581    * dispatching.  This is useful for applications that do not
00582    * register any signal handlers and want to reduce the overhead
00583    * introduce by the kernel level locks required to change the mask.
00584    */
00585   bool mask_signals_;
00586 
00587   /// Controls/access whether the notify handler should renew the
00588   /// Select_Reactor's token or not.
00589   int supress_notify_renew (void);
00590   void supress_notify_renew (int sr);
00591 
00592 private:
00593 
00594   /// Determine whether we should renew Select_Reactor's token after handling
00595   /// the notification message.
00596   int supress_renew_;
00597 
00598   /// Deny access since member-wise won't work...
00599   ACE_Select_Reactor_Impl (const ACE_Select_Reactor_Impl &);
00600   ACE_Select_Reactor_Impl &operator = (const ACE_Select_Reactor_Impl &);
00601 };
00602 
00603 ACE_END_VERSIONED_NAMESPACE_DECL
00604 
00605 #if defined (__ACE_INLINE__)
00606 #include "ace/Select_Reactor_Base.inl"
00607 #endif /* __ACE_INLINE__ */
00608 
00609 #include /**/ "ace/post.h"
00610 
00611 #endif /* ACE_SELECT_REACTOR_BASE_H */

Generated on Tue Feb 2 17:18:42 2010 for ACE by  doxygen 1.4.7