00001 // -*- C++ -*- 00002 00003 /** 00004 * @file ESF_Proxy_Collection.h 00005 * 00006 * ESF_Proxy_Collection.h,v 1.10 2006/03/15 07:52:21 jtc Exp 00007 * 00008 * @author Carlos O'Ryan (coryan@cs.wustl.edu) 00009 * 00010 * http://doc.ece.uci.edu/~coryan/EC/index.html 00011 */ 00012 00013 #ifndef TAO_ESF_PROXY_COLLECTION_H 00014 #define TAO_ESF_PROXY_COLLECTION_H 00015 #include /**/ "ace/pre.h" 00016 00017 #include "ace/CORBA_macros.h" 00018 00019 #if !defined (ACE_LACKS_PRAGMA_ONCE) 00020 # pragma once 00021 #endif /* ACE_LACKS_PRAGMA_ONCE */ 00022 00023 TAO_BEGIN_VERSIONED_NAMESPACE_DECL 00024 00025 template<class Target> class TAO_ESF_Worker; 00026 00027 /** 00028 * @class TAO_ESF_Proxy_Collection 00029 * 00030 * @brief ESF_Proxy_Collection 00031 * 00032 * Many components in an Event Service need to keep a collection 00033 * of proxies; these collections must be able to cope with several 00034 * concurrency issues: 00035 * + Some threads may need to iterate over the collection and 00036 * invoke a method on each element. Locking the collection 00037 * while this is done is not feasible in all cases: under some 00038 * configurations the same thread that is iterating over the 00039 * collection may need to make changes to the set. 00040 * + A recursive lock does not solve the concurrency problems 00041 * because recursive changes to the collection still invalidate 00042 * the iterators. 00043 * 00044 * There are several solutions to this problem (see the VARIANTS) 00045 * section, and there is no single one that works bests in all 00046 * cases. As usual, we wish the strategize the protocol used to 00047 * serialize iterations and changes to the collection. This class 00048 * encapsulates that protocol. 00049 * 00050 * The usual form of the Iterator pattern does not work well in 00051 * this case: in several variants the synchronization protocol and 00052 * the iteration loop must collaborate to work efficiently. 00053 * Exposing an external iterator would require that every other 00054 * component in the system can support all the synchronization 00055 * protocols. It is possible to hide some of that complexity 00056 * using heavy weight iterators, but their use is ackward, 00057 * specially since the Koening-style iterators have become more 00058 * popular. 00059 * 00060 * Regular member functions are used to insert, remove and update 00061 * members of the collection and to shutdown (i.e. perform final 00062 * cleanup operations). 00063 * 00064 * The class must also collaborate with other components of the 00065 * EC to efficiently and safely perform memory managment of the 00066 * members in the collection. 00067 * 00068 * The PROXY object must be reference counted with the following 00069 * operations: 00070 * 00071 * + _incr_refcnt() - increment the reference count. 00072 * + _decr_refcnt() - decrement the reference count. 00073 * 00074 * = VARIANTS 00075 * 00076 * We identify several sources of variation: 00077 * 00078 * + Immediate_Changes: in this variant the iteration in performed 00079 * while holding some kind of synchronization primitive, such as a 00080 * thread mutex, a recursive mutex, a RW lock, etc. 00081 * This is only useful in configurations where a separate thread 00082 * dispatches the events, and thus, can only be used with real 00083 * locks. 00084 * + Copy_On_Read: before performing the iteration the collection 00085 * is duplicated into a temporary array. Thus no locks are held 00086 * during the iteration. This is a very expensive approach, but 00087 * useful in many cases. 00088 * The kind of lock is also strategized in this case. 00089 * + Copy_On_Write: this is very similar to the previous approach, 00090 * but the collection is only duplicated when a change is required 00091 * while some thread is performing an iteration. The iteration 00092 * continues over the original copy, while the changes are 00093 * performed in the duplicate. The new copy of the collection is 00094 * used for any subsequent operations, the original is discarded 00095 * when the last thread using it completes its work. 00096 * This approach optimizes for the case where no changes are 00097 * is duplicated into a temporary array. Thus no locks are held 00098 * during the iteration. This is a very expensive approach, but 00099 * useful in many cases. 00100 * The kind of lock is also strategized in this case. 00101 * + Delayed_Changes: before starting the iteration a counter is 00102 * incremented, this counter is used to keep track of the number 00103 * of threads concurrently using the collection. 00104 * If a thread wants to perform a change to the collection it must 00105 * first verify that there are no threads iterating over it. If 00106 * there are any threads then the thread queues the modification 00107 * for later execution, using the Command pattern. 00108 * The kind of lock is strategized, as this approach is used in 00109 * single threaded configurations. 00110 * There are two main variations: 00111 * - An upcall can result in new dispatches: in this case we 00112 * have to keep track of a the list of current threads using 00113 * a Set, to avoid dead-locks. The design is not complete, 00114 * probably similar to the next one. 00115 * - Otherwise we just need to control the concurrency using the 00116 * algorithm described below. 00117 * 00118 * It assumes ownership of the proxies added to the collection, 00119 * it increases the reference count. 00120 * 00121 * Locking is provided by derived classes. 00122 */ 00123 template<class PROXY> 00124 class TAO_ESF_Proxy_Collection 00125 { 00126 public: 00127 /// destructor 00128 virtual ~TAO_ESF_Proxy_Collection (void); 00129 00130 /** 00131 * Iterate over the collection and invoke worker->work() for each 00132 * member of the collection. 00133 * This encapsulates 00134 */ 00135 virtual void for_each (TAO_ESF_Worker<PROXY> *worker 00136 ACE_ENV_ARG_DECL) = 0; 00137 00138 /// Insert a new element into the collection. The collection assumes 00139 /// ownership of the element. 00140 virtual void connected (PROXY *proxy 00141 ACE_ENV_ARG_DECL) = 0; 00142 00143 /** 00144 * Insert an element into the collection. No errors can be raised 00145 * if the element is already present. 00146 * The collection assumes ownership, i.e. must invoke 00147 * <proxy->_decr_refcnt()> if the element is already present in the 00148 * collection. 00149 */ 00150 virtual void reconnected (PROXY *proxy 00151 ACE_ENV_ARG_DECL) = 0; 00152 00153 /// Remove an element from the collection. 00154 virtual void disconnected (PROXY *proxy 00155 ACE_ENV_ARG_DECL) = 0; 00156 00157 /// The EC is shutting down, must release all the elements. 00158 virtual void shutdown (ACE_ENV_SINGLE_ARG_DECL) = 0; 00159 }; 00160 00161 // **************************************************************** 00162 00163 TAO_END_VERSIONED_NAMESPACE_DECL 00164 00165 #if defined (ACE_TEMPLATES_REQUIRE_SOURCE) 00166 #include "orbsvcs/ESF/ESF_Proxy_Collection.cpp" 00167 #endif /* ACE_TEMPLATES_REQUIRE_SOURCE */ 00168 00169 #if defined (ACE_TEMPLATES_REQUIRE_PRAGMA) 00170 #pragma implementation ("ESF_Proxy_Collection.cpp") 00171 #endif /* ACE_TEMPLATES_REQUIRE_PRAGMA */ 00172 00173 #include /**/ "ace/post.h" 00174 #endif /* TAO_ESF_PROXY_COLLECTION_H */