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