446859c74ee8695b54f4320cc8e26b98f23bed16
[senf.git] / Scheduler / FIFORunner.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 FIFORunner non-inline non-template implementation */
25
26 #include "FIFORunner.hh"
27 //#include "FIFORunner.ih"
28
29 // Custom includes
30 #include <signal.h>
31 #include <time.h>
32 #include "../Utils/Exception.hh"
33 #include "../Utils/senfassert.hh"
34
35 //#include "FIFORunner.mpp"
36 #define prefix_
37 ///////////////////////////////cc.p////////////////////////////////////////
38
39 prefix_ senf::scheduler::detail::FIFORunner::FIFORunner()
40     : tasks_ (), next_ (tasks_.end()), watchdogMs_ (1000), watchdogCount_(0), hangCount_ (0)
41 {
42     struct sigevent ev;
43     ::memset(&ev, 0, sizeof(ev));
44     ev.sigev_notify = SIGEV_SIGNAL;
45     ev.sigev_signo = SIGURG;
46     ev.sigev_value.sival_ptr = this;
47     if (timer_create(CLOCK_MONOTONIC, &ev, &watchdogId_) < 0)
48         SENF_THROW_SYSTEM_EXCEPTION("timer_create()");
49
50     struct sigaction sa;
51     ::memset(&sa, 0, sizeof(sa));
52     sa.sa_sigaction = &watchdog;
53     sa.sa_flags = SA_SIGINFO;
54     if (sigaction(SIGURG, &sa, 0) < 0)
55         SENF_THROW_SYSTEM_EXCEPTION("sigaction()");
56
57     sigset_t mask;
58     sigemptyset(&mask);
59     sigaddset(&mask, SIGURG);
60     if (sigprocmask(SIG_UNBLOCK, &mask, 0) < 0)
61         SENF_THROW_SYSTEM_EXCEPTION("sigprocmask()");
62
63     tasks_.push_back(highPriorityEnd_);
64     tasks_.push_back(normalPriorityEnd_);
65 }
66
67 prefix_ senf::scheduler::detail::FIFORunner::~FIFORunner()
68 {
69     timer_delete(watchdogId_);
70     signal(SIGURG, SIG_DFL);
71 }
72
73 // At the moment, the FIFORunner is not very efficient with many non-runnable tasks since the
74 // complete list of tasks is traversed on each run().
75 //
76 // To optimize this, we woould need a way to find the relative ordering of two tasks in O(1) (at the
77 // moment, this is an O(N) operation by traversing the list).
78 //
79 // One idea is, to give each task an 'order' value. Whenever a task is added at the end, it's order
80 // value is set to the order value of the last task + 1. Whenever the order value such added exceeds
81 // some threshold (e.g. 2^31 -1 or some such), the task list is traversed from beginning to end to
82 // assign new consecutive order values. This O(N) operation is so seldom, that it is amortized over
83 // a very long time.
84 //
85 // With this value at hand, we can do several optimizations: One idea would be the following: The
86 // runnable set always has two types of tasks: There are tasks, which are heavily active and are
87 // signaled constantly and other tasks which lie dormant most of the time. Those dormant tasks will
88 // end up at the beginning of the task queue.
89 //
90 // With the above defined 'ordering' field available, we can manage an iterator pointing to the
91 // first and the last runnable task. This will often help a lot since the group of runnable tasks
92 // will mostly be localized to the end of the queue. only occasionally one of the dormant tasks will
93 // be runnable. This additional traversal time will be amortized over a larger time.
94
95 prefix_ void senf::scheduler::detail::FIFORunner::dequeue(TaskInfo * task)
96 {
97     TaskList::iterator i (TaskList::current(*task));
98     if (next_ == i)
99         ++next_;
100     tasks_.erase(i);
101 }
102
103 prefix_ void senf::scheduler::detail::FIFORunner::run()
104 {
105     struct itimerspec timer;
106     timer.it_interval.tv_sec = watchdogMs_ / 1000;
107     timer.it_interval.tv_nsec = (watchdogMs_ % 1000) * 1000000ul;
108     timer.it_value.tv_sec = timer.it_interval.tv_sec;
109     timer.it_value.tv_nsec = timer.it_interval.tv_nsec;
110
111     if (timer_settime(watchdogId_, 0, &timer, 0) < 0)
112         SENF_THROW_SYSTEM_EXCEPTION("timer_settime()");
113
114     timer.it_interval.tv_sec = 0;
115     timer.it_interval.tv_nsec = 0;
116     timer.it_value.tv_sec = 0;
117     timer.it_value.tv_nsec = 0;
118     
119     try {
120         TaskList::iterator f (tasks_.begin());
121         TaskList::iterator l (TaskList::current(highPriorityEnd_));
122         run(f, l);
123
124         f = l; ++f;
125         l = TaskList::current(normalPriorityEnd_);
126         run(f, l);
127         
128         f = l; ++f;
129         l = tasks_.end();
130         run(f, l);
131     }
132     catch(...) {
133         timer_settime(watchdogId_, 0, &timer, 0);
134         throw;
135     }
136
137     if (timer_settime(watchdogId_, 0, &timer, 0) < 0)
138         SENF_THROW_SYSTEM_EXCEPTION("timer_settime()");
139 }
140
141
142 prefix_ void senf::scheduler::detail::FIFORunner::run(TaskList::iterator f, TaskList::iterator l)
143 {
144     if (f == l)
145         // We'll have problems inserting NullTask between f and l below, so just explicitly bail out
146         return;
147
148     // This algorithm is carefully adjusted to make it work even when arbitrary tasks are removed
149     // from the queue
150     // - Before we begin, we add a NullTask to the queue. The only purpose of this node is, to mark
151     //   the current end of the queue. The iterator to this node becomes the end iterator of the
152     //   range to process
153     // - We update the TaskInfo and move it to the next queue Element before calling the callback so
154     //   we don't access the TaskInfo if it is removed while the callback is running
155     // - We keep the next to-be-processed node in a class variable which is checked and updated
156     //   whenever a node is removed.
157
158     NullTask null;
159     tasks_.insert(l, null);
160     TaskList::iterator end (TaskList::current(null));
161     next_ = f;
162     try {
163         while (next_ != end) {
164             TaskInfo & task (*next_);
165             if (task.runnable_) {
166                 task.runnable_ = false;
167                 runningName_ = task.name();
168 #           ifdef SENF_DEBUG
169                 runningBacktrace_ = task.backtrace_;
170 #           endif
171                 TaskList::iterator i (next_);
172                 ++ next_;
173                 tasks_.splice(l, tasks_, i);
174                 watchdogCount_ = 1;
175                 task.run();
176             }
177             else
178                 ++ next_;
179         }
180     }
181     catch (...) {
182         watchdogCount_ = 0;
183         next_ = l;
184         throw;
185     }
186     watchdogCount_ = 0;
187     next_ = l;
188 }
189
190 prefix_ senf::scheduler::detail::FIFORunner::TaskList::iterator
191 senf::scheduler::detail::FIFORunner::priorityEnd(TaskInfo::Priority p)
192 {
193     switch (p) {
194     case senf::scheduler::detail::FIFORunner::TaskInfo::PRIORITY_LOW : 
195         return tasks_.end();
196     case senf::scheduler::detail::FIFORunner::TaskInfo::PRIORITY_NORMAL : 
197         return TaskList::current(normalPriorityEnd_);
198     case senf::scheduler::detail::FIFORunner::TaskInfo::PRIORITY_HIGH : 
199         return TaskList::current(highPriorityEnd_);
200     }
201     return tasks_.begin();
202 }
203
204 prefix_ void senf::scheduler::detail::FIFORunner::watchdog(int, siginfo_t * si, void *)
205 {
206     FIFORunner & runner (*static_cast<FIFORunner *>(si->si_value.sival_ptr));
207     if (runner.watchdogCount_ > 0) {
208         ++ runner.watchdogCount_;
209         if (runner.watchdogCount_ > 2) {
210             ++ runner.hangCount_;
211             write(1, "\n\n*** Scheduler task hanging: ", 30);
212             write(1, runner.runningName_.c_str(), runner.runningName_.size());
213             write(1, "\n", 1);
214 #ifdef SENF_DEBUG
215             write(1, "Task was initialized at\n", 24);
216             write(1, runner.runningBacktrace_.c_str(), runner.runningBacktrace_.size());
217 #endif
218             write(1, "\n", 1);
219         }
220     }
221 }
222
223 ///////////////////////////////cc.e////////////////////////////////////////
224 #undef prefix_
225 //#include "FIFORunner.mpp"
226
227 \f
228 // Local Variables:
229 // mode: c++
230 // fill-column: 100
231 // comment-column: 40
232 // c-file-style: "senf"
233 // indent-tabs-mode: nil
234 // ispell-local-dictionary: "american"
235 // compile-command: "scons -u test"
236 // End: