Timer.cc

Go to the documentation of this file.
00001 /*
00002  *    Copyright 2004-2006 Intel Corporation
00003  * 
00004  *    Licensed under the Apache License, Version 2.0 (the "License");
00005  *    you may not use this file except in compliance with the License.
00006  *    You may obtain a copy of the License at
00007  * 
00008  *        http://www.apache.org/licenses/LICENSE-2.0
00009  * 
00010  *    Unless required by applicable law or agreed to in writing, software
00011  *    distributed under the License is distributed on an "AS IS" BASIS,
00012  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  *    See the License for the specific language governing permissions and
00014  *    limitations under the License.
00015  */
00016 
00017 
00018 #include <stdio.h>
00019 #include <stdlib.h>
00020 #include <unistd.h>
00021 #include <errno.h>
00022 #include <sys/time.h>
00023 #include <sys/poll.h>
00024 
00025 #include "Timer.h"
00026 #include "io/IO.h"
00027 #include "../util/InitSequencer.h"
00028 
00029 namespace oasys {
00030 
00031 template <> TimerSystem* Singleton<TimerSystem>::instance_ = 0;
00032 
00033 //----------------------------------------------------------------------
00034 TimerSystem::TimerSystem()
00035     : Logger("TimerSystem", "/timer"),
00036       system_lock_(new SpinLock()),
00037       notifier_(logpath_),
00038       timers_()
00039 {
00040     memset(handlers_, 0, sizeof(handlers_));
00041     memset(signals_, 0, sizeof(signals_));
00042     sigfired_ = false;
00043 }
00044 
00045 //----------------------------------------------------------------------
00046 TimerSystem::~TimerSystem()
00047 {
00048     while (! timers_.empty()) {
00049         Timer* t = timers_.top();
00050         t->pending_ = false; // to avoid assertion
00051         timers_.pop();
00052         delete t;
00053     }
00054 }
00055 
00056 //----------------------------------------------------------------------
00057 void
00058 TimerSystem::schedule_at(struct timeval *when, Timer* timer)
00059 {
00060     ScopeLock l(system_lock_, "TimerSystem::schedule_at");
00061     
00062     struct timeval now;
00063     
00064     if (when == 0) {
00065         // special case a NULL timeval as an immediate timer
00066         log_debug("scheduling timer %p immediately", timer);
00067         
00068         timer->when_.tv_sec = 0;
00069         timer->when_.tv_usec = 0;
00070     } else {
00071         ::gettimeofday(&now, 0);
00072         log_debug("scheduling timer %p in %ld ms at %u:%u",
00073                   timer, TIMEVAL_DIFF_MSEC(*when, now),
00074                   (u_int)when->tv_sec, (u_int)when->tv_usec);
00075         
00076         timer->when_ = *when;
00077     }
00078     
00079     if (timer->pending_) {
00080         // XXX/demmer this could scan through the heap, find the right
00081         // timer and re-sort the heap, but it seems better to just
00082         // expose a new "reschedule" api call to make it explicit that
00083         // it's a different operation.
00084         PANIC("rescheduling timers not implemented");
00085     }
00086     
00087     timer->pending_ = 1;
00088     timer->cancelled_ = 0;
00089     timers_.push(timer);
00090 
00091     notifier_.signal();
00092 }
00093 
00094 //----------------------------------------------------------------------
00095 void
00096 TimerSystem::schedule_in(int milliseconds, Timer* timer)
00097 {
00098     struct timeval when;
00099     ::gettimeofday(&when, 0);
00100     when.tv_sec += milliseconds / 1000;
00101     when.tv_usec += (milliseconds % 1000) * 1000;
00102     while (when.tv_usec > 1000000) {
00103         when.tv_sec += 1;
00104         when.tv_usec -= 1000000;
00105     }
00106     
00107     return schedule_at(&when, timer);
00108 }
00109 
00110 //----------------------------------------------------------------------
00111 void
00112 TimerSystem::schedule_immediate(Timer* timer)
00113 {
00114     return schedule_at(0, timer);
00115 }
00116 
00117 //----------------------------------------------------------------------
00118 bool
00119 TimerSystem::cancel(Timer* timer)
00120 {
00121     ScopeLock l(system_lock_, "TimerSystem::cancel");
00122 
00123     // There's no good way to get a timer out of a heap, so we let it
00124     // stay in there and mark it as cancelled so when it bubbles to
00125     // the top, we don't bother with it. This makes rescheduling a
00126     // single timer instance tricky...
00127     if (timer->pending_) {
00128         timer->cancelled_ = true;
00129         return true;
00130     }
00131     
00132     return false;
00133 }
00134 
00135 //----------------------------------------------------------------------
00136 void
00137 TimerSystem::post_signal(int sig)
00138 {
00139     TimerSystem* _this = TimerSystem::instance();
00140 
00141     _this->sigfired_ = true;
00142     _this->signals_[sig] = true;
00143     
00144     _this->notifier_.signal();
00145 }
00146 
00147 //----------------------------------------------------------------------
00148 void
00149 TimerSystem::add_sighandler(int sig, sighandlerfn_t* handler)
00150 {
00151     log_debug("adding signal handler %p for signal %d", handler, sig);
00152     handlers_[sig] = handler;
00153     signal(sig, post_signal);
00154 }
00155 
00156 //----------------------------------------------------------------------
00157 void
00158 TimerSystem::pop_timer(const struct timeval& now)
00159 {
00160     ASSERT(system_lock_->is_locked_by_me());
00161     
00162     Timer* next_timer = timers_.top();
00163     timers_.pop();
00164 
00165     // clear the pending bit since it could get rescheduled 
00166     ASSERT(next_timer->pending_);
00167     next_timer->pending_ = 0;
00168 
00169     int late = TIMEVAL_DIFF_MSEC(now, next_timer->when());
00170     if (late > 2000) {
00171         log_warn("timer thread running slow -- timer is %d msecs late", late);
00172     }
00173         
00174     if (! next_timer->cancelled_) {
00175         log_debug("popping timer %p at %u.%u", next_timer,
00176                   (u_int)now.tv_sec, (u_int)now.tv_usec);
00177         next_timer->timeout(now);
00178     } else {
00179         log_debug("popping cancelled timer %p at %u.%u", next_timer,
00180                   (u_int)now.tv_sec, (u_int)now.tv_usec);
00181         next_timer->cancelled_ = 0;
00182         
00183         if (next_timer->cancel_flags_ == Timer::DELETE_ON_CANCEL) {
00184             log_debug("deleting cancelled timer %p at %u.%u", next_timer,
00185                       (u_int)now.tv_sec, (u_int)now.tv_usec);
00186             delete next_timer;
00187         }
00188     }
00189 }
00190 
00191 //----------------------------------------------------------------------
00192 void
00193 TimerSystem::handle_signals()
00194 {        
00195     // KNOWN ISSUE: if a second signal is received before the first is
00196     // handled it is ignored, i.e. sending signal gives at-least-once
00197     // semantics, not exactly-once semantics
00198     if (sigfired_) {
00199         sigfired_ = 0;
00200         
00201         log_debug("sigfired_ set, calling registered handlers");
00202         for (int i = 0; i < NSIG; ++i) {
00203             if (signals_[i]) {
00204                 handlers_[i](i);
00205                 signals_[i] = 0;
00206             }
00207         }
00208     }
00209 }
00210 
00211 //----------------------------------------------------------------------
00212 int
00213 TimerSystem::run_expired_timers()
00214 {
00215     ScopeLock l(system_lock_, "TimerSystem::run_expired_timers");
00216     
00217     handle_signals();
00218     
00219     struct timeval now;    
00220     while (! timers_.empty()) 
00221     {
00222         if (::gettimeofday(&now, 0) != 0) {
00223             PANIC("gettimeofday");
00224         }
00225 
00226         Timer* next_timer = timers_.top();
00227         if (TIMEVAL_LT(now, next_timer->when_)) {
00228             int diff_ms = TIMEVAL_DIFF_MSEC(next_timer->when_, now);
00229             ASSERT(diff_ms >= 0);
00230             
00231             // there's a chance that we're within a millisecond of the
00232             // time to pop, but still not at the right time. in this
00233             // case case we don't return 0, but fall through to pop
00234             // the timer
00235             if (diff_ms != 0) {
00236                 log_debug("new timeout %d", diff_ms);
00237                 return diff_ms;
00238             } else {
00239                 log_debug("sub-millisecond difference found, falling through");
00240             }
00241         }
00242         pop_timer(now);
00243     }
00244 
00245     return -1;
00246 }
00247 
00248 //----------------------------------------------------------------------
00249 void
00250 TimerThread::run()
00251 {
00252     TimerSystem* sys = TimerSystem::instance();
00253     while (true) 
00254     {
00255         int timeout = sys->run_expired_timers();
00256         sys->notifier()->wait(NULL, timeout);
00257     }
00258 
00259     NOTREACHED;
00260 }
00261 
00262 //----------------------------------------------------------------------
00263 void
00264 TimerThread::init()
00265 {
00266     ASSERT(instance_ == NULL);
00267     instance_ = new TimerThread();
00268     instance_->start();
00269 }
00270 
00271 TimerThread* TimerThread::instance_ = NULL;
00272 
00273 } // namespace oasys

Generated on Thu Jun 7 16:56:53 2007 for DTN Reference Implementation by  doxygen 1.5.1