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

Generated on Sun Jan 27 12:05:36 2008 for ACE by doxygen 1.3.6