// $Id$ #ifndef ACE_TIMER_HEAP_T_CPP #define ACE_TIMER_HEAP_T_CPP #include "ace/Timer_Heap_T.h" #include "ace/Log_Msg.h" #include "ace/Guard_T.h" #include "ace/OS_NS_errno.h" #include "ace/OS_NS_string.h" #if !defined (ACE_LACKS_PRAGMA_ONCE) # pragma once #endif /* ACE_LACKS_PRAGMA_ONCE */ /* ** The ACE_Timer_Heap::max_size_ and array loops, checks, etc. are all size_t. ** The timer IDs are long, and since they are indices into the heap, we need ** to be sure that the timer heap size can fit in a long. Hence, when size ** is (re)set, limit it to the maximum long value. We use the C++ standard ** limits if available. */ #if !defined(ACE_LACKS_NUMERIC_LIMITS) // some platforms pollute the namespace by defining max() and min() macros #ifdef max #undef max #endif #ifdef min #undef min #endif #include #endif /* ACE_LACKS_NUMERIC_LIMITS */ ACE_BEGIN_VERSIONED_NAMESPACE_DECL // Define some simple macros to clarify the code. #define ACE_HEAP_PARENT(X) (X == 0 ? 0 : (((X) - 1) / 2)) #define ACE_HEAP_LCHILD(X) (((X)+(X))+1) // Constructor that takes in an to iterate over. template ACE_Timer_Heap_Iterator_T::ACE_Timer_Heap_Iterator_T (ACE_Timer_Heap_T &heap) : timer_heap_ (heap) { ACE_TRACE ("ACE_Timer_Heap_Iterator_T::ACE_Timer_Heap_Iterator"); this->first(); } template ACE_Timer_Heap_Iterator_T::~ACE_Timer_Heap_Iterator_T (void) { } // Positions the iterator at the first node in the heap array template void ACE_Timer_Heap_Iterator_T::first (void) { this->position_ = 0; } // Positions the iterator at the next node in the heap array template void ACE_Timer_Heap_Iterator_T::next (void) { if (this->position_ != this->timer_heap_.cur_size_) this->position_++; } // Returns true the is at the end of the heap array template int ACE_Timer_Heap_Iterator_T::isdone (void) const { return this->position_ == this->timer_heap_.cur_size_; } // Returns the node at the current position in the heap or 0 if at the end template ACE_Timer_Node_T * ACE_Timer_Heap_Iterator_T::item (void) { if (this->position_ != this->timer_heap_.cur_size_) return this->timer_heap_.heap_[this->position_]; return 0; } // Constructor // Note that timer_ids_curr_ and timer_ids_min_free_ both start at 0. // Since timer IDs are assigned by first incrementing the timer_ids_curr_ // value, the first ID assigned will be 1 (just as in the previous design). // When it's time to wrap, the next ID given out will be 0. template ACE_Timer_Heap_T::ACE_Timer_Heap_T (size_t size, int preallocate, FUNCTOR *upcall_functor, ACE_Free_List > *freelist) : ACE_Timer_Queue_T (upcall_functor, freelist), max_size_ (size), cur_size_ (0), cur_limbo_ (0), timer_ids_curr_ (0), timer_ids_min_free_ (0), preallocated_nodes_ (0), preallocated_nodes_freelist_ (0) { ACE_TRACE ("ACE_Timer_Heap_T::ACE_Timer_Heap_T"); // Possibly reduce size to fit in a long. #if !defined(ACE_LACKS_NUMERIC_LIMITS) if (size > static_cast (std::numeric_limits::max ())) { size = static_cast (std::numeric_limits::max ()); this->max_size_ = size; } #else if (size > LONG_MAX) { size = LONG_MAX; this->max_size_ = size; } #endif /* ACE_LACKS_NUMERIC_LIMITS */ // Create the heap array. ACE_NEW (this->heap_, ACE_Timer_Node_T *[size]); // Create the parallel ACE_NEW (this->timer_ids_, ssize_t[size]); // Initialize the "freelist," which uses negative values to // distinguish freelist elements from "pointers" into the // array. for (size_t i = 0; i < size; i++) this->timer_ids_[i] = -1; if (preallocate) { ACE_NEW (this->preallocated_nodes_, ACE_Timer_Node_T[size]); // Add allocated array to set of such arrays for deletion on // cleanup. this->preallocated_node_set_.insert (this->preallocated_nodes_); // Form the freelist by linking the next_ pointers together. for (size_t j = 1; j < size; j++) this->preallocated_nodes_[j - 1].set_next (&this->preallocated_nodes_[j]); // NULL-terminate the freelist. this->preallocated_nodes_[size - 1].set_next (0); // Assign the freelist pointer to the front of the list. this->preallocated_nodes_freelist_ = &this->preallocated_nodes_[0]; } ACE_NEW (iterator_, HEAP_ITERATOR (*this)); } // Note that timer_ids_curr_ and timer_ids_min_free_ both start at 0. // Since timer IDs are assigned by first incrementing the timer_ids_curr_ // value, the first ID assigned will be 1 (just as in the previous design). // When it's time to wrap, the next ID given out will be 0. template ACE_Timer_Heap_T::ACE_Timer_Heap_T (FUNCTOR *upcall_functor, ACE_Free_List > *freelist) : ACE_Timer_Queue_T (upcall_functor, freelist), max_size_ (ACE_DEFAULT_TIMERS), cur_size_ (0), cur_limbo_ (0), timer_ids_curr_ (0), timer_ids_min_free_ (0), preallocated_nodes_ (0), preallocated_nodes_freelist_ (0) { ACE_TRACE ("ACE_Timer_Heap_T::ACE_Timer_Heap_T"); // Possibly reduce size to fit in a long. #if !defined(ACE_LACKS_NUMERIC_LIMITS) if (this->max_size_ > static_cast (std::numeric_limits::max ())) this->max_size_ = static_cast (std::numeric_limits::max ()); #else if (this->max_size_ > LONG_MAX) this->max_size_ = LONG_MAX; #endif /* ACE_LACKS_NUMERIC_LIMITS */ // Create the heap array. #if defined (__IBMCPP__) && (__IBMCPP__ >= 400) && defined (_WINDOWS) ACE_NEW (this->heap_, ACE_Timer_Node_T *[ACE_DEFAULT_TIMERS]); #else ACE_NEW (this->heap_, ACE_Timer_Node_T *[this->max_size_]); #endif /* defined (__IBMCPP__) && (__IBMCPP__ >= 400) && defined (_WINDOWS) */ // Create the parallel array. ACE_NEW (this->timer_ids_, ssize_t[this->max_size_]); // Initialize the "freelist," which uses negative values to // distinguish freelist elements from "pointers" into the // array. for (size_t i = 0; i < this->max_size_; i++) this->timer_ids_[i] = -1; ACE_NEW (iterator_, HEAP_ITERATOR (*this)); } template ACE_Timer_Heap_T::~ACE_Timer_Heap_T (void) { ACE_TRACE ("ACE_Timer_Heap_T::~ACE_Timer_Heap_T"); delete iterator_; size_t current_size = this->cur_size_; // Clean up all the nodes still in the queue for (size_t i = 0; i < current_size; i++) { // Grab the event_handler and act, then delete the node before calling // back to the handler. Prevents a handler from trying to cancel_timer() // inside handle_close(), ripping the current timer node out from // under us. TYPE eh = this->heap_[i]->get_type (); const void *act = this->heap_[i]->get_act (); this->free_node (this->heap_[i]); this->upcall_functor ().deletion (*this, eh, act); } delete [] this->heap_; delete [] this->timer_ids_; // clean up any preallocated timer nodes if (preallocated_nodes_ != 0) { ACE_Unbounded_Set_Iterator *> set_iterator (this->preallocated_node_set_); for (ACE_Timer_Node_T **entry = 0; set_iterator.next (entry) !=0; set_iterator.advance ()) delete [] *entry; } } template long ACE_Timer_Heap_T::pop_freelist (void) { ACE_TRACE ("ACE_Timer_Heap_T::pop_freelist"); // Scan for a free timer ID. Note that since this function is called // _after_ the check for a full timer heap, we are guaranteed to find // a free ID, even if we need to wrap around and start reusing freed IDs. // On entry, the curr_ index is at the previous ID given out; start // up where we left off last time. // NOTE - a timer_ids_ slot with -2 is out of the heap, but not freed. // It must be either freed (free_node) or rescheduled (reschedule). ++this->timer_ids_curr_; while (this->timer_ids_curr_ < this->max_size_ && (this->timer_ids_[this->timer_ids_curr_] >= 0 || this->timer_ids_[this->timer_ids_curr_] == -2 )) ++this->timer_ids_curr_; if (this->timer_ids_curr_ == this->max_size_) { ACE_ASSERT (this->timer_ids_min_free_ < this->max_size_); this->timer_ids_curr_ = this->timer_ids_min_free_; // We restarted the free search at min. Since min won't be // free anymore, and curr_ will just keep marching up the list // on each successive need for an ID, reset min_free_ to the // size of the list until an ID is freed that curr_ has already // gone past (see push_freelist). this->timer_ids_min_free_ = this->max_size_; } return static_cast (this->timer_ids_curr_); } template void ACE_Timer_Heap_T::push_freelist (long old_id) { ACE_TRACE ("ACE_Timer_Heap_T::push_freelist"); // Since this ID has already been checked by one of the public // functions, it's safe to cast it here. size_t oldid = size_t (old_id); // The freelist values in the are negative, so set the // freed entry back to 'free'. If this is the new lowest value free // timer ID that curr_ won't see on it's normal march through the list, // remember it. ACE_ASSERT (this->timer_ids_[oldid] >= 0 || this->timer_ids_[oldid] == -2); if (this->timer_ids_[oldid] == -2) --this->cur_limbo_; else --this->cur_size_; this->timer_ids_[oldid] = -1; if (oldid < this->timer_ids_min_free_ && oldid <= this->timer_ids_curr_) this->timer_ids_min_free_ = oldid; return; } template long ACE_Timer_Heap_T::timer_id (void) { ACE_TRACE ("ACE_Timer_Heap_T::timer_id"); // Return the next item off the freelist and use it as the timer id. return this->pop_freelist (); } // Checks if queue is empty. template int ACE_Timer_Heap_T::is_empty (void) const { ACE_TRACE ("ACE_Timer_Heap_T::is_empty"); return this->cur_size_ == 0; } template ACE_Timer_Queue_Iterator_T & ACE_Timer_Heap_T::iter (void) { this->iterator_->first (); return *this->iterator_; } // Returns earliest time in a non-empty queue. template const ACE_Time_Value & ACE_Timer_Heap_T::earliest_time (void) const { ACE_TRACE ("ACE_Timer_Heap_T::earliest_time"); return this->heap_[0]->get_timer_value (); } template void ACE_Timer_Heap_T::dump (void) const { #if defined (ACE_HAS_DUMP) ACE_TRACE ("ACE_Timer_Heap_T::dump"); ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nmax_size_ = %d"), this->max_size_)); ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\ncur_size_ = %d"), this->cur_size_)); ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\ncur_limbo_= %d"), this->cur_limbo_)); ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nids_curr_ = %d"), this->timer_ids_curr_)); ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nmin_free_ = %d"), this->timer_ids_min_free_)); ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nheap_ = \n"))); for (size_t i = 0; i < this->cur_size_; i++) { ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("%d\n"), i)); this->heap_[i]->dump (); } ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\ntimer_ids_ = \n"))); for (size_t j = 0; j < this->max_size_; j++) ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("%d\t%d\n"), j, this->timer_ids_[j])); ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); #endif /* ACE_HAS_DUMP */ } template void ACE_Timer_Heap_T::copy (size_t slot, ACE_Timer_Node_T *moved_node) { // Insert into its new location in the heap. this->heap_[slot] = moved_node; ACE_ASSERT (moved_node->get_timer_id () >= 0 && moved_node->get_timer_id () < (int) this->max_size_); // Update the corresponding slot in the parallel array. this->timer_ids_[moved_node->get_timer_id ()] = slot; } // Remove the slot'th timer node from the heap, but do not reclaim its // timer ID or change the size of this timer heap object. The caller of // this function must call either free_node (to reclaim the timer ID // and the timer node memory, as well as decrement the size of the queue) // or reschedule (to reinsert the node in the heap at a new time). template ACE_Timer_Node_T * ACE_Timer_Heap_T::remove (size_t slot) { ACE_Timer_Node_T *removed_node = this->heap_[slot]; // NOTE - the cur_size_ is being decremented since the queue has one // less active timer in it. However, this ACE_Timer_Node is not being // freed, and there is still a place for it in timer_ids_ (the timer ID // is not being relinquished). The node can still be rescheduled, or // it can be freed via free_node. --this->cur_size_; // Only try to reheapify if we're not deleting the last entry. if (slot < this->cur_size_) { ACE_Timer_Node_T *moved_node = this->heap_[this->cur_size_]; // Move the end node to the location being removed and update // the corresponding slot in the parallel array. this->copy (slot, moved_node); // If the time_value_> is great than or equal its // parent it needs be moved down the heap. size_t parent = ACE_HEAP_PARENT (slot); if (moved_node->get_timer_value () >= this->heap_[parent]->get_timer_value ()) this->reheap_down (moved_node, slot, ACE_HEAP_LCHILD (slot)); else this->reheap_up (moved_node, slot, parent); } this->timer_ids_[removed_node->get_timer_id ()] = -2; ++this->cur_limbo_; return removed_node; } template void ACE_Timer_Heap_T::reheap_down (ACE_Timer_Node_T *moved_node, size_t slot, size_t child) { // Restore the heap property after a deletion. while (child < this->cur_size_) { // Choose the smaller of the two children. if (child + 1 < this->cur_size_ && this->heap_[child + 1]->get_timer_value () < this->heap_[child]->get_timer_value ()) child++; // Perform a if the child has a larger timeout value than // the . if (this->heap_[child]->get_timer_value () < moved_node->get_timer_value ()) { this->copy (slot, this->heap_[child]); slot = child; child = ACE_HEAP_LCHILD (child); } else // We've found our location in the heap. break; } this->copy (slot, moved_node); } template void ACE_Timer_Heap_T::reheap_up (ACE_Timer_Node_T *moved_node, size_t slot, size_t parent) { // Restore the heap property after an insertion. while (slot > 0) { // If the parent node is greater than the we need // to copy it down. if (moved_node->get_timer_value () < this->heap_[parent]->get_timer_value ()) { this->copy (slot, this->heap_[parent]); slot = parent; parent = ACE_HEAP_PARENT (slot); } else break; } // Insert the new node into its proper resting place in the heap and // update the corresponding slot in the parallel array. this->copy (slot, moved_node); } template void ACE_Timer_Heap_T::insert (ACE_Timer_Node_T *new_node) { if (this->cur_size_ + this->cur_limbo_ + 2 >= this->max_size_) this->grow_heap (); this->reheap_up (new_node, this->cur_size_, ACE_HEAP_PARENT (this->cur_size_)); this->cur_size_++; } template void ACE_Timer_Heap_T::grow_heap (void) { // All the containers will double in size from max_size_. size_t new_size = this->max_size_ * 2; #if 0 // Yikes - there's no way to flag a failure of going out of range of // a 'long' - this is a problem that should be addressed at some point. #if !defined(ACE_LACKS_NUMERIC_LIMITS) if (new_size > std::numeric_limits::max ()) new_size = std::numeric_limits::max (); #else if (new_size > LONG_MAX) new_size = LONG_MAX; #endif /* ACE_LACKS_NUMERIC_LIMITS */ if (new_size <= this->max_size_) // We are already at the limit { errno = ENOMEM; return -1; } #endif /* 0 */ // First grow the heap itself. ACE_Timer_Node_T **new_heap = 0; #if defined (__IBMCPP__) && (__IBMCPP__ >= 400) && defined (_WINDOWS) ACE_NEW (new_heap, ACE_Timer_Node_T *[1024]); #else ACE_NEW (new_heap, ACE_Timer_Node_T *[new_size]); #endif /* defined (__IBMCPP__) && (__IBMCPP__ >= 400) && defined (_WINDOWS) */ ACE_OS::memcpy (new_heap, this->heap_, this->max_size_ * sizeof *new_heap); delete [] this->heap_; this->heap_ = new_heap; // Grow the array of timer ids. ssize_t *new_timer_ids = 0; ACE_NEW (new_timer_ids, ssize_t[new_size]); ACE_OS::memcpy (new_timer_ids, this->timer_ids_, this->max_size_ * sizeof (ssize_t)); delete [] timer_ids_; this->timer_ids_ = new_timer_ids; // And add the new elements to the end of the "freelist". for (size_t i = this->max_size_; i < new_size; i++) this->timer_ids_[i] = -(static_cast (i) + 1); // Grow the preallocation array (if using preallocation) if (this->preallocated_nodes_ != 0) { // Create a new array with max_size elements to link in to // existing list. #if defined (__IBMCPP__) && (__IBMCPP__ >= 400) && defined (_WINDOWS) ACE_NEW (this->preallocated_nodes_, ACE_Timer_Node_T[88]); #else ACE_NEW (this->preallocated_nodes_, ACE_Timer_Node_T[this->max_size_]); #endif /* defined (__IBMCPP__) && (__IBMCPP__ >= 400) && defined (_WINDOWS) */ // Add it to the set for later deletion this->preallocated_node_set_.insert (this->preallocated_nodes_); // Link new nodes together (as for original list). for (size_t k = 1; k < this->max_size_; k++) this->preallocated_nodes_[k - 1].set_next (&this->preallocated_nodes_[k]); // NULL-terminate the new list. this->preallocated_nodes_[this->max_size_ - 1].set_next (0); // Link new array to the end of the existling list. if (this->preallocated_nodes_freelist_ == 0) this->preallocated_nodes_freelist_ = &preallocated_nodes_[0]; else { ACE_Timer_Node_T *previous = this->preallocated_nodes_freelist_; for (ACE_Timer_Node_T *current = this->preallocated_nodes_freelist_->get_next (); current != 0; current = current->get_next ()) previous = current; previous->set_next (&this->preallocated_nodes_[0]); } } this->max_size_ = new_size; // Force rescan of list from beginning for a free slot (I think...) // This fixed Bugzilla #2447. this->timer_ids_min_free_ = this->max_size_; } // Reschedule a periodic timer. This function must be called with the // mutex lock held. template void ACE_Timer_Heap_T::reschedule (ACE_Timer_Node_T *expired) { ACE_TRACE ("ACE_Timer_Heap_T::reschedule"); // If we are rescheduling, then the most recent call was to // remove_first (). That called remove () to remove the node from the // heap, but did not free the timer ID. The ACE_Timer_Node still has // its assigned ID - just needs to be inserted at the new proper // place, and the heap restored properly. if (this->timer_ids_[expired->get_timer_id ()] == -2) --this->cur_limbo_; this->insert (expired); } template ACE_Timer_Node_T * ACE_Timer_Heap_T::alloc_node (void) { ACE_Timer_Node_T *temp = 0; // Only allocate a node if we are *not* using the preallocated heap. if (this->preallocated_nodes_ == 0) ACE_NEW_RETURN (temp, ACE_Timer_Node_T, 0); else { // check to see if the heap needs to grow if (this->preallocated_nodes_freelist_ == 0) this->grow_heap (); temp = this->preallocated_nodes_freelist_; // Remove the first element from the freelist. this->preallocated_nodes_freelist_ = this->preallocated_nodes_freelist_->get_next (); } return temp; } template void ACE_Timer_Heap_T::free_node (ACE_Timer_Node_T *node) { // Return this timer id to the freelist. this->push_freelist (node->get_timer_id ()); // Only free up a node if we are *not* using the preallocated heap. if (this->preallocated_nodes_ == 0) delete node; else { node->set_next (this->preallocated_nodes_freelist_); this->preallocated_nodes_freelist_ = node; } } // Insert a new timer that expires at time future_time; if interval is // > 0, the handler will be reinvoked periodically. template long ACE_Timer_Heap_T::schedule_i (const TYPE &type, const void *act, const ACE_Time_Value &future_time, const ACE_Time_Value &interval) { ACE_TRACE ("ACE_Timer_Heap_T::schedule_i"); if ((this->cur_size_ + this->cur_limbo_) < this->max_size_) { // Obtain the next unique sequence number. long timer_id = this->timer_id (); // Obtain the memory to the new node. ACE_Timer_Node_T *temp = 0; ACE_ALLOCATOR_RETURN (temp, this->alloc_node (), -1); temp->set (type, act, future_time, interval, 0, timer_id); this->insert (temp); return timer_id; } else return -1; } // Locate and remove the single timer with a value of from // the timer queue. template int ACE_Timer_Heap_T::cancel (long timer_id, const void **act, int dont_call) { ACE_TRACE ("ACE_Timer_Heap_T::cancel"); ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, -1)); // Locate the ACE_Timer_Node that corresponds to the timer_id. // Check to see if the timer_id is out of range if (timer_id < 0 || (size_t) timer_id > this->max_size_) return 0; ssize_t timer_node_slot = this->timer_ids_[timer_id]; // Check to see if timer_id is still valid. if (timer_node_slot < 0) return 0; if (timer_id != this->heap_[timer_node_slot]->get_timer_id ()) { ACE_ASSERT (timer_id == this->heap_[timer_node_slot]->get_timer_id ()); return 0; } else { ACE_Timer_Node_T *temp = this->remove (timer_node_slot); // Call the close hooks. int cookie = 0; // cancel_type() called once per . this->upcall_functor ().cancel_type (*this, temp->get_type (), dont_call, cookie); // cancel_timer() called once per . this->upcall_functor ().cancel_timer (*this, temp->get_type (), dont_call, cookie); if (act != 0) *act = temp->get_act (); this->free_node (temp); return 1; } } // Locate and update the inteval on the timer_id template int ACE_Timer_Heap_T::reset_interval (long timer_id, const ACE_Time_Value &interval) { ACE_TRACE ("ACE_Timer_Heap_T::reset_interval"); ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, -1)); // Locate the ACE_Timer_Node that corresponds to the timer_id. // Check to see if the timer_id is out of range if (timer_id < 0 || (size_t) timer_id > this->max_size_) return -1; ssize_t timer_node_slot = this->timer_ids_[timer_id]; // Check to see if timer_id is still valid. if (timer_node_slot < 0) return -1; if (timer_id != this->heap_[timer_node_slot]->get_timer_id ()) { ACE_ASSERT (timer_id == this->heap_[timer_node_slot]->get_timer_id ()); return -1; } else { // Reset the timer interval this->heap_[timer_node_slot]->set_interval (interval); return 0; } } // Locate and remove all values of from the timer queue. template int ACE_Timer_Heap_T::cancel (const TYPE &type, int dont_call) { ACE_TRACE ("ACE_Timer_Heap_T::cancel"); ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, -1)); int number_of_cancellations = 0; // Try to locate the ACE_Timer_Node that matches the timer_id. for (size_t i = 0; i < this->cur_size_; ) { if (this->heap_[i]->get_type () == type) { ACE_Timer_Node_T *temp = this->remove (i); number_of_cancellations++; this->free_node (temp); // We reset to zero so that we don't miss checking any nodes // if a reheapify occurs when a node is removed. There // may be a better fix than this, however. i = 0; } else i++; } // Call the close hooks. int cookie = 0; // cancel_type() called once per . this->upcall_functor ().cancel_type (*this, type, dont_call, cookie); for (int j = 0; j < number_of_cancellations; ++j) { // cancel_timer() called once per . this->upcall_functor ().cancel_timer (*this, type, dont_call, cookie); } return number_of_cancellations; } // Returns the earliest node or returns 0 if the heap is empty. template ACE_Timer_Node_T * ACE_Timer_Heap_T::remove_first (void) { ACE_TRACE ("ACE_Timer_Heap_T::remove_first"); if (this->cur_size_ == 0) return 0; return this->remove (0); } template ACE_Timer_Node_T * ACE_Timer_Heap_T::get_first (void) { ACE_TRACE ("ACE_Timer_Heap_T::get_first"); return this->cur_size_ == 0 ? 0 : this->heap_[0]; } ACE_END_VERSIONED_NAMESPACE_DECL #endif /* ACE_TIMER_HEAP_T_CPP */