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