1 /*********************************************************************
2  *
3  * AUTHORIZATION TO USE AND DISTRIBUTE
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that:
7  *
8  * (1) source code distributions retain this paragraph in its entirety,
9  *
10  * (2) distributions including binary code include this paragraph in
11  *     its entirety in the documentation or other materials provided
12  *     with the distribution, and
13  *
14  * (3) all advertising materials mentioning features or use of this
15  *     software display the following acknowledgment:
16  *
17  *      "This product includes software written and developed
18  *       by Brian Adamson, Justin Dean, and Joe Macker of the Naval
19  *       Research Laboratory (NRL)."
20  *
21  *  The name of NRL, the name(s) of NRL  employee(s), or any entity
22  *  of the United States Government may not be used to endorse or
23  *  promote  products derived from this software, nor does the
24  *  inclusion of the NRL written and developed software  directly or
25  *  indirectly suggest NRL or United States  Government endorsement
26  *  of this product.
27  *
28  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
29  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
30  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
31  ********************************************************************/
32 
33 
34 #ifndef _PROTO_TIMER
35 #define _PROTO_TIMER
36 
37 #include "protoDebug.h"
38 #include "protoDefs.h"  // for ProtoSystemTime()
39 #include "protoTime.h"
40 #include <math.h>
41 
42 /**
43  * @class ProtoTimer
44  *
45  * @brief This is a generic timer class which will notify a ProtoTimer::Listener upon timeout.
46  *
47  * A list of ProtoTimers is managed by the ProtoTimerMgr class after they are "activated".
48  * The ProtoDispatcher's "main loop" calls ProtoTimerMgr::OnSystemTimeout at regular intervals
49  * to determine if any timer objects have timed out.  If so, the callback function
50  * associated with the ProtoTimer::Listener is invoked.
51  *
52  */
53 class ProtoTimer
54 {
55     friend class ProtoTimerMgr;
56 
57     public:
58         ProtoTimer();
59         ~ProtoTimer();
60 
61  		/**
62 		 * This function will define which method will be called upon a timer timing out
63 		 *
64 		 * TODO: Update text with new timer functionality
65 		 *
66 		 * Special notice should be taken of the boolean return value of this function.
67 		 * When returning true the number of repeats will be decremented and if below 0
68 		 * will deactivate the timer.  If in this function you are setting/changing intervals
69 		 * or repetions of the timer which invoked the call, the function should return false
70 		 * to avoid standard exit actions.
71 
72 		 * NOTE: For VC++ Debug builds, you _cannot_ use pre-compiled
73          * headers with this template code.  Also, "/ZI" or "/Z7" compile options
74          * must NOT be specified.  (or else VC++ experiences an "internal compiler error")
75 		 *
76 		 * @param theListener a pointer to the "listening" object
77 		 * @param timeoutHandler a pointer to the listener's callback function.
78 		 *
79 		 */
80 
81 		template <class listenerType>
SetListener(listenerType * theListener,bool (listenerType::* timeoutHandler)(ProtoTimer &))82         bool SetListener(listenerType* theListener, bool(listenerType::*timeoutHandler)(ProtoTimer&))
83         {
84             if (listener) delete listener;
85             listener = theListener ? new LISTENER_TYPE<listenerType>(theListener, timeoutHandler) : NULL;
86             return theListener ? (NULL != theListener) : true;
87         }
88 
89         /**
90          * This method sets the interval (in seconds) after which the
91          * timer will "fire" (i.e., the method specified by
92          * ProtoTimer::SetListener() is invoked).
93          *
94          * @param theInterval timer interval in seconds
95          */
SetInterval(double theInterval)96         void SetInterval(double theInterval)
97             {interval = (theInterval < 0.0) ? 0.0 : theInterval;}
GetInterval()98         double GetInterval() const {return interval;}
99 		/**
100         * Timer repetition (0 =  one shot, -1 = infinite repetition)
101         *
102 		* @param numRepeat number of timer repetitions
103 		*/
SetRepeat(int numRepeat)104 		void SetRepeat(int numRepeat)
105             {repeat = numRepeat;}
GetRepeat()106         int GetRepeat() const
107             {return repeat;}
108 
109 
110         /// Provided for "advanced" timer monitoring/control
ResetRepeat()111         void ResetRepeat()
112             {repeat_count = repeat;}
113        /// Provided for "advanced" timer monitoring/control
114 
DecrementRepeatCount()115 		void DecrementRepeatCount()
116             { if (repeat_count > 0) repeat_count--; }
117        /// Provided for "advanced" timer monitoring/control
118 
GetRepeatCount()119 		int GetRepeatCount() {return repeat_count;}
120        /// Provided for "advanced" timer monitoring/control
121 
122 		/// This must be used with wisdom!
SetRepeatCount(int repeatCount)123         void SetRepeatCount(int repeatCount)
124             {repeat_count = repeatCount;}
125 
126         //double GetTimeout() {return timeout;}
127 
128         // Activity status/control
129         /**
130 		* Returns true if timer is associated with a ProtoTimerMgr
131 		*/
IsActive()132 		bool IsActive() const {return (NULL != mgr);}
133         double GetTimeRemaining() const;
Reset()134         void Reset()
135         {
136             ResetRepeat();
137             if (IsActive()) Reschedule();
138         }
139         bool Reschedule();
140         void Scale(double factor);
141         void Deactivate();
142 
143         // Ancillary
SetUserData(const void * userData)144         void SetUserData(const void* userData) {user_data = userData;}
GetUserData()145         const void* GetUserData() {return user_data;}
146 
147         /**
148 		* Installer commands
149         */
150 		enum Command {INSTALL, MODIFY, REMOVE};
151 
152     private:
153 		/**
154 		* Invokes the "listener's" callback function.
155 		*
156 		* TODO: Update with new timer functionality.
157 		*
158 		* @retval Returns the result of the listener's callback function if
159 		* a listener exists for the timer.
160 		*/
DoTimeout()161         bool DoTimeout() {return listener ? listener->on_timeout(*this) : true;}
162 		/**
163 		* @class Listener
164 		*
165 		* @brief Listener base class
166 		*/
167         class Listener
168         {
169             public:
170 				/// virtual destructor
~Listener()171                 virtual ~Listener() {}
172                 /// virtual on_timeout function
173 				virtual bool on_timeout(ProtoTimer& theTimer) = 0;
174         };
175 		/**
176 		* @class LISTENER_TYPE
177 		*
178 		* @brief Template for Listener classes.
179 		*/
180         template <class listenerType>
181         class LISTENER_TYPE : public Listener
182         {
183             public:
184 				/**
185 				* Listener constructor
186 				*
187 				* @param theListener pointer to the "listening" object
188 				* @param timeoutHandler *pointer to the Listener's callback function.
189 				*/
LISTENER_TYPE(listenerType * theListener,bool (listenerType::* timeoutHandler)(ProtoTimer &))190                 LISTENER_TYPE(listenerType* theListener, bool(listenerType::*timeoutHandler)(ProtoTimer&))
191                     : listener(theListener), timeout_handler(timeoutHandler) {}
192 				/**
193 				* @retval Returns the result of the Listeners timeout handler.
194                 */
on_timeout(ProtoTimer & theTimer)195 				bool on_timeout(ProtoTimer& theTimer)
196                     {return (listener->*timeout_handler)(theTimer);}
197                 /**
198 				* Duplicates the Listener member and returns a pointer to the new
199 				* object.
200 				*/
duplicate()201 				Listener* duplicate()
202                     {return (static_cast<Listener*>(new LISTENER_TYPE<listenerType>(listener, timeout_handler)));}
203             private:
204                 listenerType* listener;
205                 bool (listenerType::*timeout_handler)(ProtoTimer&);
206         };
207         Listener*                   listener;
208 
209         double                      interval;
210         int                         repeat;
211         int                         repeat_count;
212 
213         const void*                 user_data;
214         ProtoTime                   timeout;
215         bool                        is_precise;
216         class ProtoTimerMgr*        mgr;
217         ProtoTimer*                 prev;
218         ProtoTimer*                 next;
219 };  // end class ProtoTimer
220 
221 /**
222  * @class ProtoTimerMgr
223  *
224  * @brief This class manages ProtoTimer instances when they are "activated".
225  * The ProtoDispatcher(see below) derives from this to manage ProtoTimers
226  * for an application. (The ProtoSimAgent base class contains a ProtoTimerMgr
227  * to similarly manage timers for a simulation instance).
228  */
229 class ProtoTimerMgr
230 {
231     friend class ProtoTimer;
232 
233     public:
234 		/// Default constructor
235         ProtoTimerMgr();
236 		/// Default destructor
237         virtual ~ProtoTimerMgr();
238 
239         // ProtoTimer activation/deactivation
240         virtual void ActivateTimer(ProtoTimer& theTimer);
241         virtual void DeactivateTimer(ProtoTimer& theTimer);
242 
243         /**
244 		* @retval Returns any time remaining for the active short timer or -1
245 		*/
GetTimeRemaining()246         double GetTimeRemaining() const
247             {return (short_head ? short_head->GetTimeRemaining() : -1.0);}
248 
249         /// Call this when the timer mgr's one-shot system timer fires
250         void OnSystemTimeout();
251 
252         /// Special call to allow early dispatch of timeouts
DoSystemTimeout()253         void DoSystemTimeout()
254         {
255             bool updateStatus = update_pending;
256             update_pending = true;
257             OnSystemTimeout();
258             update_pending = updateStatus;
259         }
260 
261         virtual void GetSystemTime(struct timeval& currentTime);
262 
263     protected:
264         /// System timer association/management definitions and routines
265         virtual bool UpdateSystemTimer(ProtoTimer::Command command,
266                                        double              delay) = 0;// {return true;}
267 
268     private:
269         // Methods used internally
270         void ReactivateTimer(ProtoTimer& theTimer, const ProtoTime& now);
271         void InsertLongTimer(ProtoTimer& theTimer);
272         bool InsertLongTimerReverse(ProtoTimer& theTimer);
273         void RemoveLongTimer(ProtoTimer& theTimer);
274         void InsertShortTimer(ProtoTimer& theTimer);
275         bool InsertShortTimerReverse(ProtoTimer& theTimer);
276         void RemoveShortTimer(ProtoTimer& theTimer);
277         void Update();
278 
GetNextTimeout(ProtoTime & nextTimeout)279         bool GetNextTimeout(ProtoTime& nextTimeout) const
280         {
281             if (NULL != short_head)
282             {
283                 nextTimeout = short_head->timeout;
284                 return true;
285             }
286             else
287             {
288                 return false;
289             }
290         }
GetPulseTime(ProtoTime & pulseTime)291         void GetPulseTime(ProtoTime& pulseTime) const
292         {
293             pulseTime = pulse_mark;
294             pulseTime += (1.0 - pulse_timer.GetTimeRemaining());
295         }
296         bool OnPulseTimeout(ProtoTimer& theTimer);
297 
298         static const double PRECISION_TIME_THRESHOLD;
299 
300 
GetCurrentSystemTime(struct timeval & currentTime)301         void GetCurrentSystemTime(struct timeval& currentTime)
302         {
303 #if  (defined(SIMULATE) && defined(NS2))
304             GetSystemTime(currentTime);
305 #else
306             ProtoSystemTime(currentTime);
307 #endif // if/else (SIMULATE && NS2)
308         }
309 
310         // We need this to support some of our SIM code so it can
311         // find its simulation context
GetCurrentProtoTime(ProtoTime & currentTime)312         void GetCurrentProtoTime(ProtoTime& currentTime)
313         {
314 #if  (defined(SIMULATE) && defined(NS2))
315            GetSystemTime(currentTime.AccessTimeVal());
316 #else
317            currentTime.GetCurrentTime();
318 #endif // if/else (SIMULATE && NS2)
319         }
320 
321 
322         // Member variables
323         bool            update_pending;
324         bool            timeout_scheduled;
325         ProtoTime       scheduled_timeout;
326         ProtoTimer      pulse_timer;  // one second pulse timer
327         ProtoTime       pulse_mark;
328         ProtoTimer*     long_head;
329         ProtoTimer*     long_tail;
330         ProtoTimer*     short_head;
331         ProtoTimer*     short_tail;
332 };  // end class ProtoTimerMgr
333 
334 #endif // _PROTO_TIMER
335