diff options
Diffstat (limited to 'ACE/ace/Timer_Queue_T.cpp')
-rw-r--r-- | ACE/ace/Timer_Queue_T.cpp | 447 |
1 files changed, 447 insertions, 0 deletions
diff --git a/ACE/ace/Timer_Queue_T.cpp b/ACE/ace/Timer_Queue_T.cpp new file mode 100644 index 00000000000..0aa4f2904b5 --- /dev/null +++ b/ACE/ace/Timer_Queue_T.cpp @@ -0,0 +1,447 @@ +// $Id$ + +#ifndef ACE_TIMER_QUEUE_T_CPP +#define ACE_TIMER_QUEUE_T_CPP + +#include "ace/config-all.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +/* + * Hook to specialize to add includes + */ +//@@ REACTOR_SPL_INCLUDE_FORWARD_DECL_ADD_HOOK + +#include "ace/Timer_Queue_T.h" +#include "ace/Guard_T.h" +#include "ace/Reverse_Lock_T.h" +#include "ace/Log_Msg.h" +#include "ace/Null_Mutex.h" +#include "ace/OS_NS_sys_time.h" +#include "ace/Functor.h" + +#if !defined (__ACE_INLINE__) +#include "ace/Timer_Queue_T.inl" +#endif /* __ACE_INLINE__ */ + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL + +// This fudge factor can be overriden for timers that need it, such as on +// Solaris, by defining the ACE_TIMER_SKEW symbol in the appropriate config +// header. +#if !defined (ACE_TIMER_SKEW) +# define ACE_TIMER_SKEW 0 +#endif /* ACE_TIMER_SKEW */ + +template <class TYPE, class FUNCTOR> ACE_INLINE +ACE_Timer_Queue_Upcall_Base<TYPE, FUNCTOR>::ACE_Timer_Queue_Upcall_Base (FUNCTOR * upcall_functor) + : ACE_Abstract_Timer_Queue<TYPE>() + , ACE_Copy_Disabled() + , upcall_functor_(upcall_functor) + , delete_upcall_functor_ (upcall_functor == 0) +{ + ACE_TRACE ("ACE_Timer_Queue_Upcall_Base::ACE_Timer_Queue_Upcall_Base"); + + if (upcall_functor != 0) + { + return; + } + + ACE_NEW (upcall_functor_, FUNCTOR); +} + +template <class TYPE, class FUNCTOR> ACE_INLINE +ACE_Timer_Queue_Upcall_Base<TYPE, FUNCTOR>::~ACE_Timer_Queue_Upcall_Base () +{ + ACE_TRACE ("ACE_Timer_Queue_Upcall_Base::~ACE_Timer_Queue_Upcall_Base"); + if (this->delete_upcall_functor_) + { + delete this->upcall_functor_; + } +} + +template <class TYPE, class FUNCTOR, class ACE_LOCK, typename TIME_POLICY> ACE_Time_Value +ACE_Timer_Queue_T<TYPE, FUNCTOR, ACE_LOCK, TIME_POLICY>::gettimeofday() +{ + return this->gettimeofday_static(); +} + +template <class TYPE, class FUNCTOR, class ACE_LOCK, typename TIME_POLICY> void +ACE_Timer_Queue_T<TYPE, FUNCTOR, ACE_LOCK, TIME_POLICY>::gettimeofday (ACE_Time_Value (*gettimeofday)(void)) +{ + this->time_policy_.set_gettimeofday (gettimeofday); +} + +template <class TYPE, class FUNCTOR, class ACE_LOCK, typename TIME_POLICY> ACE_Time_Value * +ACE_Timer_Queue_T<TYPE, FUNCTOR, ACE_LOCK, TIME_POLICY>::calculate_timeout (ACE_Time_Value *max_wait_time) +{ + ACE_TRACE ("ACE_Timer_Queue_T::calculate_timeout"); + ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, max_wait_time)); + + if (this->is_empty ()) + // Nothing on the Timer_Queue, so use whatever the caller gave us. + return max_wait_time; + else + { + ACE_Time_Value const cur_time = this->gettimeofday_static (); + + if (this->earliest_time () > cur_time) + { + // The earliest item on the Timer_Queue is still in the + // future. Therefore, use the smaller of (1) caller's wait + // time or (2) the delta time between now and the earliest + // time on the Timer_Queue. + + this->timeout_ = this->earliest_time () - cur_time; + if (max_wait_time == 0 || *max_wait_time > timeout_) + return &this->timeout_; + else + return max_wait_time; + } + else + { + // The earliest item on the Timer_Queue is now in the past. + // Therefore, we've got to "poll" the Reactor, i.e., it must + // just check the descriptors and then dispatch timers, etc. + this->timeout_ = ACE_Time_Value::zero; + return &this->timeout_; + } + } +} + +template <class TYPE, class FUNCTOR, class ACE_LOCK, typename TIME_POLICY> ACE_Time_Value * +ACE_Timer_Queue_T<TYPE, FUNCTOR, ACE_LOCK, TIME_POLICY>::calculate_timeout (ACE_Time_Value *max_wait_time, + ACE_Time_Value *the_timeout) +{ + ACE_TRACE ("ACE_Timer_Queue_T::calculate_timeout"); + + if (the_timeout == 0) + return 0; + + ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, max_wait_time)); + + if (this->is_empty ()) + { + // Nothing on the Timer_Queue, so use whatever the caller gave us. + if (max_wait_time) + *the_timeout = *max_wait_time; + else + return 0; + } + else + { + ACE_Time_Value cur_time = this->gettimeofday_static (); + + if (this->earliest_time () > cur_time) + { + // The earliest item on the Timer_Queue is still in the + // future. Therefore, use the smaller of (1) caller's wait + // time or (2) the delta time between now and the earliest + // time on the Timer_Queue. + + *the_timeout = this->earliest_time () - cur_time; + if (!(max_wait_time == 0 || *max_wait_time > *the_timeout)) + *the_timeout = *max_wait_time; + } + else + { + // The earliest item on the Timer_Queue is now in the past. + // Therefore, we've got to "poll" the Reactor, i.e., it must + // just check the descriptors and then dispatch timers, etc. + *the_timeout = ACE_Time_Value::zero; + } + } + return the_timeout; +} + +template <class TYPE, class FUNCTOR, class ACE_LOCK, typename TIME_POLICY> ACE_Time_Value +ACE_Timer_Queue_T<TYPE, FUNCTOR, ACE_LOCK, TIME_POLICY>::current_time() +{ + ACE_Time_Value tv = this->gettimeofday_static (); + tv += this->timer_skew(); + return tv; +} + +template <class TYPE, class FUNCTOR, class ACE_LOCK, typename TIME_POLICY> void +ACE_Timer_Queue_T<TYPE, FUNCTOR, ACE_LOCK, TIME_POLICY>::dump (void) const +{ +#if defined (ACE_HAS_DUMP) + ACE_TRACE ("ACE_Timer_Queue_T::dump"); + ACE_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + this->timeout_.dump (); + this->timer_skew_.dump (); + ACE_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +#endif /* ACE_HAS_DUMP */ +} + +template <class TYPE, class FUNCTOR, class ACE_LOCK, typename TIME_POLICY> +ACE_Timer_Queue_T<TYPE, FUNCTOR, ACE_LOCK, TIME_POLICY>::ACE_Timer_Queue_T (FUNCTOR *upcall_functor, + ACE_Free_List<ACE_Timer_Node_T <TYPE> > *freelist, + TIME_POLICY const & time_policy) + : ACE_Timer_Queue_Upcall_Base<TYPE,FUNCTOR>(upcall_functor), + time_policy_ (time_policy), + delete_free_list_ (freelist == 0), + timer_skew_ (0, ACE_TIMER_SKEW) +{ + ACE_TRACE ("ACE_Timer_Queue_T::ACE_Timer_Queue_T"); + + if (!freelist) + ACE_NEW (free_list_, + (ACE_Locked_Free_List<ACE_Timer_Node_T<TYPE>,ACE_Null_Mutex>)); + else + free_list_ = freelist; +} + +template <class TYPE, class FUNCTOR, class ACE_LOCK, typename TIME_POLICY> +ACE_Timer_Queue_T<TYPE, FUNCTOR, ACE_LOCK, TIME_POLICY>::~ACE_Timer_Queue_T (void) +{ + ACE_TRACE ("ACE_Timer_Queue_T::~ACE_Timer_Queue_T"); + + // Cleanup the free_list on the way out + if (this->delete_free_list_) + delete this->free_list_; +} + +template <class TYPE, class FUNCTOR, class ACE_LOCK, typename TIME_POLICY> ACE_Timer_Node_T<TYPE> * +ACE_Timer_Queue_T<TYPE, FUNCTOR, ACE_LOCK, TIME_POLICY>::alloc_node (void) +{ + return this->free_list_->remove (); +} + +template <class TYPE, class FUNCTOR, class ACE_LOCK, typename TIME_POLICY> void +ACE_Timer_Queue_T<TYPE, FUNCTOR, ACE_LOCK, TIME_POLICY>::free_node (ACE_Timer_Node_T<TYPE> *node) +{ + this->free_list_->add (node); +} + +template <class TYPE, class FUNCTOR, class ACE_LOCK, typename TIME_POLICY> ACE_LOCK & +ACE_Timer_Queue_T<TYPE, FUNCTOR, ACE_LOCK, TIME_POLICY>::mutex (void) +{ + return this->mutex_; +} + +template <class TYPE, class FUNCTOR, class ACE_LOCK, typename TIME_POLICY> long +ACE_Timer_Queue_T<TYPE, FUNCTOR, ACE_LOCK, TIME_POLICY>::schedule (const TYPE &type, + const void *act, + const ACE_Time_Value &future_time, + const ACE_Time_Value &interval) +{ + ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, -1)); + + // Schedule the timer. + long const result = + this->schedule_i (type, + act, + future_time, + interval); + + // Return on failure. + if (result == -1) + return result; + + // Inform upcall functor of successful registration. + this->upcall_functor ().registration (*this, + type, + act); + + // Return result; + return result; +} + +// Run the <handle_timeout> method for all Timers whose values are <= +// <cur_time>. +template <class TYPE, class FUNCTOR, class ACE_LOCK, typename TIME_POLICY> int +ACE_Timer_Queue_T<TYPE, FUNCTOR, ACE_LOCK, TIME_POLICY>::expire (const ACE_Time_Value &cur_time) +{ + ACE_TRACE ("ACE_Timer_Queue_T::expire"); + ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, -1)); + + // Keep looping while there are timers remaining and the earliest + // timer is <= the <cur_time> passed in to the method. + + if (this->is_empty ()) + return 0; + + int number_of_timers_expired = 0; + int result = 0; + + ACE_Timer_Node_Dispatch_Info_T<TYPE> info; + + while ((result = this->dispatch_info_i (cur_time, info)) != 0) + { + ACE_MT (ACE_Reverse_Lock<ACE_LOCK> rev_lk(this->mutex_)); + ACE_MT (ACE_GUARD_RETURN (ACE_Reverse_Lock<ACE_LOCK>, rmon, rev_lk, -1)); + + const void *upcall_act = 0; + + this->preinvoke (info, cur_time, upcall_act); + + this->upcall (info, cur_time); + + this->postinvoke (info, cur_time, upcall_act); + + ++number_of_timers_expired; + + } + + ACE_UNUSED_ARG (result); + return number_of_timers_expired; +} + +template <class TYPE, class FUNCTOR, class ACE_LOCK, typename TIME_POLICY> void +ACE_Timer_Queue_T<TYPE, FUNCTOR, ACE_LOCK, TIME_POLICY>::recompute_next_abs_interval_time + (ACE_Timer_Node_T<TYPE> *expired, + const ACE_Time_Value &cur_time) +{ + if ( expired->get_timer_value () <= cur_time ) + { + /* + * Somehow the current time is past when this time was + * supposed to expire (e.g., timer took too long, + * somebody changed system time, etc.). There used to + * be a simple loop here that skipped ahead one timer + * interval at a time, but that was horribly inefficient + * (an O(n) algorithm) when the timer duration was small + * relative to the amount of time skipped. + * + * So, we replace the loop with a simple computation, + * which also happens to be O(1). All times get + * normalized in the computation to microseconds. + * + * For reference, the loop looked like this: + * + * do + * expired->set_timer_value (expired->get_timer_value () + + * expired->get_interval ()); + * while (expired->get_timer_value () <= cur_time); + * + */ + + // Compute the duration of the timer's interval + ACE_UINT64 interval_usec; + expired->get_interval ().to_usec (interval_usec); + + // Compute the span between the current time and when + // the timer would have expired in the past (and + // normalize to microseconds). + ACE_Time_Value old_diff = cur_time - expired->get_timer_value (); + ACE_UINT64 old_diff_usec; + old_diff.to_usec (old_diff_usec); + + // Compute the delta time in the future when the timer + // should fire as if it had advanced incrementally. The + // modulo arithmetic accomodates the likely case that + // the current time doesn't fall precisely on a timer + // firing interval. + ACE_UINT64 new_timer_usec = + interval_usec - (old_diff_usec % interval_usec); + + // Compute the absolute time in the future when this + // interval timer should expire. + ACE_Time_Value new_timer_value + (cur_time.sec () + + static_cast<time_t>(new_timer_usec / ACE_ONE_SECOND_IN_USECS), + cur_time.usec () + + static_cast<suseconds_t>(new_timer_usec % ACE_ONE_SECOND_IN_USECS)); + + expired->set_timer_value (new_timer_value); + } +} + +template <class TYPE, class FUNCTOR, class ACE_LOCK, typename TIME_POLICY> int +ACE_Timer_Queue_T<TYPE, FUNCTOR, ACE_LOCK, TIME_POLICY>::expire_single ( + ACE_Command_Base & pre_dispatch_command) +{ + ACE_TRACE ("ACE_Timer_Queue_T::expire_single"); + ACE_Timer_Node_Dispatch_Info_T<TYPE> info; + ACE_Time_Value cur_time; + { + // Create a scope for the lock ... + ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, -1)); + + if (this->is_empty ()) + return 0; + + // Get the current time + cur_time = this->gettimeofday_static () + this->timer_skew (); + + // Look for a node in the timer queue whose timer <= the present + // time. + if (!this->dispatch_info_i (cur_time, info)) + { + return 0; + } + } + // We do not need the lock anymore, all these operations take place + // with local variables. + const void *upcall_act = 0; + + // Preinvoke (handles refcount if needed, etc.) + this->preinvoke (info, cur_time, upcall_act); + + // Release the token before expiration upcall. + pre_dispatch_command.execute(); + + // call the functor + this->upcall (info, cur_time); + + // Postinvoke (undo refcount if needed, etc.) + this->postinvoke (info, cur_time, upcall_act); + + // We have dispatched a timer + return 1; +} + +template <class TYPE, class FUNCTOR, class ACE_LOCK, typename TIME_POLICY> int +ACE_Timer_Queue_T<TYPE, FUNCTOR, ACE_LOCK, TIME_POLICY>::dispatch_info_i (const ACE_Time_Value &cur_time, + ACE_Timer_Node_Dispatch_Info_T<TYPE> &info) +{ + ACE_TRACE ("ACE_Timer_Queue_T::dispatch_info_i"); + + if (this->is_empty ()) + return 0; + + ACE_Timer_Node_T<TYPE> *expired = 0; + + if (this->earliest_time () <= cur_time) + { + expired = this->remove_first (); + + // Get the dispatch info + expired->get_dispatch_info (info); + + // 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". + this->recompute_next_abs_interval_time (expired, cur_time); + + // Since this is an interval timer, we need to reschedule + // it. + this->reschedule (expired); + } + else + { + // Call the factory method to free up the node. + this->free_node (expired); + } + + return 1; + } + + return 0; +} + +template <class TYPE, class FUNCTOR, class ACE_LOCK, typename TIME_POLICY> void +ACE_Timer_Queue_T<TYPE, FUNCTOR, ACE_LOCK, TIME_POLICY>::return_node (ACE_Timer_Node_T<TYPE> *node) +{ + ACE_MT (ACE_GUARD (ACE_LOCK, ace_mon, this->mutex_)); + this->free_node (node); +} + +ACE_END_VERSIONED_NAMESPACE_DECL + +#endif /* ACE_TIMER_QUEUE_T_CPP */ |