520500d6e4d4763255ba804f21ba8e4c2e12f248
[senf.git] / Scheduler / TimerDispatcher.cc
1 // $Id$
2 //
3 // Copyright (C) 2008 
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 //     Stefan Bund <g0dil@berlios.de>
7 //
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the
20 // Free Software Foundation, Inc.,
21 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
23 /** \file
24     \brief TimerDispatcher non-inline non-template implementation */
25
26 #include "TimerDispatcher.hh"
27 //#include "TimerDispatcher.ih"
28
29 // Custom includes
30
31 //#include "TimerDispatcher.mpp"
32 #define prefix_
33 ///////////////////////////////cc.p////////////////////////////////////////
34
35 unsigned senf::scheduler::TimerDispatcher::useCount_ (0);
36
37 prefix_ senf::scheduler::TimerDispatcher::TimerDispatcher(FdManager & manager,
38                                                           FIFORunner & runner)
39     : manager_ (manager), runner_ (runner), lastId_ (0), blocked_ (true)
40 {
41     if (pipe(timerPipe_) < 0)
42         SENF_THROW_SYSTEM_EXCEPTION("pipe()");
43     manager_.set(timerPipe_[0], FdManager::EV_READ, this);
44
45     sigemptyset(&sigSet_);
46     sigaddset(&sigSet_, SIGALRM);
47     sigprocmask(SIG_BLOCK, &sigSet_, 0);
48
49     if (useCount_ == 0) {
50         struct sigaction act;
51         act.sa_sigaction = &sigHandler;
52         act.sa_mask = sigSet_;
53         act.sa_flags = SA_SIGINFO | SA_RESTART;
54         if (sigaction(SIGALRM, &act, 0) < 0)
55             SENF_THROW_SYSTEM_EXCEPTION("sigaction()");
56     }
57
58     struct sigevent ev;
59     ::memset(&ev, 0, sizeof(ev));
60     ev.sigev_notify = SIGEV_SIGNAL;
61     ev.sigev_signo = SIGALRM;
62     ev.sigev_value.sival_ptr = this;
63     if (timer_create(CLOCK_MONOTONIC, &ev, &timerId_) < 0)
64         SENF_THROW_SYSTEM_EXCEPTION("timer_create()");
65     
66     ++ useCount_;
67 }
68
69 prefix_ senf::scheduler::TimerDispatcher::~TimerDispatcher()
70 {
71     -- useCount_;
72
73     TimerMap::iterator i (timers_.begin());
74     TimerMap::iterator const i_end (timers_.end());
75     for (; i != i_end; ++i)
76         runner_.dequeue(&(i->second));
77
78     timer_delete(timerId_);
79     if (useCount_ == 0)
80         ::signal(SIGALRM, SIG_IGN);
81     sigprocmask(SIG_UNBLOCK, &sigSet_, 0);
82     manager_.remove(timerPipe_[0]);
83     close(timerPipe_[0]);
84     close(timerPipe_[1]);
85 }
86
87 prefix_ senf::scheduler::TimerDispatcher::timer_id
88 senf::scheduler::TimerDispatcher::add(std::string const & name,
89                                       ClockService::clock_type timeout, Callback const & cb)
90 {
91     while (timerIdIndex_.find(++lastId_) != timerIdIndex_.end()) ;
92     TimerMap::iterator i (
93         timers_.insert(std::make_pair(timeout, TimerEvent(lastId_, cb, *this, name))));
94     timerIdIndex_.insert(std::make_pair(lastId_, i));
95     runner_.enqueue(&(i->second));
96     if (! blocked_)
97         reschedule();
98     return lastId_;
99 }
100
101 prefix_ void senf::scheduler::TimerDispatcher::remove(timer_id id)
102 {
103     TimerIdMap::iterator i (timerIdIndex_.find(id));
104     if (i == timerIdIndex_.end())
105         return;
106     runner_.dequeue(&(i->second->second));
107     timers_.erase(i->second);
108     timerIdIndex_.erase(i);
109     if (! blocked_)
110         reschedule();
111 }
112
113 prefix_ void senf::scheduler::TimerDispatcher::blockSignals()
114 {
115     if (blocked_) 
116         return;
117     sigprocmask(SIG_BLOCK, &sigSet_, 0);
118     blocked_ = true;
119 }
120
121 prefix_ void senf::scheduler::TimerDispatcher::unblockSignals()
122 {
123     if (! blocked_)
124         return;
125     reschedule();
126     sigprocmask(SIG_UNBLOCK, &sigSet_, 0);
127     blocked_ = false;
128 }
129
130 prefix_ void senf::scheduler::TimerDispatcher::signal(int events)
131 {
132     siginfo_t info;
133     if (read(timerPipe_[0], &info, sizeof(info)) < int(sizeof(info)))
134         return;
135
136     TimerMap::iterator i (timers_.begin());
137     TimerMap::iterator const i_end (timers_.end());
138     ClockService::clock_type now (manager_.eventTime());
139     for (; i != i_end && i->first <= now ; ++i)
140         i->second.runnable = true;
141 }
142
143 prefix_ void senf::scheduler::TimerDispatcher::sigHandler(int signal, ::siginfo_t * siginfo,
144                                                           void *)
145 {
146     // The manpage says, si_signo is unused in linux so we set it here
147     siginfo->si_signo = signal; 
148     // We can't do much on error anyway so we ignore errors here
149     if (siginfo->si_value.sival_ptr == 0)
150         return;
151     write(static_cast<TimerDispatcher*>(siginfo->si_value.sival_ptr)->timerPipe_[1], 
152           siginfo, sizeof(*siginfo));
153 }
154
155 prefix_ void senf::scheduler::TimerDispatcher::reschedule()
156 {
157     struct itimerspec timer;
158     memset(&timer, 0, sizeof(timer));
159     timer.it_interval.tv_sec = 0;
160     timer.it_interval.tv_nsec = 0;
161     if (timers_.empty()) {
162         SENF_LOG( (senf::log::VERBOSE)("Timer disabled") );
163         timer.it_value.tv_sec = 0;
164         timer.it_value.tv_nsec = 0;
165     }
166     else {
167         ClockService::clock_type next (timers_.begin()->first);
168         if (next <= 0)
169             next = 1;
170         timer.it_value.tv_sec = ClockService::in_seconds(next);
171         timer.it_value.tv_nsec = ClockService::in_nanoseconds(
172             next - ClockService::seconds(timer.it_value.tv_sec));
173         SENF_LOG( (senf::log::VERBOSE)("Next timeout scheduled @" << timer.it_value.tv_sec << "." 
174                    << std::setw(9) << std::setfill('0') << timer.it_value.tv_nsec) );
175     }
176     if (timer_settime(timerId_, TIMER_ABSTIME, &timer, 0)<0)
177         SENF_THROW_SYSTEM_EXCEPTION("timer_settime()");
178 }
179
180 ///////////////////////////////////////////////////////////////////////////
181 // senf::scheduler::TimerDispatcher::TimerEvent
182
183 prefix_ void senf::scheduler::TimerDispatcher::TimerEvent::run()
184 {
185     Callback savedCb (cb);
186     dispatcher.remove(id);
187     // The member is now running WITHOUT AN OBJECT ... that has been destroyed above !!!!!!  On the
188     // other hand, if we do things the other way round, we have no idea, whether the callback might
189     // explicitly remove us and we have the same problem then ...
190     savedCb();
191 }
192
193 ///////////////////////////////cc.e////////////////////////////////////////
194 #undef prefix_
195 //#include "TimerDispatcher.mpp"
196
197 \f
198 // Local Variables:
199 // mode: c++
200 // fill-column: 100
201 // comment-column: 40
202 // c-file-style: "senf"
203 // indent-tabs-mode: nil
204 // ispell-local-dictionary: "american"
205 // compile-command: "scons -u test"
206 // End: