diff options
author | nobody <nobody@ae88bc3d-4319-0410-8dbf-d08b4c9d3795> | 2002-08-28 20:53:07 +0000 |
---|---|---|
committer | nobody <nobody@ae88bc3d-4319-0410-8dbf-d08b4c9d3795> | 2002-08-28 20:53:07 +0000 |
commit | d45ee5cd26f6c4d19bcae17cbad89b8dd01a2a6b (patch) | |
tree | b618363234107ab78d886a06299419bf6c16c02f | |
parent | e4e316d8173a6e49720b06b645c71e20e91b6cc7 (diff) | |
download | ATCD-d45ee5cd26f6c4d19bcae17cbad89b8dd01a2a6b.tar.gz |
This commit was manufactured by cvs2svn to create branch
'notification_problem_stage_1'.
-rw-r--r-- | ace/Select_Reactor_Handler_Repository.cpp | 459 | ||||
-rw-r--r-- | ace/Select_Reactor_Handler_Repository.h | 230 | ||||
-rw-r--r-- | ace/Select_Reactor_Handler_Repository.inl | 41 | ||||
-rw-r--r-- | ace/Select_Reactor_Notify.cpp | 603 | ||||
-rw-r--r-- | ace/Select_Reactor_Notify.h | 200 | ||||
-rw-r--r-- | tests/ChangeLog | 54 | ||||
-rw-r--r-- | tests/Select_Reactor_Notify_Stress_Test.cpp | 352 | ||||
-rw-r--r-- | tests/Select_Reactor_Notify_Stress_Test.dsp | 162 |
8 files changed, 2101 insertions, 0 deletions
diff --git a/ace/Select_Reactor_Handler_Repository.cpp b/ace/Select_Reactor_Handler_Repository.cpp new file mode 100644 index 00000000000..62eee31185e --- /dev/null +++ b/ace/Select_Reactor_Handler_Repository.cpp @@ -0,0 +1,459 @@ +#include "Select_Reactor_Handler_Repository.h" +#include "ACE.h" +#include "Select_Reactor_Base.h" +#include "Reactor.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Select_Reactor_Handler_Repository.inl" +#endif /* __ACE_INLINE__ */ + +ACE_RCSID(ace, + Select_Reactor_Handler_Repository, + "$Id$") + +#if defined (ACE_WIN32) +#define ACE_SELECT_REACTOR_HANDLE(H) (this->event_handlers_[(H)].handle_) +#define ACE_SELECT_REACTOR_EVENT_HANDLER(THIS,H) ((THIS)->event_handlers_[(H)].event_handler_) +#else +#define ACE_SELECT_REACTOR_HANDLE(H) (H) +#define ACE_SELECT_REACTOR_EVENT_HANDLER(THIS,H) ((THIS)->event_handlers_[(H)]) +#endif /* ACE_WIN32 */ + + +ACE_Select_Reactor_Handler_Repository::~ACE_Select_Reactor_Handler_Repository (void) +{ +} + +// Performs sanity checking on the ACE_HANDLE. +int +ACE_Select_Reactor_Handler_Repository::invalid_handle (ACE_HANDLE handle) +{ + ACE_TRACE ("ACE_Select_Reactor_Handler_Repository::invalid_handle"); +#if defined (ACE_WIN32) + // It's too expensive to perform more exhaustive validity checks on + // Win32 due to the way that they implement SOCKET HANDLEs. + if (handle == ACE_INVALID_HANDLE) +#else /* !ACE_WIN32 */ + if (handle < 0 || handle >= this->max_size_) +#endif /* ACE_WIN32 */ + { + errno = EINVAL; + return 1; + } + else + return 0; +} + +// Performs sanity checking on the ACE_HANDLE. + +int +ACE_Select_Reactor_Handler_Repository::handle_in_range (ACE_HANDLE handle) +{ + ACE_TRACE ("ACE_Select_Reactor_Handler_Repository::handle_in_range"); +#if defined (ACE_WIN32) + // It's too expensive to perform more exhaustive validity checks on + // Win32 due to the way that they implement SOCKET HANDLEs. + if (handle != ACE_INVALID_HANDLE) +#else /* !ACE_WIN32 */ + if (handle >= 0 && handle < this->max_handlep1_) +#endif /* ACE_WIN32 */ + return 1; + else + { + errno = EINVAL; + return 0; + } +} + +size_t +ACE_Select_Reactor_Handler_Repository::max_handlep1 (void) +{ + ACE_TRACE ("ACE_Select_Reactor_Handler_Repository::max_handlep1"); + + return this->max_handlep1_; +} + +int +ACE_Select_Reactor_Handler_Repository::open (size_t size) +{ + ACE_TRACE ("ACE_Select_Reactor_Handler_Repository::open"); + this->max_size_ = size; + this->max_handlep1_ = 0; + +#if defined (ACE_WIN32) + // Try to allocate the memory. + ACE_NEW_RETURN (this->event_handlers_, + ACE_Event_Tuple[size], + -1); + + // Initialize the ACE_Event_Handler * to { ACE_INVALID_HANDLE, 0 }. + for (size_t h = 0; h < size; h++) + { + ACE_SELECT_REACTOR_HANDLE (h) = ACE_INVALID_HANDLE; + ACE_SELECT_REACTOR_EVENT_HANDLER (this, h) = 0; + } +#else + // Try to allocate the memory. + ACE_NEW_RETURN (this->event_handlers_, + ACE_Event_Handler *[size], + -1); + + // Initialize the ACE_Event_Handler * to NULL. + for (size_t h = 0; h < size; h++) + ACE_SELECT_REACTOR_EVENT_HANDLER (this, h) = 0; +#endif /* ACE_WIN32 */ + + // Try to increase the number of handles if <size> is greater than + // the current limit. + return ACE::set_handle_limit (size); +} + +// Initialize a repository of the appropriate <size>. + +ACE_Select_Reactor_Handler_Repository::ACE_Select_Reactor_Handler_Repository ( + ACE_Select_Reactor_Impl &select_reactor) + : select_reactor_ (select_reactor), + max_size_ (0), + max_handlep1_ (0), + event_handlers_ (0) +{ + ACE_TRACE ("ACE_Select_Reactor_Handler_Repository::ACE_Select_Reactor_Handler_Repository"); +} + +int +ACE_Select_Reactor_Handler_Repository::unbind_all (void) +{ + // Unbind all of the <handle, ACE_Event_Handler>s. + for (int handle = 0; + handle < this->max_handlep1_; + handle++) + this->unbind (ACE_SELECT_REACTOR_HANDLE (handle), + ACE_Event_Handler::ALL_EVENTS_MASK); + + return 0; +} + +int +ACE_Select_Reactor_Handler_Repository::close (void) +{ + ACE_TRACE ("ACE_Select_Reactor_Handler_Repository::close"); + + if (this->event_handlers_ != 0) + { + this->unbind_all (); + + delete [] this->event_handlers_; + this->event_handlers_ = 0; + } + return 0; +} + +// Return the <ACE_Event_Handler *> associated with the <handle>. + +ACE_Event_Handler * +ACE_Select_Reactor_Handler_Repository::find (ACE_HANDLE handle, + size_t *index_p) +{ + ACE_TRACE ("ACE_Select_Reactor_Handler_Repository::find"); + + ACE_Event_Handler *eh = 0; + ssize_t i; + + // Only bother to search for the <handle> if it's in range. + if (this->handle_in_range (handle)) + { +#if defined (ACE_WIN32) + i = 0; + + for (; i < this->max_handlep1_; i++) + if (ACE_SELECT_REACTOR_HANDLE (i) == handle) + { + eh = ACE_SELECT_REACTOR_EVENT_HANDLER (this, i); + break; + } +#else + i = handle; + + eh = ACE_SELECT_REACTOR_EVENT_HANDLER (this, handle); +#endif /* ACE_WIN32 */ + } + else + // g++ can't figure out that <i> won't be used below if the handle + // is out of range, so keep it happy by defining <i> here . . . + i = 0; + + if (eh != 0) + { + if (index_p != 0) + *index_p = i; + } + else + errno = ENOENT; + + return eh; +} + +// Bind the <ACE_Event_Handler *> to the <ACE_HANDLE>. + +int +ACE_Select_Reactor_Handler_Repository::bind (ACE_HANDLE handle, + ACE_Event_Handler *event_handler, + ACE_Reactor_Mask mask) +{ + ACE_TRACE ("ACE_Select_Reactor_Handler_Repository::bind"); + + if (handle == ACE_INVALID_HANDLE) + handle = event_handler->get_handle (); + + if (this->invalid_handle (handle)) + return -1; + +#if defined (ACE_WIN32) + int assigned_slot = -1; + + for (ssize_t i = 0; i < this->max_handlep1_; i++) + { + // Found it, so let's just reuse this location. + if (ACE_SELECT_REACTOR_HANDLE (i) == handle) + { + assigned_slot = i; + break; + } + // Here's the first free slot, so let's take it. + else if (ACE_SELECT_REACTOR_HANDLE (i) == ACE_INVALID_HANDLE + && assigned_slot == -1) + assigned_slot = i; + } + + if (assigned_slot > -1) + // We found a free spot, let's reuse it. + { + ACE_SELECT_REACTOR_HANDLE (assigned_slot) = handle; + ACE_SELECT_REACTOR_EVENT_HANDLER (this, assigned_slot) = event_handler; + } + else if (this->max_handlep1_ < this->max_size_) + { + // Insert at the end of the active portion. + ACE_SELECT_REACTOR_HANDLE (this->max_handlep1_) = handle; + ACE_SELECT_REACTOR_EVENT_HANDLER (this, this->max_handlep1_) = event_handler; + this->max_handlep1_++; + } + else + { + // No more room at the inn! + errno = ENOMEM; + return -1; + } +#else + ACE_SELECT_REACTOR_EVENT_HANDLER (this, handle) = event_handler; + + if (this->max_handlep1_ < handle + 1) + this->max_handlep1_ = handle + 1; +#endif /* ACE_WIN32 */ + + // Add the <mask> for this <handle> in the Select_Reactor's wait_set. + this->select_reactor_.bit_ops (handle, + mask, + this->select_reactor_.wait_set_, + ACE_Reactor::ADD_MASK); + + // Note the fact that we've changed the state of the <wait_set_>, + // which is used by the dispatching loop to determine whether it can + // keep going or if it needs to reconsult select(). + this->select_reactor_.state_changed_ = 1; + + return 0; +} + +// Remove the binding of <ACE_HANDLE>. + +int +ACE_Select_Reactor_Handler_Repository::unbind (ACE_HANDLE handle, + ACE_Reactor_Mask mask) +{ + ACE_TRACE ("ACE_Select_Reactor_Handler_Repository::unbind"); + + size_t slot; + ACE_Event_Handler *eh = this->find (handle, &slot); + + if (eh == 0) + return -1; + + // Clear out the <mask> bits in the Select_Reactor's wait_set. + this->select_reactor_.bit_ops (handle, + mask, + this->select_reactor_.wait_set_, + ACE_Reactor::CLR_MASK); + + // And suspend_set. + this->select_reactor_.bit_ops (handle, + mask, + this->select_reactor_.suspend_set_, + ACE_Reactor::CLR_MASK); + + // Note the fact that we've changed the state of the <wait_set_>, + // which is used by the dispatching loop to determine whether it can + // keep going or if it needs to reconsult select(). + this->select_reactor_.state_changed_ = 1; + + // Close down the <Event_Handler> unless we've been instructed not + // to. + if (ACE_BIT_ENABLED (mask, ACE_Event_Handler::DONT_CALL) == 0) + eh->handle_close (handle, mask); + + // If there are no longer any outstanding events on this <handle> + // then we can totally shut down the Event_Handler. + if (this->select_reactor_.wait_set_.rd_mask_.is_set (handle) == 0 + && this->select_reactor_.wait_set_.wr_mask_.is_set (handle) == 0 + && this->select_reactor_.wait_set_.ex_mask_.is_set (handle) == 0) +#if defined (ACE_WIN32) + { + ACE_SELECT_REACTOR_HANDLE (slot) = ACE_INVALID_HANDLE; + ACE_SELECT_REACTOR_EVENT_HANDLER (this, slot) = 0; + + if (this->max_handlep1_ == (int) slot + 1) + { + // We've deleted the last entry (i.e., i + 1 == the current + // size of the array), so we need to figure out the last + // valid place in the array that we should consider in + // subsequent searches. + + int i; + + for (i = this->max_handlep1_ - 1; + i >= 0 && ACE_SELECT_REACTOR_HANDLE (i) == ACE_INVALID_HANDLE; + i--) + continue; + + this->max_handlep1_ = i + 1; + } + } +#else + { + ACE_SELECT_REACTOR_EVENT_HANDLER (this, handle) = 0; + + if (this->max_handlep1_ == handle + 1) + { + // We've deleted the last entry, so we need to figure out + // the last valid place in the array that is worth looking + // at. + ACE_HANDLE wait_rd_max = this->select_reactor_.wait_set_.rd_mask_.max_set (); + ACE_HANDLE wait_wr_max = this->select_reactor_.wait_set_.wr_mask_.max_set (); + ACE_HANDLE wait_ex_max = this->select_reactor_.wait_set_.ex_mask_.max_set (); + + ACE_HANDLE suspend_rd_max = this->select_reactor_.suspend_set_.rd_mask_.max_set (); + ACE_HANDLE suspend_wr_max = this->select_reactor_.suspend_set_.wr_mask_.max_set (); + ACE_HANDLE suspend_ex_max = this->select_reactor_.suspend_set_.ex_mask_.max_set (); + + // Compute the maximum of six values. + this->max_handlep1_ = wait_rd_max; + if (this->max_handlep1_ < wait_wr_max) + this->max_handlep1_ = wait_wr_max; + if (this->max_handlep1_ < wait_ex_max) + this->max_handlep1_ = wait_ex_max; + + if (this->max_handlep1_ < suspend_rd_max) + this->max_handlep1_ = suspend_rd_max; + if (this->max_handlep1_ < suspend_wr_max) + this->max_handlep1_ = suspend_wr_max; + if (this->max_handlep1_ < suspend_ex_max) + this->max_handlep1_ = suspend_ex_max; + + this->max_handlep1_++; + } + } +#endif /* ACE_WIN32 */ + + return 0; +} + +/****************************************************************************/ + +ACE_Select_Reactor_Handler_Repository_Iterator::~ACE_Select_Reactor_Handler_Repository_Iterator (void) +{ +} + +ACE_Select_Reactor_Handler_Repository_Iterator::ACE_Select_Reactor_Handler_Repository_Iterator + (const ACE_Select_Reactor_Handler_Repository *s) + : rep_ (s), + current_ (-1) +{ + this->advance (); +} + +// Pass back the <next_item> that hasn't been seen in the Set. +// Returns 0 when all items have been seen, else 1. + +int +ACE_Select_Reactor_Handler_Repository_Iterator::next (ACE_Event_Handler *&next_item) +{ + int result = 1; + + if (this->current_ >= this->rep_->max_handlep1_) + result = 0; + else + next_item = ACE_SELECT_REACTOR_EVENT_HANDLER (this->rep_, + this->current_); + return result; +} + +int +ACE_Select_Reactor_Handler_Repository_Iterator::done (void) const +{ + return this->current_ >= this->rep_->max_handlep1_; +} + +// Move forward by one element in the set. + +int +ACE_Select_Reactor_Handler_Repository_Iterator::advance (void) +{ + if (this->current_ < this->rep_->max_handlep1_) + this->current_++; + + while (this->current_ < this->rep_->max_handlep1_) + if (ACE_SELECT_REACTOR_EVENT_HANDLER (this->rep_, this->current_) != 0) + return 1; + else + this->current_++; + + return this->current_ < this->rep_->max_handlep1_; +} + +// Dump the state of an object. + +void +ACE_Select_Reactor_Handler_Repository_Iterator::dump (void) const +{ + ACE_TRACE ("ACE_Select_Reactor_Handler_Repository_Iterator::dump"); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("rep_ = %u"), this->rep_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("current_ = %d"), this->current_)); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +void +ACE_Select_Reactor_Handler_Repository::dump (void) const +{ + ACE_TRACE ("ACE_Select_Reactor_Handler_Repository::dump"); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, + ACE_LIB_TEXT ("(%t) max_handlep1_ = %d, max_size_ = %d\n"), + this->max_handlep1_, this->max_size_)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("["))); + + ACE_Event_Handler *eh = 0; + + for (ACE_Select_Reactor_Handler_Repository_Iterator iter (this); + iter.next (eh) != 0; + iter.advance ()) + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT (" (eh = %x, eh->handle_ = %d)"), + eh, eh->get_handle ())); + + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT (" ]"))); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +ACE_ALLOC_HOOK_DEFINE(ACE_Select_Reactor_Handler_Repository_Iterator) diff --git a/ace/Select_Reactor_Handler_Repository.h b/ace/Select_Reactor_Handler_Repository.h new file mode 100644 index 00000000000..b6cc5bc7189 --- /dev/null +++ b/ace/Select_Reactor_Handler_Repository.h @@ -0,0 +1,230 @@ +/* -*- C++ -*- */ +//============================================================================= +/** + * @file Select_Reactor_Handler_Repository.h + * + * $Id$ + * + * @author Douglas C. Schmidt <schmidt@cs.wustl.edu> + */ +//============================================================================= + +#ifndef ACE_SELECT_REACTOR_HANDLER_REPOSITORY_H +#define ACE_SELECT_REACTOR_HANDLER_REPOSITORY_H + +#include "ace/pre.h" + +#include "ACE_export.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ +#include "Event_Handler.h" + +class ACE_Select_Reactor_Impl; +class ACE_Event_Handler; + +/** + * @class ACE_Event_Tuple + * + * @brief An ACE_Event_Handler and its associated ACE_HANDLE. + * + * One <ACE_Event_Handler> is registered for one or more + * <ACE_HANDLE>. At various points, this information must be + * stored explicitly. This class provides a lightweight + * mechanism to do so. + */ +class ACE_Export ACE_Event_Tuple +{ +public: + /// Default constructor. + ACE_Event_Tuple (void); + + /// Constructor. + ACE_Event_Tuple (ACE_Event_Handler *eh, + ACE_HANDLE h); + + /// Destructor. + ~ACE_Event_Tuple (void); + + /// Equality operator. + int operator== (const ACE_Event_Tuple &rhs) const; + + /// Inequality operator. + int operator!= (const ACE_Event_Tuple &rhs) const; + + /// Handle. + ACE_HANDLE handle_; + + /// <ACE_Event_Handler> associated with the <ACE_HANDLE>. + ACE_Event_Handler *event_handler_; +}; + + +//=================================================================== +/** + * @class ACE_Select_Reactor_Handler_Repository + * + * @brief Used to map <ACE_HANDLE>s onto the appropriate + * <ACE_Event_Handler> *. + * + * This class is necessary to shield differences between UNIX + * and Win32. In UNIX, <ACE_HANDLE> is an int, whereas in Win32 + * it's a void *. This class hides all these details from the + * bulk of the <ACE_Select_Reactor> code. All of these methods + * are called with the main <Select_Reactor> token lock held. + */ +class ACE_Export ACE_Select_Reactor_Handler_Repository +{ +public: + friend class ACE_Select_Reactor_Handler_Repository_Iterator; + + // = Initialization and termination methods. + /// Default "do-nothing" constructor. + ACE_Select_Reactor_Handler_Repository (ACE_Select_Reactor_Impl &); + + /// Destructor. + ~ACE_Select_Reactor_Handler_Repository (void); + + /// Initialize a repository of the appropriate <size>. + /** + * On Unix platforms, the size parameter should be as large as the + * maximum number of file descriptors allowed for a given process. + * This is necessary since a file descriptor is used to directly + * index the array of event handlers maintained by the Reactor's + * handler repository. Direct indexing is used for efficiency + * reasons. + */ + int open (size_t size); + + /// Close down the repository. + int close (void); + + // = Search structure operations. + + /** + * Return the <ACE_Event_Handler *> associated with <ACE_HANDLE>. + * If <index_p> is non-0, then return the index location of the + * <handle>, if found. + */ + ACE_Event_Handler *find (ACE_HANDLE handle, size_t *index_p = 0); + + /// Bind the <ACE_Event_Handler *> to the <ACE_HANDLE> with the + /// appropriate <ACE_Reactor_Mask> settings. + int bind (ACE_HANDLE, + ACE_Event_Handler *, + ACE_Reactor_Mask); + + /// Remove the binding of <ACE_HANDLE> in accordance with the <mask>. + int unbind (ACE_HANDLE, + ACE_Reactor_Mask mask); + + /// Remove all the <ACE_HANDLE, ACE_Event_Handler> tuples. + int unbind_all (void); + + // = Sanity checking. + + // Check the <handle> to make sure it's a valid ACE_HANDLE that + // within the range of legal handles (i.e., >= 0 && < max_size_). + int invalid_handle (ACE_HANDLE handle); + + // Check the <handle> to make sure it's a valid ACE_HANDLE that + // within the range of currently registered handles (i.e., >= 0 && < + // max_handlep1_). + int handle_in_range (ACE_HANDLE handle); + + // = Accessors. + /// Returns the current table size. + size_t size (void) const; + + /// Maximum ACE_HANDLE value, plus 1. + size_t max_handlep1 (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// Reference to our <Select_Reactor>. + ACE_Select_Reactor_Impl &select_reactor_; + + /// Maximum number of handles. + ssize_t max_size_; + + /// The highest currently active handle, plus 1 (ranges between 0 and + /// <max_size_>. + int max_handlep1_; + +#if defined (ACE_WIN32) + // = The mapping from <HANDLES> to <Event_Handlers>. + + /** + * The NT version implements this via a dynamically allocated + * array of <ACE_Event_Tuple *>. Since NT implements ACE_HANDLE + * as a void * we can't directly index into this array. Therefore, + * we just do a linear search (for now). Next, we'll modify + * things to use hashing or something faster... + */ + ACE_Event_Tuple *event_handlers_; +#else + /** + * The UNIX version implements this via a dynamically allocated + * array of <ACE_Event_Handler *> that is indexed directly using + * the ACE_HANDLE value. + */ + ACE_Event_Handler **event_handlers_; +#endif /* ACE_WIN32 */ +}; + +//================================================================= + +/** + * @class ACE_Select_Reactor_Handler_Repository_Iterator + * + * @brief Iterate through the <ACE_Select_Reactor_Handler_Repository>. + */ +class ACE_Export ACE_Select_Reactor_Handler_Repository_Iterator +{ +public: + // = Initialization method. + ACE_Select_Reactor_Handler_Repository_Iterator (const ACE_Select_Reactor_Handler_Repository *s); + + /// dtor. + ~ACE_Select_Reactor_Handler_Repository_Iterator (void); + + // = Iteration methods. + + /// Pass back the <next_item> that hasn't been seen in the Set. + /// Returns 0 when all items have been seen, else 1. + int next (ACE_Event_Handler *&next_item); + + /// Returns 1 when all items have been seen, else 0. + int done (void) const; + + /// Move forward by one element in the set. Returns 0 when all the + /// items in the set have been seen, else 1. + int advance (void); + + /// Dump the state of an object. + void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +private: + /// Reference to the Handler_Repository we are iterating over. + const ACE_Select_Reactor_Handler_Repository *rep_; + + /// Pointer to the current iteration level. + ssize_t current_; +}; + + +#if defined (__ACE_INLINE__) +#include "Select_Reactor_Handler_Repository.inl" +#endif /* __ACE_INLINE__ */ + +#include "ace/post.h" +#endif /*ACE_SELECT_REACTOR_HANDLER_REPOSITORY_H*/ diff --git a/ace/Select_Reactor_Handler_Repository.inl b/ace/Select_Reactor_Handler_Repository.inl new file mode 100644 index 00000000000..9c9c234ec56 --- /dev/null +++ b/ace/Select_Reactor_Handler_Repository.inl @@ -0,0 +1,41 @@ +/* -*- C++ -*- */ +//$Id$ +ACE_INLINE +ACE_Event_Tuple::~ACE_Event_Tuple (void) +{ +} + +ACE_INLINE +ACE_Event_Tuple::ACE_Event_Tuple (void) +: handle_ (ACE_INVALID_HANDLE), + event_handler_ (0) +{ +} + +ACE_INLINE +ACE_Event_Tuple::ACE_Event_Tuple (ACE_Event_Handler* eh, + ACE_HANDLE h) +: handle_ (h), + event_handler_ (eh) +{ +} + +ACE_INLINE int +ACE_Event_Tuple::operator== (const ACE_Event_Tuple &rhs) const +{ + return this->handle_ == rhs.handle_; +} + +ACE_INLINE int +ACE_Event_Tuple::operator!= (const ACE_Event_Tuple &rhs) const +{ + return !(*this == rhs); +} + +/************************************************************/ + +ACE_INLINE size_t +ACE_Select_Reactor_Handler_Repository::size (void) const +{ + return this->max_size_; +} diff --git a/ace/Select_Reactor_Notify.cpp b/ace/Select_Reactor_Notify.cpp new file mode 100644 index 00000000000..aaf43e8a110 --- /dev/null +++ b/ace/Select_Reactor_Notify.cpp @@ -0,0 +1,603 @@ +#include "Select_Reactor_Notify.h" +#include "ACE.h" +#include "Select_Reactor_Base.h" + +ACE_RCSID(ace, + Select_Reactor_Notify, + "$Id$") + + +ACE_Select_Reactor_Notify::ACE_Select_Reactor_Notify (void) + : max_notify_iterations_ (-1) +{ +} + +ACE_Select_Reactor_Notify::~ACE_Select_Reactor_Notify (void) +{ +} + +void +ACE_Select_Reactor_Notify::max_notify_iterations (int iterations) +{ + // Must always be > 0 or < 0 to optimize the loop exit condition. + if (iterations == 0) + iterations = 1; + + this->max_notify_iterations_ = iterations; +} + +int +ACE_Select_Reactor_Notify::max_notify_iterations (void) +{ + return this->max_notify_iterations_; +} + +int +ACE_Select_Reactor_Notify::purge_pending_notifications (ACE_Event_Handler *eh, + ACE_Reactor_Mask mask ) +{ + ACE_TRACE ("ACE_Select_Reactor_Notify::purge_pending_notifications"); + +#if defined (ACE_HAS_REACTOR_NOTIFICATION_QUEUE) + + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, mon, this->notify_queue_lock_, -1); + + if (this->notify_queue_.is_empty ()) + return 0; + + ACE_Notification_Buffer *temp; + ACE_Unbounded_Queue <ACE_Notification_Buffer *> local_queue; + + size_t queue_size = this->notify_queue_.size (); + int number_purged = 0; + size_t i; + for (i = 0; i < queue_size; ++i) + { + if (-1 == this->notify_queue_.dequeue_head (temp)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("dequeue_head")), + -1); + + // If this is not a Reactor notify (it is for a particular handler), + // and it matches the specified handler (or purging all), + // and applying the mask would totally eliminate the notification, then + // release it and count the number purged. + if ((0 != temp->eh_) && + (0 == eh || eh == temp->eh_) && + ACE_BIT_DISABLED (temp->mask_, ~mask)) // the existing notificationmask + // is left with nothing when + // applying the mask + { + if (-1 == this->free_queue_.enqueue_head (temp)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("enqueue_head")), + -1); + ++number_purged; + } + else + { + // To preserve it, move it to the local_queue. + // But first, if this is not a Reactor notify (it is for a particularhandler), + // and it matches the specified handler (or purging all), then + // apply the mask + if ((0 != temp->eh_) && + (0 == eh || eh == temp->eh_)) + ACE_CLR_BITS(temp->mask_, mask); + if (-1 == local_queue.enqueue_head (temp)) + return -1; + } + } + + if (this->notify_queue_.size ()) + { // should be empty! + ACE_ASSERT (0); + return -1; + } + + // now put it back in the notify queue + queue_size = local_queue.size (); + for (i = 0; i < queue_size; ++i) + { + if (-1 == local_queue.dequeue_head (temp)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("dequeue_head")), + -1); + + if (-1 == this->notify_queue_.enqueue_head (temp)) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("enqueue_head")), + -1); + } + + return number_purged; + +#else /* defined (ACE_HAS_REACTOR_NOTIFICATION_QUEUE) */ + ACE_UNUSED_ARG (eh); + ACE_UNUSED_ARG (mask); + ACE_NOTSUP_RETURN (-1); +#endif /* defined (ACE_HAS_REACTOR_NOTIFICATION_QUEUE) */ +} + +void +ACE_Select_Reactor_Notify::dump (void) const +{ + ACE_TRACE ("ACE_Select_Reactor_Notify::dump"); + + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("select_reactor_ = %x"), this->select_reactor_)); + this->notification_pipe_.dump (); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +} + +int +ACE_Select_Reactor_Notify::open (ACE_Reactor_Impl *r, + ACE_Timer_Queue *, + int disable_notify_pipe) +{ + ACE_TRACE ("ACE_Select_Reactor_Notify::open"); + + if (disable_notify_pipe == 0) + { + this->select_reactor_ = + ACE_dynamic_cast (ACE_Select_Reactor_Impl *, r); + + if (this->select_reactor_ == 0) + { + errno = EINVAL; + return -1; + } + + if (this->notification_pipe_.open () == -1) + return -1; +#if defined (F_SETFD) + ACE_OS::fcntl (this->notification_pipe_.read_handle (), F_SETFD, 1); + ACE_OS::fcntl (this->notification_pipe_.write_handle (), F_SETFD, 1); +#endif /* F_SETFD */ + +#if defined (ACE_HAS_REACTOR_NOTIFICATION_QUEUE) + ACE_Notification_Buffer *temp; + + ACE_NEW_RETURN (temp, + ACE_Notification_Buffer[ACE_REACTOR_NOTIFICATION_ARRAY_SIZE], + -1); + + if (this->alloc_queue_.enqueue_head (temp) == -1) + { + delete [] temp; + return -1; + } + + for (size_t i = 0; i < ACE_REACTOR_NOTIFICATION_ARRAY_SIZE; i++) + if (free_queue_.enqueue_head (temp + i) == -1) + return -1; + +#endif /* ACE_HAS_REACTOR_NOTIFICATION_QUEUE */ + + // There seems to be a Win32 bug with this... Set this into + // non-blocking mode. + if (ACE::set_flags (this->notification_pipe_.read_handle (), + ACE_NONBLOCK) == -1) + return -1; + else + return this->select_reactor_->register_handler + (this->notification_pipe_.read_handle (), + this, + ACE_Event_Handler::READ_MASK); + } + else + { + this->select_reactor_ = 0; + return 0; + } +} + +int +ACE_Select_Reactor_Notify::close (void) +{ + ACE_TRACE ("ACE_Select_Reactor_Notify::close"); + +#if defined (ACE_HAS_REACTOR_NOTIFICATION_QUEUE) + // Free up the dynamically allocated resources. + ACE_Notification_Buffer **b; + + for (ACE_Unbounded_Queue_Iterator<ACE_Notification_Buffer *> alloc_iter (this->alloc_queue_); + alloc_iter.next (b) != 0; + alloc_iter.advance ()) + { + delete [] *b; + *b = 0; + } + + this->alloc_queue_.reset (); + this->notify_queue_.reset (); + this->free_queue_.reset (); +#endif /* ACE_HAS_REACTOR_NOTIFICATION_QUEUE */ + + return this->notification_pipe_.close (); +} + +int +ACE_Select_Reactor_Notify::notify (ACE_Event_Handler *eh, + ACE_Reactor_Mask mask, + ACE_Time_Value *timeout) +{ + ACE_TRACE ("ACE_Select_Reactor_Notify::notify"); + + // Just consider this method a "no-op" if there's no + // <ACE_Select_Reactor> configured. + if (this->select_reactor_ == 0) + return 0; + + ACE_Notification_Buffer buffer (eh, mask); + + int notification_required = 1; + +#if defined (ACE_HAS_REACTOR_NOTIFICATION_QUEUE) + // Artificial scope to limit the duration of the mutex. + { + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, + mon, + this->notify_queue_lock_, -1); + + if (this->notify_message_to_queue (buffer, + notification_required) == -1) + return -1; + } +#endif /* ACE_HAS_REACTOR_NOTIFICATION_QUEUE */ + + ssize_t n = 0; + + // Send a notification message on the pipe if required. + if (notification_required) + { + n = ACE::send (this->notification_pipe_.write_handle (), + (char *) &buffer, + sizeof buffer, + timeout); + } + + if (n == -1) + return -1; + + return 0; +} + + +#if defined (ACE_HAS_REACTOR_NOTIFICATION_QUEUE) +int +ACE_Select_Reactor_Notify::notify_message_to_queue (ACE_Notification_Buffer &buffer, + int ¬ification_required) +{ + // If the notification queue is not empty, set the + // notification_required flag to zero. + if (!this->notify_queue_.is_empty ()) + notification_required = 0; + + ACE_Notification_Buffer *temp = 0; + + // Take a node from the free list. + if (this->free_queue_.dequeue_head (temp) == -1) + { + // Grow the queue of available buffers. + ACE_Notification_Buffer *temp1; + + ACE_NEW_RETURN (temp1, + ACE_Notification_Buffer[ACE_REACTOR_NOTIFICATION_ARRAY_SIZE], + -1); + + if (this->alloc_queue_.enqueue_head (temp1) == -1) + { + delete [] temp1; + return -1; + } + + // Start at 1 and enqueue only + // (ACE_REACTOR_NOTIFICATION_ARRAY_SIZE - 1) elements since + // the first one will be used right now. + for (size_t i = 1; + i < ACE_REACTOR_NOTIFICATION_ARRAY_SIZE; + i++) + this->free_queue_.enqueue_head (temp1 + i); + + temp = temp1; + } + + + ACE_ASSERT (temp != 0); + *temp = buffer; + + // Enqueue the node in the <notify_queue> + if (this->notify_queue_.enqueue_tail (temp) == -1) + return -1; + + return 0; +} + +#endif /*ACE_HAS_REACTOR_NOTIFICATION_QUEUE*/ + +int +ACE_Select_Reactor_Notify::dispatch_notifications (int &number_of_active_handles, + ACE_Handle_Set &rd_mask) +{ + ACE_TRACE ("ACE_Select_Reactor_Notify::dispatch_notifications"); + + ACE_HANDLE read_handle = + this->notification_pipe_.read_handle (); + + if (read_handle != ACE_INVALID_HANDLE + && rd_mask.is_set (read_handle)) + { + number_of_active_handles--; + rd_mask.clr_bit (read_handle); + return this->handle_input (read_handle); + } + else + return 0; +} + + +ACE_HANDLE +ACE_Select_Reactor_Notify::notify_handle (void) +{ + ACE_TRACE ("ACE_Select_Reactor_Notify::notify_handle"); + + return this->notification_pipe_.read_handle (); +} + + +// Special trick to unblock <select> when updates occur in somewhere +// other than the main <ACE_Select_Reactor> thread. All we do is +// write data to a pipe that the <ACE_Select_Reactor> is listening on. +// Thanks to Paul Stephenson for suggesting this approach. +int +ACE_Select_Reactor_Notify::is_dispatchable (ACE_Notification_Buffer &buffer) +{ + // There is tonnes of code that can be abstracted... +#if defined (ACE_HAS_REACTOR_NOTIFICATION_QUEUE) + { + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, mon, this->notify_queue_lock_, -1); + + ACE_Notification_Buffer *temp; + + ACE_UNUSED_ARG (buffer); + + // If the queue is empty just return 0 + if (notify_queue_.is_empty ()) + return 0; + + if (this->notify_queue_.dequeue_head (temp) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("dequeue_head")), + -1); + if (temp->eh_ != 0) + { + // If the queue had a buffer that has an event handler, put + // the element back in the queue and return a 1 + if (this->notify_queue_.enqueue_head (temp) == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("enque_head")), + -1); + } + + return 1; + } + // Else put the element in the free queue + if (free_queue_.enqueue_head (temp) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("enqueue_head")), + -1); + } +#else + // If eh == 0 then another thread is unblocking the + // <ACE_Select_Reactor> to update the <ACE_Select_Reactor>'s + // internal structures. Otherwise, we need to dispatch the + // appropriate handle_* method on the <ACE_Event_Handler> + // pointer we've been passed. + if (buffer.eh_ != 0) + return 1; + +#endif /*ACE_HAS_REACTOR_NOTIFICATION_QUEUE */ + + // has no dispatchable buffer + return 0; +} + +int +ACE_Select_Reactor_Notify::dispatch_notify (ACE_Notification_Buffer &buffer) +{ + int result = 0; +#if defined (ACE_HAS_REACTOR_NOTIFICATION_QUEUE) + // Dispatch all messages that are in the <notify_queue_>. + for (;;) + { + ////////@@@@@@@@@@ Bala Need to work from here... + { + // We acquire the lock in a block to make sure we're not + // holding the lock while delivering callbacks... + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, mon, this->notify_queue_lock_, -1); + + if (this->notify_queue_.is_empty ()) + return 0; + + ACE_Notification_Buffer *temp; + + if (notify_queue_.dequeue_head (temp) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("dequeue_head")), + -1); + buffer = *temp; + if (free_queue_.enqueue_head (temp) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_LIB_TEXT ("%p\n"), + ACE_LIB_TEXT ("enqueue_head")), + -1); + } + + // If eh == 0 then another thread is unblocking the + // <ACE_Select_Reactor> to update the <ACE_Select_Reactor>'s + // internal structures. Otherwise, we need to dispatch the + // appropriate handle_* method on the <ACE_Event_Handler> + // pointer we've been passed. + if (buffer.eh_ != 0) + { + + switch (buffer.mask_) + { + case ACE_Event_Handler::READ_MASK: + case ACE_Event_Handler::ACCEPT_MASK: + result = buffer.eh_->handle_input (ACE_INVALID_HANDLE); + break; + case ACE_Event_Handler::WRITE_MASK: + result = buffer.eh_->handle_output (ACE_INVALID_HANDLE); + break; + case ACE_Event_Handler::EXCEPT_MASK: + result = buffer.eh_->handle_exception (ACE_INVALID_HANDLE); + break; + default: + // Should we bail out if we get an invalid mask? + ACE_ERROR ((LM_ERROR, ACE_LIB_TEXT ("invalid mask = %d\n"), buffer.mask_)); + } + if (result == -1) + buffer.eh_->handle_close (ACE_INVALID_HANDLE, + ACE_Event_Handler::EXCEPT_MASK); + } +#else + // If eh == 0 then another thread is unblocking the + // <ACE_Select_Reactor> to update the <ACE_Select_Reactor>'s + // internal structures. Otherwise, we need to dispatch the + // appropriate handle_* method on the <ACE_Event_Handler> + // pointer we've been passed. + if (buffer.eh_ != 0) + { + switch (buffer.mask_) + { + case ACE_Event_Handler::READ_MASK: + case ACE_Event_Handler::ACCEPT_MASK: + result = buffer.eh_->handle_input (ACE_INVALID_HANDLE); + break; + case ACE_Event_Handler::WRITE_MASK: + result = buffer.eh_->handle_output (ACE_INVALID_HANDLE); + break; + case ACE_Event_Handler::EXCEPT_MASK: + result = buffer.eh_->handle_exception (ACE_INVALID_HANDLE); + break; + case ACE_Event_Handler::QOS_MASK: + result = buffer.eh_->handle_qos (ACE_INVALID_HANDLE); + break; + case ACE_Event_Handler::GROUP_QOS_MASK: + result = buffer.eh_->handle_group_qos (ACE_INVALID_HANDLE); + break; + default: + // Should we bail out if we get an invalid mask? + ACE_ERROR ((LM_ERROR, + ACE_LIB_TEXT ("invalid mask = %d\n"), + buffer.mask_)); + } + if (result == -1) + buffer.eh_->handle_close (ACE_INVALID_HANDLE, + ACE_Event_Handler::EXCEPT_MASK); + } + +#endif /* ACE_HAS_REACTOR_NOTIFICATION_QUEUE */ + + return 1; +} + +int +ACE_Select_Reactor_Notify::read_notify_pipe (ACE_HANDLE handle, + ACE_Notification_Buffer &buffer) +{ + ACE_TRACE ("ACE_Select_Reactor_Notify::read_notify_pipe"); + + ssize_t n = ACE::recv (handle, (char *) &buffer, sizeof buffer); + + if (n > 0) + { + // Check to see if we've got a short read. + if (n != sizeof buffer) + { + ssize_t remainder = sizeof buffer - n; + + // If so, try to recover by reading the remainder. If this + // doesn't work we're in big trouble since the input stream + // won't be aligned correctly. I'm not sure quite what to + // do at this point. It's probably best just to return -1. + if (ACE::recv (handle, + ((char *) &buffer) + n, + remainder) != remainder) + return -1; + } + + + return 1; + } + + // Return -1 if things have gone seriously wrong. + if (n <= 0 && (errno != EWOULDBLOCK && errno != EAGAIN)) + return -1; + + return 0; +} + + +int +ACE_Select_Reactor_Notify::handle_input (ACE_HANDLE handle) +{ + ACE_TRACE ("ACE_Select_Reactor_Notify::handle_input"); + // Precondition: this->select_reactor_.token_.current_owner () == + // ACE_Thread::self (); + + int number_dispatched = 0; + int result = 0; + ACE_Notification_Buffer buffer; + + while ((result = this->read_notify_pipe (handle, buffer)) > 0) + { + // Dispatch the buffer + // NOTE: We count only if we made any dispatches ie. upcalls. + if (this->dispatch_notify (buffer) > 0) + number_dispatched++; + + // Bail out if we've reached the <notify_threshold_>. Note that + // by default <notify_threshold_> is -1, so we'll loop until all + // the notifications in the pipe have been dispatched. + if (number_dispatched == this->max_notify_iterations_) + break; + } + + // Reassign number_dispatched to -1 if things have gone seriously + // wrong. + if (result < 0) + number_dispatched = -1; + + // Enqueue ourselves into the list of waiting threads. When we + // reacquire the token we'll be off and running again with ownership + // of the token. The postcondition of this call is that + // <select_reactor_.token_.current_owner> == <ACE_Thread::self>. + this->select_reactor_->renew (); + return number_dispatched; +} + + +#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) +#if defined (ACE_HAS_REACTOR_NOTIFICATION_QUEUE) +template class ACE_Unbounded_Queue <ACE_Notification_Buffer *>; +template class ACE_Unbounded_Queue_Iterator <ACE_Notification_Buffer *>; +template class ACE_Node <ACE_Notification_Buffer *>; +#endif /* ACE_HAS_REACTOR_NOTIFICATION_QUEUE */ +#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) +#if defined (ACE_HAS_REACTOR_NOTIFICATION_QUEUE) +#pragma instantiate ACE_Unbounded_Queue <ACE_Notification_Buffer *> +#pragma instantiate ACE_Unbounded_Queue_Iterator <ACE_Notification_Buffer *> +#pragma instantiate ACE_Node <ACE_Notification_Buffer *> +#endif /* ACE_HAS_REACTOR_NOTIFICATION_QUEUE */ +#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ diff --git a/ace/Select_Reactor_Notify.h b/ace/Select_Reactor_Notify.h new file mode 100644 index 00000000000..c920395f9a3 --- /dev/null +++ b/ace/Select_Reactor_Notify.h @@ -0,0 +1,200 @@ +/* -*- C++ -*- */ + +//============================================================================= +/** + * @file Select_Reactor_Base.h + * + * $Id$ + * + * @author Douglas C. Schmidt <schmidt@cs.wustl.edu> + */ +//============================================================================= + +#ifndef ACE_SELECT_REACTOR_NOTIFY_H +#define ACE_SELECT_REACTOR_NOTIFY_H +#include "ace/pre.h" +#include "Reactor_Impl.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ +#include "ace/Pipe.h" + +#if defined (ACE_HAS_REACTOR_NOTIFICATION_QUEUE) +#include "Unbounded_Queue.h" +#endif /* ACE_HAS_REACTOR_NOTIFICATION_QUEUE */ + +class ACE_Select_Reactor_Impl; + + +/** + * @class ACE_Select_Reactor_Notify + * + * @brief Unblock the <ACE_Select_Reactor> from its event loop. + * + * This implementation is necessary for cases where the + * <ACE_Select_Reactor> is run in a multi-threaded program. In + * this case, we need to be able to unblock <select> or <poll> + * when updates occur other than in the main + * <ACE_Select_Reactor> thread. To do this, we signal an + * auto-reset event the <ACE_Select_Reactor> is listening on. + * If an <ACE_Event_Handler> and <ACE_Select_Reactor_Mask> is + * passed to <notify>, the appropriate <handle_*> method is + * dispatched in the context of the <ACE_Select_Reactor> thread. + */ +class ACE_Export ACE_Select_Reactor_Notify : public ACE_Reactor_Notify +{ +public: + /// Constructor. + ACE_Select_Reactor_Notify (void); + + /// Destructor. + ~ACE_Select_Reactor_Notify (void); + + // = Initialization and termination methods. + /// Initialize. + virtual int open (ACE_Reactor_Impl *, + ACE_Timer_Queue * = 0, + int disable_notify_pipe = 0); + + /// Destroy. + virtual int close (void); + + /** + * Called by a thread when it wants to unblock the + * <ACE_Select_Reactor>. This wakeups the <ACE_Select_Reactor> if + * currently blocked in <select>/<poll>. Pass over both the + * <Event_Handler> *and* the <mask> to allow the caller to dictate + * which <Event_Handler> method the <ACE_Select_Reactor> will + * invoke. The <ACE_Time_Value> indicates how long to blocking + * trying to notify the <ACE_Select_Reactor>. If <timeout> == 0, + * the caller will block until action is possible, else will wait + * until the relative time specified in *<timeout> elapses). + */ + virtual int notify (ACE_Event_Handler * = 0, + ACE_Reactor_Mask = ACE_Event_Handler::EXCEPT_MASK, + ACE_Time_Value * = 0); + + /// Handles pending threads (if any) that are waiting to unblock the + /// <ACE_Select_Reactor>. + virtual int dispatch_notifications (int &number_of_active_handles, + ACE_Handle_Set &rd_mask); + + /// Returns the ACE_HANDLE of the notify pipe on which the reactor + /// is listening for notifications so that other threads can unblock + /// the Select_Reactor + virtual ACE_HANDLE notify_handle (void); + + /// Handle one of the notify call on the <handle>. This could be + /// because of a thread trying to unblock the <Reactor_Impl> + virtual int dispatch_notify (ACE_Notification_Buffer &buffer); + + /// Read one of the notify call on the <handle> into the + /// <buffer>. This could be because of a thread trying to unblock + /// the <Reactor_Impl> + virtual int read_notify_pipe (ACE_HANDLE handle, + ACE_Notification_Buffer &buffer); + + /// Verify whether the buffer has dispatchable info or not. + virtual int is_dispatchable (ACE_Notification_Buffer &buffer); + + /// Called back by the <ACE_Select_Reactor> when a thread wants to + /// unblock us. + virtual int handle_input (ACE_HANDLE handle); + + /** + * Set the maximum number of times that the + * <ACE_Select_Reactor_Notify::handle_input> method will iterate and + * dispatch the <ACE_Event_Handlers> that are passed in via the + * notify pipe before breaking out of its <recv> loop. By default, + * this is set to -1, which means "iterate until the pipe is empty." + * Setting this to a value like "1 or 2" will increase "fairness" + * (and thus prevent starvation) at the expense of slightly higher + * dispatching overhead. + */ + virtual void max_notify_iterations (int); + + /** + * Get the maximum number of times that the + * <ACE_Select_Reactor_Notify::handle_input> method will iterate and + * dispatch the <ACE_Event_Handlers> that are passed in via the + * notify pipe before breaking out of its <recv> loop. + */ + virtual int max_notify_iterations (void); + + /** + * Purge any notifications pending in this reactor for the specified + * <ACE_Event_Handler> object. If <eh> == 0, all notifications for all + * handlers are removed (but not any notifications posted just to wake up + * the reactor itself). Returns the number of notifications purged. + * Returns -1 on error. + */ + virtual int purge_pending_notifications (ACE_Event_Handler *, + ACE_Reactor_Mask = ACE_Event_Handler::ALL_EVENTS_MASK); + + /// Dump the state of an object. + virtual void dump (void) const; + + /// Declare the dynamic allocation hooks. + ACE_ALLOC_HOOK_DECLARE; + +protected: + +#if defined (ACE_HAS_REACTOR_NOTIFICATION_QUEUE) + + /// Add the <buffer> to the user level notification queue. The + /// <notification_required> returns whether the queue needs a + /// notification to be sent in the pipe or not. + int notify_message_to_queue (ACE_Notification_Buffer &buffer, + int ¬ification_required); +#endif/*ACE_HAS_REACTOR_NOTIFICATION_QUEUE*/ + +protected: + /** + * Keep a back pointer to the <ACE_Select_Reactor>. If this value + * if NULL then the <ACE_Select_Reactor> has been initialized with + * <disable_notify_pipe>. + */ + ACE_Select_Reactor_Impl *select_reactor_; + + /** + * Contains the <ACE_HANDLE> the <ACE_Select_Reactor> is listening + * on, as well as the <ACE_HANDLE> that threads wanting the + * attention of the <ACE_Select_Reactor> will write to. + */ + ACE_Pipe notification_pipe_; + + /** + * Keeps track of the maximum number of times that the + * <ACE_Select_Reactor_Notify::handle_input> method will iterate and + * dispatch the <ACE_Event_Handlers> that are passed in via the + * notify pipe before breaking out of its <recv> loop. By default, + * this is set to -1, which means "iterate until the pipe is empty." + */ + int max_notify_iterations_; + +#if defined (ACE_HAS_REACTOR_NOTIFICATION_QUEUE) + // = This configuration queues up notifications in separate buffers that + // are in user-space, rather than stored in a pipe in the OS + // kernel. The kernel-level notifications are used only to trigger + // the Reactor to check its notification queue. This enables many + // more notifications to be stored than would otherwise be the case. + + /// Keeps track of allocated arrays of type + /// <ACE_Notification_Buffer>. + ACE_Unbounded_Queue <ACE_Notification_Buffer *> alloc_queue_; + + /// Keeps track of all pending notifications. + ACE_Unbounded_Queue <ACE_Notification_Buffer *> notify_queue_; + + /// Keeps track of all free buffers. + ACE_Unbounded_Queue <ACE_Notification_Buffer *> free_queue_; + + /// Synchronization for handling of queues. + ACE_SYNCH_MUTEX notify_queue_lock_; +#endif /* ACE_HAS_REACTOR_NOTIFICATION_QUEUE */ +}; + + +#include "ace/post.h" +#endif /*ACE_SELECT_REACTOR_NOTIFY_H*/ diff --git a/tests/ChangeLog b/tests/ChangeLog new file mode 100644 index 00000000000..a379b41745f --- /dev/null +++ b/tests/ChangeLog @@ -0,0 +1,54 @@ +Wed Aug 28 15:51:59 2002 Balachandran Natarajan <bala@isis-server.vuse.vanderbilt.edu> + + * ace/*: Integrated with the main trunk and moved it to a new + branch. + +Mon Aug 12 08:14:51 2002 Balachandran Natarajan <bala@cs.wustl.edu> + + * ace/Select_Reactor_Notify.cpp: + * ace/Select_Reactor_Notify.h: Added a new method + notify_message_to_queue () which puts the notify message into a + user-level queue. Now the reactor sends only message into the + pipe for all the messages in queue. + +Tue Aug 06 00:39:15 2002 Balachandran Natarajan <bala@cs.wustl.edu> + + * ace/Select_Reactor_Notify.h: + * ace/Select_Reactor_Notify.cpp: Moved the declaration and + definition of ACE_Select_Reactor_Notify from + Select_Reactor_Base.* to the above files. + + * ace/Select_Reactor_Handler_Repository.h: + * ace/Select_Reactor_Handler_Repository.cpp: + * ace/Select_Reactor_Handler_Repository.inl: Moved the declaration + and definition of ACE_Select_Reactor_Handler_Repository from + Select_Reactor_Base.* to the above files. + + * ace/Select_Reactor_Base.h: + * ace/Select_Reactor_Base.cpp: + * ace/Select_Reactor_Base.i: Moved the classes mentioned above to + a new file. The above classes were cluttering the file and + things were getting confusing posing maintenance nightmare. + + * ace/Select_Reactor_T.cpp: Needed an include to compile on + Win32. + + * ace/ace_dll.dsp: + * ace/ace_lib.dsp: Added the new files to the project files. + +Mon Aug 5 22:37:13 2002 Balachandran Natarajan <bala@cs.wustl.edu> + + * tests/Makefile: Added the new test to the Makefile. + +Mon Aug 05 22:41:43 2002 Balachandran Natarajan <bala@cs.wustl.edu> + + * tests/Select_Reactor_Notify_Stress_Test.cpp: A stress test for + the notify () mechanism in the select reactor. The motivation + for this test is [BUGID 1268]. This test needs to work fine if + the bug is fixed. + + * tests/Select_Reactor_Notify_Stress_Test.dsp: Project file for + the above test. + + * tests/tests.dsw: Added the new project file to the workspace. + diff --git a/tests/Select_Reactor_Notify_Stress_Test.cpp b/tests/Select_Reactor_Notify_Stress_Test.cpp new file mode 100644 index 00000000000..6f61e6bd083 --- /dev/null +++ b/tests/Select_Reactor_Notify_Stress_Test.cpp @@ -0,0 +1,352 @@ +// $Id$ + +// ============================================================================ +/** + * + * @file Select_Reactor_Notify_Stress_Test.cpp + * + * @brief A stress test for the ACE_Select_Reactor's notify () + * mechanism. + * + * This test stresses the user level queues maintained by the + * ACE_Select_Reactor's notify () mechanism. This test has been + * designed to make sure that the ACE_Select_Reactor's notify () + * mechanism works with large number of notify messages that cannot be + * easily handled with a pipe. + * + * @@NOTE: In ACE 5.2, ACE wrote a message in the pipe for every + * message in the user level queue which caused weird deadlocks. This + * test is to make sure that scalability problems of that sort dont + * find its way back into the ACE_Select_Reactor + * + * @author Balachandran Natarajan <bala@cs.wustl.edu> + */ +//============================================================================ +#include "test_config.h" +#include "ace/Select_Reactor.h" + +ACE_RCSID(tests, + Select_Reactor_Notify_Stress_Test, + "$Id$") + +#if defined (ACE_HAS_THREADS) && (ACE_HAS_REACTOR_NOTIFICATION_QUEUE) +#include "ace/Log_Msg.h" +#include "ace/Synch.h" +#include "ace/Task.h" +#include "ace/Pipe.h" +#include "ace/Auto_Ptr.h" + +static const int max_notify_iterations = 100000; +static const int nthreads = 6; + +class Notify_Handler: public ACE_Event_Handler +{ +public: + /// Ctor + Notify_Handler (void); + + /// Template method + virtual int handle_input (ACE_HANDLE h = ACE_INVALID_HANDLE); + virtual int handle_close (ACE_HANDLE h, + ACE_Reactor_Mask mask); + + /// Method that returns the number of upcalls received. + int calls_made (void); + +private: + /// Mutex to restore internal sanity + ACE_SYNCH_MUTEX mutex_; + + /// Number of upcalls received + int no_upcalls_; +}; + + +Notify_Handler::Notify_Handler (void) + : mutex_ (), + no_upcalls_ (0) +{ +} + +int +Notify_Handler::handle_input (ACE_HANDLE) +{ + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, + ace_mon, + this->mutex_, + -1); + + ++this->no_upcalls_; + + for (size_t j = 0; j < 100; ++j) + { + // Eat a little CPU + /* takes about 40.2 usecs on a 167 MHz Ultra2 */ + u_long n = 11UL; + ACE::is_prime (n, 2, n / 2); + } + + return 0; +} + +int +Notify_Handler::handle_close (ACE_HANDLE, + ACE_Reactor_Mask m) +{ + // no-op + return 0; +} + +int +Notify_Handler::calls_made (void) +{ + ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, + ace_mon, + this->mutex_, + -1); + // no-op + + return this->no_upcalls_; +} + + +/******************************************************/ +class Notify_Task : public ACE_Task_Base +{ +public: + /// Constructor. + Notify_Task (ACE_Reactor *r, + ACE_Event *event, + Notify_Handler *nh); + + /// Destructor. + ~Notify_Task (void); + + /// Generates events and sends them to the <Reactor>'s <notify> + /// method. + virtual int svc (void); + +private: + /// The reactor to which we need to send notifies + ACE_Reactor *r_; + + /// The ACE_Event in which we wait + ACE_Event *event_; + + /// The notify handler that should handle the upcalls from the + /// reactor. + Notify_Handler *nh_; +}; + +Notify_Task::Notify_Task (ACE_Reactor *r, + ACE_Event *event, + Notify_Handler *nh) + : r_ (r), + event_ (event), + nh_ (nh) +{ +} + +Notify_Task::~Notify_Task (void) +{ +} + +int +Notify_Task::svc (void) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Waking up for first set..\n"))); + + int i = 0; + + // Send notificationa to the reactor + for (i = 0; + i != max_notify_iterations; + i++) + { + int retval = this->r_->notify (this->nh_, + ACE_Event_Handler::READ_MASK); + if (retval == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Error while sending notify () \n")), + -1); + } + + // Wait on the event before proceeding + this->event_->wait (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Waking for second set..\n"))); + + for (i = 0; + i != max_notify_iterations; + i++) + { + this->r_->notify (this->nh_, + ACE_Event_Handler::READ_MASK); + } + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Return..\n"))); + + return 0; +} + +/**************************************************************/ +class Run_Task: public ACE_Task_Base +{ +public: + Run_Task (ACE_Event &ev); + + /// template method.. + int svc (void); + +private: + ACE_Event &ev_; +}; + +Run_Task::Run_Task (ACE_Event &ev) + :ev_ (ev) +{ +} + +int +Run_Task::svc (void) +{ + int iter, retval = 0; + + ACE_Reactor::instance ()->owner (ACE_Thread::self ()); + + ACE_Time_Value tv (40, 0); + + for (iter = 0; + iter != nthreads*max_notify_iterations; + iter++) + { + retval = ACE_Reactor::instance ()->handle_events (tv); + + // ACE_DEBUG ((LM_DEBUG, + // "(%P|%t) going on 1..\n")); + if (retval == -1) + { + // Signal the threads + this->ev_.signal (); + + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Error in handle events in [%d] due to %p\n"), + iter), + -1); + + } + } + + // Signal the threads + this->ev_.signal (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) Handle events again\n"))); + + for (iter = 0; + iter != (nthreads -1 ) * max_notify_iterations; + iter++) + { + retval = ACE_Reactor::instance ()->handle_events (); + + //ACE_DEBUG ((LM_DEBUG, + // "(%P|%t) going on 2..\n")); + if (retval == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Error in handle events \n")), + -1); + } + } + + return 0; +} + +static int +do_work (void) +{ + ACE_Select_Reactor sr; + ACE_Reactor new_reactor (&sr); + ACE_Reactor::instance (&new_reactor); + + // Set the max_notify_iterations to 1 + ACE_Reactor::instance ()->max_notify_iterations (1); + + Notify_Handler *nh = 0; + ACE_NEW_RETURN (nh, + Notify_Handler (), + -1); + + ACE_Manual_Event ev; + + Notify_Task notify_tasks (&new_reactor, + &ev, + nh); + + Run_Task run_task (ev); + + if (notify_tasks.activate (THR_NEW_LWP | THR_JOINABLE, + nthreads) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + "Cannot activate notify_task threads\n"), + -1); + + if (run_task.activate (THR_NEW_LWP | THR_JOINABLE, + 1) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + "Cannot activate run_task thread\n"), + -1); + + notify_tasks.thr_mgr ()->wait (); + + run_task.thr_mgr ()->wait (); + + int purged = + ACE_Reactor::instance ()->purge_pending_notifications (nh); + + if (purged != max_notify_iterations) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) Problems with the test \n")), + -1); + + return 0; +} + + +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Select_Reactor_Notify_Stress_Test")); + + + if (do_work () == -1) + { + ACE_DEBUG ((LM_ERROR, + ACE_TEXT ("(%P|%t) Error while doing work \n"))); + + return -1; + } + + return 0; + +} + +#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) +#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) +#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */ + +#else /*ACE_HAS_THREADS && ACE_HAS_REACTOR_NOTIFICATION_QUEUE*/ + +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) This test cannot be run in this configuration \n"))); + + return 0; +} + +#endif /*ACE_HAS_THREADS && ACE_HAS_REACTOR_NOTIFICATION_QUEUE*/ diff --git a/tests/Select_Reactor_Notify_Stress_Test.dsp b/tests/Select_Reactor_Notify_Stress_Test.dsp new file mode 100644 index 00000000000..519174ee517 --- /dev/null +++ b/tests/Select_Reactor_Notify_Stress_Test.dsp @@ -0,0 +1,162 @@ +# Microsoft Developer Studio Project File - Name="Select_Reactor_Notify_Stress_Test" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=Select_Reactor_Notify_Stress_Test - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "Select_Reactor_Notify_Stress_Test.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "Select_Reactor_Notify_Stress_Test.mak" CFG="Select_Reactor_Notify_Stress_Test - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "Select_Reactor_Notify_Stress_Test - Win32 Static Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE "Select_Reactor_Notify_Stress_Test - Win32 Static Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "Select_Reactor_Notify_Stress_Test - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "Select_Reactor_Notify_Stress_Test - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "Select_Reactor_Notify_Stress_Test - Win32 Static Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Static_Debug"
+# PROP BASE Intermediate_Dir "Static_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Static_Debug"
+# PROP Intermediate_Dir "Static_Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "../" /D "_DEBUG" /D ACE_AS_STATIC_LIBS /D "WIN32" /D "_CONSOLE" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 acesd.lib advapi32.lib user32.lib /nologo /subsystem:console /debug /machine:I386 /libpath:"..\ace"
+
+!ELSEIF "$(CFG)" == "Select_Reactor_Notify_Stress_Test - Win32 Static Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Static_Release"
+# PROP BASE Intermediate_Dir "Static_Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Static_Release"
+# PROP Intermediate_Dir "Static_Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "../" /D "NDEBUG" /D ACE_AS_STATIC_LIBS /D "WIN32" /D "_CONSOLE" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 aces.lib advapi32.lib user32.lib /nologo /subsystem:console /machine:I386 /libpath:"..\ace"
+
+!ELSEIF "$(CFG)" == "Select_Reactor_Notify_Stress_Test - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "../" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 ace.lib /nologo /subsystem:console /machine:I386 /libpath:"..\ace"
+
+!ELSEIF "$(CFG)" == "Select_Reactor_Notify_Stress_Test - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ""
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "../" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 aced.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"..\ace"
+
+!ENDIF
+
+# Begin Target
+
+# Name "Select_Reactor_Notify_Stress_Test - Win32 Static Debug"
+# Name "Select_Reactor_Notify_Stress_Test - Win32 Static Release"
+# Name "Select_Reactor_Notify_Stress_Test - Win32 Release"
+# Name "Select_Reactor_Notify_Stress_Test - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=.\Select_Reactor_Notify_Stress_Test.cpp
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=.\test_config.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
|