00001 /* 00002 * 00003 * 00004 * Distributed under the OpenDDS License. 00005 * See: http://www.opendds.org/license.html 00006 */ 00007 00008 #ifndef OPENDDS_DCPS_BASICQUEUE_T_H 00009 #define OPENDDS_DCPS_BASICQUEUE_T_H 00010 00011 #include "BasicQueueLinkPool_T.h" 00012 #include "BasicQueueVisitor_T.h" 00013 #include "dds/DCPS/PoolAllocationBase.h" 00014 00015 namespace OpenDDS { 00016 namespace DCPS { 00017 00018 template <typename T> 00019 class BasicQueue : public PoolAllocationBase { 00020 private: 00021 00022 typedef BasicQueueLinkPool<T> PoolType; 00023 typedef BasicQueueLink<T> LinkType; 00024 typedef BasicQueueVisitor<T> VisitorType; 00025 00026 public: 00027 00028 BasicQueue(size_t links_per_pool, size_t initial_pools) 00029 : head_(0), 00030 tail_(0), 00031 size_(0), 00032 pool_(links_per_pool, initial_pools) 00033 {} 00034 00035 ~BasicQueue() { 00036 } 00037 00038 /// Put a pointer to an element (T*) on to the queue. 00039 int put(T* elem) { 00040 // Get a "new" link from the pool. 00041 LinkType* link = pool_.obtain(elem); 00042 00043 if (link == 0) { 00044 // Shouldn't happen unless we run out of memory (or encounter 00045 // an internal error condition of some sort, making the 00046 // pool_ unable to provide us with a valid link). 00047 return -1; 00048 } 00049 00050 // Insert the link on the the end of the queue. 00051 if (this->tail_ == 0) { 00052 // The tail_ is NULL only when the queue is empty. 00053 // Set both the head_ and tail_ to point to the "new" link. 00054 this->head_ = this->tail_ = link; 00055 00056 } else { 00057 // There is at least one element in the queue. 00058 // Set the next() of the current tail_ link to point to 00059 // the "new" link. Then set the tail_ to point to the 00060 // "new" link. 00061 this->tail_->next(link); 00062 this->tail_ = link; 00063 } 00064 00065 // Increment our size_ by one. 00066 ++this->size_; 00067 00068 return 0; 00069 } 00070 00071 /// Peek at the element at the top of the queue. This is just 00072 /// like the get() operation except that the queue remains intact. 00073 T* peek() const { 00074 if (this->head_ == 0) { 00075 // The queue is empty. Return a NULL pointer (0). 00076 return 0; 00077 } 00078 00079 return this->head_->elem(); 00080 } 00081 00082 void replace_head(T* value) { 00083 if (this->head_ != 0) { 00084 this->head_->elem(value); 00085 } 00086 } 00087 00088 /// Extract the top element from the queue. Returns 0 if there 00089 /// are no elements in the queue. 00090 T* get() { 00091 LinkType* link = this->head_; 00092 00093 if (link == 0) { 00094 // The queue is empty. Return a NULL pointer (0). 00095 return 0; 00096 } 00097 00098 // Adjust the head_ (and possibly the tail_ if there is only 00099 // one link currently in the queue). 00100 this->head_ = link->next(); 00101 00102 if (this->head_ == 0) { 00103 // That was the only link in the queue. Now that the head_ 00104 // indicates that the queue is empty, we need to update the 00105 // tail_ accordingly because we know it was pointing to the 00106 // same link as the head_. 00107 this->tail_ = 0; 00108 } 00109 00110 // Save off the pointer to the link's elem(). 00111 T* elem = link->elem(); 00112 00113 // Release the link object back to the pool. 00114 this->pool_.release(link); 00115 00116 // Decrement our size_ by one. 00117 --this->size_; 00118 00119 // And finally return the elem. 00120 return elem; 00121 } 00122 00123 /// Accessor for the current number of elements in the queue. 00124 size_t size() const { 00125 return this->size_; 00126 } 00127 00128 /// Standard way to supply a visitor to the queue - this will 00129 /// invoke visit_element(T* element) on the supplied visitor object 00130 /// once for each element in this BasicQueue<T> object, in order. 00131 /// The visitor can stop visitation early by returning 0 from 00132 /// its visit_element(T* element) method. 00133 void accept_visitor(VisitorType& visitor) const { 00134 LinkType* link = this->head_; 00135 00136 while (link != 0) { 00137 if (visitor.visit_element(link->elem()) == 0) { 00138 return; 00139 } 00140 00141 link = link->next(); 00142 } 00143 } 00144 00145 /// Alternate way to supply a visitor to the queue - this will 00146 /// invoke visit_element(T* element, int& remove) on the supplied 00147 /// visitor object once for each element in this BasicQueue<T> 00148 /// object, in order. 00149 /// 00150 /// The remove argument is a flag that should be set to true (1) 00151 /// in the visitor's visit_element_remove(T* element, int& remove) 00152 /// method if the visitor decides that the element should be removed 00153 /// from the queue. The remove flag is always set to false (0) 00154 /// prior to calling the visitor's visit_element_remove(T* element, 00155 /// int& remove) method. 00156 /// 00157 /// The visitor can stop visitation early by returning 0 from 00158 /// its visit_element_remove(T* element, int& remove) method. 00159 void accept_remove_visitor(VisitorType& visitor) { 00160 LinkType* prev_link = 0; 00161 LinkType* link = this->head_; 00162 00163 while (link != 0) { 00164 // Save off the next_link now, in case we end up removing 00165 // (and releasing) the current link via a call to our 00166 // remove_next() call further on. 00167 LinkType* next_link = link->next(); 00168 00169 T* element = link->elem(); 00170 00171 int remove = 0; 00172 00173 int keep_going = visitor.visit_element_remove(element, remove); 00174 00175 if (remove == 1) { 00176 this->remove_next(prev_link); 00177 00178 // We shouldn't change the prev_link here, because it 00179 // has become the prev_link of the next_link since the 00180 // (middle-man) link was just removed. 00181 00182 } else { 00183 // Set it up for visitation of the next link. 00184 // Adjust the prev_link here because we didn't remove 00185 // the link. 00186 prev_link = link; 00187 } 00188 00189 link = next_link; 00190 00191 if (keep_going == 0) { 00192 // We are done. 00193 return; 00194 } 00195 } 00196 } 00197 00198 /// This kind of visitation may cause the visitor to replace 00199 /// the currently visited element with a new element. 00200 void accept_replace_visitor(VisitorType& visitor) { 00201 LinkType* link = this->head_; 00202 00203 while (link != 0) { 00204 // The subtle difference between this visitation method 00205 // and the normal "accept_visitor" visitation method is 00206 // that we supply a reference to a pointer to an element 00207 // here rather than just a pointer to an element. This 00208 // allows the visitor to "replace" the current element 00209 // with a new element. 00210 if (visitor.visit_element_ref(link->elem_ref()) == 0) { 00211 return; 00212 } 00213 00214 link = link->next(); 00215 } 00216 } 00217 00218 private: 00219 00220 /// Used by our accept_remove_visitor() method when an element needs 00221 /// to be removed from this BasicQueue<T> object. The element that 00222 /// is supposed to be removed is the next() link object of the 00223 /// supplied "prev" link object. It's done this way so that the 00224 /// (singlely) linked-list can be stitched back together after the 00225 /// appropriate link has been removed. 00226 void remove_next(LinkType* prev) { 00227 // We are either removing the head_ or the prev->next(). 00228 // Note that we assume that in this contect, head_ will not 00229 // be NULL (0). And, if prev is not NULL (0), then we assume 00230 // that prev->next() is not NULL (0). 00231 // 00232 // Note that we also assume that the prev link (if not NULL), 00233 // is indeed a member of the queue! 00234 LinkType* link = (prev == 0) ? this->head_ : prev->next(); 00235 00236 // Handle the case where the queue only contains one link 00237 // (we can then assume that this is the link we are removing). 00238 if (this->head_ == this->tail_) { 00239 // We are removing the only link in the queue. 00240 this->head_ = this->tail_ = 0; 00241 00242 } else if (link == this->head_) { 00243 // We are removing the head link, and we know that there 00244 // are other links still in the queue. 00245 this->head_ = this->head_->next(); 00246 00247 } else if (link == this->tail_) { 00248 // We are removing the tail link, and we know that there 00249 // are other links still in the queue. 00250 prev->next(0); 00251 this->tail_ = prev; 00252 00253 } else { 00254 // We are removing a link that is neither the head_ link 00255 // nor the tail_ link. 00256 prev->next(link->next()); 00257 } 00258 00259 // Decrement our size by one 00260 --this->size_; 00261 00262 // And finally, we can release the link back to the pool. 00263 this->pool_.release(link); 00264 } 00265 00266 /// The first (oldest) link in the queue. 00267 LinkType* head_; 00268 00269 /// The last (newest) link in the queue. 00270 LinkType* tail_; 00271 00272 /// The number of links currently in the queue. 00273 size_t size_; 00274 00275 /// The pool of precreated LinkType objects (a "self-growing" 00276 /// allocator is used by the pool). 00277 PoolType pool_; 00278 }; 00279 00280 } // namespace DCPS 00281 } // namespace OpenDDS 00282 00283 #endif /* OPENDDS_DCPS_BASICQUEUE_T_H */