Changes to attempt to silence bcc64x
[ACE_TAO.git] / ACE / ace / Proactor.h
blob556bb63b5884a0bbbe736b574606841395d6499c
1 // -*- C++ -*-
3 //=============================================================================
4 /**
5 * @file Proactor.h
7 * @author Irfan Pyarali <irfan@cs.wustl.edu>
8 * @author Tim Harrison <harrison@cs.wustl.edu>
9 * @author Alexander Babu Arulanthu <alex@cs.wustl.edu>
10 * @author Alexander Libman <alibman@ihug.com.au>
12 //=============================================================================
14 #ifndef ACE_PROACTOR_H
15 #define ACE_PROACTOR_H
17 #include /**/ "ace/pre.h"
19 #include /**/ "ace/config-all.h"
20 #include /**/ "ace/ACE_export.h"
22 #if !defined (ACE_LACKS_PRAGMA_ONCE)
23 #pragma once
24 #endif /* ACE_LACKS_PRAGMA_ONCE */
26 #if defined (ACE_HAS_WIN32_OVERLAPPED_IO) || defined (ACE_HAS_AIO_CALLS)
27 // This only works on Win32 platforms and on Unix platforms supporting
28 // POSIX aio calls.
30 # include "ace/Asynch_IO.h"
31 # include "ace/Asynch_IO_Impl.h"
32 # include "ace/Thread_Manager.h"
33 # include "ace/Timer_Queue.h"
34 # include "ace/Timer_List.h"
35 # include "ace/Timer_Heap.h"
36 # include "ace/Timer_Wheel.h"
38 ACE_BEGIN_VERSIONED_NAMESPACE_DECL
40 // Forward declarations.
41 class ACE_Proactor_Impl;
42 class ACE_Proactor_Timer_Handler;
44 /// Type def for the timer queue.
45 typedef ACE_Abstract_Timer_Queue<ACE_Handler *> ACE_Proactor_Timer_Queue;
47 /**
48 * @class ACE_Proactor_Handle_Timeout_Upcall
50 * @brief Functor for ACE_Timer_Queue.
52 * This class implements the functor required by the Timer
53 * Queue to call <handle_timeout> on ACE_Handlers.
55 class ACE_Export ACE_Proactor_Handle_Timeout_Upcall
57 /// The main Proactor class has special permissions.
58 friend class ACE_Proactor;
60 public:
61 /// Constructor.
62 ACE_Proactor_Handle_Timeout_Upcall ();
64 /// This method is called when a timer is registered.
65 int registration (ACE_Proactor_Timer_Queue &timer_queue,
66 ACE_Handler *handler,
67 const void *arg);
69 /// This method is called before the timer expires.
70 int preinvoke (ACE_Proactor_Timer_Queue &timer_queue,
71 ACE_Handler *handler,
72 const void *arg,
73 int recurring_timer,
74 const ACE_Time_Value &cur_time,
75 const void *&upcall_act);
77 /// This method is called when the timer expires.
78 int timeout (ACE_Proactor_Timer_Queue &timer_queue,
79 ACE_Handler *handler,
80 const void *arg,
81 int recurring_timer,
82 const ACE_Time_Value &cur_time);
84 /// This method is called after the timer expires.
85 int postinvoke (ACE_Proactor_Timer_Queue &timer_queue,
86 ACE_Handler *handler,
87 const void *arg,
88 int recurring_timer,
89 const ACE_Time_Value &cur_time,
90 const void *upcall_act);
92 /// This method is called when a handler is canceled.
93 int cancel_type (ACE_Proactor_Timer_Queue &timer_queue,
94 ACE_Handler *handler,
95 int dont_call_handle_close,
96 int &requires_reference_counting);
98 /// This method is called when a timer is canceled.
99 int cancel_timer (ACE_Proactor_Timer_Queue &timer_queue,
100 ACE_Handler *handler,
101 int dont_call_handle_close,
102 int requires_reference_counting);
104 /// This method is called when the timer queue is destroyed and the
105 /// timer is still contained in it.
106 int deletion (ACE_Proactor_Timer_Queue &timer_queue,
107 ACE_Handler *handler,
108 const void *arg);
110 protected:
111 /// Set the proactor. This will fail, if one is already set!
112 int proactor (ACE_Proactor &proactor);
114 /// Handle to the proactor. This is needed for posting a timer result
115 /// to the Proactor's completion queue.
116 ACE_Proactor *proactor_;
120 * @class ACE_Proactor
122 * @brief A manager for asynchronous event demultiplexing.
124 * See the Proactor pattern description at
125 * http://www.dre.vanderbilt.edu/~schmidt/PDF/proactor.pdf for more
126 * details.
128 class ACE_Export ACE_Proactor
130 // = Here are the private typedefs that the ACE_Proactor uses.
132 typedef ACE_Timer_Queue_Iterator_T<ACE_Handler *>
133 TIMER_QUEUE_ITERATOR;
134 typedef ACE_Timer_List_T<ACE_Handler *,
135 ACE_Proactor_Handle_Timeout_Upcall,
136 ACE_SYNCH_RECURSIVE_MUTEX>
137 TIMER_LIST;
138 typedef ACE_Timer_List_Iterator_T<ACE_Handler *,
139 ACE_Proactor_Handle_Timeout_Upcall,
140 ACE_SYNCH_RECURSIVE_MUTEX>
141 TIMER_LIST_ITERATOR;
142 typedef ACE_Timer_Heap_T<ACE_Handler *,
143 ACE_Proactor_Handle_Timeout_Upcall,
144 ACE_SYNCH_RECURSIVE_MUTEX>
145 TIMER_HEAP;
146 typedef ACE_Timer_Heap_Iterator_T<ACE_Handler *,
147 ACE_Proactor_Handle_Timeout_Upcall,
148 ACE_SYNCH_RECURSIVE_MUTEX>
149 TIMER_HEAP_ITERATOR;
150 typedef ACE_Timer_Wheel_T<ACE_Handler *,
151 ACE_Proactor_Handle_Timeout_Upcall,
152 ACE_SYNCH_RECURSIVE_MUTEX>
153 TIMER_WHEEL;
154 typedef ACE_Timer_Wheel_Iterator_T<ACE_Handler *,
155 ACE_Proactor_Handle_Timeout_Upcall,
156 ACE_SYNCH_RECURSIVE_MUTEX>
157 TIMER_WHEEL_ITERATOR;
159 // = Friendship.
161 /// Timer handler runs a thread and manages the timers, on behalf of
162 /// the Proactor.
163 friend class ACE_Proactor_Timer_Handler;
165 public:
167 * Constructor. If @a implementation is 0, the correct implementation
168 * object will be created. @a delete_implementation flag determines
169 * whether the implementation object should be deleted by the
170 * Proactor or not. If @a tq is 0, a new TIMER_QUEUE is created.
172 ACE_Proactor (ACE_Proactor_Impl *implementation = 0,
173 bool delete_implementation = false,
174 ACE_Proactor_Timer_Queue *tq = 0);
176 /// Destruction.
177 ~ACE_Proactor ();
179 /// Get pointer to a process-wide ACE_Proactor. @a threads should
180 /// be part of another method.
181 static ACE_Proactor *instance (size_t threads = 0);
183 /// Set pointer to a process-wide ACE_Proactor and return existing
184 /// pointer.
185 static ACE_Proactor *instance (ACE_Proactor * proactor,
186 bool delete_proactor = false);
188 /// Delete the dynamically allocated Singleton.
189 static void close_singleton ();
191 /// Cleanup method, used by the ACE_Object_Manager to destroy the
192 /// singleton.
193 static void cleanup (void *instance, void *arg);
195 /// Name of dll in which the singleton instance lives.
196 static const ACE_TCHAR *dll_name ();
198 /// Name of component--ACE_Proactor in this case.
199 static const ACE_TCHAR *name ();
201 // = Proactor event loop management methods.
203 /// Run the event loop until the <ACE_Proactor::handle_events> method
204 /// returns -1 or the <end_event_loop> method is invoked.
205 static int run_event_loop ();
208 * Run the event loop until the <ACE_Proactor::handle_events> method
209 * returns -1, the <end_event_loop> method is invoked, or the
210 * ACE_Time_Value expires, in which case 0 is returned.
212 static int run_event_loop (ACE_Time_Value &tv);
215 * Instruct the <ACE_Proactor::instance> to terminate its event
216 * loop.
217 * This method wakes up all the threads blocked on waiting for
218 * completions and end the event loop.
220 static int end_event_loop ();
223 * Resets the <ACE_Proactor::end_event_loop_> static so that the
224 * <run_event_loop> method can be restarted.
226 static int reset_event_loop ();
229 * The singleton proactor is used by the ACE_Service_Config.
230 * Therefore, we must check for the reconfiguration request and
231 * handle it after handling an event.
233 static int check_reconfiguration (ACE_Proactor *);
235 /// Report if the <ACE_Proactor::instance> event loop is finished.
236 static int event_loop_done ();
238 /// Close the associated @c ACE_Proactor_Impl implementation object.
240 * If @arg delete_implementation was specified to the @c open() method,
241 * the implementation object is also deleted.
243 int close ();
246 * You can add a hook to various run_event methods and the hook will
247 * be called after handling every proactor event. If this function
248 * returns 0, proactor_run_event_loop will check for the return value of
249 * handle_events. If it is -1, the the proactor_run_event_loop will return
250 * (pre-maturely.)
252 typedef int (*PROACTOR_EVENT_HOOK)(ACE_Proactor *);
254 // These methods work with an instance of a proactor.
256 * Run the event loop until the
257 * <ACE_Proactor::handle_events>
258 * method returns -1 or the <end_proactor_event_loop> method is invoked.
260 int proactor_run_event_loop (PROACTOR_EVENT_HOOK = 0);
263 * Run the event loop until the <ACE_Proactor::handle_events>
264 * method returns -1, the
265 * <end_proactor_event_loop> method is invoked,
266 * or the ACE_Time_Value
267 * expires, in which case a 0 is returned.
269 int proactor_run_event_loop (ACE_Time_Value &tv,
270 PROACTOR_EVENT_HOOK = 0);
273 * Instruct the ACE_Proactor to terminate its event loop
274 * and notifies the ACE_Proactor so that it can wake up
275 * and close down gracefully.
277 int proactor_end_event_loop ();
279 /// Report if the ACE_Proactor event loop is finished.
280 int proactor_event_loop_done ();
282 /// Resets the <ACE_Proactor::end_event_loop_> static so that the
283 /// <run_event_loop> method can be restarted.
284 int proactor_reset_event_loop ();
287 /// This method adds the @a handle to the I/O completion port. This
288 /// function is a no-op function for Unix systems and returns 0;
289 int register_handle (ACE_HANDLE handle,
290 const void *completion_key);
292 // = Timer management.
294 * Schedule a @a handler that will expire after <time>. If it
295 * expires then @a act is passed in as the value to the @a handler's
296 * <handle_timeout> callback method. This method returns a
297 * <timer_id>. This <timer_id> can be used to cancel a timer before
298 * it expires. The cancellation ensures that <timer_ids> are unique
299 * up to values of greater than 2 billion timers. As long as timers
300 * don't stay around longer than this there should be no problems
301 * with accidentally deleting the wrong timer. Returns -1 on
302 * failure (which is guaranteed never to be a valid <timer_id>).
304 long schedule_timer (ACE_Handler &handler,
305 const void *act,
306 const ACE_Time_Value &time);
308 long schedule_repeating_timer (ACE_Handler &handler,
309 const void *act,
310 const ACE_Time_Value &interval);
312 /// Same as above except @a interval it is used to reschedule the
313 /// @a handler automatically.
315 /// This combines the above two methods into one. Mostly for backward
316 /// compatibility.
317 long schedule_timer (ACE_Handler &handler,
318 const void *act,
319 const ACE_Time_Value &time,
320 const ACE_Time_Value &interval);
322 /// Cancel all timers associated with this @a handler. Returns number
323 /// of timers cancelled.
324 int cancel_timer (ACE_Handler &handler,
325 int dont_call_handle_close = 1);
328 * Cancel the single <ACE_Handler> that matches the @a timer_id value
329 * (which was returned from the <schedule> method). If @a act is
330 * non-NULL then it will be set to point to the ``magic cookie''
331 * argument passed in when the <Handler> was registered. This makes
332 * it possible to free up the memory and avoid memory leaks.
333 * Returns 1 if cancellation succeeded and 0 if the @a timer_id
334 * wasn't found.
336 int cancel_timer (long timer_id,
337 const void **act = 0,
338 int dont_call_handle_close = 1);
341 * Dispatch a single set of events, waiting up to a specified time limit
342 * if necessary.
343 * @param wait_time the time to wait for an event to occur. This is
344 * a relative time. On successful return, the time is updated to
345 * reflect the amount of time spent waiting for event(s) to occur.
346 * @return Returns 0 if no events occur before the @a wait_time expires.
347 * Returns 1 when a completion is dispatched. On error, returns -1
348 * and sets errno accordingly.
350 int handle_events (ACE_Time_Value &wait_time);
353 * Block indefinitely until at least one event is dispatched.
354 * @return Returns 1 when a completion is dispatched. On error, returns -1
355 * and sets errno accordingly.
357 int handle_events ();
359 /// Add wakeup dispatch threads (reinit).
360 int wake_up_dispatch_threads ();
362 /// Close all dispatch threads.
363 int close_dispatch_threads (int wait);
365 /// Get number of thread used as a parameter to CreatIoCompletionPort.
366 size_t number_of_threads () const;
368 /// Set number of thread used as a parameter to CreatIoCompletionPort.
369 void number_of_threads (size_t threads);
371 /// Get timer queue.
372 ACE_Proactor_Timer_Queue *timer_queue () const;
374 /// Set timer queue.
375 void timer_queue (ACE_Proactor_Timer_Queue *timer_queue);
378 * Get the event handle.
379 * It is a no-op in POSIX platforms and it returns
380 * ACE_INVALID_HANDLE.
382 ACE_HANDLE get_handle () const;
384 /// Get the implementation class.
385 ACE_Proactor_Impl *implementation () const;
387 // = Factory methods for the operations
389 // Note that the user does not have to use or know about these
390 // methods.
392 /// Create the correct implementation class for doing
393 /// Asynch_Read_Stream.
394 ACE_Asynch_Read_Stream_Impl *create_asynch_read_stream ();
396 /// Create the correct implementation class for doing
397 /// Asynch_Write_Stream.
398 ACE_Asynch_Write_Stream_Impl *create_asynch_write_stream ();
400 /// Create the correct implementation class for doing
401 /// Asynch_Read_File.
402 ACE_Asynch_Read_File_Impl *create_asynch_read_file ();
404 /// Create the correct implementation class for doing
405 /// Asynch_Write_File.
406 ACE_Asynch_Write_File_Impl *create_asynch_write_file ();
408 /// Create the correct implementation class for doing Asynch_Accept.
409 ACE_Asynch_Accept_Impl *create_asynch_accept ();
411 /// Create the correct implementation class for doing Asynch_Connect.
412 ACE_Asynch_Connect_Impl *create_asynch_connect ();
414 /// Create the correct implementation class for doing
415 /// Asynch_Transmit_File.
416 ACE_Asynch_Transmit_File_Impl *create_asynch_transmit_file ();
418 /// Create the correct implementation class for doing
419 /// Asynch_Read_Dgram.
420 ACE_Asynch_Read_Dgram_Impl *create_asynch_read_dgram ();
422 /// Create the correct implementation class for doing
423 /// Asynch_Write_Dgram.
424 ACE_Asynch_Write_Dgram_Impl *create_asynch_write_dgram ();
426 // = Factory methods for the results
428 // Note that the user does not have to use or know about these
429 // methods unless they want to "fake" results.
431 /// Create the correct implementation class for
432 /// ACE_Asynch_Read_Stream::Result class.
433 ACE_Asynch_Read_Stream_Result_Impl *
434 create_asynch_read_stream_result (ACE_Handler::Proxy_Ptr &handler_proxy,
435 ACE_HANDLE handle,
436 ACE_Message_Block &message_block,
437 u_long bytes_to_read,
438 const void* act,
439 ACE_HANDLE event = ACE_INVALID_HANDLE,
440 int priority = 0,
441 int signal_number = ACE_SIGRTMIN);
443 /// Create the correct implementation class for
444 /// ACE_Asynch_Write_Stream::Result.
445 ACE_Asynch_Write_Stream_Result_Impl *
446 create_asynch_write_stream_result (ACE_Handler::Proxy_Ptr &handler_proxy,
447 ACE_HANDLE handle,
448 ACE_Message_Block &message_block,
449 u_long bytes_to_write,
450 const void* act,
451 ACE_HANDLE event = ACE_INVALID_HANDLE,
452 int priority = 0,
453 int signal_number = ACE_SIGRTMIN);
455 /// Create the correct implementation class for
456 /// ACE_Asynch_Read_File::Result.
457 ACE_Asynch_Read_File_Result_Impl *
458 create_asynch_read_file_result (ACE_Handler::Proxy_Ptr &handler_proxy,
459 ACE_HANDLE handle,
460 ACE_Message_Block &message_block,
461 u_long bytes_to_read,
462 const void* act,
463 u_long offset,
464 u_long offset_high,
465 ACE_HANDLE event = ACE_INVALID_HANDLE,
466 int priority = 0,
467 int signal_number = ACE_SIGRTMIN);
469 /// Create the correct implementation class for
470 /// ACE_Asynch_Write_File::Result.
471 ACE_Asynch_Write_File_Result_Impl *
472 create_asynch_write_file_result (ACE_Handler::Proxy_Ptr &handler_proxy,
473 ACE_HANDLE handle,
474 ACE_Message_Block &message_block,
475 u_long bytes_to_write,
476 const void* act,
477 u_long offset,
478 u_long offset_high,
479 ACE_HANDLE event = ACE_INVALID_HANDLE,
480 int priority = 0,
481 int signal_number = ACE_SIGRTMIN);
483 /// Create the correct implementation class for
484 /// ACE_Asynch_Read_Dgram::Result.
485 ACE_Asynch_Read_Dgram_Result_Impl *
486 create_asynch_read_dgram_result (ACE_Handler::Proxy_Ptr &handler_proxy,
487 ACE_HANDLE handle,
488 ACE_Message_Block *message_block,
489 size_t bytes_to_read,
490 int flags,
491 int protocol_family,
492 const void* act,
493 ACE_HANDLE event = ACE_INVALID_HANDLE,
494 int priority = 0,
495 int signal_number = ACE_SIGRTMIN);
497 /// Create the correct implementation class for
498 /// ACE_Asynch_Write_Dgram::Result.
499 ACE_Asynch_Write_Dgram_Result_Impl *
500 create_asynch_write_dgram_result (ACE_Handler::Proxy_Ptr &handler_proxy,
501 ACE_HANDLE handle,
502 ACE_Message_Block *message_block,
503 size_t bytes_to_write,
504 int flags,
505 const void* act,
506 ACE_HANDLE event = ACE_INVALID_HANDLE,
507 int priority = 0,
508 int signal_number = ACE_SIGRTMIN);
510 /// Create the correct implementation class for ACE_Asynch_Accept::Result.
511 ACE_Asynch_Accept_Result_Impl *
512 create_asynch_accept_result (ACE_Handler::Proxy_Ptr &handler_proxy,
513 ACE_HANDLE listen_handle,
514 ACE_HANDLE accept_handle,
515 ACE_Message_Block &message_block,
516 u_long bytes_to_read,
517 const void* act,
518 ACE_HANDLE event = ACE_INVALID_HANDLE,
519 int priority = 0,
520 int signal_number = ACE_SIGRTMIN);
522 /// Create the correct implementation class for ACE_Asynch_Connect::Result
523 ACE_Asynch_Connect_Result_Impl *
524 create_asynch_connect_result (ACE_Handler::Proxy_Ptr &handler_proxy,
525 ACE_HANDLE connect_handle,
526 const void* act,
527 ACE_HANDLE event = ACE_INVALID_HANDLE,
528 int priority = 0,
529 int signal_number = ACE_SIGRTMIN);
531 /// Create the correct implementation class for
532 /// ACE_Asynch_Transmit_File::Result.
533 ACE_Asynch_Transmit_File_Result_Impl *
534 create_asynch_transmit_file_result (ACE_Handler::Proxy_Ptr &handler_proxy,
535 ACE_HANDLE socket,
536 ACE_HANDLE file,
537 ACE_Asynch_Transmit_File::Header_And_Trailer *header_and_trailer,
538 u_long bytes_to_write,
539 u_long offset,
540 u_long offset_high,
541 u_long bytes_per_send,
542 u_long flags,
543 const void *act,
544 ACE_HANDLE event = ACE_INVALID_HANDLE,
545 int priority = 0,
546 int signal_number = ACE_SIGRTMIN);
549 * Create a timer result object which can be used with the Timer
550 * mechanism of the Proactor.
551 * If @a signal_number is -1, <POSIX_SIG_Proactor> will create a
552 * Timer object with a meaningful signal number, choosing the
553 * largest signal number from the signal mask of the Proactor.
555 ACE_Asynch_Result_Impl *
556 create_asynch_timer (ACE_Handler::Proxy_Ptr &handler_proxy,
557 const void *act,
558 const ACE_Time_Value &tv,
559 ACE_HANDLE event = ACE_INVALID_HANDLE,
560 int priority = 0,
561 int signal_number = ACE_SIGRTMIN);
563 protected:
565 * Post <how_many> completions to the completion port so that all
566 * threads can wake up. This is used in conjunction with the
567 * <run_event_loop>.
569 static int post_wakeup_completions (int how_many);
572 * Post <how_many> completions to the completion port so that all
573 * threads can wake up. This is used in conjunction with the
574 * <proactor_run_event_loop>.
576 int proactor_post_wakeup_completions (int how_many);
578 /// Set the implementation class.
579 void implementation (ACE_Proactor_Impl *implementation);
581 /// Delegation/implementation class that all methods will be
582 /// forwarded to.
583 ACE_Proactor_Impl *implementation_;
585 /// Flag used to indicate whether we are responsible for cleaning up
586 /// the implementation instance.
587 bool delete_implementation_;
589 /// Pointer to a process-wide ACE_Proactor.
590 static ACE_Proactor *proactor_;
592 /// Must delete the <proactor_> if true.
593 static bool delete_proactor_;
595 /// Handles timeout events.
596 ACE_Proactor_Timer_Handler *timer_handler_;
598 /// This will manage the thread in the Timer_Handler.
599 ACE_Thread_Manager thr_mgr_;
601 /// Timer Queue.
602 ACE_Proactor_Timer_Queue *timer_queue_;
604 /// Flag on whether to delete the timer queue.
605 int delete_timer_queue_;
607 /// Terminate the proactor event loop.
608 sig_atomic_t end_event_loop_;
610 /// Number of threads in the event loop.
611 sig_atomic_t event_loop_thread_count_;
613 /// Mutex to protect work with lists.
614 ACE_SYNCH_MUTEX mutex_;
617 private:
618 /// Deny access since member-wise won't work...
619 ACE_Proactor (const ACE_Proactor &);
620 ACE_Proactor &operator= (const ACE_Proactor &);
623 ACE_END_VERSIONED_NAMESPACE_DECL
625 # if defined (__ACE_INLINE__)
626 # include "ace/Proactor.inl"
627 # endif /* __ACE_INLINE__ */
629 #else /* NOT WIN32 or POSIX with AIO features. */
631 # include "ace/os_include/os_stddef.h"
632 # include "ace/os_include/os_signal.h"
634 ACE_BEGIN_VERSIONED_NAMESPACE_DECL
636 class ACE_Time_Value;
638 class ACE_Export ACE_Proactor
640 public:
641 class Timer_Queue {};
642 ACE_Proactor (size_t /* number_of_threads */ = 0,
643 Timer_Queue * /* tq */ = 0) {}
644 ~ACE_Proactor () {}
645 int handle_events () { return -1; }
646 int handle_events (ACE_Time_Value &) { return -1; }
648 /// Placeholder to enable compilation on non-Win32 platforms
649 static ACE_Proactor *instance (size_t threads = 0);
651 /// Placeholder to enable compilation on non-Win32 platforms
652 static ACE_Proactor *instance (ACE_Proactor *);
654 /// Placeholder to enable compilation on non-Win32 platforms
655 static void close_singleton ();
657 /// Placeholder to enable compilation on non-Win32 platforms
658 static int run_event_loop ();
660 /// Placeholder to enable compilation on non-Win32 platforms
661 static int run_event_loop (ACE_Time_Value &tv);
663 /// Placeholder to enable compilation on non-Win32 platforms
664 static int end_event_loop ();
666 /// Placeholder to enable compilation on non-Win32 platforms
667 static sig_atomic_t event_loop_done ();
670 ACE_END_VERSIONED_NAMESPACE_DECL
672 #endif /* ACE_HAS_WIN32_OVERLAPPED_IO || ACE_HAS_AIO_CALLS */
674 #include /**/ "ace/post.h"
676 #endif /* ACE_PROACTOR_H */