// $Id$ #ifndef ACE_TIMER_WHEEL_T_C #define ACE_TIMER_WHEEL_T_C #include "ace/Timer_Wheel_T.h" #if !defined (ACE_LACKS_PRAGMA_ONCE) # pragma once #endif /* ACE_LACKS_PRAGMA_ONCE */ #include "ace/High_Res_Timer.h" #include "ace/Log_Msg.h" ACE_RCSID(ace, Timer_Wheel_T, "$Id$") /** * Just initializes the iterator with a ACE_Timer_Wheel_T and then calls * first() to initialize the rest of itself. * * @param wheel A reference for a timer queue to iterate over */ template ACE_Timer_Wheel_Iterator_T::ACE_Timer_Wheel_Iterator_T ( ACE_Timer_Wheel_T &wheel ) : timer_wheel_ (wheel) { this->first(); } /** * Destructor, at this level does nothing. */ template ACE_Timer_Wheel_Iterator_T::~ACE_Timer_Wheel_Iterator_T (void) { } /** * Positions the iterator at the first position in the timing wheel * that contains something. pos_ will be set to the position of this entry * and list_item_ will point to the first entry in that position. Since * this is an iterator, * * If the wheel is empty, pos_ will be equal timer_wheel_.wheel_size_ and * list_item_ would be 0. */ template void ACE_Timer_Wheel_Iterator_T::first (void) { for (this->pos_ = 0; this->pos_ < this->timer_wheel_.wheel_size_; this->pos_++) { // Skip over empty entries if (this->timer_wheel_.wheel_[this->pos_]->get_next () != this->timer_wheel_.wheel_[this->pos_]) { this->list_item_ = this->timer_wheel_.wheel_[this->pos_]->get_next (); return; } } // The queue is empty if we are here this->list_item_ = 0; } /** * Positions the iterator at the next node in list or goes to the next * list */ template void ACE_Timer_Wheel_Iterator_T::next (void) { if (this->isdone ()) return; this->list_item_ = this->list_item_->get_next (); // If there is no more in the current list, go to the next if (this->list_item_ == this->timer_wheel_.wheel_[this->pos_]) { for (this->pos_++; this->pos_ < this->timer_wheel_.wheel_size_; this->pos_++) { // Check for an empty entry if (this->timer_wheel_.wheel_[this->pos_]->get_next () != this->timer_wheel_.wheel_[this->pos_]) { this->list_item_ = this->timer_wheel_.wheel_[this->pos_]->get_next (); return; } } this->list_item_ = 0; } } /** * @return True when we there isn't anymore items (when list_item_ == 0) */ template int ACE_Timer_Wheel_Iterator_T::isdone (void) const { return this->list_item_ == 0; } /** * @return The node at the current position in the sequence or 0 if the wheel * is empty */ template ACE_Timer_Node_T * ACE_Timer_Wheel_Iterator_T::item (void) { if (this->isdone ()) return 0; return this->list_item_; } /** * Constructor that sets up the timing wheel and also may preallocate * some nodes on the free list * * @param wheelsize The number of lists in the timer wheel * @param resolution The time resolution used by the hashing function * @param prealloc The number of entries to prealloc in the free_list * @param upcall_functor A pointer to a functor to use instead of the default * @param freelist A pointer to a freelist to use instead of the default */ template ACE_Timer_Wheel_T::ACE_Timer_Wheel_T ( size_t wheelsize, size_t resolution, size_t prealloc, FUNCTOR *upcall_functor, ACE_Free_List > *freelist ) : ACE_Timer_Queue_T (upcall_functor, freelist), wheel_size_ (wheelsize), resolution_ (resolution), earliest_pos_ (0) { ACE_TRACE ("ACE_Timer_Wheel_T::ACE_Timer_Wheel_T"); size_t i; this->gettimeofday (ACE_OS::gettimeofday); // Create the timing wheel ACE_NEW (this->wheel_, ACE_Timer_Node_T *[wheelsize]); // Create the dummy nodes for (i = 0; i < wheelsize; i++) { ACE_Timer_Node_T *tempnode = this->alloc_node (); tempnode->set_next (tempnode); tempnode->set_prev (tempnode); this->wheel_[i] = tempnode; } // Do the preallocation this->free_list_->resize (prealloc); ACE_NEW (iterator_, WHEEL_ITERATOR (*this)); } /** * Default Constructor that sets defaults for wheel_size_ and resolution_ * and doesn't do any preallocation. * * @param upcall_functor A pointer to a functor to use instead of the default * @param freelist A pointer to a freelist to use instead of the default */ template ACE_Timer_Wheel_T::ACE_Timer_Wheel_T ( FUNCTOR *upcall_functor, ACE_Free_List > *freelist ) : ACE_Timer_Queue_T (upcall_functor, freelist), wheel_size_ (ACE_DEFAULT_TIMER_WHEEL_SIZE), resolution_ (ACE_DEFAULT_TIMER_WHEEL_RESOLUTION), earliest_pos_ (0) { ACE_TRACE ("ACE_Timer_Wheel_T::ACE_Timer_Wheel_T"); size_t i; this->gettimeofday (ACE_OS::gettimeofday); // Create the timing wheel ACE_NEW (this->wheel_, ACE_Timer_Node_T *[this->wheel_size_]); // Create the dummy nodes for (i = 0; i < this->wheel_size_; i++) { ACE_Timer_Node_T *tempnode = this->alloc_node (); tempnode->set_next (tempnode); tempnode->set_prev (tempnode); this->wheel_[i] = tempnode; } ACE_NEW (iterator_, WHEEL_ITERATOR (*this)); } // Destructor just cleans up its memory template ACE_Timer_Wheel_T::~ACE_Timer_Wheel_T (void) { ACE_TRACE ("ACE_Timer_Wheel_T::~ACE_Timer_Wheel_T"); delete iterator_; for (size_t i = 0; i < this->wheel_size_; i++) { // delete nodes until only the dummy node is left while (this->wheel_[i]->get_next () != this->wheel_[i]) { ACE_Timer_Node_T *next = this->wheel_[i]->get_next (); this->wheel_[i]->set_next (next->get_next ()); next->get_next ()->set_prev (this->wheel_[i]); this->upcall_functor ().deletion (*this, next->get_type (), next->get_act ()); this->free_node (next); } // and now delete the dummy node delete this->wheel_[i]; } // finally delete the wheel delete [] this->wheel_; } /** * Checks to see if points to a empty list (then it is empty). * * @return True if empty */ template int ACE_Timer_Wheel_T::is_empty (void) const { ACE_TRACE ("ACE_Timer_Wheel_T::is_empty"); return this->wheel_[this->earliest_pos_]->get_next () == this->wheel_[this->earliest_pos_]; } /** * @return First (earliest) node in the wheel_'s earliest_pos_ list */ template const ACE_Time_Value & ACE_Timer_Wheel_T::earliest_time (void) const { ACE_TRACE ("ACE_Timer_Wheel_T::earliest_time"); return this->wheel_[this->earliest_pos_]->get_next ()->get_timer_value (); } /** * Creates a ACE_Timer_Node_T based on the input parameters. Then inserts * the node into the wheel using reschedule (). Then returns a timer_id * (which is actually a pointer to the actual timer_node). * * @param type The data of the timer node * @param act Asynchronous Completion Token (AKA magic cookie) * @param future_time The time the timer is scheduled for (in absolute time) * @param interval If not ACE_Time_Value::zero, then this is a periodic * timer and interval is the time period * * @return Unique identifier (can be used to cancel the timer). * -1 on failure. */ template long ACE_Timer_Wheel_T::schedule ( const TYPE &type, const void *act, const ACE_Time_Value &future_time, const ACE_Time_Value &interval ) { ACE_TRACE ("ACE_Timer_Wheel_T::schedule"); ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, -1)); ACE_Timer_Node_T *tempnode = this->alloc_node (); if (tempnode) { // Note that the timer_id is actually the pointer to the node // Set the details of the node tempnode->set (type, act, future_time, interval, 0, 0, (long) tempnode); // Reschedule will insert it into the correct position this->reschedule (tempnode); return tempnode->get_timer_id (); } // Failure return errno = ENOMEM; return -1; } /** * Find the timer node by using the id as a pointer. Then use set_interval() * on the node to update the interval. * * @param timer_id The timer identifier * @param interval The new interval * * @return 0 if successful, -1 if no. */ template int ACE_Timer_Wheel_T::reset_interval ( long timer_id, const ACE_Time_Value &interval ) { ACE_TRACE ("ACE_Timer_Wheel_T::reset_interval"); ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, -1)); // Make sure we are getting a valid , not an error // returned by . if (timer_id == -1) return -1; ACE_Timer_Node_T *node = ACE_reinterpret_cast (ACE_Timer_Node_T *, timer_id); // Check to see if the node looks like a true // ACE_Timer_Node_T. if (timer_id != node->get_timer_id ()) return -1; node->set_interval (interval); return 0; } /** * Goes through every list in the wheel and whenever we find one with the * correct type value, we remove it and continue. At the end make sure * we reset the earliest time value in case the earliest timers were * removed. * * @param type The value to search for. * @param skip_close If this non-zero, the cancellation method of the * functor will not be called for each cancelled timer. * * @return Number of timers cancelled */ template int ACE_Timer_Wheel_T::cancel (const TYPE &type, int skip_close) { ACE_TRACE ("ACE_Timer_Wheel_T::cancel"); ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, -1)); int number_of_cancellations = 0; size_t i; // Walk through the wheel for (i = 0; i < this->wheel_size_; i++) { // Walk through the list. for (ACE_Timer_Node_T *curr = this->wheel_[i]->get_next (); curr != this->wheel_[i]; ) { if (curr->get_type () == type) { // Cancel it and remove it. number_of_cancellations++; // Detach it from the list ACE_Timer_Node_T *tempnode = curr; curr->get_prev ()->set_next (curr->get_next ()); curr->get_next ()->set_prev (curr->get_prev ()); // Go on to the next and delete the detached node curr = curr->get_next (); this->free_node (tempnode); } else curr = curr->get_next (); } } // Look for a new earliest time // Defaults to zero. ACE_Time_Value earliest_time; // Check every entry in the table for (i = 0; i < this->wheel_size_; i++) { // Skip empty entries if (this->wheel_[i]->get_next () != this->wheel_[i]) { // if initialization or if the time is earlier if (earliest_time == ACE_Time_Value::zero || this->wheel_[i]->get_timer_value () < earliest_time) { earliest_time = this->wheel_[i]->get_next ()->get_timer_value (); this->earliest_pos_ = i; } } } if (skip_close == 0) this->upcall_functor ().cancellation (*this, type); return number_of_cancellations; } /** * Cancels the single timer that is specified by the timer_id. In this * case the timer_id is actually a pointer to the node, so we cast it * to the node. This can be dangerous if the timer_id is made up * (or deleted twice) so we do a little sanity check. Finally we update * the earliest time in case the earliest timer was removed. * * @param timer_id Timer Identifier * @param act Asychronous Completion Token (AKA magic cookie): * If this is non-zero, stores the magic cookie of * the cancelled timer here. * @param skip_close If this non-zero, the cancellation method of the * functor will not be called. * * @return 1 for sucess and 0 if the timer_id wasn't found (or was * found to be invalid) */ template int ACE_Timer_Wheel_T::cancel (long timer_id, const void **act, int skip_close) { ACE_TRACE ("ACE_Timer_Wheel_T::cancel"); ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, -1)); // Make sure we are getting a valid , not an error // returned by . if (timer_id == -1) return 0; ACE_Timer_Node_T *node = ACE_reinterpret_cast (ACE_Timer_Node_T *, timer_id); // Check to see if the node looks like a true ACE_Timer_Node_T. if (timer_id == node->get_timer_id ()) { node->get_next ()->set_prev (node->get_prev ()); node->get_prev ()->set_next (node->get_next ()); if (act != 0) *act = node->get_act (); if (skip_close == 0) this->upcall_functor ().cancellation (*this, node->get_type ()); // Find out what position it is in. size_t pos = (node->get_timer_value ().usec () / this->resolution_) % this->wheel_size_; this->free_node (node); // Get the new earliest time if we have to if (pos == this->earliest_pos_) { ACE_Time_Value earliest_time; // defaults to zero // Check every entry in the table for (size_t i = 0; i < this->wheel_size_; i++) { // Skip empty entries if (this->wheel_[i]->get_next () != this->wheel_[i]) { // if initialization or if the time is earlier if (earliest_time == ACE_Time_Value::zero || this->wheel_[i]->get_timer_value () < earliest_time) { earliest_time = this->wheel_[i]->get_next ()->get_timer_value (); this->earliest_pos_ = i; } } } } return 1; } // Didn't find it if we are here return 0; } /** * Dumps out the size of the wheel, the resolution, and the contents * of the wheel. */ template void ACE_Timer_Wheel_T::dump (void) const { ACE_TRACE ("ACE_Timer_Wheel_T::dump"); ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nwheel_size_ = %d"), this->wheel_size_)); ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nresolution_ = %d"), this->resolution_)); ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("\nwheel_ = \n"))); for (size_t i = 0; i < this->wheel_size_; i++) { ACE_DEBUG ((LM_DEBUG, ACE_LIB_TEXT ("%d\n"), i)); ACE_Timer_Node_T *temp = this->wheel_[i]->get_next (); while (temp != this->wheel_[i]) { temp->dump (); temp = temp->get_next (); } } ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); } /** * Removes the earliest node and then find the new * * @return The earliest timer node. */ template ACE_Timer_Node_T * ACE_Timer_Wheel_T::remove_first (void) { ACE_TRACE ("ACE_Timer_Wheel_T::remove_first"); // Remove the item ACE_Timer_Node_T *temp = this->wheel_[this->earliest_pos_]->get_next (); temp->get_prev ()->set_next (temp->get_next ()); temp->get_next ()->set_prev (temp->get_prev ()); ACE_Time_Value earliest_time; // Check every entry in the table for the new earliest item for (size_t i = 0; i < this->wheel_size_; i++) { // Check for an empty entry if (this->wheel_[i]->get_next () != this->wheel_[i]) { // if initialization or if the time is earlier if (earliest_time == ACE_Time_Value::zero || this->wheel_[i]->get_timer_value () < earliest_time) { earliest_time = this->wheel_[i]->get_next ()->get_timer_value (); this->earliest_pos_ = i; } } } return temp; } /** * Returns the earliest node without removing it * * @return The earliest timer node. */ template ACE_Timer_Node_T * ACE_Timer_Wheel_T::get_first (void) { ACE_TRACE ("ACE_Timer_Wheel_T::get_first"); return this->wheel_[this->earliest_pos_]->get_next (); } /** * Takes an ACE_Timer_Node and inserts it into the correct position in * the correct list. Also makes sure to update the earliest time. * * @param expired The timer node to reschedule */ template void ACE_Timer_Wheel_T::reschedule ( ACE_Timer_Node_T *expired ) { ACE_TRACE ("ACE_Timer_Wheel_T::reschedule"); size_t pos = (expired->get_timer_value ().usec () / this->resolution_) % this->wheel_size_; // See if we need to update the earliest time if (this->is_empty () || expired->get_timer_value () < this->earliest_time ()) this->earliest_pos_ = pos; // Insert time into dummy node. this->wheel_[pos]->set_timer_value (expired->get_timer_value ()); ACE_Timer_Node_T *cursor = this->wheel_[pos]->get_next (); // Find position to insert while (cursor->get_timer_value () < expired->get_timer_value ()) cursor = cursor->get_next (); // Insert expired->set_prev (cursor->get_prev ()); expired->set_next (cursor); cursor->set_prev (expired); expired->get_prev ()->set_next (expired); } /** * @return The iterator */ template ACE_Timer_Queue_Iterator_T & ACE_Timer_Wheel_T::iter (void) { this->iterator_->first (); return *this->iterator_; } /** * Dummy version of expire to get rid of warnings in Sun CC 4.2 * Just call the expire of the base class. */ template int ACE_Timer_Wheel_T::expire () { return ACE_Timer_Queue_T::expire (); } /** * This is a specialized version of expire that is more suited for the * internal data representation. Notice that we are still expiring * timers in order, even though this can be really speeded up if we * didn't worry about this. * * @param cur_time The time to expire timers up to. * * @return Number of timers expired */ template int ACE_Timer_Wheel_T::expire ( const ACE_Time_Value &cur_time ) { ACE_TRACE ("ACE_Timer_Wheel_T::expire"); ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, -1)); int number_of_timers_expired = 0; size_t i; size_t earliest_pos = this->wheel_size_; ACE_Time_Value earliest_time = cur_time; size_t next_earliest_pos = this->wheel_size_; ACE_Time_Value next_earliest_time; // Find the earliest time and location for (i = 0; i < this->wheel_size_; i++) { if (this->wheel_[i]->get_next () != this->wheel_[i] && this->wheel_[i]->get_next ()->get_timer_value () <= earliest_time) { earliest_pos = i; earliest_time = this->wheel_[i]->get_next ()->get_timer_value (); } } // Check to see if the timer queue is empty if (earliest_pos == this->wheel_size_) return 0; do { next_earliest_time = cur_time; next_earliest_pos = this->wheel_size_; // Find the next earliest position and time. for (i = 0; i < this->wheel_size_; i++) { if (i != earliest_pos && this->wheel_[i]->get_next () != this->wheel_[i] && this->wheel_[i]->get_next ()->get_timer_value () <= next_earliest_time) { next_earliest_pos = i; next_earliest_time = this->wheel_[i]->get_next ()->get_timer_value (); } } // Keep expiring timers until we need to move to the next list while (this->wheel_[earliest_pos]->get_next () != this->wheel_[earliest_pos] && this->wheel_[earliest_pos]->get_next ()->get_timer_value () <= next_earliest_time) { // Remove the first node in the earliest position ACE_Timer_Node_T *expired = this->wheel_[earliest_pos]->get_next (); this->wheel_[earliest_pos]->set_next (expired->get_next ()); expired->get_next ()->set_prev (this->wheel_[earliest_pos]); TYPE &type = expired->get_type (); const void *act = expired->get_act (); int reclaim = 1; // Check if this is an interval timer. if (expired->get_interval () > ACE_Time_Value::zero) { // Make sure that we skip past values that have already // "expired". do expired->set_timer_value (expired->get_timer_value () + expired->get_interval ()); while (expired->get_timer_value () <= cur_time); // Since this is an interval timer, we need to // reschedule it. this->reschedule (expired); reclaim = 0; } // Call the functor. this->upcall (type, act, cur_time); if (reclaim) // Free up the node and the token. this->free_node (expired); ++number_of_timers_expired; } earliest_pos = next_earliest_pos; } while (earliest_pos != this->wheel_size_); // Look for a new earliest time earliest_time = ACE_Time_Value::zero; // Check every entry in the table for (i = 0; i < this->wheel_size_; i++) { // Skip empty entries if (this->wheel_[i]->get_next () != this->wheel_[i]) { // if initialization or if the time is earlier if (earliest_time == ACE_Time_Value::zero || this->wheel_[i]->get_timer_value () < earliest_time) { earliest_time = this->wheel_[i]->get_next ()->get_timer_value (); this->earliest_pos_ = i; } } } return number_of_timers_expired; } #endif /* ACE_TIMER_WHEEL_T_C */